以前被面试或者面试别人的时候,出现频率比较高的一个问题是:看你简历上写熟悉容器技术,那你是怎么理解容器的?还算可以的回答是:容器技术本身不是新的技术,容器本质上就是宿主机操作系统上的一个特殊的进程,利用Namespace和Cgroup技术实现了资源的隔离与限制,并且实现了容器镜像,解决了应用程序分发的问题。答到这里,可以说对容器的理解还是比较全面的了。
Docker的安装:本不想写网上一搜一堆,且不需要理解的东西,但是看好多文章里写的安装过程,多少有点坑,所以还是写一下,安装过程就在ubuntu上操作,因为CentOS7将来也就不维护了,听说ebay生产环境系统也都用ubuntu了。
step1: 更新ubuntu apt-get源
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
step2: 添加Docker官方GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
step3: 安装Docker稳定版仓库地址
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
step4: 安装Docker
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
通常安装完Docker就自动运行了,但是很多人想长期用这个Docker作为一个test或staging环境,那么直接用估计会有问题,Docker默认的存储目录在/var/lib/docker/下,有些主机的这个盘很小,尤其是云主机,用一段时间把系统盘占满了,还得迁走,就很麻烦,不如一步到位,把数据存储目录放到较大的盘上,比如/data/docker/。
step5: 编辑/lib/systemd/system/docker.service文件,在ExecStart选项后加上--data-root=/data/docker,这样就自定义了数据目录,结果如下所示:
ExecStart=/usr/bin/dockerd -H fd:// --data-root=/data/docker --containerd=/run/containerd/containerd.sock --log-level warn --log-opt max-size=100m --log-opt max-file=5
然后重新加载systemd并重启Docker进程即可。
systemctl daemon-reload
systemctl restart docker
step6: 验证容器已经安装成功
# docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
用Docker运行一个Nginx容器:Docker作为工具来用,其实没有什么复杂的东西,日常只要掌握Dockerfile,会用一些基本的命令就可以了,下面以Nginx为基础,制作我们的镜像并运行。
step1: 编写Dockerfile
# 这里指定你的基础镜像,建议写固定的版本号,不然默认的latest更新可能会导致你的代码不兼容
FROM nginx:1.21.3
# 进入容器后的默认目录
WORKDIR /usr/share/nginx/html
# 把index.html内容改成我们想要的内容
RUN echo "This is my first image base nginx!" > index.html
step2: 生成Docker镜像
docker build -t my_nginx:v1.0 -f Dockerfile .
step3: 查看生成的镜像,并使用该镜像启动容器
# docker images
my_nginx v1.0 5d0547b2cc8d 3 hours ago 133MB
# docker run -d 5d0547b2cc8d
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6961dd4fce83 5d0547b2cc8d "/docker-entrypoint.…" 18 minutes ago Up 18 minutes 80/tcp strange_jepsen
step4: 查看容器IP地址,并使用容器IP访问nginx服务,这里我们介绍使用nsenter命令,场景是我们的nginx镜像中并没有ifconfig命令,但是我们又想查看IP地址,这时就可以通过nsenter命令进入到容器的Namespace,并用宿主机上的命令来调整容器。
[root@iZ2zec5wzaupsrdu55oeaiZ docker]# docker inspect 6961dd4fce83 | grep -i pid
"Pid": 30176,
"PidMode": "",
"PidsLimit": null,
[root@iZ2zec5wzaupsrdu55oeaiZ docker]# nsenter -t 30176 -n ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.2 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:ac:12:00:02 txqueuelen 0 (Ethernet)
RX packets 18 bytes 1256 (1.2 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 14 bytes 1386 (1.3 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@iZ2zec5wzaupsrdu55oeaiZ docker]# curl 172.18.0.2
This is my first image base nginx!
转入正题,细说Namespace: Namespace不是一项新技术,它是内核自带的一项功能,Docker之所以能实现资源隔离,就是调用了内核的Namespace功能。该功能对内核资源进行隔离,使得容器进程都可以在单独的命名空间中运行,并且只可以访问当前容器命名空间的资源。
Linux下的资源类型有很多,那么Namespace就也分为下面几类对资源进行限制:
- PID namespace:用于隔离进程 ID。
- Network namespace:隔离网络接口,在虚拟的 net namespace 内用户可以拥有自己独立的 IP、路由、端口等。
- Mount namespace:文件系统挂载点隔离。
- IPC namespace:信号量、消息队列和共享内存的隔离。
- UTS namespace:主机名和域名的隔离。
- User namespace: 用户和组的隔离。
上面列出了各种资源的隔离,下面就拿 PID namespace 来举例,看看这个在Linux操作系统里是如何实现的,Linux 系统中创建进程的系统调用是 clone(),如下代码所示:
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
但是一旦我们传入了 CLONE_NEWPID 参数,新创建的进程就进入了一个独立的空间,在这个空间里它自己的PID是1,在外面看来它就是一个正常的进程,进程号可能是xxx这种,但肯定不是1,因为1号进程是操作系统的systemd进程,如下代码就实现了进程隔离:
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
通俗来讲,一个进程在启动后,Namespace就给他一通包装,然后这个进程就在自己的小天地里自由的玩耍了。但齐天大圣终穷逃不出如来的五指山,对于开发者来讲,它仍然是操作系统上的一个进程罢了。
对于大多数人来讲,我们只需要知道Docker是如何实现进程隔离的,如果以后有需要去深入挖掘,就按着操作系统的Namespace功能的方向去找就好了。