本文介绍docker中的contanier,基于操作系统介绍什么是container。

进程(Processes)

Container是具有指定配置的Linux进程(processes)。我们进行如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

## 启动一个redis进程
➜ ~ docker run -d --name=db redis:alpine

## 显示容器运行的进程
➜ ~ docker top db
PID USER TIME COMMAND
2288 999 0:00 redis-server

## 查看系统是否存在redis-server进程
➜ ~ ps aux | grep redis-server
donggang 10761 0.0 0.0 4258876 200 s007 R+ 6:57下午 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn redis-server

## 查看dockerd的进程树
➜ ~ pstree -c -p -A $(pgrep dockerd)

在linux系统中,一个container对应一个标准的进程。所有的container进程都是dockerd进程生成的。Linux是基于文件系统的,进程的信息都存储在/proc目录中。 每个进程对应一个目录,可以通过进程ID获得对应的目录位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

## 设置DBPID变量,方便后续测试
➜ ~ DBPID=$(pgrep redis-server)
➜ ~ echo Redis is $DBPID
Redis is 6950
## 找到对应的进程目录文件,该容器相关配置都保存在该目录下
➜ ~ ls /proc/$DBPID
attr cmdline environ io mem ns pagemap sched stack task
autogroup comm exe limits mountinfo numa_maps patch_state schedstat stat timers
auxv coredump_filter fd loginuid mounts oom_adj personality sessionid statm uid_map
cgroup cpuset fdinfo map_files mountstats oom_score projid_map setgroups status wchan
clear_refs cwd gid_map maps net oom_score_adj root smaps syscall

## 通过文件获取容器db的目录
➜ ~ cat /proc/$DBPID/environ
HOSTNAME=a1f8a38c84d4SHLVL=2REDIS_DOWNLOAD_SHA=6624841267e142c5d5d5be292d705f8fb6070677687c5aad1645421a936d22b3HOME=/home/redisPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binREDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-5.0.6.tar.gzREDIS_VERSION=5.0.6PWD=/data#
## 通过docker api获取,
➜ ~ docker exec -it db env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=a1f8a38c84d4
TERM=xterm
REDIS_VERSION=5.0.6
REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-5.0.6.tar.gz
REDIS_DOWNLOAD_SHA=6624841267e142c5d5d5be292d705f8fb6070677687c5aad1645421a936d22b3
HOME=/root

命名空间(namespace)

容器的一个基础部分就是namespace,namespace限制某个进程只能获取系统的某些部分,比如网络接口或这进程。 当一个容器开始运行时,容器运行时(container runtime)比如docker将会创建namespace,容器对应的进程将在这个namespace中运行,

有以下种类的namespace: Mount(mnt)、Process ID(pid)、Network(net)、Interprocess Communication(ipc)、UTC(hostnames) 、UserId(user)、Control group(cgroup)。有关Linux的namspace介绍移步于https://en.wikipedia.org/wiki/Linux_namespaces

我们可以通过工具unshare创建一个隔离沙箱。使其中执行的进程不共享父进程的Pid等namespace。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## bash运行在"沙箱中"
➜ ~ sudo unshare --fork --pid --mount-proc bash

## 打印进程信息
[root@master ~]# ps
PID TTY TIME CMD
1 pts/0 00:00:00 bash
12 pts/0 00:00:00 ps
[root@master ~]# exit
## 退出沙箱
exit

## 打印进程信息
➜ ~ ps
PID TTY TIME CMD
4673 pts/0 00:00:00 zsh
9088 pts/0 00:00:00 ps
16860 pts/0 00:00:00 zsh
18119 pts/0 00:00:00 zsh

Namespace本质上仍然是文件,它存储在磁盘中,所以Namespace是可以共享的。工具nsenter可以在指定的namespace运行一个程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## 获取容器db的namespace资源
➜ ~ ls -lha /proc/$DBPID/ns
总用量 0
dr-x--x--x 2 polkitd 1000 0 10月 30 19:17 .
dr-xr-xr-x 9 polkitd 1000 0 10月 30 19:17 ..
lrwxrwxrwx 1 polkitd 1000 0 10月 30 19:18 ipc -> ipc:[4026533033]
lrwxrwxrwx 1 polkitd 1000 0 10月 30 19:18 mnt -> mnt:[4026533031]
lrwxrwxrwx 1 polkitd 1000 0 10月 30 19:17 net -> net:[4026533036]
lrwxrwxrwx 1 polkitd 1000 0 10月 30 19:18 pid -> pid:[4026533034]
lrwxrwxrwx 1 polkitd 1000 0 10月 30 19:18 user -> user:[4026531837]
lrwxrwxrwx 1 polkitd 1000 0 10月 30 19:18 uts -> uts:[4026533032]

## 在container db中执行ps程序
➜ ns nsenter --target $DBPID -m -u -i -p /bin/ps aux
PID USER TIME COMMAND
1 redis 1:03 redis-server
37 root 0:00 /bin/ps aux

docker可以通过语法container:<container-name>共享namespace,下面的例子中我们创建一个web容器,该容器网络共享db container的网络namespace。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  ns docker run -d --name=web --net=container:db nginx:alpine
Unable to find image 'nginx:alpine' locally
alpine: Pulling from library/nginx
89d9c30c1d48: Already exists
110ad692b782: Pull complete
Digest: sha256:085e84650dbe56f27ca3ed00063a12d5b486e40c3d16d83c4e6c2aad1e4045ab
Status: Downloaded newer image for nginx:alpine
4f982d745f346a7087c42c55e455448d21794641df7005c4d76299f781ba474f
➜ ns WEBPID=$(pgrep nginx | tail -n1)
➜ ns echo nginx is $WEBPID
nginx is 21499
➜ ns ls -alh /proc/$WEBPID/ns | grep net
lrwxrwxrwx 1 101 101 0 10月 31 09:19 net -> net:[4026533036]
➜ ns ls -alh /proc/$DBPID/ns | grep net
lrwxrwxrwx 1 polkitd 1000 0 10月 30 19:17 net -> net:[4026533036]

Chroot

Chroot能够是容器进程拥有独立的根目录(root directory/),chroot --help能够更改当前 运行的进程及其子进程的根目录。 chroot不能够起到隔离的作用,仍然可以通过相对路径访问新根以外的地址

Cgroups(Control Groups)

CGroups技术限制进程消耗资源,

Secomp/AppArmor

AppArmor描述了进程可以访问系统中哪些部分。Seccomp提供了限制可以进行哪些系统调用,阻止安装内核模块或更改文件权限等方面的功能。

参考

  1. Docker容器技术详解