可以像 Docker 一样方便的使用 Containerd 吗?
作者:阳明 2021-08-27 06:41:34云计算 nerdctl 是一个与 docker cli 风格兼容的 containerd 客户端工具,而且直接兼容 docker compose 的语法的,这就大大提高了直接将 containerd 作为本地开发、测试或者单机容器部署使用的效率。
[[419814]]
前面我们介绍了可以使用 ctr 操作管理 containerd 镜像容器,但是大家都习惯了使用 docker cli,ctr 使用起来可能还是不太顺手,为了能够让大家更好的转到 containerd 上面来,社区提供了一个新的命令行工具:nerdctl(https://github.com/containerd/nerdctl)。nerdctl 是一个与 docker cli 风格兼容的 containerd 客户端工具,而且直接兼容 docker compose 的语法的,这就大大提高了直接将 containerd 作为本地开发、测试或者单机容器部署使用的效率。
安装
同样直接在 GitHub Release 页面下载对应的压缩包解压到 PATH 路径下即可:
#如果没有安装containerd,则可以下载nerdctl-full-<VERSION>-linux-amd64.tar.gz包进行安装~wgethttps://github.com/containerd/nerdctl/releases/download/v0.11.0/nerdctl-0.11.0-linux-amd64.tar.gz#如果有限制,也可以替换成下面的URL加速下载#wgethttps://download.fastgit.org/containerd/nerdctl/releases/download/v0.11.0/nerdctl-0.11.0-linux-amd64.tar.gz~mkdir-p/usr/local/containerd/bin/&&tar-zxvfnerdctl-0.11.0-linux-amd64.tar.gznerdctl&&mvnerdctl/usr/local/containerd/bin/~ln-s/usr/local/containerd/bin/nerdctl/usr/local/bin/nerdctl~nerdctlversionClient:Version:v0.11.0Gitcommit:c802f934791f83dacf20a041cd1c865f8fac954eServer:containerd:Version:v1.5.5Revision:72cec4be58a9eb6b2910f5d10f1c01ca47d231c0
安装完成后接下来学习下 nerdctl 命令行工具的使用。
命令
Run&Exec
nerdctl run
和 docker run 类似可以使用 nerdctl run 命令运行容器,例如:
~nerdctlrun-d-p80:80--name=nginx--restart=alwaysnginx:alpinedocker.io/library/nginx:alpine:resolved|++++++++++++++++++++++++++++++++++++++|index-sha256:bead42240255ae1485653a956ef41c9e458eb077fcb6dc664cbc3aa9701a05ce:done|++++++++++++++++++++++++++++++++++++++|manifest-sha256:ce6ca11a3fa7e0e6b44813901e3289212fc2f327ee8b1366176666e8fb470f24:done|++++++++++++++++++++++++++++++++++++++|config-sha256:7ce0143dee376bfd2937b499a46fb110bda3c629c195b84b1cf6e19be1a9e23b:done|++++++++++++++++++++++++++++++++++++++|elapsed:5.3stotal:3.1Ki(606.0B/s)6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8
可选的参数使用和 docker run 基本一致,比如 -i、-t、–cpus、–memory 等选项,可以使用 nerdctl run –help 获取可使用的命令选项:
~nerdctlrun--helpNAME:nerdctlrun-RunacommandinanewcontainerUSAGE:nerdctlrun[commandoptions][arguments...]OPTIONS:--helpshowhelp(default:false)--tty,-t(Currently-tneedstocorrespondto-i)(default:false)--interactive,-iKeepSTDINopenevenifnotattached(default:false)--detach,-dRuncontainerinbackgroundandprintcontainerID(default:false)--restartvalueRestartpolicytoapplywhenacontainerexits(implementedvalues:"no"|"always")(default:"no")--rmAutomaticallyremovethecontainerwhenitexits(default:false)--pullvaluePullimagebeforerunning("always"|"missing"|"never")(default:"missing")--networkvalue,--netvalueConnectacontainertoanetwork("bridge"|"host"|"none")(default:"bridge")--dnsvalueSetcustomDNSservers(default:"8.8.8.8","1.1.1.1")--publishvalue,-pvaluePublishacontainer'sport(s)tothehost--hostnamevalue,-hvalueContainerhostname--cpusvalueNumberofCPUs(default:0)--memoryvalue,-mvalueMemorylimit--pidvaluePIDnamespacetouse--pids-limitvalueTunecontainerpidslimit(set-1forunlimited)(default:-1)--cgroupnsvalueCgroupnamespacetouse,thedefaultdependsonthecgroupversion("host"|"private")(default:"host")--cpuset-cpusvalueCPUsinwhichtoallowexecution(0-3,0,1)--cpu-sharesvalueCPUshares(relativeweight)(default:0)--devicevalueAddahostdevicetothecontainer--uservalue,-uvalueUsernameorUID(format:<name|uid>[:<group|gid>])--security-optvalueSecurityoptions--cap-addvalueAddLinuxcapabilities--cap-dropvalueDropLinuxcapabilities--privilegedGiveextendedprivilegestothiscontainer(default:false)--runtimevalueRuntimetouseforthiscontainer,e.g."crun",or"io.containerd.runsc.v1"(default:"io.containerd.runc.v2")--sysctlvalueSysctloptions--gpusvalueGPUdevicestoaddtothecontainer('all'topassallGPUs)--volumevalue,-vvalueBindmountavolume--read-onlyMountthecontainer'srootfilesystemasreadonly(default:false)--rootfsThefirstargumentisnotanimagebuttherootfstotheexplodedcontainer(default:false)--entrypointvalueOverwritethedefaultENTRYPOINToftheimage--workdirvalue,-wvalueWorkingdirectoryinsidethecontainer--envvalue,-evalueSetenvironmentvariables--env-filevalueSetenvironmentvariablesfromfile--namevalueAssignanametothecontainer--labelvalue,-lvalueSetmetadataonacontainer--label-filevalueReadinalinedelimitedfileoflabels--cidfilevalueWritethecontainerIDtothefile--shm-sizevalueSizeof/dev/shm
nerdctl exec
同样也可以使用 exec 命令执行容器相关命令,例如:
~nerdctlexec-itnginx/bin/sh/#dateThuAug1906:43:19UTC2021/#
容器管理
nerdctl ps:列出容器
使用 nerdctl ps 命令可以列出所有容器。
~nerdctlpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES6e489777d2f7docker.io/library/nginx:alpine"/docker-entrypoint.…"10minutesagoUp0.0.0.0:80->80/tcpnginx
同样可以使用 -a 选项显示所有的容器列表,默认只显示正在运行的容器,不过需要注意的是 nerdctl ps 命令并没有实现 docker ps 下面的 –filter、–format、–last、–size 等选项。
nerdctl inspect:获取容器的详细信息。
~nerdctlinspectnginx[{"Id":"6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8","Created":"2021-08-19T06:35:46.403464674Z","Path":"/docker-entrypoint.sh","Args":["nginx","-g","daemonoff;"],"State":{"Status":"running","Running":true,"Paused":false,"Pid":2002,"ExitCode":0,"FinishedAt":"0001-01-01T00:00:00Z"},"Image":"docker.io/library/nginx:alpine","ResolvConfPath":"/var/lib/nerdctl/1935db59/containers/default/6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8/resolv.conf","LogPath":"/var/lib/nerdctl/1935db59/containers/default/6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8/6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8-json.log","Name":"nginx","Driver":"overlayfs","Platform":"linux","AppArmorProfile":"nerdctl-default","NetworkSettings":{"Ports":{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"80"}]},"GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"IPAddress":"10.4.0.3","IPPrefixLen":24,"MacAddress":"f2:b1:8e:a2:fe:18","Networks":{"unknown-eth0":{"IPAddress":"10.4.0.3","IPPrefixLen":24,"GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"f2:b1:8e:a2:fe:18"}}}}]
可以看到显示结果和 docker inspect 也基本一致的。
nerdctl logs:获取容器日志
查看容器日志是我们平时经常会使用到的一个功能,同样我们可以使用 nerdctl logs 来获取日志数据:
~nerdctllogs-fnginx......2021/08/1906:35:46[notice]1#1:startworkerprocesses2021/08/1906:35:46[notice]1#1:startworkerprocess322021/08/1906:35:46[notice]1#1:startworkerprocess33
同样支持 -f、-t、-n、–since、–until 这些选项。
nerdctl stop:停止容器
~nerdctlstopnginxnginx~nerdctlpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES~nerdctlps-aCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES6e489777d2f7docker.io/library/nginx:alpine"/docker-entrypoint.…"20minutesagoUp0.0.0.0:80->80/tcpnginx
nerdctl rm:删除容器
~nerdctlrmnginxYoucannotremovearunningcontainerf4ac170235595f28bf962bad68aa81b20fc83b741751e7f3355bd77d8016462d.Stopthecontainerbeforeattemptingremovalorforceremove~nerdctlrm-fginxnginx~nerdctlpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
要强制删除同样可以使用 -f 或 –force 选项来操作。
镜像管理
nerdctl images:镜像列表
~nerdctlimagesREPOSITORYTAGIMAGEIDCREATEDSIZEalpinelatesteb3e4e175ba66daysago5.9MiBnginxalpinebead4224025529minutesago16.0KiB
也需要注意的是没有实现 docker images 的一些选项,比如 –all、–digests、–filter、–format。
nerdctl pull:拉取镜像
~nerdctlimagermbusyboxUntagged:docker.io/library/busybox:latest@sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60Deleted:sha256:5b8c72934dfc08c7d2bd707e93197550f06c0751023dabb3a045b723c5e7b373docker.io/library/busybox:latest:resolved|++++++++++++++++++++++++++++++++++++++|index-sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60:done|++++++++++++++++++++++++++++++++++++++|manifest-sha256:dca71257cd2e72840a21f0323234bb2e33fea6d949fa0f21c5102146f583486b:done|++++++++++++++++++++++++++++++++++++++|config-sha256:69593048aa3acfee0f75f20b77acb549de2472063053f6730c4091b53f2dfb02:done|++++++++++++++++++++++++++++++++++++++|layer-sha256:b71f96345d44b237decc0c2d6c2f9ad0d17fde83dad7579608f1f0764d9686f2:done|++++++++++++++++++++++++++++++++++++++|elapsed:5.7stotal:752.8(132.0KiB/s)
nerdctl push:推送镜像
当然在推送镜像之前也可以使用 nerdctl login 命令登录到镜像仓库,然后再执行 push 操作。
可以使用 nerdctl login –username xxx –password xxx 进行登录,使用 nerdctl logout 可以注销退出登录。
nerdctl tag:镜像标签
使用 tag 命令可以为一个镜像创建一个别名镜像:
~nerdctlimagesREPOSITORYTAGIMAGEIDCREATEDSIZEbusyboxlatest0f354ec1728d6minutesago1.3MiBnginxalpinebead4224025541minutesago16.0KiB~nerdctltagnginx:alpineharbor.k8s.local/course/nginx:alpine~nerdctlimagesREPOSITORYTAGIMAGEIDCREATEDSIZEbusyboxlatest0f354ec1728d7minutesago1.3MiBnginxalpinebead4224025541minutesago16.0KiBharbor.k8s.local/course/nginxalpinebead422402552secondsago16.0KiB
nerdctl save:导出镜像
使用 save 命令可以导出镜像为一个 tar 压缩包。
~nerdctlsave-obusybox.tar.gzbusybox:latest~ls-lhbusybox.tar.gz-rw-r--r--1rootroot761KAug1915:19busybox.tar.gz
nerdctl rmi:删除镜像
~nerdctlrmibusyboxUntagged:docker.io/library/busybox:latest@sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60Deleted:sha256:5b8c72934dfc08c7d2bd707e93197550f06c0751023dabb3a045b723c5e7b373
nerdctl load:导入镜像
使用 load 命令可以将上面导出的镜像再次导入:
~nerdctlload-ibusybox.tar.gzunpackingdocker.io/library/busybox:latest(sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60)...done
使用 -i 或 –input 选项指定需要导入的压缩包。
镜像构建
镜像构建是平时我们非常重要的一个需求,我们知道 ctr 并没有构建镜像的命令,而现在我们又不使用 Docker 了,那么如何进行镜像构建了,幸运的是 nerdctl 就提供了 nerdctl build 这样的镜像构建命令。
nerdctl build:从 Dockerfile 构建镜像
比如现在我们定制一个 nginx 镜像,新建一个如下所示的 Dockerfile 文件:
FROMnginxRUNecho'这是一个基于containerd使用nerdctl构建的nginx镜像'>/usr/share/nginx/html/index.html
然后在文件所在目录执行镜像构建命令:
~nerdctlbuild-tnginx:nerdctl-fDockerfile.FATA[0000]`buildctl`needstobeinstalledand`buildkitd`needstoberunning,seehttps://github.com/moby/buildkit:exec:"buildctl":executablefilenotfoundin$PATH
可以看到有一个错误提示,需要我们安装 buildctl 并运行 buildkitd,这是因为 nerdctl build 需要依赖 buildkit 工具。
buildkit 项目也是 Docker 公司开源的一个构建工具包,支持 OCI 标准的镜像构建。它主要包含以下部分:
服务端 buildkitd:当前支持 runc 和 containerd 作为 worker,默认是 runc,我们这里使用 containerd客户端 buildctl:负责解析 Dockerfile,并向服务端 buildkitd 发出构建请求buildkit 是典型的 C/S 架构,客户端和服务端是可以不在一台服务器上,而 nerdctl 在构建镜像的时候也作为 buildkitd 的客户端,所以需要我们安装并运行 buildkitd。
所以接下来我们先来安装 buildkit:
~wgethttps://github.com/moby/buildkit/releases/download/v0.9.0/buildkit-v0.9.0.linux-amd64.tar.gz#如果有限制,也可以替换成下面的URL加速下载#wgethttps://download.fastgit.org/moby/buildkit/releases/download/v0.9.0/buildkit-v0.9.0.linux-amd64.tar.gz~tar-zxvfbuildkit-v0.9.0.linux-amd64.tar.gz-C/usr/local/containerd/bin/bin/buildctlbin/buildkit-qemu-aarch64bin/buildkit-qemu-armbin/buildkit-qemu-i386bin/buildkit-qemu-mips64bin/buildkit-qemu-mips64elbin/buildkit-qemu-ppc64lebin/buildkit-qemu-riscv64bin/buildkit-qemu-s390xbin/buildkit-runcbin/buildkitd~ln-s/usr/local/containerd/bin/buildkitd/usr/local/bin/buildkitd~ln-s/usr/local/containerd/bin/buildctl/usr/local/bin/buildctl
这里我们使用 Systemd 来管理 buildkitd,创建如下所示的 systemd unit 文件:
~cat/etc/systemd/system/buildkit.service[Unit]Description=BuildKitDocumentation=https://github.com/moby/buildkit[Service]ExecStart=/usr/local/bin/buildkitd--oci-worker=false--containerd-worker=true[Install]WantedBy=multi-user.target
然后启动 buildkitd:
~systemctldaemon-reload~systemctlenablebuildkit--nowCreatedsymlink/etc/systemd/system/multi-user.target.wants/buildkit.service→/etc/systemd/system/buildkit.service.~systemctlstatusbuildkit●buildkit.service-BuildKitLoaded:loaded(/etc/systemd/system/buildkit.service;enabled;vendorpreset:enabled)Memory:8.6MCGroup:/system.slice/buildkit.service└─5779/usr/local/bin/buildkitd--oci-worker=false--containerd-worker=trueAug1916:03:10ydzsiosystemd[1]:StartedBuildKit.Aug1916:03:10ydzsiobuildkitd[5779]:time="2021-08-19T16:03:10+08:00"level=warningmsg="usinghostnetworkasthedefault"Aug1916:03:10ydzsiobuildkitd[5779]:time="2021-08-19T16:03:10+08:00"level=infomsg="foundworker\"euznuelxhxb689bc5of7pxmbc\",labels>Aug1916:03:10ydzsiobuildkitd[5779]:time="2021-08-19T16:03:10+08:00"level=infomsg="found1workers,default=\"euznuelxhxb689bc5of7pxm>Aug1916:03:10ydzsiobuildkitd[5779]:time="2021-08-19T16:03:10+08:00"level=warningmsg="currently,onlythedefaultworkercanbeused."Aug1916:03:10ydzsiobuildkitd[5779]:time="2021-08-19T16:03:10+08:00"level=infomsg="runningserveron/run/buildkit/buildkitd.sock"~
现在我们再来重新构建镜像:
nerdctl 构建镜像
构建完成后查看镜像是否构建成功:
~nerdctlimagesWARN[0000]unparsableimagename"overlayfs@sha256:d5b9b9e4c930f30340650cb373f62f97c93ee3b92c83f01c6e00b7b87d62c624"REPOSITORYTAGIMAGEIDCREATEDSIZEnginxlatest4d4d96ac750a4minutesago16.0KiBnginxnerdctld5b9b9e4c930Aboutaminuteago24.0KiBd5b9b9e4c930Aboutaminuteago24.0KiB
我们可以看到已经有我们构建的 nginx:nerdctl 镜像了,不过出现了一个 WARN[0000] unparsable image name “xxx” 的 Warning 信息,在镜像列表里面也可以看到有一个镜像 tag 为空的镜像,和我们构建的镜像 ID 一样,在 nerdctl 的 github issue 上也有提到这个问题:https://github.com/containerd/nerdctl/issues/177,不过到现在为止还没有 FIX,幸运的是这只是一个️,不会影响我们的使用。
接下来使用上面我们构建的镜像来启动一个容器进行测试:
~nerdctlrun-d-p80:80--name=nginx--restart=alwaysnginx:nerdctlf8f639cb667926023231b13584226b2c7b856847e0a25bd5f686b9a6e7e3cacd~nerdctlpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMESf8f639cb6679docker.io/library/nginx:nerdctl"/docker-entrypoint.…"1secondagoUp0.0.0.0:80->80/tcpnginx~curllocalhostThisisanerdctlbuild'snginximagebaseoncontainerd
这样我们就使用 nerdctl + buildkitd 轻松完成了容器镜像的构建。
当然如果你还想在单机环境下使用 Docker Compose,在 containerd 模式下,我们也可以使用 nerdctl 来兼容该功能。同样我们可以使用 nerdctl compose、nerdctl compose up、nerdctl compose logs、nerdctl compose build、nerdctl compose down 等命令来管理 Compose 服务。这样使用 containerd、nerdctl 结合 buildkit 等工具就完全可以替代 docker 在镜像构建、镜像容器方面的管理功能了。