Docker / Docker Desktop / Rancher Desktop有什么区别?
这是一篇稍晚发布的文章,我大致写了一下以便简单解释。
背景 – 背景情况
最近的开发越来越多地使用Docker(Docker Compose)作为在个人的本地PC上进行开发的工具。
比如说,现今绝大部分的Web应用都是通过以下三种方式来实现其功能。
-
- RDB (e.g. mysql)
-
- in-memory data store (e.g. redis)
- Object Storage (e.g. Amazon S3 , MinIO)
过去,在开发者的wiki和README.md文件中写明了上述的构建方法,开发者们都努力地自行在本地机器上进行构建。
或者进行 VMWare 圖像的分发,尝试使用 Vagrant 等工具。
最近,我们已经可以轻松地建立开发环境,而不受开发者之间环境差异的影响(M1 Mac除外?)。这是一个了不起的时代。
docker-compose up -d
然而,除了那些最初进行了黑盒化设计的人以外,大多数人似乎只是按照步骤行事,虽然能使其运行,但完全不知道其原理。
为了什么
所以,在本书中,我们将解释以下三个内容。
-
- 首先,Docker 是什么东西?
Docker Desktop for Mac / Windows 和基本的 Docker 有什么区别?
Docker Desktop 变成付费了。听说 Rancher Desktop 不错,那是什么?
容器虚拟化
そもそもコンテナって? (VM とは違うよ)
虽然通常会将 VM(虚拟机)与之混淆,但它们在很大程度上是不同的地方有以下几点。
-
- VM (仮想マシン) は OS を仮想化する
- コンテナ技術は プロセス を仮想化する
例如,VM(虚拟机)可以如下图所示。
AWS和类似的云服务采用了Hypervisor类型,在使用Xen。

一方コンテナ技術はこうです。
実は Linux OS 上で直接プロセスを起動しているだけで、普通のプロセスと大きく変わりはありません。

换句话说,容器虚拟化是在Linux上”分离进程运行空间”的操作,现在我们具体看看这是什么意思。
进程空间分离
让我们在AWS上的Amazon Linux 2实例上进行实际验证。
使用以下命令启动nginx容器。
docker run -d --name nginx -p 8080:80 nginx:1.21-alpine
您可以通过浏览器访问在Docker镜像中通过 http://localhost:8080 启动的nginx。

在接下来的说明中,我们将通过实际的容器示例进行解释。您可以使用以下命令进入容器。
docker exec -it nginx sh
进程标识符(PID)命名空间
「PID 名称空间」是指在执行 ps aux 命令时可见的进程列表。
我会在主机操作系统上尝试运行 ps aux 命令。这样可以看到所有运行中的进程,包括以容器形式启动的进程。
[ec2-user@ip-172-30-1-40 ~]$ ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.2 123716 5608 ? Ss 06:46 0:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
root 2 0.0 0.0 0 0 ? S 06:46 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? I< 06:46 0:00 [rcu_gp]
...
root 3757 0.0 4.1 1446720 82940 ? Ssl 06:48 0:05 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 2531 0.0 0.4 711720 9136 ? Sl 09:21 0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id ff6516b31969bfa33b
root 2554 0.0 0.2 6280 4516 ? Ss 09:21 0:00 \_ nginx: master process nginx -g daemon off;
101 2622 0.0 0.0 6736 1776 ? S 09:21 0:00 | \_ nginx: worker process
root 2742 0.0 0.0 1692 1180 pts/0 Ss+ 09:26 0:00 \_ sh
...
PID 2554和2622是作为容器启动的nginx进程。
在容器内部运行 ps aux 命令时,只能看到三个进程。主机操作系统和其他容器的 PID 空间被隔离开来,不会对外部进程产生影响。
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 nginx: master process nginx -g daemon off;
32 nginx 0:00 nginx: worker process
39 root 0:00 sh
在容器中运行的进程基本上只有一个,它是由 Dockerfile 中的 ENTRYPOINT 和 CMD 指定的命令。它将始终成为 PID 1。
上述的其他过程(32,39)是出于以下原因而产生的。
1nginxDockerfile で ENTRYPOINT, CMD に指定したコマンド32nginxnginx (PID 1) が起動時に生成した子プロセスです39shコンテナに入る際のおまじない docker exec -it nginx sh は、この通りコンテナ内に sh プロセスを起動する為の物でした容器虚拟化与虚拟机(操作系统虚拟化)不同,例如在Linux操作系统启动时不执行以下处理:
执行启动脚本 (例如:/etc/init.d)
启动服务 (例如:crond, sshd)
网络命名空间
「网络命名空间」是指可以在 ifconfig 命令中看到的网络配置(NIC)的名称空间。
我将在主机操作系统上执行 ifconfig 实际操作。这将列出连接到该计算机的网络接口(如 LAN 连接器或 Wi-Fi)。
172.17.0.1このホストマシン上に Docker コンテナが仮想ネットワークがあります。その入口 (Bridge) です。
コンテナの IP アドレス範囲は 172.17.0.2 〜 172.17.0.255 になります。eth0172.30.1.40インターネット LAN 接続の出入り口です。 AWS VPC 上で IP アドレス 172.30.1.40 が割り振られています。lo127.0.0.1ループバックアドレスと言い、所謂 localhost (127.0.0.1) アドレスです。veth–※ 省略
[ec2-user@ip-172-30-1-40 ~]$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
...
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001
inet 172.30.1.40 netmask 255.255.255.0 broadcast 172.30.1.255
...
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
...
veth3f835b9: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
...
veth902e697: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
...
一方、コンテナ内部で ifconfig をすると、2つのネットワーク・インターフェースしか見えません。
このコンテナはホストマシン上の Docker 仮想ネットワークに所属し、IP アドレス 172.17.0.2 でアクセスが可能です。
172.17.0.2インターネット LAN 接続の出入り口です。ホストマシンの Docker 仮想ネットワーク上で IP アドレス 172.17.0.2 が割り振られています。lo127.0.0.1ループバックアドレスと言い、所謂 localhost (127.0.0.1) アドレスです。/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
...
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
...
我将从以前写过的文章中引用网络图。

容器命名空间 (指Docker镜像)
「Mount命名空间」是指在使用ls命令时可以看到的文件系统(文件和目录)。
我将在宿主操作系统上试试运行 ls /。
列出了这台机器根目录下的文件和文件夹。
[ec2-user@ip-172-30-1-40 ~]$ ls /
bin boot dev etc home lib lib64 local media mnt opt proc root run sbin srv sys tmp usr var
一方、コンテナ内部で ls / をすると、全く別のファイル・ディレクトリが一覧されています。
/ # ls /
bin etc mnt run tmp
dev home opt sbin usr
docker-entrypoint.d lib proc srv var
docker-entrypoint.sh media root sys
このディレクトリ構造は皆さんご存知の Docker Image そのもの です。
- docker hub → nginx:1.21-alpine
例如,构建 nginx:1.21-alpine 镜像所使用的 Dockerfile 如下所示。
-
- ベース Image に Alpine Linux を用いている。コンテナ中のファイル・ディレクトリは、ほぼここから来てる
-
- Image のビルド過程で nginx をインストールしている
- この Image からコンテナを起動すると nginx -g daemon off; が実行され最初のプロセス (PID 1) になる
FROM alpine:3.15
RUN set -x \
... \
&& apk add -X "https://nginx.org/packages/mainline/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \
...
CMD ["nginx", "-g", "daemon off;"]
https://github.com/nginxinc/docker-nginx/blob/d039609e3a537df4e15a454fdb5a004d519e9a11/mainline/alpine/Dockerfile 的中文翻译如下:
只要在容器中执行 ls 等命令,就可以看到被隔离的容器特有的文件系统(包括文件和目录)。
これらコンテナのファイル・ディレクトリ実体はホストマシン上の下記ディレクトリにあります。
/var/lib/docker/overlay2/
Docker (原生中文翻译:容器)
Linux 容器技术与 Docker
其实,我们之前讨论的大部分内容都是通过利用 Linux 标准功能来实现命名空间的分离。
Docker 是将 Linux 容器技术转化为 API 形式并且使其更易于所有人使用的软件。
Docker 客户端和 dockerd.
在 Amazon Linux 2 或其他操作系统上,可以使用 yum install -y docker 命令来安装 Docker。Docker 的命令和服务可分为以下两种类型。
docker コマンドは、dockerd に HTTP 通信で指示を出す為の CLI です。2dockerd (moby)Docker サービスの本体で、コンテナの管理等をしている Engine API (HTTP) を持ったデーモンプロセスです。如果按照之前所述的方法使用docker run -d nginx:1.21-alpine命令,nginx将作为dockerd下的一个进程启动。
[ec2-user@ip-172-30-1-40 ~]$ ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
...
root 3566 0.0 2.1 1367168 43612 ? Ssl 06:47 0:04 /usr/bin/containerd
root 3757 0.0 4.1 1446720 82940 ? Ssl 06:48 0:05 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 2531 0.0 0.4 711720 9136 ? Sl 09:21 0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id ff6516b31969bfa33b
root 2554 0.0 0.2 6280 4516 ? Ss 09:21 0:00 \_ nginx: master process nginx -g daemon off;
101 2622 0.0 0.0 6736 1776 ? S 09:21 0:00 | \_ nginx: worker process
root 2742 0.0 0.0 1692 1180 pts/0 Ss+ 09:26 0:00 \_ sh
Docker 客户端(docker 命令)通过 HTTP 通信调用 API 向 dockerd 发出请求。
在这种情况下,Docker客户端不会使用TCP通信,而是通过/var/run/docker.sock套接字文件进行UNIX域套接字通信。

如果在Docker服务尚未启动时运行docker命令,通常会遇到下面的错误,这是因为无法建立UNIX域套接字连接所导致的。
$ docker ps
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Mac/Windows 的 Docker 桌面版
如前所述,Docker 是利用Linux的容器技术实现的。
因此,在Mac / Windows上,dockerd服务无法运行。
因此,Docker Desktop 使用Linux虚拟机来作为运行dockerd服务的基础设施。
然而,使用Linux虚拟机时,必须进入虚拟机中才能访问docker命令或已启动的容器(进程)。
然而,使用Linux虚拟机时,不进入虚拟机就无法访问docker命令或已启动的容器(进程)。
然而,如果使用Linux虚拟机,就必须进入虚拟机才能访问docker命令或者已启动的容器(进程)。
然而,当使用Linux虚拟机时,必须进入虚拟机才能使用docker命令或访问已启动的容器(进程)。
然而,使用Linux虚拟机的话,必须进入虚拟机才能访问docker命令或已启动的容器(进程)。
然而,当然,如果使用Linux虚拟机,则只有进入虚拟机才能访问docker命令或已启动的容器(进程)。
因此,Docker Desktop引入了下图所示的机制,以确保无需进入VM也可以正常运行。

/var/run/docker.sock被挂载
在Docker Desktop中,为了使主机上的Docker客户端(docker命令)可以与Linux虚拟机中的dockerd进行通信,主机上会准备/var/run/docker.sock。
$ ls -alht /var/run/docker.sock
lrwxr-xr-x 1 root daemon 38B Jun 9 15:13 /var/run/docker.sock -> /Users/{user-name}/.rd/docker.sock
因此,诸如在主机上运行的docker ps命令将被传递到Linux虚拟机中的dockerd。
容器的本地端口已经进行了端口转发
在启动容器时,通过指定 -p 8080:80 选项,可以使本地端口 :8080 在容器中以 :80 的方式进行监听。
docker run -d --name nginx -p 8080:80 nginx:1.21-alpine
但是,在这种情况下,Linux虚拟机应该正在监听本地端口:8080,所以即使在宿主机的浏览器上访问下面的URL,通常也无法建立HTTP连接。
- http://localhost:8080

原本应该是这样的,但是…?
然而实际上,通过Docker Desktop可以直接从主机访问。
这是因为Linux VM(虚拟机)上的容器的本地端口被端口转发到主机上。

牧场主桌面
很遗憾,Docker Desktop 在2022年2月开始收费了。
- https://www.docker.com/pricing/
作为2022年6月的搬家首选,推荐使用Rancher Desktop。
- https://rancherdesktop.io/
Rancher Desktop几乎和Docker Desktop有相同的使用体验,可以在主机上完成Docker操作。
CPU仿真支持(M1 Mac arm64问题)
通过 Rancher Desktop,可以在具有 CPU 模拟功能的 arm64 架构的计算机上(即 M1 Mac)运行 x86_64(Intel CPU)的 Docker 镜像,与 Docker Desktop 相同。
我认为很多情况下都会使用docker-compose来配置开发环境,但是当从Docker Hub获取镜像时,docker pull将自动获取与主机CPU架构相匹配的Docker镜像。

如果您正在使用类似mysql的OS/ARCH为linux/amd64的Docker镜像,那么在M1 Mac上将无法运行,因为没有适用于该平台的镜像。

然而,Rancher Desktop 支持 CPU 模拟,所以可以将 OS/ARCH 设定为 linux/amd64,以便使 docker pull 可以正常运行,从而继续保持之前的功能。
version: '3.1'
services:
db:
image: mysql
+ platform: 'linux/amd64'
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: example
虽然如此,不过性能方面遗憾地会有所下降… , de huì suǒ …)