文章目录
- 01 系统要求
- 02 安装概述
- 03 私有镜像库配置与升级
- 3.1 私有镜像库配置
- 3.2 升级
- 04 CRI(容器运行时接口)详解
- 4.1 CRI 概述
- 4.2 CRI 的主要组件
- 4.3 Pod 和容器的生命周期管理
- 4.4 面向容器级别的设计思路
- 4.5 尝试使用新的 Docker-CRI 来创建容器
- 4.6 CRI 的进展
01 系统要求
Kubernetes 系统由一组可执行程序组成,用户可以通过 Kubernetes 在GitHub的项目网站下载编译好的二进制文件或镜像文件,或者下载源码并自行将其编译为二进制文件。
安装 Kubernetes 对软件和硬件的系统要求下图所示:

Kubernetes 需要容器运行时(Container Runtime Interface, CRI)的支持,目前官方支持的容器运行时包括:Docker、Containerd、CRI-O 和 fraktis 等。
宿主机操作系统以CentOS7为例,使用 Systemd 系统完成对 Kubernetes 服务的配置。为了便于管理,常见的做法是将 Kubernetes 服务程序配置为 Linux系统开机自启动的服务。
需要注意的是,CentOS7 默认启动了防火墙服务(firewalld. Service),而 Kubernetes 的 Master与工作Node之间会有大量的网络通信。安全的做法是在防火墙上配置各组件需要相互通信的端口号,具体要配置的端口号下图所示:

其他组件可能还需要开通某些端口号,例如 CNI 网络插件 calico 需要 179 端口号;镜像库需要 5000 端口号等,需要根据系统要求逐个在防火墙服务上配置网络策略。
在安全的网络环境中,可以简单地关闭防火墙服务:

另外,建议在主机上禁用SELinux(修改文件/etc/sysconfig/selinux,将 SELINUX=enforcing 修改为 SELINUX=disabled),让容器可以读取主机文件系统。随着 Kubernetes 对 SELinux 支持的增强,可以逐步启用 SELinux 机制,并通过 Kubernetes 设置容器的安全机制。
02 安装概述
本文不会详细地解说K8S的安装过程,但是会简单的说明下。
K8S集群一般分为以下两种方式安装。
① 使用 kubeadm 工具快速安装 Kubernetes 集群,流程如下:
- 安装 kubeadm
- 修改 kubeadm 的默认配置
- 下载 Kubernetes 的相关镜像
- 运行 kubeadm init 命令安装 Master 节点
- 将新的 Node 加入集群
- 安装 CNI 网络插件
- 验证 Kubernetes集群是否工作正常
② 以二进制文件方式安装 Kubernetes安全高可用集群,流程如下:
- Master高可用部署架构
- 创建 CA 根证书
- 部署安全的 etcd 高可用集群
- 部署安全的 Kubernetes Masteri 高可用集群
- 部署 Node 的服务
- kube-apiserver 基于 token 的认证机制
03 私有镜像库配置与升级
3.1 私有镜像库配置
在 Kubernetes集群中,容器应用都是基于镜像启动的,在私有云环境中建议搭建私有镜像库对镜像进行统一管理,在公有云环境中可以直接使用云服务商提供的镜像库。
私有镜像库有两种选择:
Docker提供的Registry镜像库 :详细说明请参考官网的说明。Harbor镜像仓库:详细说明请参考官网的说明或者 Harbor 项目维护者及贡献者编写的《Harbor 权威指南》一书。
此外,Kubernetes 对于创建 Pod 需要使用一个名为“pause”的镜像,tag名为 k8s.gcr.io/pause:3.2”,默认从镜像库k8s.gcr.io下载,在私有云环境中可以将其上传到私有镜像库,并修改 kubelet 的启动参数-pod-infra-container--image,将其设置为使用镜像库的镜像名称,例如:

3.2 升级
升级方式也分为两种。
① 二进制文件升级
在进行 Kubernetes 的版本升级之前,需要考虑不中断正在运行的业务容器的灰度升级方案。
常见的做法是:
- 先更新
Master上Kubernetes服务的版本,再逐个或批量更新集群中的Node上Kubernetes服务的版本。 - 更新
Node的Kubernetes服务的步骤通常包括:先隔离一个或多个Node的业务流量,等待这些Node上运行的Pod将当前任务全部执行完成后,停掉业务应用(Pod),再更新这些Node上的kubelet和kube-proxy版本,更新完成后重启业务应用(Pod),并将业务流量导入新启动的这些Node上,再隔离剩余的Node,逐步完成Node的版本升级,最终完成整个集群的Kubernetes版本升级。
同时,应该考虑高版本的 Master 对低版本的 Node 的兼容性问题。高版本的 Master 通常可以管理低版本的 Node,但版本差异不应过大,以免某些功能或 API版本被弃用后,低版本的 Node无法运行。
- 通过官网获取最新版本的二进制包
kubernetes.tar.gz,解压后提取服务的二进制文件。 - 更新
Master的kube-apiserver、kube-controller-manager、kube- scheduler服务的二进制文件和相关配置(在需要修改时更新)并重启服务。 - 逐个或批量隔离
Node,等待其上运行的全部容器工作完成后停掉Pod,更新kubelet、kube-proxy。服务文件和相关配置(在需要修改时更新),然后重启这两个服务。
② 使用 kubeadm 进行集群升级
kubeadm 提供了 upgrade 命令用于对 kubeadm 安装的 Kubernetes 集群进行升级。这一功能提供了从 1.10 到 1.11、从 1.11 到 1.12、从 1.12 到 1.13 及从 1.13 到 1.14 升级的能力,此处不再详述。
升级之前需要注意:
- 虽然
kubeadm的升级不会触及工作负载,但还是要在升级之前做好备份; - 升级过程中可能会因为
Pod的变化而造成容器重启。
04 CRI(容器运行时接口)详解
归根结底,Kubernetes Node (kubelet)的主要功能就是启动和停止容器的组件,我们称之为容器运行时(Container Runtime),其中最知名的就是 Docker 了。为了更具扩展性,Kubernetes 从 1.5 版本开始就加入了容器运行时插件 API,即 Container Runtime Interface,简称 CRI。
4.1 CRI 概述
每个容器运行时都有特点,因此不少用户希望 Kubernetes 能够支持更多的容器运行时。Kubernetes从 1.5 版本开始引入了 CRI 接口规范,通过插件接口模式,Kubernetes 无须重新编译就可以使用更多的容器运行时。
CRI 包含 Protocol Buffers、gRPC API、运行库支持及开发中的标准规范和工具。Docker 的 CRI 实现在 Kubernetes1.6 中被更新为 Beta 版本,并在 kubelet启动时默认启动。
可替代的容器运行时支持是 Kubernetes 中的新概念。在 Kubernetes1.3 发布时,rktnetes 项目同时发布,让 rkt 容器引擎成为除 Docker 外的又一选择。
然而,不管是 Docker 还是 rkt,都用到了 kubelet 的内部接口,同 kubelet 源码纠缠不清。这种程度的集成需要对 kubelet 的内部机制有非常深入的了解,还会给社区带来管理压力,这就给新生代容器运行时造成了难以跨越的集成壁垒。CRI 接口规范尝试用定义清晰的抽象层清除这一壁垒,让开发者能够专注于容器运行时本身。
4.2 CRI 的主要组件
kubelet 使用 gRPC 框架通过 UNIX Socket 与容器运行时(或 CRI 代理)进行通信。在这个过程中kubelet是客户端,CRI 代理(shim)是服务端,如图所示:

Protocol Buffers API 包含两个gRPC服务:ImageService 和 RuntimeService。
- ImageService: 提供了从仓库中拉取镜像、查看和移除镜像的功能。
- RuntimeService: 负责
Pod和容器的生命周期管理,以及与容器的交互 (exec/attach/port-forward) ,rkt和Docker这样的容器运行时可以使用一个Socket同时提供两个服务,在kubelet中可以用container-runtime-endpoint和image-service-endpoint参数设置这个Socket。
4.3 Pod 和容器的生命周期管理
Pod 由一组应用容器组成,其中包含共有的环境和资源约束,在 CRI 里,这个环境被称为 PodSandbox。
Kubernetes有意为容器运行时留下一些发挥空间,它们可以根据自己的内部实现来解释 PodSandbox。
- 对于
Hypervisor类的运行时,PodSandbox会具体化为一个虚拟机。 - 其他例如
Docker,会是一个Linux命名空间。在vialpha1 API中,kubelet会创建Pod级别的cgroup传递给容器运行时,并以此运行所有进程来满足PodSandbox对Pod的资源保障。
在启动 Pod 之前,kubelet调用 RuntimeService RunPodSandbox来创建环境。这一过程包括为 Pod 设置网络资源(分配IP等操作)。PodSandbox 被激活之后,就可以独立地创建、启动、停止和删除不同的容器了。kubelet 会在停止和删除 PodSandbox 之前首先停止和删除其中的容器。
kubelet 的职责在于通过 RPC 管理容器的生命周期,实现容器生命周期的钩子、存活和健康监测,以及执行 Pod 的重启策略等。
RuntimeService服务包括对 Sandboxi 和 Container 操作的方法,下面的伪代码展示了主要的 RPC方法:


4.4 面向容器级别的设计思路
众所周知,Kubernetes 的最小调度单元是 Pod,它曾经可能采用的一个 CRI 设计就是复用 Pod 对象,使得容器运行时可以自行实现控制逻辑和状态转换,这样一来,就能极大地简化 API,让 CRI 能够更广泛地适用于多种容器运行时。但是经过深入讨论之后,Kubernetes放弃了这一想法。
首先,kubelet 有很多 Pod 级别的功能和机制(例如 crash-loop backoff 机制),如果交给容器运行时去实现,则会造成很重的负担;然后,Pod 标准还在快速演进。很多新功能(如初始化容器)都是由 kubelet完成管理的,无须交给容器运行时实现。
CRI 选择了在容器级别进行实现,使得容器运行时能够共享这些通用特性,以获得更快的开发速度。这并不意味着设计哲学的改变一 kubelet要负责、保证容器应用的实际状态和声明状态的一致性。
Kubernetes 为用户提供了与Pod及其中的容器进行交互的功能 (kubectl exec/attach/port-forward),kubelet目前提供了两种方式来支持这些功能:
- ①调用容器的本地方法;
- ②使用
Node上的工具(例如nsenter)及socat)。
因为多数工具都假设 Pod 用 Linux namespace 做了隔离,因此使用Node上的工具并不是一种容易移植的方案。在 CRI 中显式定义了这些调用方法,让容器运行时进行具体实现。下面的伪代码显示了 Exec、Attach、PortForward:这几个调用需要实现的 RuntimeService 方法:
目前还有一个潜在的问题是,kubelet处理所有的请求连接,使其有成为 Node 通信瓶颈的可能。在设计 CRI 时,要让容器运行时能够跳过中间过程。容器运行时可以启动一个单独的流式服务来处理请求(还能对 Pod 的资源使用情况进行记录),并将服务地址返回给 kubelet。这样 kubelet 就能反馈信息给 API Server,使之可以直接连接到容器运行时提供的服务,并连接到客户端。
4.5 尝试使用新的 Docker-CRI 来创建容器
要尝试新的 kubelet-CRI-Docker 集成,只需为 kubelet 启动参数加上enable- cri=true 开关来启动 CRI。这个选项从 Kubernetes1.6 开始已经作为 kubelet 的默认选项了。如果不希望使用 CRI,则可以设置enable-cri=false 来关闭这个功能。
查看 kubelet 的日志,可以看到启用CRI和创建 gRPC Server 的日志:


创建一个Deployment:

查看 Pod 的详细信息,可以看到将会创建沙箱(Sandbox)的 Event:

这表明 kubelet 使用了 CRI 接口来创建容器。
4.6 CRI 的进展
目前已经有多款开源 CRI 项目可用于 Kubernetes、Docker、CRI-O、Containerd、frakti(基于 Hypervisor 的容器运行时),各 CRI 运行时的安装手册可参考官网的说明。



















