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
$ 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"
        ]
    }
}
[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