Containerd簡明教程
最近在玩這個玩意兒,準備在此之上開發自己的容器編排系統huang 。這裡簡單看一下containerd 的使用。containerd是從Docker分離出來的一個用於容器、映象增刪改查的程式,這是它的架構圖:
安裝
我是使用ArchLinux,因此可以直接安裝:
$ sudo pacman -S containerd resolving dependencies... looking for conflicting packages... Packages (2) runc-1.0.0rc6-1containerd-1.2.0-3 Total Installed Size:108.18 MiB :: Proceed with installation? [Y/n] (2/2) checking keys in keyring[##############################################################] 100% (2/2) checking package integrity[##############################################################] 100% (2/2) loading package files[##############################################################] 100% (2/2) checking for file conflicts[##############################################################] 100% (2/2) checking available disk space[##############################################################] 100% :: Processing package changes... (1/2) installing runc[##############################################################] 100% (2/2) installing containerd[##############################################################] 100% :: Running post-transaction hooks... (1/2) Reloading system manager configuration... (2/2) Arming ConditionNeedsUpdate...
也可以自己下載二進位制包,也可以把二進位制檔案放到$PATH
裡包含的資料夾之一例如/usr/local/bin
中,然後把對應的
systemd service檔案也拷貝到對應的位置。
使用預設配置:
# containerd config default > /etc/containerd/config.toml
使用containerd
我們使用這段程式碼來連線containerd並且拉取一個redis映象:
package main import ( "context" "log" "github.com/containerd/containerd" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/oci" ) func main() { if err := redisExample(); err != nil { log.Fatal(err) } } func redisExample() error { client, err := containerd.New("/run/containerd/containerd.sock") // 連線到containerd預設監聽的地址 if err != nil { return err } defer client.Close() ctx := namespaces.WithNamespace(context.Background(), "example") // 使用一個獨立的namespace防止衝突 image, err := client.Pull(ctx, "docker.io/library/redis:alpine", containerd.WithPullUnpack) if err != nil { return err } log.Printf("Successfully pulled %s image\n", image.Name()) // 建立一額容器 container, err := client.NewContainer( ctx, "redis-server", containerd.WithNewSnapshot("redis-server-snapshot", image), containerd.WithNewSpec(oci.WithImageConfig(image)), ) if err != nil { return err } defer container.Delete(ctx, containerd.WithSnapshotCleanup) // 記得把容器刪了 log.Printf("Successfully created container with ID %s and snapshot with ID redis-server-snapshot", container.ID()) return nil }
執行一下:
$ go build main.go $ sudo ./main [sudo] password for jiajun: 2019/02/27 23:44:40 Successfully pulled docker.io/library/redis:alpine image 2019/02/27 23:44:40 Successfully created container with ID redis-server and snapshot with ID redis-server-snapshot
檢查一下映象是否拉下來了:
$ sudo ctr -n example images ls REFTYPEDIGESTSIZEPLATFORMSLABELS docker.io/library/redis:alpine application/vnd.docker.distribution.manifest.list.v2+json sha256:3fa51e0b90b42ed2dd9bd87620fe7c45c43eb4e1f81c83813a78474cbe2f7457 16.9 MiB linux/386,linux/amd64,linux/arm/v6,linux/arm64/v8,linux/ppc64le,linux/s390x -
注意其中的-n example
,因為程式碼中,我們就使用了example
這個namespace,如果不加這一行我們是看不到結果的,因為預設的namespace
是default
。
Task
Container是永駐的,Task相當於一條臨時命令,例如執行docker exec -it
的時候。這個例子可以看得出來:
package main import ( "context" "fmt" "log" "syscall" "time" "github.com/containerd/containerd" "github.com/containerd/containerd/cio" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/oci" ) func main() { if err := redisExample(); err != nil { log.Fatal(err) } } func redisExample() error { // create a new client connected to the default socket path for containerd client, err := containerd.New("/run/containerd/containerd.sock") // 連線containerd預設地址 if err != nil { return err } defer client.Close() // create a new context with an "example" namespace ctx := namespaces.WithNamespace(context.Background(), "example") // 使用example名稱空間 // pull the redis image from DockerHub image, err := client.Pull(ctx, "docker.io/library/redis:alpine", containerd.WithPullUnpack) // 拉映象 if err != nil { return err } // 建立容器 container, err := client.NewContainer( ctx, "redis-server", containerd.WithImage(image), containerd.WithNewSnapshot("redis-server-snapshot", image), containerd.WithNewSpec(oci.WithImageConfig(image)), ) if err != nil { return err } defer container.Delete(ctx, containerd.WithSnapshotCleanup) // 連上容器的stdio task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio)) if err != nil { return err } defer task.Delete(ctx) // make sure we wait before calling start exitStatusC, err := task.Wait(ctx) if err != nil { fmt.Println(err) } // call start on the task to execute the redis server if err := task.Start(ctx); err != nil { return err } // sleep for a lil bit to see the logs time.Sleep(3 * time.Second) // kill the process and get the exit status if err := task.Kill(ctx, syscall.SIGTERM); err != nil { return err } // wait for the process to fully exit and print out the exit status status := <-exitStatusC code, _, err := status.Result() if err != nil { return err } fmt.Printf("redis-server exited with status: %d\n", code) return nil }
執行一下:
$ sudo ./main 1:C 27 Feb 2019 23:52:08.999 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 1:C 27 Feb 2019 23:52:08.999 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=1, just started 1:C 27 Feb 2019 23:52:08.999 # 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 27 Feb 2019 23:52:09.002 # You requested maxclients of 10000 requiring at least 10032 max file descriptors. 1:M 27 Feb 2019 23:52:09.002 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted. 1:M 27 Feb 2019 23:52:09.002 # 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 27 Feb 2019 23:52:09.003 * Running mode=standalone, port=6379. 1:M 27 Feb 2019 23:52:09.003 # 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 27 Feb 2019 23:52:09.003 # Server initialized 1:M 27 Feb 2019 23:52:09.003 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. 1:M 27 Feb 2019 23:52:09.003 # 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 27 Feb 2019 23:52:09.003 * Ready to accept connections 1:signal-handler (1551325932) Received SIGTERM scheduling shutdown... 1:M 27 Feb 2019 23:52:12.113 # User requested shutdown... 1:M 27 Feb 2019 23:52:12.113 * Saving the final RDB snapshot before exiting. 1:M 27 Feb 2019 23:52:12.127 * DB saved on disk 1:M 27 Feb 2019 23:52:12.127 # Redis is now ready to exit, bye bye... redis-server exited with status: 0