commit
48bf98a77b
|
@ -56,7 +56,7 @@
|
||||||
* 0.3.0: 2014-11-25
|
* 0.3.0: 2014-11-25
|
||||||
* 完成仓库章节;
|
* 完成仓库章节;
|
||||||
* 重写安全章节;
|
* 重写安全章节;
|
||||||
* 修正底层实现章节的架构、名字空间、控制组、文件系统、容器格式等内容;
|
* 修正底层实现章节的架构、命名空间、控制组、文件系统、容器格式等内容;
|
||||||
* 添加对常见仓库和镜像的介绍;
|
* 添加对常见仓库和镜像的介绍;
|
||||||
* 添加 Dockerfile 的介绍;
|
* 添加 Dockerfile 的介绍;
|
||||||
* 重新校订中英文混排格式。
|
* 重新校订中英文混排格式。
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
* [多台物理主机之间的容器互联](cases/container_connect.md)
|
* [多台物理主机之间的容器互联](cases/container_connect.md)
|
||||||
* [标准化开发测试和生产环境](cases/environment.md)
|
* [标准化开发测试和生产环境](cases/environment.md)
|
||||||
* [安全](security/README.md)
|
* [安全](security/README.md)
|
||||||
* [内核名字空间](security/kernel_ns.md)
|
* [内核命名空间](security/kernel_ns.md)
|
||||||
* [控制组](security/control_group.md)
|
* [控制组](security/control_group.md)
|
||||||
* [服务端防护](security/daemon_sec.md)
|
* [服务端防护](security/daemon_sec.md)
|
||||||
* [内核能力机制](security/kernel_capability.md)
|
* [内核能力机制](security/kernel_capability.md)
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
* [创建镜像](dockerfile/build_image.md)
|
* [创建镜像](dockerfile/build_image.md)
|
||||||
* [底层实现](underly/README.md)
|
* [底层实现](underly/README.md)
|
||||||
* [基本架构](underly/arch.md)
|
* [基本架构](underly/arch.md)
|
||||||
* [名字空间](underly/namespace.md)
|
* [命名空间](underly/namespace.md)
|
||||||
* [控制组](underly/cgroups.md)
|
* [控制组](underly/cgroups.md)
|
||||||
* [联合文件系统](underly/ufs.md)
|
* [联合文件系统](underly/ufs.md)
|
||||||
* [容器格式](underly/container_format.md)
|
* [容器格式](underly/container_format.md)
|
||||||
|
|
|
@ -13,7 +13,7 @@ $ sudo docker run -i -t --rm --net=none base /bin/bash
|
||||||
root@12e343489d2f:/#
|
root@12e343489d2f:/#
|
||||||
```
|
```
|
||||||
|
|
||||||
找到进程号,然后创建网络名字空间的跟踪文件。
|
找到进程号,然后创建网络命名空间的跟踪文件。
|
||||||
```
|
```
|
||||||
$ sudo docker inspect -f '{{.State.Pid}}' 1f1f4c1f931a
|
$ sudo docker inspect -f '{{.State.Pid}}' 1f1f4c1f931a
|
||||||
2989
|
2989
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# 安全
|
# 安全
|
||||||
评估 Docker 的安全性时,主要考虑三个方面:
|
评估 Docker 的安全性时,主要考虑三个方面:
|
||||||
|
|
||||||
* 由内核的名字空间和控制组机制提供的容器内在安全
|
* 由内核的命名空间和控制组机制提供的容器内在安全
|
||||||
* Docker 程序(特别是服务端)本身的抗攻击性
|
* Docker 程序(特别是服务端)本身的抗攻击性
|
||||||
* 内核安全性的加强机制对容器安全性的影响
|
* 内核安全性的加强机制对容器安全性的影响
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
用户仍可以利用 HTTP 提供 REST API 访问。建议使用安全机制,确保只有可信的网络或 VPN,或证书保护机制(例如受保护的 stunnel 和 ssl 认证)下的访问可以进行。此外,还可以使用 HTTPS 和证书来加强保护。
|
用户仍可以利用 HTTP 提供 REST API 访问。建议使用安全机制,确保只有可信的网络或 VPN,或证书保护机制(例如受保护的 stunnel 和 ssl 认证)下的访问可以进行。此外,还可以使用 HTTPS 和证书来加强保护。
|
||||||
|
|
||||||
最近改进的 Linux 名字空间机制将可以实现使用非 root 用户来运行全功能的容器。这将从根本上解决了容器和主机之间共享文件系统而引起的安全问题。
|
最近改进的 Linux 命名空间机制将可以实现使用非 root 用户来运行全功能的容器。这将从根本上解决了容器和主机之间共享文件系统而引起的安全问题。
|
||||||
|
|
||||||
终极目标是改进 2 个重要的安全特性:
|
终极目标是改进 2 个重要的安全特性:
|
||||||
* 将容器的 root 用户映射到本地主机上的非 root 用户,减轻容器和主机之间因权限提升而引起的安全问题;
|
* 将容器的 root 用户映射到本地主机上的非 root 用户,减轻容器和主机之间因权限提升而引起的安全问题;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
## 内核名字空间
|
## 内核命名空间
|
||||||
Docker 容器和 LXC 容器很相似,所提供的安全特性也差不多。当用 `docker run` 启动一个容器时,在后台 Docker 为容器创建了一个独立的名字空间和控制组集合。
|
Docker 容器和 LXC 容器很相似,所提供的安全特性也差不多。当用 `docker run` 启动一个容器时,在后台 Docker 为容器创建了一个独立的命名空间和控制组集合。
|
||||||
|
|
||||||
名字空间提供了最基础也是最直接的隔离,在容器中运行的进程不会被运行在主机上的进程和其它容器发现和作用。
|
命名空间提供了最基础也是最直接的隔离,在容器中运行的进程不会被运行在主机上的进程和其它容器发现和作用。
|
||||||
|
|
||||||
每个容器都有自己独有的网络栈,意味着它们不能访问其他容器的 sockets 或接口。不过,如果主机系统上做了相应的设置,容器可以像跟主机交互一样的和其他容器交互。当指定公共端口或使用 links 来连接 2 个容器时,容器就可以相互通信了(可以根据配置来限制通信的策略)。
|
每个容器都有自己独有的网络栈,意味着它们不能访问其他容器的 sockets 或接口。不过,如果主机系统上做了相应的设置,容器可以像跟主机交互一样的和其他容器交互。当指定公共端口或使用 links 来连接 2 个容器时,容器就可以相互通信了(可以根据配置来限制通信的策略)。
|
||||||
|
|
||||||
从网络架构的角度来看,所有的容器通过本地主机的网桥接口相互通信,就像物理机器通过物理交换机通信一样。
|
从网络架构的角度来看,所有的容器通过本地主机的网桥接口相互通信,就像物理机器通过物理交换机通信一样。
|
||||||
|
|
||||||
那么,内核中实现名字空间和私有网络的代码是否足够成熟?
|
那么,内核中实现命名空间和私有网络的代码是否足够成熟?
|
||||||
|
|
||||||
内核名字空间从 2.6.15 版本(2008 年 7 月发布)之后被引入,数年间,这些机制的可靠性在诸多大型生产系统中被实践验证。
|
内核命名空间从 2.6.15 版本(2008 年 7 月发布)之后被引入,数年间,这些机制的可靠性在诸多大型生产系统中被实践验证。
|
||||||
|
|
||||||
实际上,名字空间的想法和设计提出的时间要更早,最初是为了在内核中引入一种机制来实现 [OpenVZ](http://en.wikipedia.org/wiki/OpenVZ) 的特性。
|
实际上,命名空间的想法和设计提出的时间要更早,最初是为了在内核中引入一种机制来实现 [OpenVZ](http://en.wikipedia.org/wiki/OpenVZ) 的特性。
|
||||||
而 OpenVZ 项目早在 2005 年就发布了,其设计和实现都已经十分成熟。
|
而 OpenVZ 项目早在 2005 年就发布了,其设计和实现都已经十分成熟。
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# 底层实现
|
# 底层实现
|
||||||
|
|
||||||
Docker 底层的核心技术包括 Linux 上的名字空间(Namespaces)、控制组(Control groups)、Union 文件系统(Union file systems)和容器格式(Container format)。
|
Docker 底层的核心技术包括 Linux 上的命名空间(Namespaces)、控制组(Control groups)、Union 文件系统(Union file systems)和容器格式(Container format)。
|
||||||
|
|
||||||
我们知道,传统的虚拟机通过在宿主主机中运行 hypervisor 来模拟一整套完整的硬件环境提供给虚拟机的操作系统。虚拟机系统看到的环境是可限制的,也是彼此隔离的。
|
我们知道,传统的虚拟机通过在宿主主机中运行 hypervisor 来模拟一整套完整的硬件环境提供给虚拟机的操作系统。虚拟机系统看到的环境是可限制的,也是彼此隔离的。
|
||||||
这种直接的做法实现了对资源最完整的封装,但很多时候往往意味着系统资源的浪费。
|
这种直接的做法实现了对资源最完整的封装,但很多时候往往意味着系统资源的浪费。
|
||||||
|
@ -10,4 +10,4 @@ Docker 底层的核心技术包括 Linux 上的名字空间(Namespaces)、
|
||||||
要想实现虚拟化,除了要实现对内存、CPU、网络IO、硬盘IO、存储空间等的限制外,还要实现文件系统、网络、PID、UID、IPC等等的相互隔离。
|
要想实现虚拟化,除了要实现对内存、CPU、网络IO、硬盘IO、存储空间等的限制外,还要实现文件系统、网络、PID、UID、IPC等等的相互隔离。
|
||||||
前者相对容易实现一些,后者则需要宿主机系统的深入支持。
|
前者相对容易实现一些,后者则需要宿主机系统的深入支持。
|
||||||
|
|
||||||
随着 Linux 系统对于名字空间功能的完善实现,程序员已经可以实现上面的所有需求,让某些进程在彼此隔离的名字空间中运行。大家虽然都共用一个内核和某些运行时环境(例如一些系统命令和系统库),但是彼此却看不到,都以为系统中只有自己的存在。这种机制就是容器(Container),利用名字空间来做权限的隔离控制,利用 cgroups 来做资源分配。
|
随着 Linux 系统对于命名空间功能的完善实现,程序员已经可以实现上面的所有需求,让某些进程在彼此隔离的命名空间中运行。大家虽然都共用一个内核和某些运行时环境(例如一些系统命令和系统库),但是彼此却看不到,都以为系统中只有自己的存在。这种机制就是容器(Container),利用命名空间来做权限的隔离控制,利用 cgroups 来做资源分配。
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
## 名字空间
|
## 命名空间
|
||||||
名字空间是 Linux 内核一个强大的特性。每个容器都有自己单独的名字空间,运行在其中的应用都像是在独立的操作系统中运行一样。名字空间保证了容器之间彼此互不影响。
|
命名空间是 Linux 内核一个强大的特性。每个容器都有自己单独的命名空间,运行在其中的应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。
|
||||||
|
|
||||||
### pid 名字空间
|
### pid 命名空间
|
||||||
不同用户的进程就是通过 pid 名字空间隔离开的,且不同名字空间中可以有相同 pid。所有的 LXC 进程在 Docker 中的父进程为Docker进程,每个 LXC 进程具有不同的名字空间。同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。
|
不同用户的进程就是通过 pid 命名空间隔离开的,且不同命名空间中可以有相同 pid。所有的 LXC 进程在 Docker 中的父进程为Docker进程,每个 LXC 进程具有不同的命名空间。同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。
|
||||||
|
|
||||||
### net 名字空间
|
### net 命名空间
|
||||||
有了 pid 名字空间, 每个名字空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net 名字空间实现的, 每个 net 名字空间有独立的 网络设备, IP 地址, 路由表, /proc/net 目录。这样每个容器的网络就能隔离开来。Docker 默认采用 veth 的方式,将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起。
|
有了 pid 命名空间, 每个命名空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net 命名空间实现的, 每个 net 命名空间有独立的 网络设备, IP 地址, 路由表, /proc/net 目录。这样每个容器的网络就能隔离开来。Docker 默认采用 veth 的方式,将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起。
|
||||||
|
|
||||||
### ipc 名字空间
|
### ipc 命名空间
|
||||||
容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、消息队列和共享内存等。然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 名字空间中的进程间交互,因此需要在 IPC 资源申请时加入名字空间信息,每个 IPC 资源有一个唯一的 32 位 id。
|
容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、消息队列和共享内存等。然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互,因此需要在 IPC 资源申请时加入命名空间信息,每个 IPC 资源有一个唯一的 32 位 id。
|
||||||
|
|
||||||
### mnt 名字空间
|
### mnt 命名空间
|
||||||
类似 chroot,将一个进程放到一个特定的目录执行。mnt 名字空间允许不同名字空间的进程看到的文件结构不同,这样每个名字空间 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个名字空间中的容器在 /proc/mounts 的信息只包含所在名字空间的 mount point。
|
类似 chroot,将一个进程放到一个特定的目录执行。mnt 命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个命名空间中的容器在 /proc/mounts 的信息只包含所在命名空间的 mount point。
|
||||||
|
|
||||||
### uts 名字空间
|
### uts 命名空间
|
||||||
UTS("UNIX Time-sharing System") 名字空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程。
|
UTS("UNIX Time-sharing System") 命名空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程。
|
||||||
|
|
||||||
### user 名字空间
|
### user 命名空间
|
||||||
每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。
|
每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。
|
||||||
|
|
||||||
*注:关于 Linux 上的名字空间,[这篇文章](http://blog.scottlowe.org/2013/09/04/introducing-linux-network-namespaces/) 介绍的很好。
|
*注:关于 Linux 上的命名空间,[这篇文章](http://blog.scottlowe.org/2013/09/04/introducing-linux-network-namespaces/) 介绍的很好。
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
## Docker 网络实现
|
## Docker 网络实现
|
||||||
|
|
||||||
Docker 的网络实现其实就是利用了 Linux 上的网络名字空间和虚拟网络设备(特别是 veth pair)。建议先熟悉了解这两部分的基本概念再阅读本章。
|
Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 veth pair)。建议先熟悉了解这两部分的基本概念再阅读本章。
|
||||||
|
|
||||||
### 基本原理
|
### 基本原理
|
||||||
首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。
|
首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。
|
||||||
|
@ -14,14 +14,14 @@ Docker 容器网络就利用了这项技术。它在本地主机和容器内分
|
||||||
Docker 创建一个容器的时候,会执行如下操作:
|
Docker 创建一个容器的时候,会执行如下操作:
|
||||||
* 创建一对虚拟接口,分别放到本地主机和新容器中;
|
* 创建一对虚拟接口,分别放到本地主机和新容器中;
|
||||||
* 本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 veth65f9;
|
* 本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 veth65f9;
|
||||||
* 容器一端放到新容器中,并修改名字作为 eth0,这个接口只在容器的名字空间可见;
|
* 容器一端放到新容器中,并修改名字作为 eth0,这个接口只在容器的命名空间可见;
|
||||||
* 从网桥可用地址段中获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 veth65f9。
|
* 从网桥可用地址段中获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 veth65f9。
|
||||||
|
|
||||||
完成这些之后,容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。
|
完成这些之后,容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。
|
||||||
|
|
||||||
可以在 `docker run` 的时候通过 `--net` 参数来指定容器的网络配置,有4个可选值:
|
可以在 `docker run` 的时候通过 `--net` 参数来指定容器的网络配置,有4个可选值:
|
||||||
* `--net=bridge` 这个是默认值,连接到默认的网桥。
|
* `--net=bridge` 这个是默认值,连接到默认的网桥。
|
||||||
* `--net=host` 告诉 Docker 不要将容器网络放到隔离的名字空间中,即不要容器化容器内的网络。此时容器使用本地主机的网络,它拥有完全的本地主机接口访问权限。容器进程可以跟主机其它 root 进程一样可以打开低范围的端口,可以访问本地网络服务比如 D-bus,还可以让容器做一些影响整个主机系统的事情,比如重启主机。因此使用这个选项的时候要非常小心。如果进一步的使用 `--privileged=true`,容器会被允许直接配置主机的网络堆栈。
|
* `--net=host` 告诉 Docker 不要将容器网络放到隔离的命名空间中,即不要容器化容器内的网络。此时容器使用本地主机的网络,它拥有完全的本地主机接口访问权限。容器进程可以跟主机其它 root 进程一样可以打开低范围的端口,可以访问本地网络服务比如 D-bus,还可以让容器做一些影响整个主机系统的事情,比如重启主机。因此使用这个选项的时候要非常小心。如果进一步的使用 `--privileged=true`,容器会被允许直接配置主机的网络堆栈。
|
||||||
* `--net=container:NAME_or_ID` 让 Docker 将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享 IP 地址和端口等网络资源,两者进程可以直接通过 `lo` 环回接口通信。
|
* `--net=container:NAME_or_ID` 让 Docker 将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享 IP 地址和端口等网络资源,两者进程可以直接通过 `lo` 环回接口通信。
|
||||||
* `--net=none` 让 Docker 将新容器放到隔离的网络栈中,但是不进行网络配置。之后,用户可以自己进行配置。
|
* `--net=none` 让 Docker 将新容器放到隔离的网络栈中,但是不进行网络配置。之后,用户可以自己进行配置。
|
||||||
|
|
||||||
|
@ -66,4 +66,4 @@ $ sudo ip netns exec $pid ip route add default via 172.17.42.1
|
||||||
|
|
||||||
当容器结束后,Docker 会清空容器,容器内的 eth0 会随网络命名空间一起被清除,A 接口也被自动从 `docker0` 卸载。
|
当容器结束后,Docker 会清空容器,容器内的 eth0 会随网络命名空间一起被清除,A 接口也被自动从 `docker0` 卸载。
|
||||||
|
|
||||||
此外,用户可以使用 `ip netns exec` 命令来在指定网络名字空间中进行配置,从而配置容器内的网络。
|
此外,用户可以使用 `ip netns exec` 命令来在指定网络命名空间中进行配置,从而配置容器内的网络。
|
||||||
|
|
Loading…
Reference in New Issue