使用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 几乎没有文件系统足迹!

四、总结

好吧,我希望这是一个有趣的练习至少对我来说,现在我明白了为什么一半的容器管理命令看起来像文件管理操作——容器与文件和进程一样重要。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐