Kubernetes (简称k8s)是一套用于自动部署,扩展以及管理容器化应用的开源系统。
k8s集群由一个 控制平面组件 和一个或多个节点组件 构成(每个组件代表一台或多台运行容器运行时和 kubelet.service
服务的主机机器)。安装 Kubernetes 有两种选择:一种是更为正统的Kubernetes安装方式(此处所描述的方式),另一种则是使用 k3s,kind 或者 minikube包 进行本地安装。
安装
配置一个 Kubernetes 集群的方法有很多,本文将主要介绍如何使用 kubeadm包 进行配置。
部署工具
kubeadm
如果要使用 kubeadm 来引导 Kubernetes 集群,请在每一个节点上 安装 kubeadm包 和 kubelet包。
手动配置
如你想要手动创建一个 Kubernetes 集群,请 安装 etcdAUR,并安装 软件包组 kubernetes-control-plane包组 (在控制平面节点) 或 kubernetes-node包组 (在 worker 节点)。
集群管理
要管理一个 Kubernetes 集群,请在控制平面节点和要与集群交互的主机上安装 kubectl包。
容器运行时
控制平面节点和常规 worker 节点都需要一个容器运行时以运行 kubelet
实例。
安装 containerd包 或 cri-o包 来提供一个运行时。
containerd 运行时
有两种方式可以安装 containerd:
- 安装 containerd包 软件包。
- 安装非特权模式(rootless)containerd,你需要安装 nerdctl-full-binAUR,其是一个完整的 nerdctl 包(containerd,CNI 插件,和 RootlessKit)。随后你需要运行
containerd-rootless-setuptool.sh install
以安装非特权模式 containerd。
请注意,由于 Arch Linux 使用 systemd 作为 init 系统,你需要在部署控制平面之前 选择 systemd cgroup 驱动。
(可选) 包管理器
helm包 是一个用于管理预配置的 Kubernetes 资源的工具,这可能会有所帮助。
配置
集群中的所有节点(控制平面和工作节点)都需要运行一个 kubelet.service
实例。
kubelet.service
或使用 kubeadm
之前,请仔细阅读以下小节。所有提供的 systemd 服务都允许通过环境文件进行命令行参数覆盖:
-
kubelet.service
:/etc/kubernetes/kubelet.env
-
kube-apiserver.service
:/etc/kubernetes/kube-apiserver.env
-
kube-controller-manager.service
:/etc/kubernetes/kube-controller-manager.env
-
kube-proxy.service
:/etc/kubernetes/kube-proxy.env
-
kube-scheduler.service
:/etc/kubernetes/kube-scheduler.env
禁用交换分区
Kubernetes 目前不支持在系统上启用交换分区。详情请参阅 KEP-2400: 节点系统交换支持。
有关如何禁用交换分区的说明,请参阅 Swap#关闭交换分区。
为 containerd 选择 cgroup 驱动
如要在使用 runc
时,同时也在 /etc/containerd/config.toml
中使用 systemd
cgroup 驱动,请设置:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] ... [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true
如果 /etc/containerd/config.toml
不存在,可以使用以下命令生成默认配置:
# mkdir -p /etc/containerd/ # containerd config default > /etc/containerd/config.toml
请记住 重启 containerd.service
以使更改生效。
有关是否保留 cgroupfs
驱动或使用 systemd
cgroup 驱动的深入讨论,请参阅 官方文档。
选择容器运行时接口 (CRI)
在使用 kubelet.service
之前,必须配置并启动 容器运行时。
你需要在创建或加入集群时,将带有容器运行时接口端点的标志 --cri-socket
传递给 kubeadm init
或 kubeadm join
。
例如,如果你选择 containerd包 作为 CRI 运行时,则在创建时附带上--cri-socket
标志:
kubeadm init --cri-socket /run/containerd/containerd.sock
Containerd
在 Kubernetes 1.27.4 版本之前,当使用 containerd包 作为容器运行时,需要向 kubeadm init
或 kubeadm join
提供其 CRI 端点。为此,请将其 --cri-socket
标志指定为 /run/containerd/containerd.sock
[1]。
kubeadm join --cri-socket=/run/containerd/containerd.sock
在 Kubernetes 1.27.4 版本之后,kubeadm 将自动检测此 CRI,只有在安装了多个 CRI 时才需要 --cri-socket
标志。
CRI-O
当使用 CRI-O 作为容器运行时,需要向 kubeadm init
或 kubeadm join
提供其 CRI 端点:--cri-socket='unix:///run/crio/crio.sock'
选择集群网络参数
选择 Pod CIDR 范围
必须为相应的容器运行时配置集群的网络设置。这可以使用 cni-plugins包 来完成。
Pod CIDR 地址 指的是分配给 Kubernetes 集群中 Pod 的 IP 地址范围。当 Pod 被调度到集群中的节点上运行时,它们会从此 CIDR 范围中分配 IP 地址。
Pod CIDR 范围 在部署 Kubernetes 集群时指定,并且仅限于集群网络内。它不应与集群中使用的其他 IP 范围(例如如service CIDR 范围)重叠。
你将向 kubeadm init
或 kubeadm join
传递 --pod-network-cidr
标志,并指定虚拟网络的 CIDR 值,以创建或加入集群。
例如:
kubeadm init --pod-network-cidr='10.85.0.0/16'
会将你的 Kubernetes Pod CIDR 范围设置为 10.85.0.0/16
。
(可选)选择 API 服务器广播地址
如果你的控制平面节点位于多个子网中(例如,你可能安装了 Tailscale tailnet),在使用 kubeadm init 初始化 Kubernetes 主节点时,你可以使用 --apiserver-advertise-address
标志指定 API 服务器将广播的 IP 地址。注意,此 IP 地址应可被集群中的所有节点访问。
(可选)选择其他的节点网络代理提供者
节点代理提供者(如 kube-proxy
)是在集群中每个节点上运行的网络代理,用于维护节点上的网络规则,以允许从集群内部或外部的网络会话与你的 Pod 进行网络通信。
默认情况下,kubeadm包 选择 kube-proxy
作为在集群中每个节点上运行的节点代理。
容器网络接口 (CNI) 插件(如 Cilium)提供了 kube-proxy 的完整替代方案。
如果你想使用 Cilium 的节点网络代理实现以充分利用 Cilium 的网络策略功能,你应向 kubeadm init
传递 --skip-phases=addon/kube-proxy
标志,以跳过 kube-proxy 的安装。
Cilium 将在其安装过程中安装完整的替代方案。详情请参阅 [2]。
创建集群
在使用 kubeadm
创建新的 Kubernetes 集群之前,请先 启动 并 启用 kubelet.service
。
kubelet.service
会失败(但会重启),直到为其提供配置。不使用配置文件的 kubeadm
在使用 kubeadm
创建新的 Kubernetes 集群时,必须先创建一个控制平面,然后其他工作节点才能加入。
- 如果集群以后要转换为 高可用性 集群(堆叠式 etcd 拓扑),则需要在
kubeadm init
时提供--control-plane-endpoint=<IP 或域名>
(无法事后进行此操作!)。 - 可以使用 配置文件 来代替一组参数进行
kubeadm init
。
初始化控制平面
要初始化控制平面,你需要向 kubeadm init
传递以下必要的标志。
如果成功运行,kubeadm init
将在 /etc/kubernetes/
和 /var/lib/kubelet/
下生成 kubelet
和各种控制平面组件的配置。
最后,它将输出可以复制并粘贴的命令,用于设置 kubectl包 并使工作节点加入集群(基于有效期为 24 小时的令牌)。
要将 kubectl
与刚创建的控制平面节点一起使用,请设置配置(以 root 或普通用户身份):
$ mkdir -p $HOME/.kube # cp -i /etc/kubernetes/admin.conf $HOME/.kube/config # chown $(id -u):$(id -g) $HOME/.kube/config
安装 CNI 插件(Pod 网络插件)
Pod 网络插件(CNI 插件)以不同的方式实现 Kubernetes 网络模型,从简单的解决方案如 flannel 到更复杂的解决方案如 calico。有关更多选项,请参阅 此列表。
一个日益被采用的高级 CNI 插件是 cilium,它通过 eBPF 实现了 令人印象深刻的性能。要安装 cilium
作为 CNI 插件,请使用 cilium-cli包:
# cilium-cli install
这将创建 /opt/cni/bin/cilium-cni
插件、配置文件 /etc/cni/net.d/05-cilium.conflist
,并在 Kubernetes 集群上部署两个 Pod。
/opt/cni/bin/
插件目录。请参阅 CRI-O#Plugin directories。使用配置文件的 kubeadm
你很可能会发现,创建控制平面需要多次尝试才能找到适合你特定设置的最佳配置。为了使此过程更容易(并且使 kubeadm包 的过程更具可重复性),你可以使用配置文件运行初始化步骤。
创建初始化配置文件
你可以在任何地方创建此文件,但在此示例中我们将使用 /etc/kubeadm
。
# mkdir -pv /etc/kubeadm # cd /etc/kubeadm # kubeadm config print init-defaults > init.yaml
这将生成以下文件。
/etc/kubeadm/init.yaml
apiVersion: kubeadm.k8s.io/v1beta3 bootstrapTokens: - groups: - system:bootstrappers:kubeadm:default-node-token token: abcdef.0123456789abcdef ttl: 24h0m0s usages: - signing - authentication kind: InitConfiguration localAPIEndpoint: advertiseAddress: 1.2.3.4 bindPort: 6443 nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock imagePullPolicy: IfNotPresent name: node taints: null --- apiServer: timeoutForControlPlane: 4m0s apiVersion: kubeadm.k8s.io/v1beta3 certificatesDir: /etc/kubernetes/pki clusterName: kubernetes controllerManager: {} dns: {} etcd: local: dataDir: /var/lib/etcd imageRepository: registry.k8s.io kind: ClusterConfiguration kubernetesVersion: 1.29.0 networking: dnsDomain: cluster.local serviceSubnet: 10.96.0.0/12 scheduler: {}
大多数默认设置应该可以工作,但你需要更新其中的一些设置。
引导令牌
使用 kubeadm token generate
创建一个令牌,并在配置中使用它代替 token: abcdef.0123456789abcdef
。
公告地址
advertiseAddress: 1.2.3.4
应该是正在初始化的控制平面的网络接口的 IPv4 地址,可能是 192.168.0.0/16
子网中的某个地址。
节点名称
默认节点名称可以保留为 node
并添加到本地 DNS 服务器或 hosts 文件中,或者你可以将其更改为本地网络上可路由的地址。它应该是一个 DNS 兼容的主机名,例如 kcp01.example.com
。这将允许你的控制平面在加入其他节点时在本地网络上被发现。
初始化集群
完成所有这些更改后,我们可以初始化我们的集群。
# kubeadm init --config /etc/kubeadm/init.yaml
这将产生大量输出,输出提供了有关如何将节点加入集群、更新 kubeconfig 以与新集群交互以及其他任务的说明。
使用 calico 进行 CNI 配置
在开始添加节点和运行工作负载之前,你最后需要的东西是 正确配置的 CNI。此示例将使用 calico 来实现这一点。
# cd /etc/cni/net.d # curl https://raw.githubusercontent.com/projectcalico/calico/v3.27.2/manifests/calico.yaml -O # kubectl create -f calico.yaml
如果此操作成功完成,你就可以开始添加节点并在集群上运行工作负载啦。
创建重置配置文件
为防止 kubeadm包 第一次初始化不成功,你还可以创建一个用于重置命令的配置文件:
# kubeadm config print reset-defaults > /etc/kubeadm/reset.yaml
这将创建以下文件:
/etc/kubeadm/reset.yaml
apiVersion: kubeadm.k8s.io/v1beta4 certificatesDir: /etc/kubernetes/pki criSocket: unix:///var/run/containerd/containerd.sock kind: ResetConfiguration
重置集群
要将集群重置为零,请运行以下命令:
# kubeadm reset --config /etc/kubeadm/reset.yaml
可以根据需要多次执行此操作,以确定集群的理想配置。
创建加入配置文件
很可能在初始化集群后,你可以使用 init 命令输出中列出的命令加入任何节点,但如果你遇到问题,在要加入的节点上准备一个加入配置文件将会很有帮助。你可以在控制平面上创建此文件,或者在要加入集群的节点上运行命令,我们假设你执行了后者。
# kubeadm config print join-defaults > /etc/kubeadm/join.yaml
这将创建以下文件。
/etc/kubeadm/join.yaml
apiVersion: kubeadm.k8s.io/v1beta3 caCertPath: /etc/kubernetes/pki/ca.crt discovery: bootstrapToken: apiServerEndpoint: kcp01.example.com:6443 token: abcdef.0123456789abcdef unsafeSkipCAVerification: true timeout: 5m0s tlsBootstrapToken: abcdef.0123456789abcdef kind: JoinConfiguration nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock imagePullPolicy: IfNotPresent name: node01.example.com taints: null
discovery.bootstrapToken.token
,第二个用于 discovery.tlsBootstrapToken
属性。加入集群
使用在#创建集群中生成的令牌信息,可以通过命令 kubeadm join
使另一台机器作为工作节点加入集群。
请记住,你还需要通过将标志 <SOCKET>
传递给命令 kubeadm join
来为工作节点选择容器运行时接口。
例如:
# kubeadm join <api-server-ip>:<port> --token <token> --discovery-token-ca-cert-hash sha256:<hash> --node-name=<name_of_the_node> --cri-socket=<SOCKET>
要生成新的引导令牌,可以使用以下命令:
kubeadm token create --print-join-command
如果你使用 Cilium 并且发现工作节点仍然处于 NotReady
状态,请使用以下命令检查工作节点的状态:
kubectl describe node <node-id> --namespace=kube-system
如果你发现以下条件状态:
Type Status Reason ---- ------ ------ NetworkUnavailable False CiliumIsUp Ready False KubeletNotReady container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
请在工作节点上重启 containerd.service
和 kubelet.service
。
提示与技巧
拆除集群
当需要从头开始重建集群时,可使用 kubectl包 按官方文档 拆除集群 的步骤操作:
kubectl drain <节点名称> --delete-local-data --force --ignore-daemonsets
此处 <节点名称>
是需要清空并重置的节点名称,可通过 kubectl get node -A
列出所有节点。
随后重置该节点:
# kubeadm reset
在代理后方操作
kubeadm
会读取 https_proxy
、http_proxy
和 no_proxy
环境变量。需确保 Kubernetes 内部网络包含在 no_proxy
中,例如:
export no_proxy="192.168.122.0/24,10.96.0.0/12,192.168.123.0/24"
其中第二个 CIDR(10.96.0.0/12)是 Kubernetes 默认的服务网络地址段。
故障排除
无法获取容器统计信息
如果 kubelet.service
输出以下错误:
Failed to get system container stats for "/system.slice/kubelet.service": failed to get cgroup stats for "/system.slice/kubelet.service": failed to get container info for "/system.slice/kubelet.service": unknown container "/system.slice/kubelet.service"
则需要为 kubelet 添加配置(参见 相关的上游问题)。
/var/lib/kubelet/config.yaml
systemCgroups: '/systemd/system.slice' kubeletCgroups: '/systemd/system.slice'
使用 Flannel CNI 和 systemd-networkd 时 Pod 无法通信
参见 上游问题报告。
systemd-networkd 会为每个链接分配一个持久的 MAC 地址。此策略在其默认配置文件 /usr/lib/systemd/network/99-default.link
中定义。然而,Flannel 依赖于能够选择自己的 MAC 地址。要覆盖 systemd-networkd 对 flannel*
接口的行为,请创建以下配置文件:
/etc/systemd/network/50-flannel.link
[Match] OriginalName=flannel* [Link] MACAddressPolicy=none
然后 重启 systemd-networkd.service
。
如果集群已经在运行,你可能需要手动删除每个节点上的 flannel.1
接口和 kube-flannel-ds-*
Pod,包括主节点。Pod 会立即重新创建,并且它们自己会重新创建 flannel.1
接口。
删除 flannel.1
接口:
# ip link delete flannel.1
删除 kube-flannel-ds-*
Pod。使用以下命令删除所有节点上的所有 kube-flannel-ds-*
Pod:
$ kubectl -n kube-system delete pod -l="app=flannel"
CoreDNS Pod 一直处于 Pending 状态,控制平面节点保持 "NotReady"
当在单台机器上使用 kubeadm init
引导 Kubernetes 时,如果没有其他机器 kubeadm join
集群,控制平面节点默认会被添加上污点(Taint)。因此,工作负载将不会调度到该机器上。
可以通过以下命令确认控制平面节点是否被添加了污点:
kubectl get nodes -o json | jq '.items[].spec.taints
要临时允许在控制平面节点上调度,执行:
kubectl taint nodes <your-node-name> node-role.kubernetes.io/control-plane:NoSchedule-
然后 重启 containerd.service
和 kubelet.service
以应用更新。
[kubelet-finalize] 畸形头:缺少 HTTP content-type
你可能忘记了 选择 systemd cgroup 驱动。参见 kubeadm 问题 2767 报告此问题。
CoreDNS Pod 由于循环无法启动
当主机节点运行本地 DNS 缓存(如 systemd-resolved)时,CoreDNS 可能会由于检测到转发循环而无法启动。可以通过以下方式检查:
# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE cilium-jc98m 1/1 Running 0 21m cilium-operator-64664858c8-zjzcq 1/1 Running 0 21m coredns-7db6d8ff4d-29zfg 0/1 CrashLoopBackOff 6 (41s ago) 21m coredns-7db6d8ff4d-zlvsm 0/1 CrashLoopBackOff 6 (50s ago) 21m etcd-k8s 1/1 Running 19 21m kube-apiserver-k8s 1/1 Running 17 21m kube-controller-manager-k8s 1/1 Running 16 21m kube-proxy-cvntt 1/1 Running 0 21m kube-scheduler-k8s 1/1 Running 23 21m
# kubectl logs -n kube-system coredns-7db6d8ff4d-29zfg
... [FATAL] plugin/loop: Loop ([::1]:46171 -> :53) detected for zone ".", see https://coredns.io/plugins/loop#troubleshooting. Query: "HINFO 64811921068182325.3042126689798234092."
这是由于 kubelet 将主机的 /etc/resolv.conf
文件传递给所有使用默认 dnsPolicy
的 Pod。CoreDNS 使用此 /etc/resolv.conf
作为上游转发请求的列表。由于它包含一个环回地址(如 127.0.0.53),CoreDNS 最终将请求转发给自己。
参见 https://coredns.io/plugins/loop/#troubleshooting 以解决此问题。
参见
- Kubernetes Documentation - The upstream documentation
- Kubernetes Cluster with Kubeadm - Upstream documentation on how to setup a Kubernetes cluster using kubeadm
- Kubernetes Glossary - The official glossary explaining all Kubernetes specific terminology
- Kubernetes Addons - A list of third-party addons
- Kubelet Config File - Documentation on the Kubelet configuration file
- Taints and Tolerations - Documentation on node affinities and taints