containerd中文翻译系列(一) 开启containerd之旅

开始使用CONTAINERD

安装containerd

方法一:通过官方二进制文件

容器的官方二进制版本可用于AMD64(也称为X86_64)和ARM64(也称为aarch64)架构。

通常,您必须通过官网安装runc和cni plugins

步骤1:安装containerd

从https://github.com/containerd/containerd/releases 下载contanserd 版本 containerd-<VERSION>-<OS>-<ARCH>.tar.gz 压缩包, 验证其sha256sum,并解压到/usr/local

$ tar cxzvf /usr /local containerd-1.6.2-linux-amd64.tar.gz
bin/
bin/containerd-shim-runc-v2
bin/containerd-shim
bin/ctr
bin/containerd-shim-runc-v1
bin/containerd
bin/```
containerd-stress

该二进制可以动态构建基于glibc的linux发行版(例如Ubuntu和 Rocky Linux),但可能不适合基于musl的linux发行版如Alphine. 用户可以以尝试通过源代码或第三方软件包来安装containerd。

** FAQ **:对于Kubernetes,我需要下载 cri-containerd-(cni-)<VERSION>-<OS-<ARCH>.tar.gz吗?

答案:不。

由于kubernetes cri功能已经包含在containerd-<VERSION>-<OS>-<ARCH>.tar.gz
您不需要下载cri-containerd-....压缩包。

cri-containerd-....已经过期 请勿在旧的Linux发行版上使用,Containerd 2.0将会删除它。

Systemd

如果您打算通过SystemD启动containerd,还应从
https://raw.githubusercontent.com/containerd/containerd/main/containerd.service 下载到 /usr/usr/local/local/lib/lib/systemd/systemd/castererd.service.service
并运行以下命令:

systemctl daemon-reload
systemctl enable --now containerd
步骤2:安装runc

从https://github.com/opencontainers/runc/releases 下载runc
验证其SHA256SUM,并将其安装到/usr/local/sbin/runc

$ install -m 755 runc.amd64/usr/local/sbin/runc

二进制是静态构建的,应在任何Linux发行版上都可用。

步骤3:安装CNI插件

从https://github.com/containernetworking/plugins/releases 下载 cni-plugins-]cni-plugins-<OS>-<ARCH>-<VERSION>.tgz压缩包,验证其sha256sum,并解压到/opt/opt/cni/bin

$ mkdir -p/opt/cni/bin
$ tar cxzvf/opt/cni/bin cni-plugins-linux-amd64-v1.1.1.tgz
./
./macvlan
./static
./vlan
./portmap
./ host-local
./vrf
./bridge
./tuning
./firewall
./ host device
./sbr
./ loopback
./dhcp
./ptp
./ipvlan
./brandwidth

这些二进制文件是静态构建的,应在任何Linux发行版上可用。

选项2:通过apt-getdnf

deb和rpm格式的“containerd.io”软件包是由Docker(不是由Containerd Project)分发的。
有关如何设置apt-getdnf'的Docker文档,以安装containerd.io`软件包:

containerd.io软件包也包含runc,但不包含CNI插件。

选项3:源代码安装

通过源代码安装containerd及其依赖项,请参见` building.md

在Windows上安装containerd

在PowerShell中运行以下命令:

#下载并解压所需的Containerd Windows二进制文件
$Version="1.6.4"
curl.exe -L https://github.com/containerd/containerd/releases/download/v$Version/containerd-$Version-windows-amd64.tar.gz -o containerd-windows-amd64.tar.gz
tar.exe xvf .\containerd-windows-amd64.tar.gz

#复制和配置  
Copy-Item -Path ".\bin" -Destination "$Env:ProgramFiles\containerd" -Recurse -Container:$false -Force
cd $Env:ProgramFiles\containerd\
.\containerd.exe config default | Out-File config.toml -Encoding ascii
 
#查看配置。根据设置的不同,您可能需要调整:  
#-SandBox_Image(Kubernetes pause image)  
#-CNI bin_dir和conc_dir位置  
get-content config.toml

#注册并启动服务  
.\containerd.exe --register-service
Start-Service containerd

通过CLI与容器互动

有几个命令行接口(CLI)项目用于与Containerd交互:

名称社区API目标站点
ctrcontainerdNativeFor debugging only(None, see ctr --help to learn the usage)
nerdctlcontainerd (non-core)NativeGeneral-purposehttps://github.com/containerd/nerdctl
crictlKubernetes SIG-nodeCRIFor debugging onlyhttps://github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md

虽然“ Ct​​r”工具与容器捆绑在一起,但应注意,ctr工具仅用于调试容器。
nerdctl工具提供稳定且人性化的用户体验。

示例(ctr):

ctr images pull docker.io/library/redis:alpine
ctr run docker.io/library/redis:alpine redis

示例(nerdctl):

nerdctl run --name redis redis:alpine

为kubernetes设置Containerd

容器对Kubernetes容器运行时接口(CRI)具有内置支持。

要为托管Kubernetes服务设置容器节点,请参见服务提供商的文档:

对于非管理环境,请参见以下Kubernetes文档:

高级主题

自定义Containerd

Containerd使用位于/etc/containerd/config.toml指定守护程序级别选项的配置文件。
可以在此处找到示例配置文件。

默认配置可以通过containerd config default>/etc/containerd/config.toml生成。

实现自己的containerd客户端

使用容器有许多不同的方法。
如果您是在containerd上工作的开发人员,则可以使用ctr工具或nerdctl工具来快速测试功能,而无需编写额外的代码。
但是,如果您想将containerd集成到您的项目中,我们有一个易用的客户端软件包,使您可以与Containerd合作。

在本指南中,我们将使用客户端软件包拉取并运行用于Containerd的Redis Server。
该项目需要GO的最新版本。相关推荐,请参见go.mod

连接到containerd

我们将启动一个新的“ main.go”文件,并导入containerd client 包。

go
package main

import (
	"log"

	containerd "github.com/containerd/containerd/v2/client"
)

func main() {
	if err := redisExample(); err != nil {
		log.Fatal(err)
	}
}

func redisExample() error {
	client, err := containerd.New("/run/containerd/containerd.sock")
	if err != nil {
		return err
	}
	defer client.Close()
	return nil
}

这将使用默认containerd套接字路径创建一个新客户端。
因为我们正在使用GRPC与守护程序协作,因此需要创建一个context,以用来调用客户端方法。对于API的呼叫者,containerd也应该namespace化。创建上下文后,我们还应该为这个指南设置namespace。

ctx:= namespaces.withnamespace(context.background(),'example'

使用一个命名空间可确保containerd外的容器,图像和其他资源不会与单个守护程序的其他用户冲突。

拉取镜像

现在,我们有一个客户端,现在需要提取镜像。
我们可以使用DockerHub的Alpine Linux使用REDIS图像。

image, err := client.Pull(ctx, "docker.io/library/redis:alpine", containerd.WithPullUnpack)
	if err != nil {
		return err
	}

容器客户端对许多方法调用使用Opts模式。
我们使用contanserd.withpullunpack,因此我们不仅将内容获取并下载到Containerd的content store中,而且还将其解压到snapshotter中,以用作根文件系统。

让我们将代码放在一起,将基于Dockerhub的Alpine Linux Redis镜像,然后在控制台的打印出镜像名称。

package main

import (
        "context"
        "log"

        containerd "github.com/containerd/containerd/v2/client"
        "github.com/containerd/containerd/v2/namespaces"
)

func main() {
        if err := redisExample(); err != nil {
                log.Fatal(err)
        }
}

func redisExample() error {
        client, err := containerd.New("/run/containerd/containerd.sock")
        if err != nil {
                return err
        }
        defer client.Close()

        ctx := namespaces.WithNamespace(context.Background(), "example")
        image, err := client.Pull(ctx, "docker.io/library/redis:alpine", containerd.WithPullUnpack)
        if err != nil {
                return err
        }
        log.Printf("Successfully pulled %s image\n", image.Name())

        return nil
}
> go build main.go
> sudo ./main

2017/08/13 17:43:21 Successfully pulled docker.io/library/redis:alpine image

创建OCI规格和容器

现在,我们有了一个镜像来作为容器基础,我们需要生成一个OCI运行时规格,该规格可以被多个新容器使用。

containerd提供了合理的默认值,用于生成OCI运行时规格。
还有一个Opt,用于根据我们拉取的镜像修改默认配置。

容器将基于图像,我们将:
1.分配一个新的读写快照,以便容器可以存储任何持久信息。
2.为容器创建一个新规格。

	container, err := client.NewContainer(
		ctx,
		"redis-server",
		containerd.WithNewSnapshot("redis-server-snapshot", image),
		containerd.WithNewSpec(oci.WithImageConfig(image)),
	)
	if err != nil {
		return err
	}
            defer container.Delete(ctx, containerd.WithSnapshotCleanup)

如果有创建好了的OCI规格,则可以使用containerd.withspec(spec)将其应用在容器上。

为容器创建新快照时,我们需要提供快照ID以及用于容器的镜像。通过提供单独的快照ID,您可以轻松地复用在不同的容器上复用现有快照。

我们完成示例后可以把容器及快照一并删除。

代码示例从DockerHub绘制基于Alpine Linux的REDIS镜像码,创建OCI规格,然后基于规格创建容器,最后删除容器。

package main

import (
        "context"
        "log"

        containerd "github.com/containerd/containerd/v2/client"
        "github.com/containerd/containerd/v2/namespaces"
        "github.com/containerd/containerd/v2/oci"
)

func main() {
        if err := redisExample(); err != nil {
                log.Fatal(err)
        }
}

func redisExample() error {
        client, err := containerd.New("/run/containerd/containerd.sock")
        if err != nil {
                return err
        }
        defer client.Close()

        ctx := namespaces.WithNamespace(context.Background(), "example")
        image, err := client.Pull(ctx, "docker.io/library/redis:alpine", containerd.WithPullUnpack)
        if err != nil {
                return err
        }
        log.Printf("Successfully pulled %s image\n", image.Name())

        container, err := client.NewContainer(
                ctx,
                "redis-server",
                containerd.WithNewSnapshot("redis-server-snapshot", image),
                containerd.WithNewSpec(oci.WithImageConfig(image)),
        )
        if err != nil {
                return err
        }
        defer container.Delete(ctx, containerd.WithSnapshotCleanup)
        log.Printf("Successfully created container with ID %s and snapshot with ID redis-server-snapshot", container.ID())

        return nil
}

让我们亲自动手一试。

> go build main.go
> sudo ./main

2017/08/13 18:01:35 Successfully pulled docker.io/library/redis:alpine image
2017/08/13 18:01:35 Successfully created container with ID redis-server and snapshot with ID redis-server-snapshot

创建一个运行任务

首先,对于新容器用户而言,可能会令人困惑的一件事是containertask之间的分离。
容器是一个分配和附加到资源的元数据对象。
任务是系统上的实时运行过程。
每次运行后应删除任务,而可以多次使用,更新和查询容器。

	task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio))
	if err != nil {
		return err
	}
	defer task.Delete(ctx)

我们刚刚创建的新任务实际上是您系统上的运行过程。
我们使用cio.withstdio,以便将容器中的所有io发送到我们的main.go过程。
这是一个“ cio.opt”,它使用newCreator为新任务配置了streams用于返回cio.io

如果您熟悉OCI运行时操作,则该任务当前处于“created”状态。
这意味着已经初始化了命名空间,根文件系统和各种容器级设置,但是在此示例中,用户定义的过程“redis server”尚未启动。这使用户有机会设置网络接口或连接不同的工具以监视容器。containerd趁此也会监视您的容器。如等待容器的退出状态和CGROUP metrics之类的内容。

如果您熟悉Prometheus,则可以curl 连接容器指标endpoint(在我们创建的config.toml中)以查看您的容器的指标:

> Curl 127.0.0.1:1338/v1/metrics

很酷不是吗?.

任务等待

现在,我们已经完成了创建状态的任务,我们需要确保等待退出任务。
必须等待任务完成,以便我们可以关闭示例并清理创建的资源。
您始终想确保waitstart调用之前调用任务。
如果任务具有一个简单的程序,例如/bin/true“,调用开始后迅速退出,则可以确保您不会遇到任何竞争。

	exitStatusC, err := task.Wait(ctx)
	if err != nil {
		return err
	}

	if err := task.Start(ctx); err != nil {
		return err
	}

现在,当我们运行main.go文件时,我们应该在终端中查看redis-serverlog。

杀死任务

由于我们运行了一台长期运行的服务器,因此我们需要杀死任务才能退出示例。
为此,我们只需在等待几秒钟之后就将调用task的“ kill”方法,这样我们就有机会看到Redis服务器日志。

	time.Sleep(3 * time.Second)

	if err := task.Kill(ctx, syscall.SIGTERM); err != nil {
		return err
	}

	status := <-exitStatusC
	code, exitedAt, err := status.Result()
	if err != nil {
		return err
	}
	fmt.Printf("redis-server exited with status: %d\n", code)

我们等待我们设置的退出状态通道,以确保任务已完全退出并获得退出状态。
如果您必须重新加载容器或错过等待任务,则delete也将在最终删除任务时返回退出状态。
我们也为此提供了保障。

status, err := task.Delete(ctx)

完整示例

这是我们刚刚完成的完整示例

package main

import (
	"context"
	"fmt"
	"log"
	"syscall"
	"time"

	"github.com/containerd/containerd/v2/cio"
	containerd "github.com/containerd/containerd/v2/client"
	"github.com/containerd/containerd/v2/oci"
	"github.com/containerd/containerd/v2/namespaces"
)

func main() {
	if err := redisExample(); err != nil {
		log.Fatal(err)
	}
}

func redisExample() error {
	// create a new client connected to the default socket path for containerd
	client, err := containerd.New("/run/containerd/containerd.sock")
	if err != nil {
		return err
	}
	defer client.Close()

	// create a new context with an "example" namespace
	ctx := namespaces.WithNamespace(context.Background(), "example")

	// pull the redis image from DockerHub
	image, err := client.Pull(ctx, "docker.io/library/redis:alpine", containerd.WithPullUnpack)
	if err != nil {
		return err
	}

	// create a container
	container, err := client.NewContainer(
		ctx,
		"redis-server",
		containerd.WithImage(image),
		containerd.WithNewSnapshot("redis-server-snapshot", image),
		containerd.WithNewSpec(oci.WithImageConfig(image)),
	)
	if err != nil {
		return err
	}
	defer container.Delete(ctx, containerd.WithSnapshotCleanup)

	// create a task from the container
	task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio))
	if err != nil {
		return err
	}
	defer task.Delete(ctx)

	// make sure we wait before calling start
	exitStatusC, err := task.Wait(ctx)
	if err != nil {
		return err
	}

	// call start on the task to execute the redis server
	if err := task.Start(ctx); err != nil {
		return err
	}

	// sleep for a lil bit to see the logs
	time.Sleep(3 * time.Second)

	// kill the process and get the exit status
	if err := task.Kill(ctx, syscall.SIGTERM); err != nil {
		return err
	}

	// wait for the process to fully exit and print out the exit status

	status := <-exitStatusC
	code, _, err := status.Result()
	if err != nil {
		return err
	}
	fmt.Printf("redis-server exited with status: %d\n", code)

	return nil
}

我们可以构建此示例并按以下方式运行,看看我们辛勤工作的成果。

> go build main.go
> sudo ./main

1:C 04 Aug 20:41:37.682 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 04 Aug 20:41:37.682 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 04 Aug 20:41:37.682 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 04 Aug 20:41:37.682 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
1:M 04 Aug 20:41:37.682 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
1:M 04 Aug 20:41:37.682 # Current maximum open files is 1024. maxclients has been reduced to 992 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
1:M 04 Aug 20:41:37.683 * Running mode=standalone, port=6379.
1:M 04 Aug 20:41:37.683 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 04 Aug 20:41:37.684 # Server initialized
1:M 04 Aug 20:41:37.684 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 04 Aug 20:41:37.684 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 04 Aug 20:41:37.684 * Ready to accept connections
1:signal-handler (1501879300) Received SIGTERM scheduling shutdown...
1:M 04 Aug 20:41:40.791 # User requested shutdown...
1:M 04 Aug 20:41:40.791 * Saving the final RDB snapshot before exiting.
1:M 04 Aug 20:41:40.794 * DB saved on disk
1:M 04 Aug 20:41:40.794 # Redis is now ready to exit, bye bye...
redis-server exited with status: 0

最后,当您使用客户端软件包时,我们确实没有编写那么多代码。

我们希望本指南能够帮助您使用Containerd启动并运行。
欢迎加入cloud Native Computing Foundation(CNCF)

  • cloud-native.slack.com 如果你有任何问题,如果你想为 containerd 或本指南做出贡献,请提交拉取请求。 获取CNCF Slack的邀请。
  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值