写在前面的话

在上一篇学习 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:系统资源和网络的更多相关文章

  1. 【01】循序渐进学 docker:到底是啥

    写在前面的话 首先说一下,我本身是做运维的,4 年工作,多家公司.所以可能接下来谈到的更多的是一些在工作过程中积累的个人看法.且有些并不具备普遍性,有不合适的地方,全当我在吹牛逼就行. 一开始我们得谈 ...

  2. 【09】循序渐进学 docker:docker swarm

    写在前面的话 至此,docker 的基础知识已经了解的差不多了,接下来就来谈谈对于 docker 容器,我们如何来管理它. docker swarm 在学习 docker swarm 之前,得先知道容 ...

  3. 【08】循序渐进学 docker:docker compose

    写在前面的话 在之前的操作中,即使是单个容器每次都需要敲很长的命令,当需要多个容器组合着用的时候更加麻烦,此时我们急需找到一种一次配置,随便运行的方法. 这就是这一节重点,单机容器编排工具:docke ...

  4. 【07】循序渐进学 docker:数据持久化

    写在前面的话 学到这里相信有心的朋友都发现问题了,我们每次都会去删掉容器,在创建新的容器.那数据怎么办?岂不删库跑路了? 就算不是数据库,假设公司有日志保留的需求,那每一次发布岂不日志都被干掉了? D ...

  5. 【06】循序渐进学 docker:跨主机通信

    写在前面的话 目前解决容器跨主机通信的方案有很多种,这里给出的只是其中的一种,而且还不是最好的方案,不过归根结底,大同小异.在学习 docker swarm 之前,大家可以先看看这种. 啥是 over ...

  6. 【循序渐进学Python】15.网络编程

    Python 内置封装了很多常见的网络协议的库,因此Python成为了一个强大的网络编程工具,这里是对Python的网络方面编程的一个简单描述. 1. 常用的网络设计模块 在标准库中有很多网络设计相关 ...

  7. 【04】循序渐进学 docker:Dockerfile

    写在前面的话 从前面我们简单的了解了镜像,也运行了容器,各种官方的镜像显然无法满足我们自己的需求,我们目的终究是运行自己的业务. 所以,本章节的 Dockerfile 就主要讲怎么在官方镜像的基础上制 ...

  8. 【03】循序渐进学 docker:基础命令

    写在前面的话 之前谈了啥是 docker 和怎么安装 docker,这里就谈谈 docker 命令的使用,当然,这里的使用可能只是局限于 docker 的增删查改. 另外需要注意的是,为了图片的美观, ...

  9. 【02】循序渐进学 docker:如何安装

    写在前面的话 我们接下来的操作都是 CentOS 7.5 以下完成的,为了避免你我结果不一致,建议你也采用 CentOS 7.5,原因如下: 1. 个人几年工作下来经历的公司,包括身边的运维朋友,90 ...

随机推荐

  1. 导入本体到Jena TDB数据库

    本体的存储方法或称本体持久化,大致分为基于内存的方式.基于文件的方式.基于数据库的方式和专门的管理工具方式4种(傅柱等, 2013).其中,基于数据库的方式又有基于关系数据库.基于面向对象数据库.基于 ...

  2. Lenovo SplitScreen联想分屏软件只能在联想电脑运行,如何破解

    1.正常安装软件,重启电脑. 2.打开安装目录 C:\Program Files\Lenovo\Lenovo SplitScreen\SplitScreen 找到 MachineChecker.dll ...

  3. python3企业微信群组报警

    公司提出一个需求需要做一个企业微信的一个消息推送,需要将消息发送到聊天群里详细信息如下. 如何创建应用请阅读我的上篇文章:https://www.cnblogs.com/wangyajunblog/p ...

  4. Lambert模型

    [Lambert模型] 漫反射光的强度近似地服从于Lambert定律,即漫反射光的光强仅与入射光的方向和反射点处表面法向夹角的余弦成正比. 由此可以构造出Lambert漫反射模型:Idiffuse = ...

  5. java中的==、equals()、hashCode()

    java中的==.equals().hashCode()源码分析 在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. ==  java中 ...

  6. hdu3999-The order of a Tree (二叉树的先序遍历)

    http://acm.hdu.edu.cn/showproblem.php?pid=3999 The order of a Tree Time Limit: 2000/1000 MS (Java/Ot ...

  7. NPC问题及其解决方法(回溯法、动态规划、贪心法、深度优先遍历)

    NP问题(Non-deterministic Polynomial ):多项式复杂程度的非确定性问题,这些问题无法根据公式直接地计算出来.比如,找大质数的问题(有没有一个公式,你一套公式,就可以一步步 ...

  8. 530.Minimum Absolute Difference in BST 二叉搜索树中的最小差的绝对值

    [抄题]: Given a binary search tree with non-negative values, find the minimum absolute difference betw ...

  9. 本地yum源构建以及Docker离线安装

    Docker离线安装以及本地yum源构建 在docker的使用过程中有时候会遇到一些私有化部署的问题,就是在一些无法上网的机器上面安装使用dokcer,这就引出了docker的离线安装的问题,dock ...

  10. code1214 线段覆盖

    贪心 把线段们按终止点b的先后排序,b小的在前面,b一样随便 然后设r为当前最大的b 每加入一条新的线段i,判断是否重合(i.a>=r) 如果重合就舍弃i,否则把i加入,ans++,更新r:r= ...