containerd v1.1.2を動かして色々操作してみた

containerdを動かしてコンテナを作ったりイメージをプルしたり色々やってみようっていう内容です。containerdに対する操作はCLIツールのctrを使用します。

containerdのインストールと実行

GitHubのリリースページよりtarballをダウンロードしてきます。今回はバージョンv1.1.2をダウンロードします。

containerd-1.1.2.linux-amd64.tar.gzはビルド済みのバイナリファイルが入ってるので、解凍すればすぐ使えて便利です。

github.com

ダウンロードして解凍するとbinディレクトリが出てきます。その中にはcontainerdの他にctrなどのツールも入ってます。

ubuntu@v-Ubuntu:~$ wget https://github.com/containerd/containerd/releases/download/v1.1.2/containerd-1.1.2.linux-amd64.tar.gz
ubuntu@v-Ubuntu:~$ tar -xzf containerd-1.1.2.linux-amd64.tar.gz
ubuntu@v-Ubuntu:~$ rm containerd-1.1.2.linux-amd64.tar.gz 
ubuntu@v-Ubuntu:~$ cd bin
ubuntu@v-Ubuntu:~/bin$ ls
containerd  containerd-release  containerd-shim  containerd-stress  ctr
ubuntu@v-Ubuntu:~/bin$ sudo cp * /usr/bin/

さっそくcontianerdを起動してみましょう。

ubuntu@v-Ubuntu:~/bin$ sudo containerd
INFO[0000] starting containerd                           revision=468a545b9edcd5932818eb9de8e72413e616e86e version=v1.1.2
INFO[0000] loading plugin "io.containerd.content.v1.content"...  type=io.containerd.content.v1
INFO[0000] loading plugin "io.containerd.snapshotter.v1.btrfs"...  type=io.containerd.snapshotter.v1
WARN[0000] failed to load plugin io.containerd.snapshotter.v1.btrfs  error="path /var/lib/containerd/io.containerd.snapshotter.v1.btrfs must be a btrfs filesystem to be used with the btrfs snapshotter"
INFO[0000] loading plugin "io.containerd.snapshotter.v1.aufs"...  type=io.containerd.snapshotter.v1
INFO[0000] loading plugin "io.containerd.snapshotter.v1.native"...  type=io.containerd.snapshotter.v1
INFO[0000] loading plugin "io.containerd.snapshotter.v1.overlayfs"...  type=io.containerd.snapshotter.v1
INFO[0000] loading plugin "io.containerd.snapshotter.v1.zfs"...  type=io.containerd.snapshotter.v1
WARN[0000] failed to load plugin io.containerd.snapshotter.v1.zfs  error="path /var/lib/containerd/io.containerd.snapshotter.v1.zfs must be a zfs filesystem to be used with the zfs snapshotter"
INFO[0000] loading plugin "io.containerd.metadata.v1.bolt"...  type=io.containerd.metadata.v1
WARN[0000] could not use snapshotter zfs in metadata plugin  error="path /var/lib/containerd/io.containerd.snapshotter.v1.zfs must be a zfs filesystem to be used with the zfs snapshotter"
WARN[0000] could not use snapshotter btrfs in metadata plugin  error="path /var/lib/containerd/io.containerd.snapshotter.v1.btrfs must be a btrfs filesystem to be used with the btrfs snapshotter"
INFO[0000] loading plugin "io.containerd.differ.v1.walking"...  type=io.containerd.differ.v1
INFO[0000] loading plugin "io.containerd.gc.v1.scheduler"...  type=io.containerd.gc.v1
INFO[0000] loading plugin "io.containerd.service.v1.containers-service"...  type=io.containerd.service.v1
INFO[0000] loading plugin "io.containerd.service.v1.content-service"...  type=io.containerd.service.v1
INFO[0000] loading plugin "io.containerd.service.v1.diff-service"...  type=io.containerd.service.v1
INFO[0000] loading plugin "io.containerd.service.v1.images-service"...  type=io.containerd.service.v1
INFO[0000] loading plugin "io.containerd.service.v1.leases-service"...  type=io.containerd.service.v1
INFO[0000] loading plugin "io.containerd.service.v1.namespaces-service"...  type=io.containerd.service.v1
INFO[0000] loading plugin "io.containerd.service.v1.snapshots-service"...  type=io.containerd.service.v1
INFO[0000] loading plugin "io.containerd.monitor.v1.cgroups"...  type=io.containerd.monitor.v1
INFO[0000] loading plugin "io.containerd.runtime.v1.linux"...  type=io.containerd.runtime.v1
INFO[0000] loading plugin "io.containerd.service.v1.tasks-service"...  type=io.containerd.service.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.containers"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.content"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.diff"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.events"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.healthcheck"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.images"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.leases"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.namespaces"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.snapshots"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.tasks"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.version"...  type=io.containerd.grpc.v1
INFO[0000] loading plugin "io.containerd.grpc.v1.cri"...  type=io.containerd.grpc.v1
INFO[0000] Start cri plugin with config {PluginConfig:{ContainerdConfig:{Snapshotter:overlayfs DefaultRuntime:{Type:io.containerd.runtime.v1.linux Engine: Root:} UntrustedWorkloadRuntime:{Type: Engine: Root:}} CniConfig:{NetworkPluginBinDir:/opt/cni/bin NetworkPluginConfDir:/etc/cni/net.d NetworkPluginConfTemplate:} Registry:{Mirrors:map[docker.io:{Endpoints:[https://registry-1.docker.io]}]} StreamServerAddress: StreamServerPort:10010 EnableSelinux:false SandboxImage:k8s.gcr.io/pause:3.1 StatsCollectPeriod:10 SystemdCgroup:false EnableTLSStreaming:false MaxContainerLogLineSize:16384} ContainerdRootDir:/var/lib/containerd ContainerdEndpoint:/run/containerd/containerd.sock RootDir:/var/lib/containerd/io.containerd.grpc.v1.cri StateDir:/run/containerd/io.containerd.grpc.v1.cri} 
INFO[0000] Connect containerd service                   
INFO[0000] Get image filesystem path "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs" 
ERRO[0000] Failed to load cni during init, please check CRI plugin status before setting up network for pods  error="cni config load failed: no network config found in /etc/cni/net.d: cni plugin not initialized: failed to load cni config"
INFO[0000] loading plugin "io.containerd.grpc.v1.introspection"...  type=io.containerd.grpc.v1
INFO[0000] Start subscribing containerd event           
INFO[0000] Start recovering state                       
INFO[0000] serving...                                    address="/run/containerd/containerd.sock"
INFO[0000] containerd successfully booted in 0.032078s  
INFO[0000] Start event monitor                          
INFO[0000] Start snapshots syncer                       
INFO[0000] Start streaming server                       

CNIのエラーが起きていますが、今回はお触りするだけなのでほっといていいです。

containerdの操作

ctrを使うことでCLIからcontainerdを操作することができます。

ubuntu@v-Ubuntu:~$ sudo ctr
NAME:
   ctr - 
        __
  _____/ /______
 / ___/ __/ ___/
/ /__/ /_/ /
\___/\__/_/

containerd CLI


USAGE:
   ctr [global options] command [command options] [arguments...]

VERSION:
   v1.1.2

COMMANDS:
     plugins, plugin           provides information about containerd plugins
     version                   print the client and server versions
     containers, c, container  manage containers
     content                   manage content
     events, event             display containerd events
     images, image, i          manage images
     namespaces, namespace     manage namespaces
     pprof                     provide golang pprof outputs for containerd
     run                       run a container
     snapshots, snapshot       manage snapshots
     tasks, t, task            manage tasks
     shim                      interact with a shim directly
     cri                       interact with cri plugin
     help, h                   Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --debug                      enable debug output in logs
   --address value, -a value    address for containerd's GRPC server (default: "/run/containerd/containerd.sock")
   --timeout value              total timeout for ctr commands (default: 0s)
   --connect-timeout value      timeout for connecting to containerd (default: 0s)
   --namespace value, -n value  namespace to use with commands (default: "default") [$CONTAINERD_NAMESPACE]
   --help, -h                   show help
   --version, -v                print the version
ubuntu@v-Ubuntu:~$

イメージのプル

ctr image pullでプルができます。

ubuntu@v-Ubuntu:~$ sudo ctr image pull --help
NAME:
   ctr images pull - pull an image from a remote

USAGE:
   ctr images pull [command options] [flags] <ref>

DESCRIPTION:
   Fetch and prepare an image for use in containerd.

After pulling an image, it should be ready to use the same reference in a run
command. As part of this process, we do the following:

1. Fetch all resources into containerd.
2. Prepare the snapshot filesystem with the pulled resources.
3. Register metadata for the image.


OPTIONS:
   --skip-verify, -k       skip SSL certificate validation
   --plain-http            allow connections using plain HTTP
   --user value, -u value  user[:password] Registry user and password
   --refresh value         refresh token for authorization server
   --snapshotter value     snapshotter name. Empty value stands for the default value. (default: "overlayfs") [$CONTAINERD_SNAPSHOTTER]
   --label value           labels to attach to the image
   --platform value        Pull content from a specific platform (default: "linux/amd64")
   --all-platforms         pull content from all platforms
   
ubuntu@v-Ubuntu:~$ 

docker pullでは引数にイメージ名を渡すだけでdocker.io/library/<image name>:latestなイメージをプルすることができましたが、ctrでは「ドメインリポジトリ、イメージ名、タグ」を全て指定しないとできないようです。ちょいと面倒臭い。

ubuntu@v-Ubuntu:~$ sudo ctr image pull redis
ctr: failed to resolve reference "redis": object required
ubuntu@v-Ubuntu:~/bin$ sudo ctr image pull redis:latest
ctr: failed to resolve reference "redis:latest": object required
ubuntu@v-Ubuntu:~/bin$ sudo ctr image pull docker.io/redis:latest
docker.io/redis:latest: resolving      |--------------------------------------| 
elapsed: 1.6 s          total:   0.0 B (0.0 B/s)                                         
ctr: failed to resolve reference "docker.io/redis:latest": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
ubuntu@v-Ubuntu:~/bin$ sudo ./ctr image pull docker.io/library/redis:latest
docker.io/library/redis:latest:                                                   resolved       |++++++++++++++++++++++++++++++++++++++| 
index-sha256:096cff9e6024603decb2915ea3e501c63c5bb241e1b56830a52acfd488873843:    done           |++++++++++++++++++++++++++++++++++++++| 
manifest-sha256:f20ec679e1bdf0b6a83b4b4d4097dddf08edf0aa3cc6ff2ef4b9afebb0c8b5ef: done           |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:ce8aaa9fe90ad0dada1d2b2fed22ee9bb64bcfc1a5a4d5f7d2fe392df35050aa:    done           |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:d6f5ea773ca39a0094e300aec4c30478b616d1631382c6eba21fdc2fa20c0385:    done           |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:735cc65c0db489710ba88840b2075d6d4195f0d0c5dfc5e59fdfc6243aca0daf:    done           |++++++++++++++++++++++++++++++++++++++| 
config-sha256:f06a5773f01e1f77eb4487acb3333649716f45b3c32aad038765dc0ab0337bd4:   done           |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:ff89c30e4d8c53c3403ed2c09737963e5e1c2052f285e34c3ae3baf2d49b2017:    done           |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:59bf782a86b38d9f3b9e9c20588467b427460403d06146099026cdf1310a7285:    done           |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:be8881be8156e4068e611fe956aba2b9593ebd953be14fb7feea6d0659aa3abe:    done           |++++++++++++++++++++++++++++++++++++++| 
elapsed: 1.9 s                                                                    total:  9.2 Mi (4.8 MiB/s)                                       
unpacking sha256:096cff9e6024603decb2915ea3e501c63c5bb241e1b56830a52acfd488873843...
done
ubuntu@v-Ubuntu:~$ 

イメージ一覧の取得

ctr image lsで取得可能。

ubuntu@v-Ubuntu:~$ sudo ctr image ls --help
NAME:
   ctr images list - list images known to containerd

USAGE:
   ctr images list [command options] [flags] <ref>

DESCRIPTION:
   list images registered with containerd

OPTIONS:
   --quiet, -q  print only the image refs
   
ubuntu@v-Ubuntu:~$ 

ターミナルの横幅が狭いと一行で収まらないのがなんか嫌ですね(PLATFORMSとかTYPEのカラムはオプションで表示できるようにしたいんですけど...)

ubuntu@v-Ubuntu:~$ sudo ctr image ls
REF                            TYPE                                                      DIGEST                                                                  SIZE     PLATFORMS                                                                                LABELS 
docker.io/library/redis:latest application/vnd.docker.distribution.manifest.list.v2+json sha256:096cff9e6024603decb2915ea3e501c63c5bb241e1b56830a52acfd488873843 30.6 MiB linux/386,linux/amd64,linux/arm/v5,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x -      
ubuntu@v-Ubuntu:~/bin$ sudo ./ctr image ls -q
docker.io/library/redis:latest
ubuntu@v-Ubuntu:~$

コンテナの作成・起動

ctr runでできます。

ubuntu@v-Ubuntu:~$ sudo ctr run --help
NAME:
   ctr run - run a container

USAGE:
   ctr run [command options] [flags] Image|RootFS ID [COMMAND] [ARG...]

OPTIONS:
   --rm                      remove the container after running
   --null-io                 send all IO to /dev/null
   --detach, -d              detach from the task after it has started execution
   --fifo-dir value          directory used for storing IO FIFOs
   --snapshotter value       snapshotter name. Empty value stands for the default value. (default: "overlayfs") [$CONTAINERD_SNAPSHOTTER]
   --config value, -c value  path to the runtime-specific spec config file
   --checkpoint value        provide the checkpoint digest to restore the container
   --cwd value               specify the working directory of the process
   --env value               specify additional container environment variables (i.e. FOO=bar)
   --label value             specify additional labels (i.e. foo=bar)
   --mount value             specify additional container mount (ex: type=bind,src=/tmp,dst=/host,options=rbind:ro)
   --net-host                enable host networking for the container
   --privileged              run privileged container
   --read-only               set the containers filesystem as readonly
   --runtime value           runtime name (io.containerd.runtime.v1.linux, io.containerd.runtime.v1.windows, io.containerd.runtime.v1.com.vmware.linux) (default: "io.containerd.runtime.v1.linux")
   --tty, -t                 allocate a TTY for the container
   --with-ns value           specify existing Linux namespaces to join at container runtime (format '<nstype>:<path>')
   --pid-file value          file path to write the task's pid
   
ubuntu@v-Ubuntu:~$ 

ここでちょいと詰まります。redisのコンテナを動かそうとしたらOCI runtime errorなるものが発生。どうやらruncが必要なようです。

ubuntu@v-Ubuntu:~$ sudo ctr run -d docker.io/library/redis:latest redis1
ctr: OCI runtime create failed: unable to retrieve OCI runtime error (open /run/containerd/io.containerd.runtime.v1.linux/default/redis1/log.json: no such file or directory): exec: "runc": executable file not found in $PATH: unknown
ubuntu@v-Ubuntu:~$

というわけでruncをインストールします。GithHubのリリースページより1.0.0のバイナリファイルをダウンロードしました。

github.com

ubuntu@v-Ubuntu:~$ wget https://github.com/opencontainers/runc/releases/download/v1.0.0-rc5/runc.amd64
ubuntu@v-Ubuntu:~$ chmod +x runc.amd64 
ubuntu@v-Ubuntu:~$ sudo cp runc.amd64 /usr/bin/runc

これでどうじゃ!と思ったらまた詰まります。

ubuntu@v-Ubuntu:~$ sudo ctr run -d docker.io/library/redis:latest redis1
ctr: snapshot "redis1": already exists
ubuntu@v-Ubuntu:~$

redis1のスナップショットが存在するということです。スナップショットが何かということについてはあとで説明します。

ctr runが失敗してもスナップショットとコンテナ(ただし停止している)が作成されるようです。これらを削除するためにctr container rm redisを実行します。

ubuntu@v-Ubuntu:~$ sudo ctr container rm redis1

これでどうじゃ!

ubuntu@v-Ubuntu:~$ sudo ctr run -d docker.io/library/redis:latest redis1
1:C 30 Jul 11:18:28.266 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 30 Jul 11:18:28.266 # Redis version=4.0.10, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 30 Jul 11:18:28.266 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 30 Jul 11:18:28.267 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
1:M 30 Jul 11:18:28.267 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
1:M 30 Jul 11:18:28.267 # Current maximum open files is 1024. maxclients has been reduced to 992 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
1:M 30 Jul 11:18:28.267 * Running mode=standalone, port=6379.
1:M 30 Jul 11:18:28.267 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 30 Jul 11:18:28.267 # Server initialized
1:M 30 Jul 11:18:28.267 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 30 Jul 11:18:28.267 * Ready to accept connections
ubuntu@v-Ubuntu:~$

なんかうまくいったようです。

コンテナがRUNNINGになっているかを確認する

containerdでは起動中のコンテナ(プロセス)は「タスク」として扱います。

 ubuntu@v-Ubuntu:~$ sudo ctr task
NAME:
   ctr tasks - manage tasks

USAGE:
   ctr tasks command [command options] [arguments...]

COMMANDS:
     attach      attach to the IO of a running container
     checkpoint  checkpoint a container
     delete      [flags] delete a task
     exec        execute additional processes in an existing container
     list, ls    list tasks
     kill        signal a container (default: SIGTERM)
     pause       pause an existing container
     ps          list processes for container
     resume      resume a paused container
     start       start a container that have been created

OPTIONS:
   --help, -h  show help
   
ubuntu@v-Ubuntu:~$

ctr task lsでタスクの一覧を表示できます。

ubuntu@v-Ubuntu:~$ sudo ctr task ls
TASK      PID      STATUS    
redis1    16722    RUNNING
ubuntu@v-Ubuntu:~$

redis1が動いているのがわかりますね。

ついでにpsコマンドでどんなプロセスが動いているか確認しましょう。

ubuntu@v-Ubuntu:~$ ps axu
...
root     16705  0.0  0.0   7512  4184 pts/0    Sl   20:18   0:00 containerd-shim -namespace default -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/default/redis1 -address /run/containerd/containerd.sock -containerd-binary /home/ubuntu/bin/containerd
999      16722  0.0  0.0  41668  3528 ?        Ssl  20:18   0:00 redis-server *:6379
...
ubuntu@v-Ubuntu:~$

containerd-shimのプロセスがありますね。またredisは999というユーザーによって実行されているようです。

コンテナに入る

ctr task execdocker execライクなコマンドで、コンテナ内で特定のコマンドを実行できます。

ubuntu@v-Ubuntu:~$ sudo ctr task exec --help
NAME:
   ctr tasks exec - execute additional processes in an existing container

USAGE:
   ctr tasks exec [command options] [flags] CONTAINER CMD [ARG...]

OPTIONS:
   --cwd value       working directory of the new process
   --tty, -t         allocate a TTY for the container
   --exec-id value   exec specific id for the process
   --fifo-dir value  directory used for storing IO FIFOs
   
ubuntu@v-Ubuntu:~$ 

コンテナ内でシェルを起動してコンテナの中で操作をしてみましょう。

ubuntu@v-Ubuntu:~$ sudo ctr task exec -t redis1 /bin/bash 
ctr: exec id must not be empty: invalid argument
ubuntu@v-Ubuntu:~$ 

ありゃりゃ。exec idは絶対設定しないといけないようです。じゃあ適当にIDを指定して再度実行してみましょう。

ubuntu@v-Ubuntu:~$ sudo ctr task exec -t --exec-id 5000 redis1 /bin/bash 
root@v-Ubuntu:/data# cat /etc/hostname
debuerreotype
root@v-Ubuntu:/data# exit
exit
ubuntu@v-Ubuntu:~$ 

というわけでちゃんとコンテナの中に入れましたね。ちなみにctr attachでもコンテナの中に入れます(今回の場合はコンテナ内部でシェルが動いてないんでアタッチしたところで何もできませんが)

コンテナのリストやコンテナの情報を表示する

ubuntu@v-Ubuntu:~$ sudo ctr container
NAME:
   ctr containers - manage containers

USAGE:
   ctr containers command [command options] [arguments...]

COMMANDS:
     create           create container
     delete, del, rm  delete one or more existing containers
     info             get info about a container
     list, ls         list containers
     label            set and clear labels for a container

OPTIONS:
   --help, -h  show help
   
ubuntu@v-Ubuntu:~$

ctr container lsでコンテナ一覧を表示。停止しているコンテナも表示されます。今回はコンテナが1個だけ表示されます。

ubuntu@v-Ubuntu:~$ sudo ctr container ls 
CONTAINER    IMAGE                             RUNTIME                           
redis1       docker.io/library/redis:latest    io.containerd.runtime.v1.linux    
ubuntu@v-Ubuntu:~$

ctr container infoでコンテナの情報を見ることができます。

ubuntu@v-Ubuntu:~$ sudo ctr container info redis1
{
    "ID": "redis1",
    "Labels": null,
    "Image": "docker.io/library/redis:latest",
    "Runtime": {
        "Name": "io.containerd.runtime.v1.linux",
        "Options": null
    },
    "Spec": {
        "type_url": "types.containerd.io/opencontainers/runtime-spec/1/Spec",
        "value": "eyJvY2lWZXJzaW9uIjoiMS4wLjEiLCJwcm9jZXNzIjp7InVzZXIiOnsidWlkIjowLCJnaWQiOjB9LCJhcmdzIjpbImRvY2tlci1lbnRyeXBvaW50LnNoIiwicmVkaXMtc2VydmVyIl0sImVudiI6WyJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIiwiR09TVV9WRVJTSU9OPTEuMTAiLCJSRURJU19WRVJTSU9OPTQuMC4xMCIsIlJFRElTX0RPV05MT0FEX1VSTD1odHRwOi8vZG93bmxvYWQucmVkaXMuaW8vcmVsZWFzZXMvcmVkaXMtNC4wLjEwLnRhci5neiIsIlJFRElTX0RPV05MT0FEX1NIQT0xZGI2NzQzNWE3MDRmOGQxOGFlYzliOTYzN2IzNzNjMzRhYTIzM2Q2NWI2ZTE3NGJkYWM0YzFiMTYxZjM4Y2E0Il0sImN3ZCI6Ii9kYXRhIiwiY2FwYWJpbGl0aWVzIjp7ImJvdW5kaW5nIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl0sImVmZmVjdGl2ZSI6WyJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZTRVRJRCIsIkNBUF9GT1dORVIiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUVUlEIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUUENBUCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX1NZU19DSFJPT1QiLCJDQVBfS0lMTCIsIkNBUF9BVURJVF9XUklURSJdLCJpbmhlcml0YWJsZSI6WyJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZTRVRJRCIsIkNBUF9GT1dORVIiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUVUlEIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUUENBUCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX1NZU19DSFJPT1QiLCJDQVBfS0lMTCIsIkNBUF9BVURJVF9XUklURSJdLCJwZXJtaXR0ZWQiOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXX0sInJsaW1pdHMiOlt7InR5cGUiOiJSTElNSVRfTk9GSUxFIiwiaGFyZCI6MTAyNCwic29mdCI6MTAyNH1dLCJub05ld1ByaXZpbGVnZXMiOnRydWV9LCJyb290Ijp7InBhdGgiOiJyb290ZnMifSwibW91bnRzIjpbeyJkZXN0aW5hdGlvbiI6Ii9wcm9jIiwidHlwZSI6InByb2MiLCJzb3VyY2UiOiJwcm9jIn0seyJkZXN0aW5hdGlvbiI6Ii9kZXYiLCJ0eXBlIjoidG1wZnMiLCJzb3VyY2UiOiJ0bXBmcyIsIm9wdGlvbnMiOlsibm9zdWlkIiwic3RyaWN0YXRpbWUiLCJtb2RlPTc1NSIsInNpemU9NjU1MzZrIl19LHsiZGVzdGluYXRpb24iOiIvZGV2L3B0cyIsInR5cGUiOiJkZXZwdHMiLCJzb3VyY2UiOiJkZXZwdHMiLCJvcHRpb25zIjpbIm5vc3VpZCIsIm5vZXhlYyIsIm5ld2luc3RhbmNlIiwicHRteG1vZGU9MDY2NiIsIm1vZGU9MDYyMCIsImdpZD01Il19LHsiZGVzdGluYXRpb24iOiIvZGV2L3NobSIsInR5cGUiOiJ0bXBmcyIsInNvdXJjZSI6InNobSIsIm9wdGlvbnMiOlsibm9zdWlkIiwibm9leGVjIiwibm9kZXYiLCJtb2RlPTE3NzciLCJzaXplPTY1NTM2ayJdfSx7ImRlc3RpbmF0aW9uIjoiL2Rldi9tcXVldWUiLCJ0eXBlIjoibXF1ZXVlIiwic291cmNlIjoibXF1ZXVlIiwib3B0aW9ucyI6WyJub3N1aWQiLCJub2V4ZWMiLCJub2RldiJdfSx7ImRlc3RpbmF0aW9uIjoiL3N5cyIsInR5cGUiOiJzeXNmcyIsInNvdXJjZSI6InN5c2ZzIiwib3B0aW9ucyI6WyJub3N1aWQiLCJub2V4ZWMiLCJub2RldiIsInJvIl19LHsiZGVzdGluYXRpb24iOiIvcnVuIiwidHlwZSI6InRtcGZzIiwic291cmNlIjoidG1wZnMiLCJvcHRpb25zIjpbIm5vc3VpZCIsInN0cmljdGF0aW1lIiwibW9kZT03NTUiLCJzaXplPTY1NTM2ayJdfV0sImxpbnV4Ijp7InJlc291cmNlcyI6eyJkZXZpY2VzIjpbeyJhbGxvdyI6ZmFsc2UsImFjY2VzcyI6InJ3bSJ9XX0sImNncm91cHNQYXRoIjoiL2RlZmF1bHQvcmVkaXMxIiwibmFtZXNwYWNlcyI6W3sidHlwZSI6InBpZCJ9LHsidHlwZSI6ImlwYyJ9LHsidHlwZSI6InV0cyJ9LHsidHlwZSI6Im1vdW50In0seyJ0eXBlIjoibmV0d29yayJ9XSwibWFza2VkUGF0aHMiOlsiL3Byb2MvYWNwaSIsIi9wcm9jL2tjb3JlIiwiL3Byb2Mva2V5cyIsIi9wcm9jL2xhdGVuY3lfc3RhdHMiLCIvcHJvYy90aW1lcl9saXN0IiwiL3Byb2MvdGltZXJfc3RhdHMiLCIvcHJvYy9zY2hlZF9kZWJ1ZyIsIi9zeXMvZmlybXdhcmUiLCIvcHJvYy9zY3NpIl0sInJlYWRvbmx5UGF0aHMiOlsiL3Byb2MvYXNvdW5kIiwiL3Byb2MvYnVzIiwiL3Byb2MvZnMiLCIvcHJvYy9pcnEiLCIvcHJvYy9zeXMiLCIvcHJvYy9zeXNycS10cmlnZ2VyIl19fQ=="
    },
    "SnapshotKey": "redis1",
    "Snapshotter": "overlayfs",
    "CreatedAt": "2018-07-30T11:18:28.151489308Z",
    "UpdatedAt": "2018-07-30T11:18:28.151489308Z",
    "Extensions": null
}
ubuntu@v-Ubuntu:~$ 

スナップショットを表示する

スナップショットはDockerイメージでいうところのレイヤーです。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot
NAME:
   ctr snapshots - manage snapshots

USAGE:
   ctr snapshots command [command options] [arguments...]

COMMANDS:
     commit            commit an active snapshot into the provided name
     diff              get the diff of two snapshots. the default second snapshot is the first snapshot's parent.
     info              get info about a snapshot
     list, ls          list snapshots
     mounts, m, mount  mount gets mount commands for the snapshots
     prepare           prepare a snapshot from a committed snapshot
     remove, rm        remove snapshots
     label             add labels to content
     tree              display tree view of snapshot branches
     unpack            unpack applies layers from a manifest to a snapshot
     usage             usage snapshots
     view              create a read-only snapshot from a committed snapshot

OPTIONS:
   --snapshotter value  snapshotter name. Empty value stands for the default value. (default: "overlayfs") [$CONTAINERD_SNAPSHOTTER]
   --help, -h           show help
   
ubuntu@v-Ubuntu:~$ 

ctr snapshot lsでスナップショット一覧を表示します。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot ls
KEY                                                                     PARENT                                                                  KIND      
redis1                                                                  sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85 Active    
sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85 sha256:f69435472d99c717999e1e458828567320e5f5375365c9551c2d13b489f30a22 Committed 
sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016 sha256:e6b3eda8746c5cc312ebb40e1ca5c064638af429b9b3848280aab8ed882bd10b Committed 
sha256:aee8b479b9a768a64f4c32d69108566fbdbb71c8e541496dd1fa9f7ad19d8632 sha256:cdb3f9544e4c61d45da1ea44f7d92386639a052c620d1550376f22f5b46981af Committed 
sha256:cdb3f9544e4c61d45da1ea44f7d92386639a052c620d1550376f22f5b46981af                                                                         Committed 
sha256:e6b3eda8746c5cc312ebb40e1ca5c064638af429b9b3848280aab8ed882bd10b sha256:aee8b479b9a768a64f4c32d69108566fbdbb71c8e541496dd1fa9f7ad19d8632 Committed 
sha256:f69435472d99c717999e1e458828567320e5f5375365c9551c2d13b489f30a22 sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016 Committed 
ubuntu@v-Ubuntu:~$ 

どのスナップショットがどのスナップショットの親かが分かるのがいいですね。ちなみにスナップショットredis1はコンテナredis1のwritableなスナップショットです。それ以外はredisのイメージに含まれるRead Onlyなスナップショットですね。

ctr snapshot treeはスナップショットの親子関係をツリーで表示してくれます。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot tree
 sha256:cdb3f9544e4c61d45da1ea44f7d92386639a052c620d1550376f22f5b46981af
  \_ sha256:aee8b479b9a768a64f4c32d69108566fbdbb71c8e541496dd1fa9f7ad19d8632
    \_ sha256:e6b3eda8746c5cc312ebb40e1ca5c064638af429b9b3848280aab8ed882bd10b
      \_ sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016
        \_ sha256:f69435472d99c717999e1e458828567320e5f5375365c9551c2d13b489f30a22
          \_ sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85
            \_ redis1
ubuntu@v-Ubuntu:~$ 

タスクを削除する

ctr task deleteでタスクを終了させることができます。

ubuntu@v-Ubuntu:~$ sudo ctr task ls
TASK      PID      STATUS    
redis1    16722    RUNNING
ubuntu@v-Ubuntu:~$ sudo ctr task delete redis1
ctr: task must be stopped before deletion: running: failed precondition
ubuntu@v-Ubuntu:~$ 

タスクを削除する前にタスクを停止しろって言ってるので、一回停止してから削除をやりましょう。ctr killでコンテナにSIGTERMのシグナルを送って停止させます。

ubuntu@v-Ubuntu:~$ sudo ctr task kill redis1
ubuntu@v-Ubuntu:~$ sudo ctr task ls
TASK      PID      STATUS    
redis1    16722    STOPPED
ubuntu@v-Ubuntu:~$ sudo ctr task delete redis1
ubuntu@v-Ubuntu:~$ sudo ctr task ls
TASK    PID    STATUS    
ubuntu@v-Ubuntu:~$ 

コンテナを削除する

タスクを削除してもコンテナは残り続けます。

ubuntu@v-Ubuntu:~$ sudo ctr container ls
CONTAINER    IMAGE                             RUNTIME                           
redis1       docker.io/library/redis:latest    io.containerd.runtime.v1.linux    
ubuntu@v-Ubuntu:~$ 

ctr container rmでコンテナを削除しましょう。

ubuntu@v-Ubuntu:~$ sudo ctr container rm redis1
ubuntu@v-Ubuntu:~$ sudo ctr container ls
CONTAINER    IMAGE    RUNTIME    
ubuntu@v-Ubuntu:~$

これを実行するとついでにwritableなスナップショットも削除されます。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot ls
KEY                                                                     PARENT                                                                  KIND      
sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85 sha256:f69435472d99c717999e1e458828567320e5f5375365c9551c2d13b489f30a22 Committed 
sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016 sha256:e6b3eda8746c5cc312ebb40e1ca5c064638af429b9b3848280aab8ed882bd10b Committed 
sha256:aee8b479b9a768a64f4c32d69108566fbdbb71c8e541496dd1fa9f7ad19d8632 sha256:cdb3f9544e4c61d45da1ea44f7d92386639a052c620d1550376f22f5b46981af Committed 
sha256:cdb3f9544e4c61d45da1ea44f7d92386639a052c620d1550376f22f5b46981af                                                                         Committed 
sha256:e6b3eda8746c5cc312ebb40e1ca5c064638af429b9b3848280aab8ed882bd10b sha256:aee8b479b9a768a64f4c32d69108566fbdbb71c8e541496dd1fa9f7ad19d8632 Committed 
sha256:f69435472d99c717999e1e458828567320e5f5375365c9551c2d13b489f30a22 sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016 Committed 
ubuntu@v-Ubuntu:~$ 

ここで注意なんですが、コンテナより先にスナップショットを削除しないようにしましょう。スナップショットを削除してからコンテナを削除しようとすると以下のようなエラーが発生します。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot rm redis1
ubuntu@v-Ubuntu:~$ sudo ctr container ls
CONTAINER    IMAGE                             RUNTIME                           
redis1       docker.io/library/redis:latest    io.containerd.runtime.v1.linux    
ubuntu@v-Ubuntu:~$ sudo ctr container rm redis1
ERRO[0000] failed to delete container "redis1"           error="snapshot redis1 does not exist: not found"
ctr: snapshot redis1 does not exist: not found
ubuntu@v-Ubuntu:~$ 

「redis1のスナップショットがねーよ」って言ってますね。

こういう時の対処法としてはctr snapshot viewで同名のスナップショットを作ってやるって方法があります。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot view --help
NAME:
   ctr snapshots view - create a read-only snapshot from a committed snapshot

USAGE:
   ctr snapshots view [command options] [flags] <key> [<parent>]

OPTIONS:
   --target value, -t value  mount target path, will print mount, if provided
   
ubuntu@v-Ubuntu:~$

parentは適当に指定してスナップショットredis1を作成します。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot view redis1 sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85
ubuntu@v-Ubuntu:~$ sudo ctr snapshot ls
KEY                                                                     PARENT                                                                  KIND      
redis1                                                                  sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85 View      
sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85 sha256:f69435472d99c717999e1e458828567320e5f5375365c9551c2d13b489f30a22 Committed 
sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016 sha256:e6b3eda8746c5cc312ebb40e1ca5c064638af429b9b3848280aab8ed882bd10b Committed 
sha256:aee8b479b9a768a64f4c32d69108566fbdbb71c8e541496dd1fa9f7ad19d8632 sha256:cdb3f9544e4c61d45da1ea44f7d92386639a052c620d1550376f22f5b46981af Committed 
sha256:cdb3f9544e4c61d45da1ea44f7d92386639a052c620d1550376f22f5b46981af                                                                         Committed 
sha256:e6b3eda8746c5cc312ebb40e1ca5c064638af429b9b3848280aab8ed882bd10b sha256:aee8b479b9a768a64f4c32d69108566fbdbb71c8e541496dd1fa9f7ad19d8632 Committed 
sha256:f69435472d99c717999e1e458828567320e5f5375365c9551c2d13b489f30a22 sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016 Committed 
ubuntu@v-Ubuntu:~$ sudo ctr container rm redis1
ubuntu@v-Ubuntu:~$ sudo ctr container ls
CONTAINER    IMAGE    RUNTIME    
ubuntu@v-Ubuntu:~$ 

すると見事に削除できるようになりました。

イメージを削除する

ctr image rmでイメージを削除できます。

ubuntu@v-Ubuntu:~$ sudo ctr image rm docker.io/library/redis:latest
docker.io/library/redis:latest
ubuntu@v-Ubuntu:~$ sudo ctr image ls 
REF TYPE DIGEST SIZE PLATFORMS LABELS 
ubuntu@v-Ubuntu:~$ 

スナップショットも綺麗さっぱりなくなります。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot ls
KEY PARENT KIND 
ubuntu@v-Ubuntu:~$ 

まとめ

ctrを使ってcontainerdに対して基本的な操作を行いました。今の世の中コンテナランタイムは色々種類がありますし、色々触って使い心地(笑)を比較してみるのもいいかもしれませんね。

ちなみにちなみに、containerdのバージョン1.1にはcriのプラグインが備わっており、CRI v1alpha2に対応しています。というわけでctr以外にcrictlのようなCLIツールで操作を行うことが可能になっています。

今度はcrictlで操作してみたさがありますね。また、CNIの設定もちゃんとやりたいです。

おまけ

ボツネタにしようと思ったけどやっぱ記事に残しておくことにした内容

スナップショットを削除する

再度redisのイメージをプル

ubuntu@v-Ubuntu:~$ sudo ctr image pull docker.io/library/redis:latest
ubuntu@v-Ubuntu:~$ sudo ctr snapshot tree
 sha256:cdb3f9544e4c61d45da1ea44f7d92386639a052c620d1550376f22f5b46981af
  \_ sha256:aee8b479b9a768a64f4c32d69108566fbdbb71c8e541496dd1fa9f7ad19d8632
    \_ sha256:e6b3eda8746c5cc312ebb40e1ca5c064638af429b9b3848280aab8ed882bd10b
      \_ sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016
        \_ sha256:f69435472d99c717999e1e458828567320e5f5375365c9551c2d13b489f30a22
          \_ sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85
ubuntu@v-Ubuntu:~$

特に深い意味はないんですけどスナップショットsha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016の削除を試みます。スナップショットの削除はctr snapshot rmコマンド。

ubuntu@v-Ubuntu:~$ sudo ctr snapshot rm sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016
ctr: failed to remove "sha256:7460940a3a5274245b091035dbbb1b724e5bd2d2135326d61f542de962f3a016": cannot remove snapshot with child: failed precondition
ubuntu@v-Ubuntu:~$ 

子のスナップショットがいるときは削除できないようになっているようです。賢い。

じゃあ子がいないスナップショットを削除してみましょうか

ubuntu@v-Ubuntu:~$ sudo ctr snapshot rm sha256:3c7bfc410c0e316ba2b72a18e1ec45263b81921eafbec05d87cfb6dfd2a31f85
ubuntu@v-Ubuntu:~$ sudo ctr snapshot ls
KEY PARENT KIND 
ubuntu@v-Ubuntu:~$ sudo ctr image ls -q
docker.io/library/redis:latest
ubuntu@v-Ubuntu:~$ 

スナップショット全部消えるんですね・・・でもイメージは残ってるのか・・・

これでctr image rmiを実行したら何が起こるか気になりました(なんとなくctr container rmの時のエラーみたいに「スナップショット○○が存在しない」ってメッセージが出ると思いました)

ubuntu@v-Ubuntu:~$ sudo ctr image rm docker.io/library/redis:latest
docker.io/library/redis:latest
ubuntu@v-Ubuntu:~$ sudo ctr image ls
REF TYPE DIGEST SIZE PLATFORMS LABELS 
ubuntu@v-Ubuntu:~$ 

普通に削除できちゃった。

コンテナのポートバインディング

タスクのポートバインディングって言った方がいいのかな?containerdではDockerのdcoker0みたいな仮想のブリッジネットワークは備わってないみたいです。ただホストネットワークは使用できます。そんなわけでコンテナの80番ポートをホストの80番ポートにバインドするなんてことができます。

それでは再度redisのイメージをプルしてコンテナを起動してしましょう。オプション--net-hostをつけてctr runを実行します。

ubuntu@v-Ubuntu:~$ sudo ctr image pull docker.io/library/redis:latest
ubuntu@v-Ubuntu:~$ sudo ctr run -d --net-host docker.io/library/redis:latest redis1
ubuntu@v-Ubuntu:~$ ss -atn | grep 6379
LISTEN     0      128          *:6379                     *:*                  
LISTEN     0      128         :::6379                    :::*                  
ubuntu@v-Ubuntu:~$ 

ホストの6379番ポートが空いています。このポートで何のプロセスが動いているかを調べてみると、redisのプロセスが動いていることがわかりました。

ubuntu@v-Ubuntu:~$ sudo lsof -i:6379
lsof: no pwd entry for UID 999
lsof: no pwd entry for UID 999
COMMAND     PID     USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
lsof: no pwd entry for UID 999
redis-ser 18748      999    6u  IPv6 3558672      0t0  TCP *:6379 (LISTEN)
lsof: no pwd entry for UID 999
redis-ser 18748      999    7u  IPv4 3558673      0t0  TCP *:6379 (LISTEN)
ubuntu@v-Ubuntu:~$ 

というわけでポートバインディングは成功です。

docker-containerd、docker-containerd-shim、 docker-containerd-ctr、docker-runc

stack overflowのdockerd vs docker-containerd vs docker-runc vs docker-containerd-ctr vs docker-containerd-shimの補足的な記事です。

各コマンドの説明と、これらを説明する上で必要なcontainerdやCRIの話もちょっと書きます。

一応自分の環境の情報

ubuntu@v-Ubuntu:~$  docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 6
Server Version: 17.03.2-ce
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins: 
 Volume: local
 Network: bridge host macvlan null overlay
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 4ab9917febca54791c5f071a9d1f404867857fcc
runc version: 54296cf40ad8143b62dbcaa1d90e520a2136ddfe
init version: 949e6fa
Security Options:
 apparmor
 seccomp
  Profile: default
Kernel Version: 4.4.0-130-generic
Operating System: Ubuntu 16.04.2 LTS
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 23.54 GiB
Name: v-Ubuntu
ID: QWK4:E7QH:2SSD:QIOB:PQ2V:D4GL:M43V:5WVY:GQBT:XJCG:OSHA:5WBT
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Experimental: false
Insecure Registries:
 127.0.0.0/8
Live Restore Enabled: false

WARNING: No swap limit support

事前知識1: Dockerとcontainerd

f:id:etogen:20180730010524j:plain

https://hackernoon.com/docker-containerd-standalone-runtimes-heres-what-you-should-know-b834ef155426

Docker 1.1からDocker Engineにcontainerdが統合されました。containerdはコンテナランタイムの一種でコンテナの管理やコンテナイメージの管理を行ってくれます。

docker runなどでコンテナを作成する時はcontainerdの方に命令が渡されるんですが、そこからさらにruncに命令が行きます。runcは実際にコンテナを実行・作成するコンテナランタイムで、こちらはコンテナイメージの管理などは行いません。

両方ともコンテナランタイムでちょっと紛らわしい。どこで聞いたかは覚えてないんですが、containerdはHigh Level Container Runtime、runcはLow Level Container Runtimeって呼ばれているのを耳にしたことがあります。

詳しくはここがすごいわかりやすいのでを読んでくださいね(投げやり)

事前知識2: CRI

CRI(Container Runtime Interface)は簡単に言うと「kubeletとお話するために、これこれこういうserviceを実装してくださいね」というgRPCの仕様です。その仕様はapi.protoを見ればわかると思います。

containerdはCRIに対応していて、実際Kubernetesのコンテナランタイムとして動かすことができます。

CRIにはバージョンが複数存在するので注意してください。containerd/criのリポジトリのREADMEKubernetesとCRIのバージョンの対応表は見ておくと良いでしょう。

また、containerdはバージョンによってはkubeletとの通信でcri-contianerdを仲介させなければならない場合があります。Kubernetes対応コンテナランタイム「containerd 1.1」正式リリース。CRIにネイティブ対応し、Dockerより軽量で高速な動作を実現Containerd Brings More Container Runtime Options for Kubernetesをご参考に。

各コマンドの説明

docker-containerd

Dockerに統合されているcontainerdちゃんです。こいつはdockerdを起動すると、子プロセスとして一緒に起動されます。

dockerdを実行した後にpsコマンドを実行するとこいつが動いているのが確認できます。

ubuntu@v-Ubuntu:~$ sudo systemctl start docker
ubuntu@v-Ubuntu:~$ ps -alfx | grep docker
0  1001 14525 11858  20   0  13956   988 pipe_w S+   pts/0      0:00  |           \_ grep --color=auto docker
4     0 14327     1  20   0 637884 38944 -      Ssl  ?          0:00 /usr/bin/dockerd -H fd:// -s overlay2 -H tcp://127.0.0.1:2375
4     0 14334 14327  20   0 349300  9832 -      Ssl  ?          0:00  \_ docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc
ubuntu@v-Ubuntu:~$ 

コマンドのオプションを見ればわかると思うんですが、unixドメインソケットをエンドポイントとしていてここを叩くと色々操作ができるようになっています。ちなみにこのソケットではgRPCの通信が行われるのでcurlAPIを叩くことはできません。docker-containerd-ctrを使えばこのAPIを叩くことができます。

docker-containerd-ctr

上でも書きましたがcontainerdを操作するCLIツールです。実際に使ってみましょう。

ubuntu@v-Ubuntu:~$ sudo docker-containerd-ctr
NAME:
   ctr - High performance container daemon cli

USAGE:
   docker-containerd-ctr [global options] command [command options] [arguments...]
   
VERSION:
   0.2.3 commit: 4ab9917febca54791c5f071a9d1f404867857fcc
   
COMMANDS:
   checkpoints  list all checkpoints
   containers   interact with running containers
   events   receive events from the containerd daemon
   state    get a raw dump of the containerd state
   version  return the daemon version
   help, h  Shows a list of commands or help for one command
   
GLOBAL OPTIONS:
   --debug                      enable debug output in the logs
   --address "unix:///run/containerd/containerd.sock"   proto://address of GRPC API
   --conn-timeout "1s"                  GRPC connection timeout
   --help, -h                       show help
   --version, -v                    print the version
   
ubuntu@v-Ubuntu:~$ sudo docker-containerd-ctr version
[ctr] rpc error: code = 14 desc = grpc: the connection is unavailable
ubuntu@v-Ubuntu:~$ 

containerdのunixドメインソケットのパスをオプションで渡してやる必要があるようですね。

ubuntu@v-Ubuntu:~$ sudo docker-containerd-ctr version
[ctr] rpc error: code = 14 desc = grpc: the connection is unavailable
ubuntu@v-Ubuntu:~$ sudo docker-containerd-ctr --address "unix:///var/run/docker/libcontainerd/docker-containerd.sock" version
daemon version 0.2.3 commit: 4ab9917febca54791c5f071a9d1f404867857fcc
ubuntu@v-Ubuntu:~$ sudo docker-containerd-ctr --address "unix:///var/run/docker/libcontainerd/docker-containerd.sock" state
{"machine":{"cpus":4,"memory":24102}}ubuntu@v-Ubuntu:~$ 

OK〜!ちなみにここのgRPCの通信はCRIではなくcontainerd独自のgRPCのインタフェースに乗っ取った通信なので注意してください。crictl(CRIを叩くことができるCLIツール)でこのAPIを叩くなんてことは当前できません。

docker-containerd-shim

コンテナを作成する際はruncを実行するんですけど、これを使うことでruncを終了させてコンテナをデーモンレスで実行させることができます。このコマンド、実はdocker runでコンテナを作成するたびに裏で実行しています。実際に確認してみましょう。

現在dockerddocker-containerdのプロセスが存在します。

ubuntu@v-Ubuntu:~$ ps aux | grep docker
root     13339  0.0  0.1 643428 42508 ?        Ssl  02:01   0:02 /usr/bin/dockerd -H fd:// -s overlay2 -H tcp://127.0.0.1:2375
root     13347  0.0  0.0 226552 11248 ?        Ssl  02:01   0:01 docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc
ubuntu   13934  0.0  0.0  13956   932 pts/0    S+   02:41   0:00 grep --color=auto docker
ubuntu@v-Ubuntu:~$ 

ここでnginxのコンテナを作成するとdocker-containerd-shimのプロセスが開始されます。

ubuntu@v-Ubuntu:~$ docker run -d  nginx
94d5e8ba1dc72f68ae4c0d636e841701f8f1c812da240e562b4e47d9978c1b3f
ubuntu@v-Ubuntu:~$ ps aux | grep docker
root     13339  0.0  0.1 643428 42700 ?        Ssl  02:01   0:02 /usr/bin/dockerd -H fd:// -s overlay2 -H tcp://127.0.0.1:2375
root     13347  0.0  0.0 226552 11268 ?        Ssl  02:01   0:01 docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc
root     13999  0.0  0.0 413060  3224 ?        Sl   02:42   0:00 docker-containerd-shim 94d5e8ba1dc72f68ae4c0d636e841701f8f1c812da240e562b4e47d9978c1b3f /var/rundocker/libcontainerd/94d5e8ba1dc72f68ae4c0d636e841701f8f1c812da240e562b4e47d9978c1b3f docker-runc
ubuntu@v-Ubuntu:~$ 

このプロセスは1コンテナにつき1つ実行されます。

docker-runc

OCI準拠の設定を読み込んでコンテナを作成するruncちゃんです。Linuxのcgroupsとかnamespacesとかを使って本当に低いレイヤの処理を行ってくれてるようです(ここら辺ちゃんと勉強してないのであんまりよくわかってない)

Linux上にDockerをインストールするまでの作業

これと同じことをやることが頻繁にあるので、手順書(笑)にしておくことにしました。リモートマシンのUbuntuにDockerをインストールすることを想定。

SSH鍵転送

SSH鍵作成

TARGETは適宜変更。

TARGET=sushi

鍵作成

mkdir ~/.ssh/$TARGET
ssh-keygen -t rsa -b 4096 -N "" -C "$TARGET" -f ~/.ssh/$TARGET/id_rsa

鍵転送

変数は適宜変更。

TARGET_HOST=sushi1
TARGET_NAME=10.10.10.10
TARGET_USER=jony
TARGET_PORT=22

鍵送信

cat << EOF >> ~/.ssh/config
Host $TARGET_HOST
    HostName $TARGET_NAME
    User $TARGET_USER
    Port $TARGET_PORT
    IdentityFile ~/.ssh/$TARGET/id_rsa

EOF

ssh-copy-id -i ~/.ssh/$TARGET/id_rsa $TARGET_HOST

Dockerインストール

sshリモートホストに接続する。

sudoをパスワードなしでできるようにする

sudo -s

TARGET_USERは適宜変更

TARGET_USER=jony

sudoers編集

echo "$TARGET_USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
exit

Dockerインストール

変数は適宜変更。

TARGET_USER=jony
TARGET_DOCKER_VERSION=17.03

Hosts in Rancherにあるスクリプトを使うと、楽に特定のバージョンのDockerをインストールできます。

sudo apt-get update
sudo apt-get -y upgrade
curl https://releases.rancher.com/install-docker/${TARGET_DOCKER_VERSION}.sh | sh
sudo usermod -aG docker $TARGET_USER

Git操作メモ(1. 取り消し系)

Gitの取り消し系の操作

コミットとかステージングをやり直したい時の操作について書いていきます。

ファイルの変更の取り消し

$ git checkout <ファイルまたはディレクトリ>

これで一応ファイルの変更の取り消せるんですが、より正確にはこういうことをやっているらしい。

指定したファイルについて、インデックスが指しているブロブ をオブジェクト格納領域から取り出し、ワーキングディレクトリのファイルを置き変える。

git checkoutを図解する | To Be Decided

たとえば以下のような操作を行なったとしましょう。

コミット(1) -> hoge.txt編集(2) -> hoge.txtをステージング(3) -> hoge.txt編集(4)

この場合、(4)を行なった後にgit checkout hoge.txtを実行すると、hoge.txtの内容は(2)の時のものになります。ファイルの内容を(4)から(1)に戻すのは、後述「ステージングの取り消し」に記載した方法で(3)のステージングを取り消せばできます。

ちなみにgit checkoutで新規に作成したファイルが削除されることはありません。

参考: git checkoutを図解する | To Be Decided

ステージングの取り消し

$ git reset HEAD <file or directory>

または

$ git reset <file or directory>

git resetコマンドはステージングを取り消すっていうか、HEADやstageやworking directoryの状態を前に戻すコマンドです。オプションを特につけないで実行した場合、HEADとstageの状態はリセットされ、working directoryで行なった変更はリセットされません。

HEADのみ状態を前に戻したい場合は--softのオプションをつけます。HEAD、stage、working directoryの状態を前に戻したい場合は--hardのオプションをつけます。

たとえばこれら3つの状態を、あるコミットの状態にまで戻したい場合こんなコマンドを実行します。

git reset --hard <commit>

これは実質「ステージングの取り消し」と「ファイルの変更の取り消し」を行なっています。

参考: git-resetは結局何を戻すのか

Untracked filesの削除

$ git clean -f

追跡していないファイルは全て削除されます。追跡してないディレクトリも削除する場合は以下のコマンド。

$ git clean -df

これらを実行する前に-nオプションをつけてドライランを行なっておくといいですね。

参考: git Untracked files(未追跡ファイル)を1発で消し去る

コミットの取り消し

git reset 使います。

  • ステージングを取り消したい時: git reset <path>
  • コミットを取り消したい時: git reset <commit>

って覚えとくといいんじゃないかなぁと思います。

「今まで行なったファイルの変更とかステージングもなかったことにしてやるぜ!」って場合は

$ git reset --hard HEAD^

「今まで行なったファイルの変更やステージングはなかったことにしたくないけど、さっきのコミットは取り消したいなぁ」って場合は

$ git reset --soft HEAD^

pushの取り消し

ローカルとリモートのリポジトリで最新のコミットを取り消したい場合

$ git reset HEAD^
$ git push -f origin <branch>

ローカルでは取り消さず、リモートのみ最新のコミットを取り消したい場合

$ git push -f origin HEAD^:<branch>

その他の取り消し系操作

Gitでやらかした時に使える19個の奥義に色々書いてあります。

Tips

git resetのmode

Soft、Mixed、Hardがあります。git resetでどのmodeを指定するかによって、リセットされる対象が変化します。

  • Soft: HEAD
  • Mixed: HEAD、stage
  • Hard: HEAD、stage、working directory

@

HEADのエイリアス。コマンド実行する時HEADって書くのが面倒臭かったら代わりに@が使えるよぅ!(=゚ω゚)ノ

Kubernetes Hardwayメモ

k8sクラスタはkubeadmとかrkeとかを使うと、クラスタに必要なコンポーネントを自動で作成してくれたりしてある程度楽に構築できます。しかし、あえてそれらを手作業でデプロイしていって、手動でk8sクラスタを構築したいって人はKubernetes Hardway(下記リンク)を参考にして進めるといいです。これ結構勉強になるハンズオンです。

github.com

以下ではKubernetes Hardwayでやったこととか、注意点とかをメモしていきます。ちなみにこの記事を書いた時点でのmasterへの最新コミットは4f5cecbで、これはk8s v1.9.0対応です。

事前にk8sクラスタにはどんなコンポーネントがあるかを知っておくといいです。(下記参考)

qiita.com

Prerequisites

gcloudのインストール

gcloudコマンドはGoogle Cloud SDKコンポーネントなので、Google Cloud SDKをインストールする。

Cloud SDK のインストール  |  Cloud SDK のドキュメント  |  Google Cloud

Cloud SDKのアップデート

gcloudなどのCloud SDKのバージョンが古い場合にはgcloud components updateを実行することでアップデート可能。

Install CFSSL

cfsslインストールする

Provisioning Compute Resources

ここでふと「こういうハンズオンで冗長構成を作るとき、なんでMasterの役割があるやつは2台じゃなくて3台なんだろうな〜」とか思った。ちょっとk8sの話と脱線するけど面白い記事を見つけた。

qiita.com

スプリットブレインという言葉については知ってたけどマジョリティ(多数決)で生かす方を決定するというのは知らなかった。etcdの冗長構成を作る時は奇数台にしなくちゃいけない。

Provisioning a CA and Generating TLS Certificates

  • Cloud Flareのcfsslというツールを使ってローカルにCAを作成
  • コンポーネントに必要な証明書や秘密鍵を作成していく

CA証明書、秘密鍵の作成

$ ./cfssl gencert -initca ca-csr.json | ./cfssljson -bare ca
2018/04/23 16:35:15 [INFO] generating a new CA key and certificate from CSR
2018/04/23 16:35:15 [INFO] generate received request
2018/04/23 16:35:15 [INFO] received CSR
2018/04/23 16:35:15 [INFO] generating key: rsa-2048
2018/04/23 16:35:15 [INFO] encoded CSR
2018/04/23 16:35:15 [INFO] signed certificate with serial number 476734857894076781436314039079823636939593070907

CFSSLの使い方についてはCFSSLのWikiを参考に

github.com

Generating Kubernetes Configuration Files for Authentication

Node Authorizationついて

この記事が参考になった

qiita.com

コマンドの説明

コマンド 内容
kubectl config set-cluster kubeconfigファイルにクラスタエントリを設定(クラスタの作成)
kubectl config set-credentials kubeconfigファイルにユーザーのエントリを設定(ユーザーの作成)
kubectl config set-context コンテキストを作成し、クラスタとユーザーを関係付ける。今回はdefaultという名前のコンテキストを作成した

余談だけどkubectl config set-context--namespaceオプションをつけるとデフォルトのネームスペースを変更できる

Generating the Data Encryption Config and Key

EncryptionConfigk8s v1.7から追加された機能。etcdのデータを暗号化する。対応しているのはetcd v3以降で、etcd v2では無効。

etcdはk8sの構成情報を保存するためのKVS。

Bootstrapping the etcd Cluster

各controllerノード(controller-0, controller-1, controller-2)でetcdを起動する。

INTERNAL_IPについて

この変数に代入されるのはインスタンスのインタフェース0に割り当てられたプライベートIPアドレス。GCEの全てのインスタンスメタデータサーバー(metadata.google.internal)にメタデータを格納していて、そこにクエリを飛ばすことでインスタンスのホスト名やインスタンスIDなどを入手できるそう。

インスタンス メタデータの格納と取得  |  Compute Engine ドキュメント  |  Google Cloud

クエリを飛ばす際はヘッダーにMetadata-Flavor: Googleをつける必要がある。また、インスタンスからのクエリ発行要求では追加の承認はいらないらしい(楽でいいね)

etcdを起動する際に指定する.pemファイル

k8s API Serverの秘密鍵と証明書(kubernetes-key.pem, kubernetes.pem)、CAの証明書(ca.pem)

Bootstrapping the Kubernetes Control Plane

各controllerノードでkube-apiserver、kube-scheduler、kube-controller-managerを起動する

kube-apiserverを起動する際に指定する.pemファイル

ca.pem, ca-key.pem, kubernetes-key.pem, kubernetes.pem

RBAC

ここ参考

qiita.com

kube-apiserverにアクセスする際、kubeletはkubernetesというユーザーとして認証される。

  • どのリソースにアクセスできるかを記述したもの: ClusterRole
  • ClusterRoleをUserに結びつけるもの: ClusterRoleBinding

また、kube-apiserver起動時--kubelet-client-certificateで指定したファイル(今回はkubernetes.pem)をクライアント証明書としてkubeletの認証に用いる。

インタフェースのプライベートIPについて

自動で10.240.0.10とか割り当てられてるのかと思ったら、「Provisioning Compute Resources」の章でcontrollerのインスタンス作成時に--private-network-ipで設定していた

Bootstrapping the Kubernetes Worker Nodes

各workerノード(worker-0, worker-1, worker-2)でkubelet, kube-proxy, containerd(コンテナランタイム)を起動する。

CNI

GKEではnetwork-pluginはkubenetになっているが、hardwayのハンズオンではcniというものを使用するらしい

kubeletを起動する際に指定する.pemファイル

ca.pem, worker-n.pem, worker-n-key.pem

Configuring kubectl for Remote Access

LBを通じてk8sAPIを外部から叩けるようにする。

Provisioning Pod Network Routes

現時点ではあるノードにで作成されたPodと、その他のノードで作成されたPodが通信できないようになっているので、ルーティングテーブルにルールを追加する

pod-cidr

「Provisioning Compute Resources」でインスタンスを作成する際に、メタデータとして設定している。

Deploying the DNS Cluster Add-on

kube-dnsのデプロイ。kube-dnsはPodとしてデプロイするのでどこかのworkerノードで動くことになる。

kube-dns.yaml

リソースが4つ作られる

$ kubectl create -f https://storage.googleapis.com/kubernetes-the-hard-way/kube-dns.yaml
service "kube-dns" created
serviceaccount "kube-dns" created
configmap "kube-dns" created
deployment "kube-dns" created
$ kubectl get service,sa,configmap,deployment --namespace=kube-system
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
svc/kube-dns   ClusterIP   10.32.0.10   <none>        53/UDP,53/TCP   23m

NAME          SECRETS   AGE
sa/default    1         5h
sa/kube-dns   1         23m

NAME                                    DATA      AGE
cm/extension-apiserver-authentication   1         6h
cm/kube-dns                             0         23m

NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/kube-dns   1         1         1            1           23m

addon-manager

kube-dns.yamladdonmanager.kubernetes.io/modeっていう設定が出てきた。以下参考

qiita.com

Smoke Test

スモークテストとは - IT用語辞典

kubectl port-forward

ローカルマシンのポート8080をnginxのPodの80番ポートに割り当てた

Cleaning Up

GCPのリソース削除

Golang EchoとTemplateのメモ(自分用)

入門記事

HTMLタグが勝手にHTML特殊文字に変換されちゃう

同じお悩み

stackoverflow.com

template.HTML('<p>hoge</p>')みたいにすればOK。

ベースのテンプレートにパスごとに違ったコンテンツを埋め込むには

同じお悩み

stackoverflow.com

template.New("").ParseFiles("page1.html", "base.html")みたいにすればOK。

AlpineでRustのバイナリを動かす

Rustのプログラムをビルドする時に静的リンクにするようにして、ビルドしてできたバイナリをDockerコンテナのAlpineで動かすまでの話です。

cargo buildについて

Rustのパッケージマネージャ兼ビルドシステムのcargoというものがあります。まぁRust書いてる人なら知らない人はいないと思います。デフォルトではcargo buildは動的リンクでバイナリファイルを作成します。確認してみましょう。

$ cargo new hello --bin
     Created binary (application) `hello` project
$ cd hello
$ cargo build --release
   Compiling hello v0.1.0 (file:///usr/src/hello)
    Finished release [optimized] target(s) in 2.78 secs
$ ./target/release/hello
Hello, world!
$ file ./target/release/hello
./target/release/hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9cdb401d93645d19032e0850e1b283feb270f491, not stripped
$ ldd ./target/release/hello
        linux-vdso.so.1 (0x00007ffdffb94000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f182e9ec000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f182e7e4000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f182e5c7000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f182e3b0000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f182e011000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f182ee41000)
$

さて、このバイナリをAlpineのDockerコンテナで動かしたいんですが、この超軽量のOSイメージにはlibcが入っておらず動的ライブラリは存在しません。つまりこのバイナリをAlpineにコピーしてきてそのまま動かすことはできません。

$ docker run -itd --rm --name al alpine:3.6 /bin/sh
5beae80c6a9b7d45b3820012abf62c1fad28b73f104bb91875ee8b1a2e6a270d
$ docker cp hello al:/
$ docker attach al
/ # ls -l hello
-rwxr-xr-x    1 501      dialout    3655624 May 30 15:16 hello
/ # /hello
/bin/sh: /hello: not found
/ #

そんなわけで静的リンクに変更してRustプログラムのビルドを行います。

どうしても動的リンクのバイナリを動かしたい場合はここのQiitaの記事を参考にすると良いと思います。

qiita.com

静的リンクのバイナリを作成する

muslのRustをインストールします。

https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/advanced-linking.html

これ手順長いし面倒臭いと思った人はrust-musl-builder使うといいかもしれないです

github.com

rust-musl-builderはDockerイメージも用意されてます。実際に使ってみました。

$ docker run -itd --rm --name rustbuil ekidd/rust-musl-builder:stable /bin/bash
820af56751ce293d22b796c8bc4838b8a91deea01e8528cabbfa6dc7f61a0b36
$ docker attach rustbuil 
rust@820af56751ce:~/src$ 
rust@820af56751ce:~/src$ export USER=rust
rust@820af56751ce:~/src$ cargo new hello --bin
     Created binary (application) `hello` project
rust@820af56751ce:~/src$ cd hello/
rust@820af56751ce:~/src/hello$ cargo build --release
   Compiling hello v0.1.0 (file:///home/rust/src/hello)
    Finished release [optimized] target(s) in 1.42 secs
rust@820af56751ce:~/src/hello$ cd ./target/x86_64-unknown-linux-musl/release/
rust@820af56751ce:~/src/hello/target/x86_64-unknown-linux-musl/release$ ./hello
Hello, world!
rust@820af56751ce:~/src/hello/target/x86_64-unknown-linux-musl/release$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=e0b4652a02afe0963acacca0c7631c621f39ac3c, not stripped
rust@820af56751ce:~/src/hello/target/x86_64-unknown-linux-musl/release$ ldd hello
        not a dynamic executable
rust@820af56751ce:~/src/hello/target/x86_64-unknown-linux-musl/release$

通常のcargo buildとは違ってバイナリファイルができる場所がtarget/x86_64-unknown-linux-musl/releaseの下になっています。見事に静的リンクでビルドできたようです。ではAlpineに移して実行してみましょう。

$ docker cp rustbuil:/home/rust/src/hello/target/x86_64-unknown-linux-musl/release/hello .
$ docker cp hello al:/
$ docker attach al
/ # ls -l hello
-rwxr-xr-x    1 501      dialout    3896520 May 30 15:57 hello
/ # /hello
Hello, world!
/ #

動作しました〜...っていうかバイナリのサイズでっかいなぁ....