Introducing CRIContainer Runtime Interface in Kubernete
作者:禅与计算机程序设计艺术
1.简介
2017年10月,Kubernetes项目社区推出了CRI(Container Runtime Interface)定义标准,通过该接口标准化容器运行时(container runtimes)成为可能。这个标准定义了一套通用的容器管理系统API,使得容器编排工具(如docker、containerd等)能够无缝对接到Kubernetes平台。
2019年初,容器编排领域出现了多种开源产品或解决方案,如Docker Compose、Kubernetes Operator等,它们均可以实现编排功能。随着云原生和微服务架构的兴起,越来越多的公司开始采用Kubernetes作为自己的容器编排引擎,在当今快速变化的IT环境下,如何让各家公司使用的容器运行时(container runtimes)达成一致、互通互联是非常重要的问题。因此,CRI就是为了解决这个问题而提出的,通过CRI,各家公司可以提供统一的接口规范,使得Kubernetes集群中的不同容器运行时(container runtimes)都可以无缝集成。
本文将会从以下方面详细阐述CRI的历史背景及其作用,并结合Kubernetes中如何集成它。
- 历史背景
从Docker诞生至今,它已经成为最流行的容器运行时之一,拥有庞大的用户群体和广泛的应用场景。2013年,Docker创始人在GitHub上发布了Docker项目,使得整个容器技术领域蓬勃发展。很快,Docker社区迅速壮大,包括Red Hat、Google、CoreOS、CNCF等巨头纷纷加入其中。容器技术的发展催生了更多的云服务商,如AWS、Azure、DigitalOcean等,它们也纷纷推出基于Docker的基础设施产品和托管服务,如EKS、AKS、GKE等。
在Docker成为容器编排领域的先驱之后,Kubernetes应运而生。Kubernetes是一个开源的、可扩展的、自动化的容器编排引擎,它的目标是让部署复杂的分布式系统变得更加容易。Kubernetes通过提供管理容器化应用程序所需的一组抽象概念来降低复杂性,包括Pods、ReplicaSets、Deployments、Services、Volumes和Namespaces。Kubernetes在2015年发布1.0版本,同年宣布兼容Docker API。Kubernetes项目的功能十分强大,包括自动扩展、自我修复、负载均衡和滚动升级、访问控制以及监控等,同时还有丰富的插件机制,允许用户自定义调度策略、存储配置、网络策略、扩展控制器等。Kubernetes的普及让云计算和容器技术蓬勃发展。
2.基本概念术语说明
CRI定义: Container Runtime Interface(容器运行时接口),定义了一套通用容器管理系统API。它由三个主要组件构成:runtime service(运行时服务)、image management(镜像管理)和container networking(容器网络)。CRI使得第三方容器运行时(如containerd、crio、frakti等)能够无缝对接到Kubernetes平台,从而为Kubernetes提供了多样化的容器运行时选择空间。
Kubernetes对外提供的CRI接口如下图所示:
Kubernetes集群中的kubelet组件与CRI组件之间通过GRPC协议通信,kubelet向CRI请求容器生命周期管理相关信息,CRI根据kubelet发送的请求对容器进行创建、启停、删除、状态检查等操作。CRI还支持Container Metrics、Log Streaming、Podsandbox等能力。通过CRI接口,Kubernetes对底层的容器运行时进行了高度的解耦,让不同厂商的容器运行时能够轻松地对接到Kubernetes平台。
模块说明:
- Runtime Service(运行时服务): 对外暴露容器运行时的服务,包括Version RPC(查询CRI的版本号)、RuntimeStatus RPC(获取容器运行时状态)、ImageService RPC(镜像管理相关功能)、RuntimeService RPC(运行时管理相关功能)、NamespaceService RPC(命名空间管理相关功能)。
- Image Service: 提供镜像拉取、删除等功能。
- Runtime Service: 提供容器生命周期管理相关功能,如创建容器、启动容器、停止容器、删除容器等。
- Networking plugin: 插件用来管理容器网络。
- Volume plugin: 插件用来管理持久化存储卷。
- Container Metrics: 提供容器性能指标采集能力。
- Log Streaming: 提供容器日志流式传输能力。
- Podsandbox: 提供沙箱环境管理能力。
3.核心算法原理和具体操作步骤以及数学公式讲解
通过上面的介绍,相信大家对CRI应该有一个基本的了解。下面,我们将结合Kubernetes的CRI接口协议进行细致的剖析,进一步理解CRI。
- Version RPC
Version RPC用于获取CRI的版本号。请求方法为GET,路径为/version。响应示例:
{"gitVersion": "v1.0.1-beta.0","gitCommit": "abc123","gitTreeState": "clean","buildDate": "2019-10-01T03:57:28Z","goVersion": "go1.12.4","compiler": "gc","platform": "linux/amd64"
}
- Runtime Status RPC
RuntimeStatus RPC用于获取容器运行时状态。请求方法为GET,路径为/runtime.获得的响应包含以下字段:
- apiVersion:版本号。
- status:运行时状态,当前仅包含"ok"。
- defaultRuntimeName:默认的容器运行时名称。
- runtimes:支持的容器运行时列表。
请求示例:GET /runtime/
成功响应示例:
{"apiVersion": "v1alpha2","status": {"conditions":[{"type":"RuntimeReady","status":"True","reason":"OK","message":"Runtime ready"},{"type":"NetworkReady","status":"Unknown","reason":"NetworkPluginNotReady","message":"docker: network plugin is not ready"}],"defaultRuntimeName":"docker","runtimes":[{"name":"runc","path":"/usr/bin/runc"},{"name":"docker","path":"/usr/bin/docker"}]}
}
- Image Service RPC
Image Service RPC用于镜像管理。请求方法为POST,路径为/pull。请求示例:POST /image/{image}/pull?podsandboxid={podsandboxid}。
参数描述:
- image:镜像名。
- podsandboxid:PodSandBox ID。
返回值示例:如果镜像下载成功,则返回空;否则,返回一个错误码和错误信息。
删除镜像请求示例:DELETE /image/{image}?force=true。
参数描述: - image:镜像名。
- force:是否强制删除。
- Create Container RPC
创建容器请求方法为POST,路径为/containers/{container}/{sandbox}。请求示例:POST /containers/{container}/{sandbox}?sandboxconfig={sandboxconfig}。参数描述:
- container:容器ID。
- sandbox:沙箱ID。
- sandboxconfig:沙箱配置。
创建成功后,将返回容器ID。
- Start Container RPC
启动容器请求方法为PUT,路径为/containers/{container}/{sandbox}/start。请求示例:PUT /containers/{container}/{sandbox}/start。
参数描述:
- container:容器ID。
- sandbox:沙箱ID。
启动成功后,将返回状态码204 No Content。
4.具体代码实例和解释说明
下面给出一个完整的Kubernetes集群中的配置和示例代码。首先,创建一个配置文件kubeadm-config.yaml,内容如下:
kind: ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta1
kubernetesVersion: stable
apiServerEndpoint: <公网IP>:6443
certificateKey: mycertkey
clusterName: kubernetes
controlPlaneEndpoint: <公网IP>:6443
controllerManager: {}
dns:
type: CoreDNS
imageRepository: k8s.gcr.io
etcd:
local:dataDir: /var/lib/etcd
imageRepository: k8s.gcr.io
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12
podSubnet: 10.100.0.0/16
scheduler: {}
配置文件中的公网IP需要替换为自己的实际公网IP地址。然后执行初始化命令:
sudo kubeadm init --config=kubeadm-config.yaml
初始化成功后,将得到如下输出:
You can now join any number of machines by running the following on each node
as root:
kubeadm join 172.16.17.32:6443 --token \--discovery-token-ca-cert-hash sha256:755f8e4bf7d984fd4f44abbb8fa72e80568e8e4b2f01119d5d3756a31e751ba5
执行以上命令将节点加入Kubernetes集群,并获取初始化成功后的令牌。
接下来,编写Dockerfile,构建自定义的容器运行时,如containerd:
FROM alpine:latest
RUN apk add -U su-exec shadow && rm -rf /var/cache/apk/*
ENTRYPOINT ["/sbin/tini","--"]
COPY containerd-shim-kata /opt/bin/
CMD ["su-exec","nobody","./entrypoint.sh"]
Dockerfile中的指令为安装shadowutils包、复制containerd-shim-kata文件、设置容器入口命令。dockerfile目录结构如下:
.
├── Dockerfile
└── containerd-shim-kata
使用上述镜像构建本地镜像:
docker build -t localhost/cri-tools:latest.
将本地镜像推送至私有仓库,如Harbor。
创建kube-proxy DaemonSet并指定私有仓库地址:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-proxy
namespace: kube-system
spec:
selector:matchLabels:component: kube-proxy
template:metadata:labels:component: kube-proxyspec:hostNetwork: truecontainers:- name: kube-proxyimage: harbor.example.com/library/kubernetes/pause:3.4.1command: - "/hyperkube"args: - proxy- "--bind-address=$(POD_IP)"env:- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespace- name: CONTAINER_RUNTIME_ENDPOINTvalue: unix:///run/containerd/containerd.sock- name: KUBELET_PORTvalue: "10250"- name: PROXY_PORTvalue: "10249"securityContext:privileged: truevolumeMounts:- mountPath: /run/containerd/containerd.sockname: containerdreadOnly: truevolumes:- name: containerdhostPath:path: /run/containerd/containerd.sock
指定CONTAINER_RUNTIME_ENDPOINT为containerd的UNIX socket路径。
如果您的Kubernetes集群中没有启用Kubelet Pod资源配额限制,需要修改kubelet配置文件/etc/kubernetes/manifests/kube-apiserver.yaml,在其中添加以下两条配置项:
---
apiVersion: v1
kind: Pod
metadata:
annotations:seccomp.security.alpha.kubernetes.io/pod: runtime/default
...
上述配置项将允许Kubelet对所有Pod执行任意Seccomp Profile。
当然,您也可以直接在kubelet配置文件/var/lib/kubelet/config.yaml中添加以下内容:
authorization:
mode: Webhook
webhook:cacheAuthorizedTTL: 5mcacheUnauthorizedTTL: 30s
featureGates:
SeccompDefault: true
此时,Kubelet将根据授权Webhook API调用判断Pod是否可以被创建。
添加完毕后,启动kubelet进程。等待kubelet成功注册到API Server并启动相应的控制器。
测试容器运行时是否正常工作。首先,创建podsandbox:
apiVersion: v1
kind: PodSandbox
metadata:
name: test-sandbox
spec:
metadata:name: test-sandbox
hostname: test-hostname
logDirectory: /tmp
dnsConfig: {}
portMappings: []
然后,创建测试容器:
apiVersion: v1
kind: Container
metadata:
name: test-container
spec:
metadata:name: test-container
image: nginx
ports:
- containerPort: 80
stdin: false
tty: false
resources: {}
最后,启动容器:
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default
spec:
runtimeClassName: containerd
containers:
- name: test-containerimage: nginx
restartPolicy: Never
创建完成后,查看容器是否正常运行:
kubectl exec -it test-pod bash
curl http://localhost
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
