runc 学习
前言
项目开发中需要让一些pod不依赖于docker运行时,也就是说集群节点中docker service停止运行后,业务pod需要正常 运行,此时我们可以使用runc+systemd的方式来启动业务容器。
为了更好的理解runc,先看一下整个docker运行时架构。
docker运行架构
从Docker 1.11开始,Docker容器运行已经不是简单的通过Docker daemon来启动,而是集成了containerd、runC等多个组件,这些组件之间相互协作来完成客户端请求和容器管理。
现在架构图如下:
各个模块交互流程图如下:
Docker Daemon
Docker Daemon是docker管理容器的守护进程,但是从Docker 1.11开始,守护进程变成了dockerd。 Dockerd对外通过https与Docker client交互,是对容器和镜像操作的上层封装。
Containerd
Containerd是容器标准化的产物,当初为了兼容OCI标准,docker公司将容器运行时及容器管理 功能从Docker Daemon中剥离,dockerd只是docker公司在Containerd上的一层封装,Dockerd 通过 Containerd提供的grpc接口 来实现容器管理。
container-shim
是一个真实运行的容器的真实垫片载体,每启动一个容器都会起一个新的container-shim,而后container-shim通过调用runc api(create/delete/kill)来实现容器的增删改查。 为什么需要container-shim呢,是因为用户通过dockerd发送创建或者删除容器请求,containerd收到后需要发送信号给container-shim,由container-shim来调用runc来完成容器创建/删除, 如果containerd直接和runc交互,当删除容器后,runc所启动的容器进程不复存在,无法反馈容器是否完成正确的操作。
其次,通过container-shim可以对接不同的容器运行时,例如kata container。
Runc
OCI定义了容器运行时标准,runC是Docker按照开放容器格式标准(OCF, Open Container Format)制定的一种具体实现。 runC是从Docker的libcontainer中迁移而来的,实现了容器启停、资源隔离等功能。Docker默认提供了docker-runc实现,事实上,通过containerd的封装,可以在Docker Daemon启动的时候指定runc的实现。
我们可以通过启动Docker Daemon时增加–add-runtime参数来选择其他的runC现。
docker daemon --add-runtime "custom=/usr/local/bin/my-runc-replacement"
使用Runc
上面我们了解了容器运行时架构,由此发现我们完全可以不依赖于docker,可以直接通过runc来启动容器,并通过systemd来管理runc进程。 假设你的应用已经以pod的形式运行在kubernetes集群中,现在我们使用runc模式来运行你的应用。
- 创建容器根文件系统
#获取pod中业务容器id
$ kubectl describe pod npd
#创建npd业务rootfs文件系统
$ mkidr -p npd/rootfs
$ cd npd
$ docker export podid | tar -C rootfs -xvf -
- 创建runc spec配置文件config.json
$ runc spec
$ ls
config.json rootfs
$ cat config.json
使用runc spec创建出来的配置文件包含了启动一个容器所需要的所有配置信息,但是里面的一些字段需要我们根据自身业务进行补充,内容如下:
{
"ociVersion": "1.0.0",
"process": {
"terminal": true, //使用sysytemd启动时需要修改为false,改为非终端模式
"user": {
"uid": 0,
"gid": 0
},
"args": [
//"sh" //需要替换为你业务启动参数
"/usr/local/bin/eskort-npd",
"--v=3",
"--detached-mode=detached",
...
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
... //需要添加上你的业务启动时需要的环境变量
],
"cwd": "/",
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"inheritable": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"permitted": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"ambient": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
]
},
"rlimits": [
{
"type": "RLIMIT_NOFILE",
"hard": 1024,
"soft": 1024
}
],
"noNewPrivileges": true
},
"root": {
"path": "rootfs",
"readonly": true
},
"hostname": "runc",
"mounts": [
{ //需要将pod中mount目录添加到mounts下,此处记得包括pod service account volume下的证书和token
"destination": "{{ $key }}",
"source": "{{ $val }}",
"options": [
"bind"
]
},
{
"destination": "/proc",
"type": "proc",
"source": "proc"
},
{
"destination": "/dev",
"type": "tmpfs",
"source": "tmpfs",
"options": [
"nosuid",
"strictatime",
"mode=755",
"size=65536k"
]
},
{
"destination": "/dev/pts",
"type": "devpts",
"source": "devpts",
"options": [
"nosuid",
"noexec",
"newinstance",
"ptmxmode=0666",
"mode=0620",
"gid=5"
]
},
{
"destination": "/dev/shm",
"type": "tmpfs",
"source": "shm",
"options": [
"nosuid",
"noexec",
"nodev",
"mode=1777",
"size=65536k"
]
},
{
"destination": "/dev/mqueue",
"type": "mqueue",
"source": "mqueue",
"options": [
"nosuid",
"noexec",
"nodev"
]
},
{
"destination": "/sys",
"type": "sysfs",
"source": "sysfs",
"options": [
"nosuid",
"noexec",
"nodev",
"ro"
]
},
{
"destination": "/sys/fs/cgroup",
"type": "cgroup",
"source": "cgroup",
"options": [
"nosuid",
"noexec",
"nodev",
"relatime",
"ro"
]
}
],
"linux": {
"resources": {
"devices": [
{
"allow": false,
"access": "rwm"
}
]
},
"namespaces": [
{
"type": "pid"
},
{
"type": "network"
},
{
"type": "ipc"
},
{
"type": "uts"
},
{
"type": "mount"
}
],
"maskedPaths": [
"/proc/kcore",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/sys/firmware",
"/proc/scsi"
],
"readonlyPaths": [
"/proc/asound",
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
}
}
- 配置systemd配置文件
[Unit]
Description=Npd Detached
[Service]
Type=forking
ExecStartPre=-/usr/bin/runc kill npd KILL
ExecStartPRe=-/usr/bin/runc delete npd
ExecStart=/usr/bin/runc run -d --pid--file /run/npd.pid npd
WorkingDirectory=/home/wky/runc/npd
PIDFile=/run/npd.pid
Restart=always
RestartSec=15
[Install]
WantedBy=multi-user.target
通过systemctl来启动或者停止runc容器
systemctl enable npd.service
systemctl reload npd.service
systemctl restart npd.service
systemctl disable npd.service
systemctl stop npd.service