【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)网络~

转载于:https://www.cnblogs.com/Dy1an/p/10600980.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值