容器就是 Container,而 Container 的另一个意思是集装箱。其实容器的思想就是要变成软件交付的集装箱。集装箱的特点,一是打包,二是标准。
封闭的环境主要使用了两种技术,一种是看起来是隔离的技术,称为 namespace,也即每个 namespace 中的应用看到的是不同的 IP 地址、用户空间、程号等。另一种是用起来是隔离的技术,称为 cgroup,也即明明整台机器有很多的 CPU、内存,而一个应用只能用其中的一部分。
有了这两项技术,就相当于我们焊好了集装箱。接下来的问题就是如何“将这个集装箱标准化”,并在哪艘船上都能运输。这里的标准首先就是镜像。
所谓镜像,就是将你焊好集装箱的那一刻,将集装箱的状态保存下来,集装箱里的状态就被定在了那一刻,然后将这一刻的状态保存成一系列文件。无论从哪里运行这个镜像,都能完整地还原当时的情况。
namespace 翻译过来就是命名空间。其实很多面向对象的程序设计语言里面,都有命名空间这个东西。大家一起写代码,难免会起相同的名词,编译就会冲突。而每个功能都有自己的命名空间,在不同的空间里面,类名相同,不会冲突。
cgroup 全称 control groups,是 Linux 内核提供的一种可以限制、隔离进程使用的资源机制。cgroup 能控制哪些资源呢?它有很多子系统:
- CPU 子系统使用调度程序为进程控制 CPU 的访问;
- cpuset,如果是多核心的 CPU,这个子系统会为进程分配单独的 CPU 和内存;
- memory 子系统,设置进程的内存限制以及产生内存资源报告;
- blkio 子系统,设置限制每个块设备的输入输出控制;
- net_cls,这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。
cgroup 提供了一个虚拟文件系统,作为进行分组管理和各子系统设置的用户接口。要使用 cgroup,必须挂载 cgroup 文件系统,一般情况下都是挂载到 /sys/fs/cgroup 目录下。
Docker 有两种方式,一种是通过一个进程 docker-proxy 的方式,监听 10080,转换为 80 端口。
/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 10080 -container-ip 172.17.0.2 -container-port 80
另外一种方式是通过 DNAT 方式,在 -A PREROUTING 阶段加一个规则,将到端口 10080 的 DNAT 称为容器的私有网络。
-A DOCKER -p tcp -m tcp --dport 10080 -j DNAT --to-destination 172.17.0.2:80