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