对于用惯了docker cli的用户来说,containerd的命令行工具ctr使用起来不是很顺手,此时别慌,还有另外一个命令行工具项目nerdctl可供我们选择。 nerdctl是一个与docker cli风格兼容的containerd的cli工具。 nerdctl已经作为子项目加入了containerd项目,它的github地址是https://github.com/containerd/nerdctl,而且从最近的nerdctl 0.8开始,nerdctl直接兼容了docker compose的语法(不包含swarm), 这很大提高了直接将containerd作为本地开发、测试和单机容器部署使用的体验。本来k8s后续将不再支持dockershim,docker在k8s社区的地位急剧下降,现在单机直接使用containerd易用性也不断被完善,也许docker的辉煌已经远去了。

实际上nerdctl compose实现的是Compose Specification规范, 这个规范是从自Docker Compose file version 3 specification规范发展而来的。

1.安装nerdctl

containerd 1.5最近刚刚发布,这里先把测试机上的containerd升级到1.5,直接替换containerd的二进制文件,重启containerd的systemd服务即可,升级过程略过。 升级完成后查看客户端和服务端版本均已经是1.5.0:

 1ctr version
 2Client:
 3  Version:  v1.5.0
 4  Revision: 8c906ff108ac28da23f69cc7b74f8e7a470d1df0
 5  Go version: go1.16.3
 6
 7Server:
 8  Version:  v1.5.0
 9  Revision: 8c906ff108ac28da23f69cc7b74f8e7a470d1df0
10  UUID: dee82270-b4b4-429c-befa-45df1421da7e

注意在安装nerdctl之前,需要确认安装了它的依赖组件(runc、containerd、cni),containerd和cni的详细安装步骤可以参考本系列文章第2节和第3节中的内容。

安装nerdctl:

1wget https://github.com/containerd/nerdctl/releases/download/v0.8.1/nerdctl-0.8.1-linux-amd64.tar.gz
2tar -zxvf nerdctl-0.8.1-linux-amd64.tar.gz nerdctl && mv nerdctl /usr/local/containerd/bin/
3ln -s /usr/local/containerd/bin/nerdctl /usr/local/bin/nerdctl

打印一下版本信息:

1nerdctl version
2Client:
3 Version:       v0.8.1
4 Git commit:    e1601447477c38ceb46c9c88418af399f79b1d6a
5
6Server:
7 containerd:
8  Version:      v1.5.0
9  Revision:     8c906ff108ac28da23f69cc7b74f8e7a470d1df0

2.nerdctl初体验

查看镜像:

1nerdctl images
2REPOSITORY    TAG           IMAGE ID        CREATED        SIZE
3redis         alpine3.13    f9577ac6e68c    11 days ago    10.4 MiB

启动一个redis容器:

1nerdctl run -d --name redis redis:alpine3.13
2
3nerdctl ps
4CONTAINER ID    IMAGE                                 COMMAND                   CREATED           STATUS    PORTS    NAMES
5709665b06ab1    docker.io/library/redis:alpine3.13    "docker-entrypoint.s…"    28 seconds ago    Up                 redis

进入容器内部:

 1nerdctl exec -it redis sh
 2/data # ip addr
 31: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
 4    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
 5    inet 127.0.0.1/8 scope host lo
 6       valid_lft forever preferred_lft forever
 7    inet6 ::1/128 scope host
 8       valid_lft forever preferred_lft forever
 93: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
10    link/ether 1a:84:4b:bb:1f:43 brd ff:ff:ff:ff:ff:ff
11    inet 10.4.0.4/24 brd 10.4.0.255 scope global eth0
12       valid_lft forever preferred_lft forever
13    inet6 fe80::1884:4bff:febb:1f43/64 scope link tentative dadfailed
14       valid_lft forever preferred_lft forever

查看宿主机上的网桥和veth对设备:

 1ls /var/lib/cni/networks/bridge/
 210.4.0.4  last_reserved_ip.0  lock
 3
 4ip addr
 5......
 616: nerdctl0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
 7    link/ether 3e:92:4a:37:cb:d3 brd ff:ff:ff:ff:ff:ff
 8    inet 10.4.0.1/24 brd 10.4.0.255 scope global nerdctl0
 9       valid_lft forever preferred_lft forever
10    inet6 fe80::3c92:4aff:fe37:cbd3/64 scope link
11       valid_lft forever preferred_lft forever
1219: veth98611773@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master nerdctl0 state UP
13    link/ether 2e:cd:71:38:3d:7b brd ff:ff:ff:ff:ff:ff link-netnsid 0
14    inet6 fe80::2ccd:71ff:fe38:3d7b/64 scope link
15       valid_lft forever preferred_lft forever

和docker一样,nerdctl也有一个子命令network:

1nerdctl network ls
2NETWORK ID    NAME         FILE
30             bridge
4              host
5              none

可以看一下默认的bridge网络的cni配置:

 1[
 2    {
 3        "CNI": {
 4            "cniVersion": "0.4.0",
 5            "name": "bridge",
 6            "nerdctlID": 0,
 7            "plugins": [
 8                {
 9                    "type": "bridge",
10                    "bridge": "nerdctl0",
11                    "isGateway": true,
12                    "ipMasq": true,
13                    "hairpinMode": true,
14                    "ipam": {
15                        "type": "host-local",
16                        "routes": [
17                            {
18                                "dst": "0.0.0.0/0"
19                            }
20                        ],
21                        "ranges": [
22                            [
23                                {
24                                    "subnet": "10.4.0.0/24",
25                                    "gateway": "10.4.0.1"
26                                }
27                            ]
28                        ]
29                    }
30                },
31                {
32                    "type": "portmap",
33                    "capabilities": {
34                        "portMappings": true
35                    }
36                },
37                {
38                    "type": "firewall"
39                },
40                {
41                    "type": "tuning"
42                }
43            ]
44        },
45        "NerdctlID": 0
46    }
47]

可以看出nertctl启动的容器的网络的背后实际上仍然是CNI在工作。 nerctl run命令在启动一个容器时,可以通过--net指定容器连接到一个网络,取值可以是bridge、host、或none,默认是bridge。所以宿主机上出现了nerdctl0网桥和eth98611773@if3。

3.nerdctl兼容docker compose

这里编写一个用于测试的docker-compose.yml文件:

 1version: '3'
 2services:
 3  db:
 4    image: postgres:10.1
 5    environment:
 6      - POSTGRES_USER=miniflux
 7      - POSTGRES_PASSWORD=secret
 8    volumes:
 9      - /home/miniflux/miniflux-db:/var/lib/postgresql/data
10  db-migrate:
11    image: miniflux/miniflux:2.0.29
12    command: ["/usr/bin/miniflux", "-migrate"]
13    depends_on:
14      - db
15    environment:
16      - DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
17  miniflux:
18    image: miniflux/miniflux:2.0.29
19    ports:
20      - "8080:8080"
21    depends_on:
22      - db-migrate
23    environment:
24      - DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable

miniflux是一个开源的RSS阅读器,主要有两个服务组成miniflux web服务和postgres数据库,可以部署Miniflux作为我们自己的RSS服务。 上面的docker compose文件之前已经使用docker-compose在docker上部署过,这里我们实验一下nerdctl在containerd上部署。

在宿主机上创建挂载出来的数据目录:

1cd /home
2mkdir miniflux
3cd miniflux
4mkdir miniflux-db

使用nerdctl执行compose命令:

1nerdctl compose -f docker-compose.yml up -d
2INFO[0000] Creating network miniflux_default
3INFO[0000] Ensuring image postgres:10.1
4INFO[0000] Ensuring image miniflux/miniflux:latest
5INFO[0000] Creating container miniflux_miniflux_1
6INFO[0000] Creating container miniflux_db_1
1nerdctl ps -a
2CONTAINER ID    IMAGE                                 COMMAND                   CREATED          STATUS                      PORTS                     NAMES
3219d71b12daa    docker.io/miniflux/miniflux:2.0.29    "/usr/bin/miniflux"       6 minutes ago    Up                          0.0.0.0:8080->8080/tcp    miniflux_miniflux_1
43b5275fdb361    docker.io/miniflux/miniflux:2.0.29    "/usr/bin/miniflux -…"    6 minutes ago    Exited (1) 6 minutes ago                              miniflux_db-migrate_1
5540239bdd876    docker.io/library/postgres:10.1       "docker-entrypoint.s…"    6 minutes ago    Up                                                    miniflux_db_1

注:实际使用nerdctl compose启动容器时,因为postgres启动比较慢,而compose里的depends_on只是描述container启动后就认为依赖OK了,所以可能会出现miniflux_db-migrate_1和miniflux_miniflux_1无法启动的情况。 此时,手动按顺序执行nerdctl start miniflux_db-migrate_1 && nerdctl logs miniflux_db-migrate_1nerdctl start miniflux_miniflux_1 && nerdctl logs miniflux_miniflux_1最终确保miniflux_miniflux_1和miniflux_db_1都进入up状态即可。

创建admin用户:

1nerdctl exec -it <container-name> /usr/bin/miniflux -create-admin
2Enter Username: admin
3Enter Password:

部署成功后可以使用http://ip:8080访问。

总结

可以看出nerdctl的使用方式和docker cli几乎一致,在启动容器和管理容器方面,从docker过渡到使用nerdctl+containerd几乎没有任何学习成本。 有了nerdctl,单机玩耍containerd不再是梦。

参考