dind(docker in docker)镜像-使用Docker学习Docker
您是否曾经注意到,一半的容器管理命令看起来像进程管理操作,而另一半则看起来像文件管理操作?
文章目录
使用Docker学习Docker
Learning Docker with Docker - Toying With DinD For Fun And Profit
您是否曾经注意到,一半的容器管理命令看起来像进程管理操作,而另一半则看起来像文件管理操作?
这里有一个小练习来加深你对容器的理解… 通过玩转它们,我们的目标是证明容器不仅仅是 Linux 进程,它也是 Linux文件!
这个想法很简单——用一台安装了 Docker 守护进程的 Linux 机器,在上面运行一系列常用的命令,比如 Docker create | start | exec | … 密切关注机器的文件系统,希望能有一些有趣的发现。
🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱
一、搭建演示环境
首先,简单介绍一下临时演示环境。基本上,任何运行 Docker 的干净 Linux 机器都可以。但是由于我们想要跟踪主机文件系统上 created/deleted/modified
的文件,我们应该在每个 docker <command>
之后对它进行快照,以便之后对比这些快照。
这听起来确实像是 Docker-in-Docker 的一个很好的用例。如果实验的Docker 守护进程本身运行在容器中,我们可以随时使用标准 docker commit <CONTAINER>
命令将容器的文件系统转储到 image layer。由于容器镜像可以很容易地进行对比,我们可以使用类似 container-diff
的工具来计算两个对比快照的文件差异。
但是官方的 docker:dind容器镜像 不能用,因为它包含一个特定的 VOLUME 指令。该指令存在的理由很充分——将 /var/lib/docker
文件夹移出容器的(slow and expensive)联合文件系统。然而,正如你马上会看到的,这个文件夹将是我们实验中最热门的位置之一。因此,我们需要确保将/var/lib/docker
提交到快照镜像,不需要volumes。
$ git clone https://github.com/docker-library/docker.git
$ cd docker/20.10/dind
$ sed -i '/VOLUME/d' ./Dockerfile
$ docker build -t my-dind:origin .
另一个重要的要求是保持环境的足够小和可控性。为此,我们需要使用一个超小的测试容器镜像,其中包含我们在对实验Docker 守护进程(guinea-pig Docker daemon)进行实验时所需要的内容。一个从头开始的镜像,里面有一个简单的 Go 二进制文件,听起来是一个不错的选择。但是,这个镜像应该在实例构建容器镜像之外构建,因为这涉及到运行临时容器,因此可能会破坏临时环境。
There are many ways to build and distribute images, but for our experiment, I ended up with probably the simplest possible setup:
有许多方法可以构建和发布镜像,但是对于我们的实验,最终得到了可能是最简单的设置:
以下是如何在运行 Docker 的Linux计算机上运行上述设置:
# 1. Prepare config to make Docker trust the local registry:
$ cat > daemon.json <<EOF
{
"insecure-registries" : ["my-registry:5000"]
}
EOF
# 2. 将上述配置放入
# the host's /etc/docker/daemon.json
# 3. Restart the host's Docker daemon:
$ sudo systemctl restart docker.service
# 4. Create a user-defined network to allow the DinD container
# access the Registry container by its hostname:
$ docker network create skynet
# 5. Run an (insecure) local registry:
$ docker run --detach \
--network skynet \
--publish 5000:5000 \
--name my-registry \
registry:2
# 6. Make the local registry addressible from the host system:
$ REGISTRY_IP=$(docker inspect \
-f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
my-registry)
$ echo "$REGISTRY_IP my-registry" | sudo tee --append /etc/hosts
# 7. Run the guinea-pig Docker daemon using the patched DinD image:
$ docker run --detach --privileged \
--network skynet \
--volume `pwd`/daemon.json:/etc/docker/daemon.json \
--name my-dind \
my-dind:origin
# 8. Commit the initial state of the guinea-pig Docker system
# as the starting point for further comparisons:
$ docker commit my-dind my-dind:just-started
# [Optional] See what changes have been made to my-dind
# container's filesystem during the container's startup:
$ container-diff diff --type=file \
daemon://my-dind:origin \
daemon://my-dind:just-started
现在,准备测试应用程序——一个简单的 HTTP 程序和一个sleep的cli工具:
# syntax=docker/dockerfile:1.4
# ---=== This part is just for building ===---
FROM golang:1.18 as builder
WORKDIR /
COPY <<EOF server.go
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
log.Println("Starting HTTP server...")
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
log.Println("Incoming request")
fmt.Fprintf(w, "hello\n")
})
http.ListenAndServe(":8090", nil)
}
EOF
COPY <<EOF sleep.go
package main
import (
"log"
"time"
)
func main() {
for {
log.Println("Zzz...")
time.Sleep(1 * time.Second)
}
}
EOF
RUN CGO_ENABLED=0 go build -o server server.go
RUN CGO_ENABLED=0 go build -o sleep sleep.go
# ---=== This is the actual testing image, quite minimalistic ===---
FROM scratch
COPY --from=builder /server /server
COPY --from=builder /sleep /sleep
CMD ["/server"]
Last but not least:
docker buildx build -t my-registry:5000/my-app .
docker push my-registry:5000/my-app
二、关联基础
docker image 文件系统
一图看尽 Docker 容器文件系统
参考URL: https://zhuanlan.zhihu.com/p/362132467
docker 容器启动就是一个文件系统的启动。在docker中,每一层镜像都具备一些文件。
构建镜像的时候,从一个最基本的操作系统开始,每个构建的操作都相当于做一层的修改,增加了一层文件系统。一层层往上叠加,上层的修改会覆盖底层该位置的可见性,这也很容易理解,就像上层把底层遮住了一样。当你使用的时候,你只会看到一个完全的整体,你不知道里面有几层,也不清楚每一层所做的修改是什么。
从基本的看起,一个典型的Linux文件系统由bootfs和rootfs两部分组成,bootfs(boot file system)主要包含bootloader和kernel,bootloader主要用于引导加载kernel,当kernel被加载到内存中后bootfs会被umount掉。rootfs (root file system)包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。不同的linux发行版(如ubuntu和CentOS )在rootfs这一层会有所区别,体现发行版本的差异性。
但Docker在bootfs自检完毕之后并不会把rootfs的read-only改为read-write,而是利用union mount(UnionFS的一种挂载机制)将image中的其他的layer加载到之前的read-only的rootfs层之上,每一层layer都是rootfs的结构,并且是read-only的。所以,我们是无法修改一个已有镜像里面的layer的!只有当我们创建一个容器,也就是将Docker镜像进行实例化,系统会分配一层空的read-write的rootfs,用于保存我们做的修改。一层layer所保存的修改是增量式的,就像git一样。
综上,image其实就是一个文件系统,它与宿主机的内核一起为程序提供一个虚拟的linux环境。在启动docker container时,依据image,docker会为container构建出一个虚拟的linux环境。
Docker目前支持五种镜像层次的存储driver:aufs、device mapper、btrfs、vfs、overlay。
其中最常用的就是aufs了,但随着linux内核3.18把overlay纳入其中,overlay的地位变得更重目前docker默认的存储类型就是overlay2,docker版本是1.8,如下
docker默认的存储目录是/var/lib/docker,我们只关心image和overlay2,image:主要存放镜像中layer层的元数据和overlay2:各层的具体信息。
什么是image layer
Dockerfile由多条指令构成,Dockerfile中的每一条指令都会对应于Docker镜像中的一层。
如下Dockerfile
FROM ubuntu:14.04
ADD run.sh /
VOLUME /data
CMD ["./run.sh"]
通过docker build以上Dockerfile的时候,会在Ubuntu:14.04镜像基础上,添加三层独立的镜像,依次对应于三条不同的命令。镜像示意图如下:
Dockerfile中命令与镜像层一一对应,那么是否意味着docker build完毕之后,镜像的总大小=每一层镜像的大小总和呢?答案是肯定的。依然以上图为例:如果ubuntu:14.04镜像的大小为200MB,而run.sh的大小为5MB,那么以上三层镜像从上到下,每层大小依次为0、0以及5MB,那么最终构建出的镜像大小的确为0+0+5+200=205MB。
Dockerfile VOLUME(数据卷) 指令
有状态容器都有数据持久化需求。Docker 采用 AFUS 分层文件系统时,文件系统的改动都是发生在最上面的容器层。在容器的生命周期内,它是持续的,包括容器在被停止后。但是,当容器被删除后,该数据层也随之被删除了。因此,Docker 采用 volume (卷)的形式来向容器提供持久化存储。
想要了解Docker Volume,首先我们需要知道Docker的文件系统是如何工作的。Docker镜像是由多个文件系统(只读层)叠加而成。当我们启动一个容器的时候,Docker会加载只读镜像层并在其上(译者注:镜像栈顶部)添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除Docker容器,并通过该镜像重新启动时,之前的更改将会丢失。在Docker中,只读层及在顶部的读写层的组合被称为Union File System(联合文件系统)。
为了能够保存(持久化)数据以及共享容器间的数据,Docker提出了Volume的概念。简单来说,Volume就是目录或者文件,它可以绕过默认的联合文件系统,而以正常的文件或者目录的形式存在于宿主机上。
Volume:即数据卷。
- Docker Volume命令能让容器从宿主主机中读取文件,或从容器中持久化数据到宿主主机内,让容器与容器产生的数据分离开来,一个容器可以挂载多个不同的目录。
- Volume的生命周期是独立于容器的生命周期之外的,即使容器删除了,volume(数据卷)也会被保留下来,Docker也不会因为这个volume(数据卷)没有被容器使用而回收。
- 在容器中,添加或修改这个文件夹里的文件也不会影响容器的联合文件系统。
作用:创建一个匿名数据卷挂载点
格式:
VOLUME ["/data"]
详解:运行容器时可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保持的数据等
这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化
Volume命令的使用
(1)创建数据卷
命令:docker volume create 自定义名称
root@cka-k8s-master:~# docker volume create myVolume
myVolume
root@cka-k8s-master:~#
每创建一个Volume,Docker默认会在宿主机的/var/lib/docker/volumes/目录下创建一个子目录,默认情况下目录名是一串UUID。
如果指定了名称,则目录名是Volume名称(例如上面的myVolume)。Volume里的数据都存储在这个子目录的_data目录下。
之后我们可以把这个数据卷挂载到一个新的容器中,例如Nginx容器。
(2)查看本地数据卷列表
命令:docker volume ls
root@cka-k8s-master:~# docker volume ls
DRIVER VOLUME NAME
local myVolume
root@cka-k8s-master:~#
(3)打印myVolume数据卷的详细信息
命令:docker volume inspect 一个或多个Volume名称
root@cka-k8s-master:~# docker volume inspect myVolume
[
{
"CreatedAt": "2022-04-27T10:21:53Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/data/docker/volumes/myVolume/_data",
"Name": "myVolume",
"Options": {},
"Scope": "local"
}
]
root@cka-k8s-master:~#
-
每创建一个Volume,Docker默认会在宿主机的/var/lib/docker/volumes/目录下创建一个子目录,默认情况下目录名是一串UUID。
-
如果指定了名称,则目录名是Volume名称(例如上面的myVolume)。Volume里的数据都存储在这个子目录的_data目录下。
具名挂载和匿名挂载
(1)匿名挂载
匿名挂载格式:-v /容器内路径或者-v /宿主机路径:/容器内路径
(2)具名挂载
指令:docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
注意:这里 -v juming-nginx: 代表直接给定名字,但是没有指定路径
如图,查看数据卷一栏增加了刚刚添加的数据卷。
什么是container-diff
官方github: https://github.com/GoogleContainerTools/container-diff
container-diff 是 Google 开源的一个分析和比较容器镜像的工具,可用来分析 Docker 镜像之间的差异。
container-diff 可通过几个不同的标准(角度)来检查镜像,包括:
- Docker 镜像历史
- 镜像文件系统
- Apt 包管理器
- pip 包管理器
- npm 包管理器
使用
分析单个Docker镜像
container-diff analyze <image-name>
对比两个Docker镜像
container-diff diff <image1-name> <image2-name>
如果不指定type,默认分析/对比的是镜像大小,即–type=size
可以通过指定type,分析/对比特定维度
container-diff analyze <image-name> --type=<type-name>
container-diff diff <image1-name> <image2-name> --type=<type-name>
type类型支持如下:
- history:镜像构建历史
- file:镜像文件
- size:镜像大小
- rpm:rpm包管理器
- pip:pip包管理器
- apt:apt包管理器
- node:node包管理器
通过设置--type=file和--filename=/path/file
,可以比较比较两个docker镜像中某目录或文件的区别,例如:
container-diff diff nginx:v1 nginx:v2 --type=file --filename=/etc/
复制代码通过设置-j,可以使用json格式输出结果。
通过设置-w ,可以将结果输入到文件。
三、演示时间
For the experiment itself, I’ll use two terminals simultaneously:
对于实验本身,我将同时使用两个终端:
如下,我们进入到 my-dind 容器中,使用docker ps,在容器中我们看到其他容器- -!这就是容器中的容器!
# Terminal 1 - DinD
$ docker exec -it my-dind sh
$ docker ps
<empty>
# Terminal 2 - host system
$ docker ps
CONTAINER ID IMAGE COMMAND ... STATUS NAMES
d07b66a353b0 my-dind:origin "dockerd-entrypoint.…" ... Up 25 minutes my-dind
9a6addf796f6 registry:2 "/entrypoint.sh /etc…" ... Up 26 minutes my-registry
探索 docker create 命令
第一个实验——使用 DinD Docker 实例创建一个 my-app 容器,然后看看会创建哪些文件:
使用两个终端进行实验-DinD 在左边,主机在右边。
# Terminal 1 (DinD)
# Create container:
$ docker create --name my-app my-registry:5000/my-app
# List existing containers:
$ docker ps -a
CONTAINER ID IMAGE COMMAND ... STATUS NAMES
2c23a0da2b19 my-registry:5000/my-app "/server" Created my-app
# List running processes:
$ ps auxf
PID USER TIME COMMAND
1 root 0:00 docker-init -- dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.
61 root 0:00 dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376 --tlsv
70 root 0:21 containerd --config /var/run/docker/containerd/containerd.toml --log-level
176 root 0:00 sh
232 root 0:00 ps auxf
发现 # 1: docker create 创建了一个容器,但是没有创建任何新的进程!
# Terminal 2 (Host)
# Snapshot the DinD container filesystem:
$ docker commit my-dind my-dind:cont-created
# Compare the current state with the previous one:
$ container-diff diff --type=file \
daemon://my-dind:just-started \
daemon://my-dind:cont-created
-----File-----
These entries have been added to my-dind:just-started: # <-- looks like a bug in container-diff output
FILE
...
# This group of files seems important
/var/lib/docker/buildkit/cache.db
/var/lib/docker/buildkit/containerdmeta.db
/var/lib/docker/buildkit/content
/var/lib/docker/buildkit/content/ingest
/var/lib/docker/buildkit/executor
/var/lib/docker/buildkit/metadata_v2.db
/var/lib/docker/buildkit/snapshots.db
# So... This is where containers live on disk!
/var/lib/docker/containers/<CONTAINER-ID>
/var/lib/docker/containers/<CONTAINER-ID>/checkpoints
/var/lib/docker/containers/<CONTAINER-ID>/config.v2.json
/var/lib/docker/containers/<CONTAINER-ID>/hostconfig.json
# And this is where images live
/var/lib/docker/image
...
# Look ma', our `app` image files!
/var/lib/docker/vfs/dir/<LAYER-ID1>
/var/lib/docker/vfs/dir/<LAYER-ID1>/server
/var/lib/docker/vfs/dir/<LAYER-ID2>
/var/lib/docker/vfs/dir/<LAYER-ID2>/server
/var/lib/docker/vfs/dir/<LAYER-ID2>/sleep
...
These entries have been deleted from my-dind:just-started: None
These entries have been changed between my-dind:just-started and my-dind:cont-created:
FILE
/certs/...
发现 # 2:
/var/lib/docker/containers/<container-id>
——这是我们的容器在磁盘存储的文件。
发现 # 3:
docker create
似乎与runc create
非常不同——到目前为止还没有创建任何运行时绑定!
Finding #4: Container logs aren’t a thing at this stage yet. The container-diff output above is abridged, but if you’re performing this exercise while reading the article, try searching for files containing the log word in their name - you won’t find anything.
发现 # 4: 在此阶段,容器日志还找不到。上面的container-diff输出是删减的,但是如果您在阅读文章时执行此练习,请尝试搜索包含其名称的日志单词的文件 - 您找不到任何东西。
探索 docker start 命令
是时候启动 my-app 容器了:
# Terminal 1 (DinD)
$ docker start my-app
$ docker ps -a
CONTAINER ID IMAGE COMMAND ... STATUS NAMES
435edb948b83 my-registry:5000/my-app "/server" Up 21 seconds my-app
$ ps auxf
PID USER TIME COMMAND
1 root 0:00 docker-init -- dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376 --tlsverify --tlscacert /certs/server/ca.pem --tlscert /certs/server/cert.pem --tlskey /certs/server/key.pem
60 root 0:00 dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376 --tlsverify --tlscacert /certs/server/ca.pem --tlscert /certs/server/cert.pem --tlskey /certs/server/key.pem
69 root 0:07 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
210 root 0:00 sh
265 root 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace moby -id 435edb948b8360ffcbae5452fd6fc0451b5c17daf6940f63db6795c099958357 -address /var/run/docker/containerd/containerd.sock
👉284 root 0:00 /server
320 root 0:00 ps auxf
那么,已经创建了哪些文件?
# Terminal 2 (Host)
$ docker commit my-dind my-dind:cont-started
# Compare the current state with the previous one:
$ container-diff diff --type=file \
daemon://my-dind:cont-created \
daemon://my-dind:cont-started
-----File-----
These entries have been added to my-dind:cont-created: # <-- rather in cont-started...
FILE
...
# Here it goes - the OCI runtime bundle!
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/address
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/config.json
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/init.pid
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/log.json
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/options.json
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/rootfs
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/runtime
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/work
...
发现 # 5: OCI runtime bundle 是在容器启动时创建的,而不是在容器创建时创建的。
发现 # 6: bundle 是在临时文件系统上创建的! 我现在很好奇——它总是这样吗?
发现 #7: Container logs finally appeared, and it’s just a plain file on disk (depends on the log driver, though):
最终出现了容器日志,它只是磁盘上的一个普通文件(不过取决于日志驱动程序):
📦 # Terminal 1 (DinD)
cat /var/lib/docker/containers/<CONTAINER-ID>/<CONTAINER-ID>-json.log
{"log":"2022/04/26 05:21:59 Starting HTTP server...\n","stream":"stderr","time":"2022-04-26T05:21:59.3588249Z"}
探索 docker stop 命令
那么,当您停止容器时会发生什么? 它的文件会被删除吗?
# Terminal 1 (DinD)
$ docker stop my-app
$ docker ps -a
CONTAINER ID IMAGE COMMAND ... STATUS NAMES
435edb948b83 my-registry:5000/my-app "/server" Exited (2) 2 seconds ago my-app
$ ps auxf
PID USER TIME COMMAND
1 root 0:00 docker-init -- dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376 --tlsverify --tlscacert /certs/server/ca.pem --tlscert /certs/server/cert.pem --tlskey /certs/server/key.pem
60 root 0:00 dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376 --tlsverify --tlscacert /certs/server/ca.pem --tlscert /certs/server/cert.pem --tlskey /certs/server/key.pem
69 root 0:10 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
210 root 0:00 sh
372 root 0:00 ps auxf
发现 # 8: 停止容器会删除 OCI runtime bundle,但不会删除容器
在/var/lib/docker/<container-id>
的状态(除非 --rm 在创建步骤中被使用)。所以,重新开始是可能的!
探索 docker exec 命令
让我们重复这个实验,但是这一次试着捕捉当你执行到一个运行的容器中时会发生什么:
# Terminal 1 (DinD)
$ docker start my-app
# ...jump to the second terminal for a second
# Terminal 2 (Host)
$ docker commit my-dind my-dind:cont-restarted
# ...back to the DinD terminal
# Terminal 1 (DinD)
$ docker exec -it my-app /sleep
2022/04/21 19:19:52 Zzz...
2022/04/21 19:19:53 Zzz...
2022/04/21 19:19:54 Zzz...
2022/04/21 19:19:55 Zzz...
2022/04/21 19:19:56 Zzz...
# Terminal 3 (also DinD)
$ ps auxf
PID USER TIME COMMAND
1 root 0:00 docker-init -- dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376 --tlsverify --tlscacert /certs/server/ca.pem --tlscert /certs/server/cert.pem --tlskey /certs/server/key.pem
60 root 0:00 dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376 --tlsverify --tlscacert /certs/server/ca.pem --tlscert /certs/server/cert.pem --tlskey /certs/server/key.pem
69 root 0:07 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
210 root 0:00 sh
265 root 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace moby -id 435edb948b8360ffcbae5452fd6fc0451b5c17daf6940f63db6795c099958357 -address /var/run/docker/containerd/containerd.sock
👉284 root 0:00 /server
320 root 0:00 ps auxf
👉363 root 0:00 /sleep
但是在实际执行期间究竟发生了什么?
# Terminal 2 (Host)
$ docker commit my-dind my-dind:cont-execed
$ container-diff diff --type=file \
daemon://my-dind:cont-restarted \
daemon://my-dind:cont-execed
-----File-----
These entries have been added to my-dind:cont-restarted:
FILE
/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/<CONTAINER-ID>/<SOME-OTHER-ID>.pid
These entries have been deleted from my-dind:cont-restarted: None
These entries have been changed between my-dind:cont-restarted and my-dind:cont-execed: None
好吧,这是令人惊讶的!我期待着为 exec 会话创建另一个临时(和匿名)容器,因为 exec 不是真正的 OCI 运行时命令,而只是由 Docker 和类似的容器管理器实现的一个方便的高级操作,它重用 OCI 运行时 start 命令。但是实际的实现似乎非常轻量级,并且使用与主容器相同的 OCI runtime bundle。
发现 # 9: docker exec 几乎没有文件系统足迹!
四、总结
好吧,我希望这是一个有趣的练习至少对我来说,现在我明白了为什么一半的容器管理命令看起来像文件管理操作——容器与文件和进程一样重要。
更多推荐
所有评论(0)