写在前面的话

在上一篇学习 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. Sqlserver ROW_NUMBER()

    SELECT ROW_NUMBER() OVER (ORDER BY A.orderdate,A.orderid),* from Sales.Orders A

  2. 使用opencv-python画OpenCV LOGO

    OpenCV2-Python 官方教程的练习 代码: #-*- coding:utf-8 -*- import numpy as np import cv2 img = np.zeros((512, ...

  3. Python解释器种类以及特点 (经典概括, 便于理解和记忆)

    CPython c语言开发的 使用最广的解释器 IPython 基于cpython之上的一个交互式计时器 交互方式增强 功能和cpython一样 PyPy 目标是执行效率 采用JIT技术 对pytho ...

  4. 【LA11248 训练指南】网络扩容【最大流】

    题意: 给定一个有向网络,每条边均有一个容量.问是否存在一个从点1到点N,流量为C的流.如果不存在,是否可以恰好修改一条弧的容量,使得存在这样的流? 分析: 先跑一遍最大流,如果最大流大于等于C,则输 ...

  5. 150. Evaluate Reverse Polish Notation (Stack)

    Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, ...

  6. IIS网站最大并发连接数

    打开网站服务器IIS---[控制面板]-[管理工具]--[Internet信息服务IIS管理器]   打开应用程序池--找到网站对应使用的应用程序池--选择右侧[高级设置]   在[高级设置]窗口可看 ...

  7. vmware workstation环境下虚拟机固定ip的设置

    一.准备 (1)vmware workstation (2)centOS 6.5 二.配置 采用nat模式对虚拟机进行固定ip配置,nat模式相当于在windows的操作系统下构建了一个独立的内部局域 ...

  8. 全新的css网站布局--Grid布局

    Grid布局全新的css网站布局 CSS Grid 布局由两个核心组成部分是 wrapper(父元素)和 items(子元素). wrapper 是实际的 grid(网格),items 是 grid( ...

  9. [C++] Sign and magnitude,Ones' complement and Two's complement

    Sign and magnitude,Ones' complement and Two's complement

  10. noi2729 Blah数集

    Blah数集 大数学家高斯小时候偶然间发现一种有趣的自然数集合Blah,对于以a为基的集合Ba定义如下: (1) a是集合Ba的基,且a是Ba的第一个元素: (2)如果x在集合Ba中,则2x+1和3x ...