【05】循序渐进学 docker:系统资源和网络
写在前面的话
在上一篇学习 Dockerfile 的时候其实还有几个相当重要得关键中没有谈到,但没关系,在后面的内容会单独提出来一个一个的学习。这里就先谈谈关于资源的控制个容器的网络~
资源限制
其实对于容器的资源使用,无外乎在意 3 个方面,内存,CPU,IO,但 IO 很多时候是没法避免的,所以更多的还是通过内存和 CPU来进行资源限制。
【1】限制内存:
docker run --memory=200M centos
--memory:配置 200M,但这并不意味容器最大内存是 200M,在没额外对 Swap 进行限制时,Swap 和内存对等,所以是 400M。
--memory-swap:限制 Swap。
【2】CPU 限制:
docker run --cpu-shares=10 centos
限制 CPU,并不是限制使用几核,而是权重占比。
假如一个容器 10,一个 20,另外一个 10,那 CPU 占用实际是 25%,50% 和 25%。
关于资源限制的其他参数可以通过 --help 看到,这里也就谈谈使用较多的,其他都相对于比较冷门,需要的时候再去查。
Network namespace
在学习容器网络之前,可以先学习一下 Linux 网络名称空间,有助于更好的理解容器网络~
【1】先建立两个 network namespace,并启动内部的网卡:
# 查看本机的 network namespace ip netns list # 添加两个 network namespace ip netns add ns-1 ip netns add ns-2 # 查看 network namespace 的网卡信息 ip netns exec ns-1 ip a ip netns exec ns-2 ip a # 启动 lo 网卡 ip netns exec ns-1 ip link set dev lo up ip netns exec ns-2 ip link set dev lo up # 再度查看网卡信息 ip netns exec ns-1 ip a ip netns exec ns-2 ip a
结果如图:
此时,lo 网卡启动状态显示 UNKNOWN 状态,情景就好像一根网线只插了一头。
【2】新建两个网卡,并将它们各自分到 network namespace:
# 新建两个网卡 ip link add veth-1 type veth peer name veth-2 # 查看网卡连接信息 ip link # 将网卡分别分配到不同的 network namespace ip link set veth-1 netns ns-1 ip link set veth-2 netns ns-2 # 再度查看网卡信息 ip link # 查看 network namespace 中的网卡信息 ip netns exec ns-1 ip link ip netns exec ns-2 ip link
结果如图:
在网卡刚创建时,属于本机。但当将网卡分配到别的命名空间后,本机就不存在了。而是存在于各自的命名空间。
这就好比新加了一条网线,本来两头都是插在本机的,但现在将它拔了出来,一头插在了 ns-1 的网络命名空间上,另一头插在了 ns-2 上面。
【3】将两个网卡都启动起来并配置上 IP,测试连通信:
# 给 network namespace 中的网卡配置 IP 地址 ip netns exec ns-1 ip addr add 192.168.1.1/24 dev veth-1 ip netns exec ns-2 ip addr add 192.168.1.2/24 dev veth-2 # 启动 network namespace 中的网卡 ip netns exec ns-1 ip link set dev veth-1 up ip netns exec ns-2 ip link set dev veth-2 up # 查看配置后的网卡信息 ip netns exec ns-1 ip a ip netns exec ns-2 ip a # 测试连通信性 ip netns exec ns-1 ping 192.168.1.2
结果如图:
可以看到两个命名空间已经可以独立的通信了,但本机其实和这个两个网卡是不通的。
这样就产生了一个存在本机,但是又独立于本机,能够互相通信的网络,最终达到的效果类似下图:
以上的内容完全是作为帮助理解,只需要知道有这么一回事就行。
带着这个基础,再来看容器网络。
容器网络
先看容器到底是怎么通信的:
和前面的图很像。但相比于那个图,本机多了一个 docker0 网卡,简单的可以把这个 docker0 假设成一个交换机,一根网线接到互联网,然后我们的电脑都可以用网线连接上去,不仅内网互通,还能上网。
这样的技术放在容器的实现里面就是通过本机的防火墙的 NAT 转发实现。
docker 自带了 3 种网络:
docker network ls
结果如图:
在三种网络中,默认是 bridge(桥接),其他两种用的相对较少。
host:容器和本机一起共享端口,共享一个 IP。
none:“孤儿网络”,建立一个容器,完完全全独立。
当然,除了这 3 种网络,还可以自己建立一个其他名称的网络,但是在接触 overlay 网络之前,你的网络的 DRIVER 终究逃不脱这 3 种。
比如新建一个网络:
docker network create -d bridge my-network
结果如图:
通过 --network 参数将新建立的容器指定使用这个网络~
docker run --name b3 --network my-network -it -d busybox /bin/sh -c "while true; do sleep 300; done"
后面执行个死循环的目的是为了容器不自动退出。
docker container inspect b3
查看容器的详细信息,里面的 Networks 项变成了 my-network,默认网段也变了。
再新建一个容器,让它属于默认的网络:
docker run --name b4 -it -d busybox /bin/sh -c "while true; do sleep 300; done"
此时,用新的网络来测试和默认的网络的连通性:
结果:无法通信。
查看某个网络的接入情况需要借助 brctl 了,默认不存在,需要自己安装:
yum install bridge-utils -y
查看:
brctl show
结果如下:
有两个桥接网络,每个都连接了一个网卡,docker0 我们知道,但是 br-xxx 是啥?
其实,br-xxx 就是手动创建的 my-network,xxx 部分就是 network 的 ID,可以查看:
docker network ls
查看本机目前的网络情况:
结合上面的图,30 和 32 网卡其实是连接到 docker0 和 br-xxx 的。
这时候的 docker0 和 br-xxx 就相当于两个网络接口,30 和 32 网卡就像两条网线,一头已经插在了这两个接口上。那另外一头,自然就插在了容器里面,这样就实现了容器与当前主机的通信了。
再配合防火墙转发规则,那么就实现了容器的上网。
查看指定网络到底有哪些容器连接到上面:
docker network inspect my-network
结果如图:
但现在有一个需求来了:
有一个机器,需要以容器的形式运行一个 MySQL 数据库,有一个同样以容器运行的程序需连接到这个数据库,但是由于容器被分配到的 IP 地址是自动分配的,下一次重新构建可能就变了,那怎么保证下次不需要修改配置依然能够连接到数据库呢?
--link:将一个容器连接到另外一个容器。
先一次性删除掉之前的建立的所有容器,在不停止容器的前提下依然删除容器:-f 参数,强制执行:
docker container rm -f $(docker ps -qa)
【1】新建两个容器,都隶属相同的默认网络:
docker run --name b1 -it -d busybox /bin/sh -c "while true; do sleep 300; done" docker run --name b2 -it -d busybox /bin/sh -c "while true; do sleep 300; done"
【2】通过容器名称测试容器连通性:
docker exec -it b1 ping b2
结果如图:
结果:通过容器名称是无法通信。
【3】新建一个容器,连接到 b1:
docker run --name b3 --link b1 -it -d busybox /bin/sh -c "while true; do sleep 300; done"
再度测试和 b1 与 b2 的连通性:
docker exec -it b3 ping b1 docker exec -it b3 ping b2
结果如图:
结果:b3 在使用 --link 连接到 b1 后可以通过容器名称直接进行通信,但反过来不行。
【4】再度建立一个容器,link 到 b1:
docker run --name b4 --link b1 -it -d busybox /bin/sh -c "while true; do sleep 300; done"
测试 b4 和 b1,b3 的连通性:
docker exec -it b4 ping b1 docker exec -it b4 ping b3
结果如图:
结果:单独 link 的容器正向可以通信,反过来不行。
由此,可以暂时得出这样一个结论:
在默认的 bridge 网络中,可以使用 --link 参数实现容器通过容器名称访问容器,但是这个参数的作用是单向的。
上面的方法实现了简单的需求,但是每次都需要指定,岂不麻烦?再次删除掉所有容器,用自己创建网络测试:
【1】创建两个容器,让它们属于我们自己建立的网络:
docker container rm -f $(docker ps -qa) docker run --name b1 --network my-network -it -d busybox /bin/sh -c "while true; do sleep 300; done" docker run --name b2 --network my-network -it -d busybox /bin/sh -c "while true; do sleep 300; done"
测试网络的连通性:
docker exec -it b1 ping b2 docker exec -it b2 ping b1
结果如图:
结果:自动实现互相通过容器名称通信。
是不是真香?再新建一个容器试试?
docker run --name b3 --network my-network -it -d busybox /bin/sh -c "while true; do sleep 300; done"
测试连通性:
docker exec -it b3 ping b1 docker exec -it b3 ping b2
结果如图:
完全没问题,由此可以得出另外一条结论:
在自建的网络中容器,默认会自动互相 link,由此能够实现直接通过容器名称进行互相通信。
这就很好解决了上面 IP 会变但不想修改配置照样连上数据库,直接使用容器名称代替 IP 就行。
结论:在容器中,需要摒弃之前在虚拟机应用时代关于 IP 的重视程度,在容器应用中,IP 几乎可以不需要理会。
端口映射
之前 Flask 的例子已经发现了,当容器中启动某个端口的服务,宿主机以外的其他用户是不能访问的。
那么如何才能访问呢?这就得依靠端口映射,将 docker 容器的端口转换成宿主机的某个端口。
其实就是 2 个参数,-p 和 -P:
-p:将宿主机的某个端口映射到容器中的某个端口。
-P:将容器中的所有端口映射成宿主机 32768 之上的随机端口。
对之前的 Flask 项目做改进:app.py 如下
from flask import Flask from redis import Redis import os import socket app = Flask(__name__) redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379) @app.route('/') def hello(): redis.incr('hits') return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'),socket.gethostname()) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)
项目是这样的,用户在每请求一次,redis 给这个值增加 1,进而实现页面显示的数字加 1 的效果:
mkdir flask-redis cd flask-redis/ vim app.py
添加 Dockerfile:
FROM python:2.7 LABEL maintainer="Dylan <1214966109@qq.com>" COPY app.py /app/ RUN pip install flask && pip install redis WORKDIR /app/ EXPOSE 5000 CMD ["python", "app.py"]
构建镜像:
docker build -t dylan/flask-demo:v1.0 .
再运行一个 redis 容器,和之前 MySQL 问题类似,这里也准备使用容器名称来连接,因为只连接了一个服务,直接使用 --link 就行~
【1】运行 redis 容器:
docker run -d --name redis redis
【2】运行刚刚的 flask-redis:
docker run -d --name flask-redis-demo --link redis -e REDIS_HOST=redis dylan/flask-demo:v1.0
-e:给容器设置一个环境变量,因为程序代码会从环境变量中获取 redis 的地址,如果没有,才默认使用 127.0.0.1。
这样写的达到了灵活配置的目的。
curl http://172.17.0.3:5000
在本机通过 curl 访问容器 IP 加端口就能够访问到内容了,如下图:
那为啥之前那个简单的 flask 项目在宿主机无法直接访问呢?原因很简单。
因为监听 IP 的原因,之前监听的是 127.0.0.1 只能本机访问,而这里监听的是 0.0.0.0 都允许~
【3】再运行一个随机映射的项目:
docker run -d --name flask-redis-demo1 -P --link redis -e REDIS_HOST=redis dylan/flask-demo:v1.0
通过查看本机的端口可知是 32769 端口,访问测试:
【4】再运行一个指定端口的:
docker run -d --name flask-redis-demo2 -p 8080:5000 --link redis -e REDIS_HOST=redis dylan/flask-demo:v1.0
将它映射到 8080,访问测试:
小结
对于资源管理和网络的知识暂时谈到这里,后面会谈到另外一种网络 DRIVER(overlay)网络~
【05】循序渐进学 docker:系统资源和网络的更多相关文章
- 【01】循序渐进学 docker:到底是啥
写在前面的话 首先说一下,我本身是做运维的,4 年工作,多家公司.所以可能接下来谈到的更多的是一些在工作过程中积累的个人看法.且有些并不具备普遍性,有不合适的地方,全当我在吹牛逼就行. 一开始我们得谈 ...
- 【09】循序渐进学 docker:docker swarm
写在前面的话 至此,docker 的基础知识已经了解的差不多了,接下来就来谈谈对于 docker 容器,我们如何来管理它. docker swarm 在学习 docker swarm 之前,得先知道容 ...
- 【08】循序渐进学 docker:docker compose
写在前面的话 在之前的操作中,即使是单个容器每次都需要敲很长的命令,当需要多个容器组合着用的时候更加麻烦,此时我们急需找到一种一次配置,随便运行的方法. 这就是这一节重点,单机容器编排工具:docke ...
- 【07】循序渐进学 docker:数据持久化
写在前面的话 学到这里相信有心的朋友都发现问题了,我们每次都会去删掉容器,在创建新的容器.那数据怎么办?岂不删库跑路了? 就算不是数据库,假设公司有日志保留的需求,那每一次发布岂不日志都被干掉了? D ...
- 【06】循序渐进学 docker:跨主机通信
写在前面的话 目前解决容器跨主机通信的方案有很多种,这里给出的只是其中的一种,而且还不是最好的方案,不过归根结底,大同小异.在学习 docker swarm 之前,大家可以先看看这种. 啥是 over ...
- 【循序渐进学Python】15.网络编程
Python 内置封装了很多常见的网络协议的库,因此Python成为了一个强大的网络编程工具,这里是对Python的网络方面编程的一个简单描述. 1. 常用的网络设计模块 在标准库中有很多网络设计相关 ...
- 【04】循序渐进学 docker:Dockerfile
写在前面的话 从前面我们简单的了解了镜像,也运行了容器,各种官方的镜像显然无法满足我们自己的需求,我们目的终究是运行自己的业务. 所以,本章节的 Dockerfile 就主要讲怎么在官方镜像的基础上制 ...
- 【03】循序渐进学 docker:基础命令
写在前面的话 之前谈了啥是 docker 和怎么安装 docker,这里就谈谈 docker 命令的使用,当然,这里的使用可能只是局限于 docker 的增删查改. 另外需要注意的是,为了图片的美观, ...
- 【02】循序渐进学 docker:如何安装
写在前面的话 我们接下来的操作都是 CentOS 7.5 以下完成的,为了避免你我结果不一致,建议你也采用 CentOS 7.5,原因如下: 1. 个人几年工作下来经历的公司,包括身边的运维朋友,90 ...
随机推荐
- javascript变量,作用域和内存问题
1:ECMAScript所有函数的参数都是按值传递的 function setName(obj){ obj.name="finn"; obj=new Object(); obj.n ...
- screen command of linux
[screen command of linux] 常用键: 补充: Ctrl-a S # split terminal horizon Ctrl-a TAB # switch to ano ...
- ThreadLocal原理深入解析
目录 1. 从一次项目经历说起 2. ThreadLocal源码解析 2.1 set方法源码解析 2.2 get方法源码解析 2.3 ThreadLocal源码总结 3. ThreadLocalMap ...
- iOS开发时间控件怎么强制24小时制(小技巧)
1)当你的format格式是 NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];[dateFo ...
- SpringMVC单元测试-MockMvc
一 简介 MockMvc实现对Http请求的模拟,可以方便对Controller进行测试,使得测试速度快.不依赖网络环境,而且提供验证的工具,使得请求的验证统一而且很方便. 二 常见使用方式 1 ...
- eclipse添加web项目报错“Target runtime Apache Tomcat v7.0 is not defined.”
项目检出后,发现是Tomcat7 发布的,修改文件: 工作空间--->项目名称--->.settings--->org.eclipse.wst.common.project.face ...
- BOOST_TYPEOF和BOOST_AUTO
1.简介 头文件<boost/typeof.hpp>里定义了两个宏:BOOST_TYPEOF和BOOST_AUTO,分别用于仿真C++新标准的typeof和auto关键字,可以在编译期自动 ...
- p4364 [九省联考2018]IIIDX
传送门 分析 我们先考虑如果所有数都不相同我们应该怎么办 我们可以直接贪心的在每个点放可行的最大权值 但是题目要求可以有相同的数 我们可以考虑每次让当前节点可发且尽量大的同时给兄弟节点留的数尽量大 我 ...
- spring boot☞Swagger2文档构建及单元测试
首先,回顾并详细说明一下在快速入门中使用的@Controller.@RestController.@RequestMapping注解.如果您对Spring MVC不熟悉并且还没有尝试过快速入门案例,建 ...
- Ubuntu14.04 下安装Vmware-Tools
1.切换到ubuntu 图形界面 startx , 点击虚拟机菜单栏-安装VMware Tools 2. 在Ubuntu系统中找到VMwareTools-9.2.2-893683.tar.gz ,右键 ...