0%
crun#002#手工使用crun运行Docker镜像nginx-ubuntu

前面的几篇文章我们介绍了crun的编译方法、基本使用教程,以及制作了基础镜像nginx-ubuntu。本文研究在不依赖Docker的情况下,我们使用底层命令crun来运行Docker镜像的方法。

总体来说,使用crun运行Docker镜像,并且外网可以访问内部nginx服务,实施步骤如下:

  • 第一,在宿主机上添加网桥,给宿主机以及容器添加虚拟网卡对;
  • 第二,获取Docker镜像,并将其转换成bundle文件;
  • 第三,修改config.json文件,加入网络namespace的文件路径;
  • 第四,使用curn以前台或者后台方式启动容器;
  • 第五,在宿主机上添加网络转发规则,让外部请求转给容器然后传递到nginx;
  • 第六,从宿主机以外的机器,访问容器中的nginx,测试服务是否正常;

下面我们逐步开始实施。

第0x01步:添加网桥和虚拟网卡对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

brctl addbr docker1
ip link set docker1 up
ip addr add 192.168.3.1/24 dev docker1

ip link add name veth0-host type veth peer name veth0-ns
ip link set veth0-host up
brctl addif docker1 veth0-host

ip netns add ns-nginx-ubuntu
ip link set veth0-ns netns ns-nginx-ubuntu

ip netns exec ns-nginx-ubuntu ip link set veth0-ns name eth0
ip netns exec ns-nginx-ubuntu ip addr add 192.168.3.2/24 dev eth0
ip netns exec ns-nginx-ubuntu ip link set eth0 up
ip netns exec ns-nginx-ubuntu ip addr add 127.0.0.1 dev lo
ip netns exec ns-nginx-ubuntu ip link set lo up
ip netns exec ns-nginx-ubuntu ip route add default via 192.168.3.1

对IP地址的说明:

  • 我的地网络网段是 192.168.0.0/24,为了和本地网络区分开以免互相影响,我将网桥和网卡设置在网段 192.168.3.0/24 。
  • 网桥的IP地址为 192.168.3.1/24
  • 容器中网卡的地址为 192.168.3.2/24
  • 容器中网卡的默认路由规则是发送到 192.168.3.1

将上述内容保存为文件,如 setnet.sh ,然后在服务器上使用命令 sudo sh setnet.sh ,即可完成所有添加操作。结果如下图所示:

第0x02步:下载镜像文件

获取Docker镜像,并将其转换成根文件系统,需要用到skopeo和umoci工具,首先我们使用如下命令安装依赖工具:

1
$ sudo apt install -y skopeo umoci

然后使用如下命令开始拉取镜像文件:

1
$ skopeo copy docker://mancodenet/nginx-ubuntu:1.0.0 oci:nginx-ubuntu:1.0.0

命令执行如下图所示:

nginx-ubuntu目录就是我们拉取的镜像文件,我们看下它的目录结构:

第0x03步:解包镜像文件,转换为根文件系统

1
2
3
4
5
$ sudo umoci unpack --image nginx-ubuntu:1.0.0 nginx-bundle

# 修改目录权限,mancode是我当前的用户,这里换成你自己的
$ sudo chown mancode:mancode nginx-bundle
$ sudo chmod 0775 nginx-bundle

命令执行如下图所示:

第0x04步:修改nginx-bundle/config.json,添加网络namespace路径

打开nginx-bundle/config.json,找到 namespaces 配置域,然后找到 “type”: “network” 配置域,在其配置域内添加 “path” 配置项,其值指向上述我们添加的网络namespace的文件路径。

文件修改之前,如下图所示:

文件修改之后,如下图所示:

第0x05步:使用crun前台方式启动nginx-ubuntu容器

1
$ sudo crun run --bundle=nginx-bundle demo-nginx-ubuntu 

启动容器执行过程如下图所示:

保持该当前终端窗口不关闭以及当前容器不退出的情况下,我们打开另外一个终端窗口,查看容器的运行情况:

1
$ sudo crun list

结果如下图所示:

第0x06步:打开数据转发,将对应的访问请求,转发给容器,然后传递给容器中的nginx

1
2
3
4
5
6
# 设置转发规则:将访问宿主机 TCP:80 端口的数据,转给容器 demo-nginx-ubuntu 的80端口
$ sudo iptables -t nat -I PREROUTING -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.3.2:80

# 打开转发权限
$ sudo sysctl -w net.ipv4.ip_forward=1
$ sudo iptables -P FORWARD ACCEPT

第0x07步:验证访问容器的nginx,看是否可以正常提供服务

从前文我们知道宿主机的IP是:192.168.0.51,我们在其他机器上打开浏览器,然后访问:http://192.168.0.51 ,如下图所示:

直观上看,我们已经成功的访问nginx服务。

为了进一步验证,我们访问一个特殊的URL:http://192.168.0.51/test-404-not-found.html ,然后深入容器,看下nginx的访问日志以佐证:

如何使用crun后台方式启动nginx-ubuntu容器?

我们知道加上 -d 选项,表明让容器在后台运行,那么我们先使用如下命令测试下后台运行:

1
$ sudo crun run -d --bundle=nginx-bundle demo-nginx-ubuntu  

使用上述命令启动,无法启动容器,且报错如下:

1
2
mancode@manos:~/crun$ sudo crun run -d --bundle=nginx-bundle demo-nginx-ubuntu
2024-03-07T16:54:46.000296930Z: use --console-socket with --detach when a terminal is used

提示我们后台运行错误:当使用terminal启动容器时,须配合 --console-socket 参数一起使用。按要求,它是一个Unix套接域文件,用于传递信息。

明确了后台启动的要求之后,我们总结下后台启动容器的步骤:

第一,该套接域文件必须是真实存在的;
第二,该套接域文件由一个监听程序创建,比如 /tmp/demo-nginx-ubuntu.sock ;
第三,然后使用crun后台启动容器时,使用 --console-socket=/tmp/demo-nginx-ubuntu.sock 的方式进行连接。

这个监听程序哪里来呢?
A)在crun的源代码中,crun-1.14.4/contrib/terminal-receiver 提供了一个demo程序,我们编译该文件可生成监控程序。
B)该文件可以访问 本地 或者 Github 下载。
C)使用命令 cc terminal-receiver.c -o terminal-receiver 编译,即可生成一个名为 terminal-receiver 的可用的监听程序。

下面我们开始正式后台启动容器:

第一步,启动监听程序,如下所示:

1
$ ./terminal-receiver /tmp/demo-nginx-ubuntu.sock

第二步,另起一个终端窗口,我们使用crun后台启动容器,如下所示:

1
$ sudo crun run -d --bundle=nginx-bundle --console-socke=/tmp/demo-nginx-ubuntu.sock demo-nginx-ubuntu

网络转发设置以及测试运行方法,与前文所述一致,读者自行验证。到此我们成功使用后台启动的方法运行了容器。

另外一种使用crun后台运行容器的方法

前文后台运行容器时,提示我们后台运行错误:当使用terminal启动容器时,须配合 --console-socket 参数一起使用。那我们可以禁止使用terminal,选项开关在配置文件config.json中。

打开nginx-bundle/config.json,修改 “process”>”terminal” 为false。

修改文件前:

修改文件后:

使用如下命令,以后台方式启动容器:

1
$ sudo crun run -d --bundle=nginx-bundle demo-nginx-ubuntu  

命令执行结果如下图所示:

从上图结果我们可以看出,修改配置文件之后,我们不再需要terminal-receiver,即能以后台方式启动容器。

参考文档