CentOS 8安装多节点Kubernetes Cluster
我们必须问一个问题:"如何设置Kubernetes集群?"最终我们可能会从不同的搜索结果中得到不同的答案,这对于初学者来说可能是不胜枚举的。 Kubernetes是一个复杂的系统,要对其进行安装和良好地管理"不是一件容易的事"。
但是,随着Kubernetes社区的扩展和成熟,出现了越来越多的用户友好工具。截止到今天,根据要求,有很多选项可供选择:
如果我们使用的是物理(裸机)服务器或者虚拟机(VM),则
Kubeadm
非常适合。如果我们在云环境上运行,则kops和Kubespray可以简化Kubernetes的安装以及与云提供商的集成。
如果我们想减轻管理Kubernetes控制平面的负担,几乎所有云提供商都提供其Kubernetes托管服务,例如Google Kubernetes Engine(GKE),Amazon Elastic Kubernetes Service(EKS),Azure Kubernetes Service( AKS
)和IBM Kubernetes Service
(IKS`)。如果我们只想在操场上学习Kubernetes,
Minikube
和Kind
可以在几分钟内启动Kubernetes集群。
因此,如我们所见,部署第一批Kubernetes集群有很多选择。我将介绍"使用kubeadm在CentOS 8服务器上安装Kubernetes CLuster"的步骤。
使用kubeadm构建多节点Kubernetes集群
在本节中,我们将安装具有多个节点的Kubernetes集群。我将通过Oracle VirtualBox运行虚拟机内部的节点,但是我们也可以使用其他虚拟化工具或者裸机。要同时设置主节点和工作节点,我们将使用" kubeadm"工具。
我们计划做什么
在继续进行安装之前,让我简要概述一下本节将要执行的操作,以便我们对下一步有一个基本的了解。如果我们已经阅读了上一教程,那么我们必须熟悉Kubernetes集群的体系结构,我将再次放置该图像以供参考:
我们会看到在主节点和工作节点上安装了不同的组件,现在手动安装这些组件,修改配置文件等可能是非常繁琐的任务。这就是我们使用kubeadm
的原因,因为它将简化构建Kubernetes集群并在相应节点上启动所有组件的过程。
以下是简要介绍的步骤(图中从下至上):
创建虚拟机或者安排裸机服务器来设置集群
在所有主机上安装操作系统
在所有主机上安装容器运行时,以便我们可以在所有主机上运行容器,我们将使用Docker
在所有节点上安装kubeadm
初始化主服务器,在此过程中,所有必需的组件都已在主服务器上安装和配置
Kubernetes在主节点和工作节点之间需要一个特殊的网络,即Pod网络或者集群网络。
将主节点加入工作节点
实验室环境
我将有一个控制器或者主节点,以及两个工作节点。
资源 | 控制器 | worker-1 | worker-2 |
---|---|---|---|
操作系统 | CentOS 8 | CentOS 8 | CentOS 8 |
主机名 | 控制器 | worker-1 | worker-2 |
FQDN | controller.example.com | worker-1. example.com | worker-2. example.com |
存储容量 | 20GB | 20GB | 20GB |
vCPU | 2 | 2 | 2 |
内存 | 6GB | 6GB | 6GB |
适配器-1(桥接) | 192.168.43.48 | 192.168.43.49 | 192.168.43.50 |
我在环境中使用了两个不同的接口,这不是必需的。我将Adapter-1作为NAT,用于SSH端口转发(我们可以忽略它)。在本教程中,我们将仅使用通过桥接适配器配置的Adapter-2. 我们可以在Oracle VirtualBox中了解有关不同网络选项的更多信息。
这是我的VM的"网络设置"中的摘录
安装Oracle VirtualBox
我们可以从其官方存储库下载VirtualBox软件。
我写了另一篇文章来安装Oracle Virtual Box,它在Windows上是安静的。
只需双击下载的软件,然后按照说明进行操作,即可将所有设置保留为默认设置。
安装CentOS 8
我将使用CentOS 8设置我的Kubernetes集群,我们可以按照分步说明在VM上安装CentOS。
如果我们不熟悉CentOS 8的安装步骤,并且想选择简单的方法,则可以从https://www.osboxes.org/centos/下载CentOS 8镜像,我们只需其中将下载的文件部署到VirtualBox中,然后我们将无需任何手动步骤即可启动并运行预安装的虚拟机。
更新/etc/hosts
如果我们没有DNS服务器来解析主机名,则必须使用所有节点上所有集群节点的主机名和IP信息更新/etc/hosts文件,以下是我的一个集群节点的示例输出:
禁用掉期
必须禁用交换内存以使kubelet正常工作。在所有群集节点上执行以下步骤。
首先检查Linux服务器上交换内存的可用性:
[root@controller ~]# free -m total used free shared buff/cache available Mem: 6144 454 5207 9 472 3100 Swap: 2046 0 2046
因此,我们有2 GB的交换空间,要禁用交换空间,我们将使用swapoff
命令
[root@controller ~]# swapoff -a
接下来,重新验证分配的交换内存:
[root@controller ~]# free -m total used free shared buff/cache available Mem: 3781 454 2855 9 471 3101 Swap: 0 0 0
完成后,我们还需要确保节点重启后未重新分配交换,因此请从/etc/fstab
中注释掉交换文件系统条目。以下是我的设置中的示例:
#/dev/mapper/rhel-swap swap swap defaults 0 0
在所有群集节点上重复相同的步骤。
禁用SELinux
我们必须在所有群集节点上禁用selinux或者将其更改为Permissive模式。
这是允许容器访问Pod网络所需的主机文件系统所必需的。我们必须执行此操作,直到kubelet
中的SELinux支持得到改善。
[root@controller ~]# setenforce 0
检查SELinux的状态
[root@controller ~]# getenforce Permissive
要使更改在重新引导期间保持不变,请执行以下命令:
[root@controller ~]# sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux
在所有群集节点上重复相同的步骤。
启用防火墙
接下来,我们需要在主节点和工作节点上启用某些预定义的端口。需要在主节点上打开以下端口后,我们将使用"防火墙"打开这些端口
端口范围 | 用途 |
---|---|
6443 | 这些端口用于Kubernetes API访问。 |
2379-2380 | 这些端口用于etcd服务器客户端API。 |
10250 | 此端口用于Kubelet API |
10251 | 此端口用于kube调度程序 |
10252 | 此端口用于kube控制器管理器 |
在控制器节点上启用相应的防火墙端口:
[root@controller ~]# firewall-cmd --add-port 6443/tcp --add-port 2379-2380/tcp --add-port 10250-10252/tcp --permanent [root@controller ~]# firewall-cmd --reload [root@controller ~]# firewall-cmd --list-ports 6443/tcp 2379-2380/tcp 10250-10252/tcp
在所有工作节点上都必须允许以下端口:
端口范围 | 用途 |
---|---|
10250 | 此端口用于Kubelet API |
30000-32767 | 节点端口服务 |
在所有工作节点上启用相应的防火墙端口:
[root@worker-1 ~]# firewall-cmd --add-port 10250/tcp --add-port 30000-32767/tcp --permanent [root@worker-1 ~]# firewall-cmd --reload [root@worker-1 ~]# firewall-cmd --list-ports 10250/tcp 30000-32767/tcp [root@worker-2 ~]# firewall-cmd --add-port 10250/tcp --add-port 30000-32767/tcp --permanent [root@worker-2 ~]# firewall-cmd --reload [root@worker-2 ~]# firewall-cmd --list-ports 10250/tcp 30000-32767/tcp
配置网络
如果我们有多个网络适配器,并且Kubernetes组件在默认路由上不可用,我们建议我们添加IP路由,以便Kubernetes群集地址通过适当的适配器。
在我的设置中,为桥接网络配置了默认路由,而仅主机的网络将仅用于内部路由:
确保已加载br_netfilter
模块。这可以通过运行来完成
# lsmod | grep br_netfilter
由于br_netfilter
未处于加载状态,因此我将手动加载此模块:
[root@controller ~]# modprobe br_netfilter
现在重新验证模块状态:
[root@controller ~]# lsmod | grep br_netfilter br_netfilter 24576 0 bridge 188416 1 br_netfilter
作为Linux节点iptables正确查看桥接流量的要求,我们应确保在sysctl配置中将net.bridge.bridge-nf-call-iptables设置为1.
[root@controller ~]# sysctl -a | grep net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-iptables = 1
它是默认启用的,但是为了安全起见,我们还将创建一个" sysctl"配置文件,并将其添加到IPv4和IPv6中:
[root@controller ~]# cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF
激活新添加的更改:
[root@controller ~]# sysctl --system
安装容器运行时(Docker CE)
我们需要将容器运行时安装到群集中的每个节点中,以便Pod可以其中运行。有多个容器运行时,例如:
码头工人:
/var/run/docker.sock
容器化的:
/run/containerd/containerd.sock
CRI-O:
/var/run/crio/crio.sock
如果同时检测到" Docker"和" containerd",则Docker优先。在本教程中,我们将使用Docker作为所有节点上的运行时。首先,我们需要安装一些相关的软件包:
~]# dnf install -y yum-utils device-mapper-persistent-data lvm2
添加docker存储库以能够在所有节点上安装docker`:
~]# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
在所有节点上安装Docker CE软件包`:
~]# dnf install containerd.io docker-ce docker-ce-cli -y
在所有节点上创建一个新目录:
[root@controller ~]# mkdir -p /etc/docker /etc/systemd/system/docker.service.d
通过创建一个新文件/etc/docker/daemon.json并在此文件中添加以下内容,在所有节点上设置Docker守护进程:
[root@controller ~]# cat /etc/docker/daemon.json { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ] }
我们已经完成了配置,是时候在所有节点上启动(重新启动)docker守护进程了:
[root@controller ~]# systemctl daemon-reload [root@controller ~]# systemctl enable docker --now [root@worker-1 ~]# systemctl daemon-reload [root@worker-1 ~]# systemctl enable docker --now [root@worker-2 ~]# systemctl daemon-reload [root@worker-1 ~]# systemctl enable docker --now
提示:
启动docker服务时,我得到了以下容器:符号查找错误:/usr/bin/containerd:未定义符号:seccomp_api_se错误,要解决此问题,我们需要使用dnf update libseccomp -y更新libseccomp
rpm。
在所有节点上使用systemctl status docker
检查Docker服务的状态,以确保Docker已启动并正常运行:
安装Kubernetes组件(kubelet,kubectl和kubeadm)
我们将在所有计算机上安装以下软件包:
kubeadm:引导集群的命令。
kubelet:在集群中所有机器上运行的组件,它执行诸如启动pod和容器之类的操作。
kubectl:
命令行工具与集群通信。
重要的提示:
kubeadm
将不会为我们安装或者管理kubelet
或者kubectl
,因此我们需要确保它们与我们希望kubeadm
为我们安装的Kubernetes控制飞机的版本相匹配。
在所有节点上创建kubernetes存储库文件,该文件将用于下载软件包:
~]# cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-$basearch enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg exclude=kubelet kubeadm kubectl EOF
在所有节点上使用软件包管理器`安装Kubernetes组件软件包:
~]# dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
在此阶段,我们将不会启动kubelet
服务,因为这将自动完成
在控制器节点上,当我们初始化控制节点时
在主节点上,以及当我们将工作节点加入主节点时
初始化控制节点
控制平面节点是运行控制平面组件的机器,其中包括`etcd'(集群数据库)和API服务器(" kubectl"命令行工具与之通信)。提示:" kubeadm"使用与默认网关关联的网络接口来设置此特定控制平面节点的API服务器的AD地址。由于我们使用不同的网络(Adapter-2)进行内部通信,因此必须为kubeadm init指定参数--apiserver-advertise-address = <ip-address>。其中将" ip-address"替换为控制节点的IP地址,即本例中的" 192.168.43.48"
[root@controller ~]# kubeadm init W1127 23:05:26.412480 7525 configset.go:348] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io] [init] Using Kubernetes version: v1.19.4 [preflight] Running pre-flight checks [WARNING Firewalld]: firewalld is active, please ensure ports [6443 10250] are open or your cluster Jan not function correctly [preflight] Pulling images required for setting up a Kubernetes cluster [preflight] This might take a minute or two, depending on the speed of your internet connection [preflight] You can also perform this action in beforehand using 'kubeadm config images pull' [certs] Using certificateDir folder "/etc/kubernetes/pki" [certs] Generating "ca" certificate and key [certs] Generating "apiserver" certificate and key [certs] apiserver serving cert is signed for DNS names [controller.example.com kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.43.48] [certs] Generating "apiserver-kubelet-client" certificate and key [certs] Generating "front-proxy-ca" certificate and key [certs] Generating "front-proxy-client" certificate and key [certs] Generating "etcd/ca" certificate and key [certs] Generating "etcd/server" certificate and key [certs] etcd/server serving cert is signed for DNS names [controller.example.com localhost] and IPs [192.168.43.48 127.0.0.1 ::1] [certs] Generating "etcd/peer" certificate and key [certs] etcd/peer serving cert is signed for DNS names [controller.example.com localhost] and IPs [192.168.43.48 127.0.0.1 ::1] [certs] Generating "etcd/healthcheck-client" certificate and key [certs] Generating "apiserver-etcd-client" certificate and key [certs] Generating "sa" key and public key [kubeconfig] Using kubeconfig folder "/etc/kubernetes" [kubeconfig] Writing "admin.conf" kubeconfig file [kubeconfig] Writing "kubelet.conf" kubeconfig file [kubeconfig] Writing "controller-manager.conf" kubeconfig file [kubeconfig] Writing "scheduler.conf" kubeconfig file [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Starting the kubelet [control-plane] Using manifest folder "/etc/kubernetes/manifests" [control-plane] Creating static Pod manifest for "kube-apiserver" [control-plane] Creating static Pod manifest for "kube-controller-manager" [control-plane] Creating static Pod manifest for "kube-scheduler" [etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests" [wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s [apiclient] All control plane components are healthy after 18.507221 seconds [upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace [kubelet] Creating a ConfigMap "kubelet-config-1.19" in namespace kube-system with the configuration for the kubelets in the cluster [upload-certs] Skipping phase. Please see --upload-certs [mark-control-plane] Marking the node controller.example.com as control-plane by adding the label "node-role.kubernetes.io/master=''" [mark-control-plane] Marking the node controller.example.com as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule] [bootstrap-token] Using token: rdet9b.4pugjes5hwq3aynk [bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles [bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes [bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials [bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token [bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster [bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace [kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key [addons] Applied essential addon: CoreDNS [addons] Applied essential addon: kube-proxy Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 192.168.43.48:6443 --token rdet9b.4pugjes5hwq3aynk \ --discovery-token-ca-cert-hash sha256:d2408f85e478b5a9927f1fafd89630fb71a1ce07d5e26e0cf4c7ff4320d433a2
重要的提示:
从上面的输出中保存kubeadm join
命令很重要,因为以后需要它来加入工作节点。
初始化已成功完成。如果我们注意到前一个命令突出显示了输出,那么如果我们以普通用户身份执行了上述命令,则必须执行某些步骤。但是,由于我们使用的是root用户,因此我们必须执行以下命令:
export KUBECONFIG=/etc/kubernetes/admin.conf
并将其添加到/etc/profile
[root@controller ~]# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
使用systemctl status kubelet
检查kubelet
服务的状态:
检查集群信息:
[root@controller ~]# kubectl cluster-info Kubernetes master is running at https://192.168.43.48:6443 KubeDNS is running at https://192.168.43.48:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
获取集群中的节点列表
[root@controller ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION controller.example.com NotReady master 63m v1.19.3
在这一阶段,我们有一个单节点,它也处于"未就绪"状态,我们将在下一章中进行修复。
安装Pod网络添加插件(weave-net)
我们必须部署基于"容器网络接口(CNI)"的Pod网络添加组件,以便Pod可以相互通信。在安装网络之前,群集DNS(CoreDNS)将不会启动。
存在提供Kubernetes网络支持的不同项目,该项目需要对以下类型的网络的支持:
容器到容器
豆荚
到服务
外部到服务
通用Pod网络添加插件:
法兰:这是群集节点之间的第3层IPv4网络,可以使用多种后端机制,例如VXLAN
编织:启用CNI的Kubernetes集群的常见添加组件
Calico:使用IP封装的第3层网络解决方案,并在Kubernetes,Docker,OpenStack,OpenShift和其他应用中使用
" AWS VPC:"一种网络插件,通常用于AWS环境
提示:
要获取安装不同的Pod Networking添加插件的步骤列表,我们可以按照官方指南进行操作。
我们将为Kubernetes集群使用weave-net
网络插件。我们必须允许流量在所有节点上流经TCP 6783和UDP 6783/6784,这些节点是Weave的控制和数据端口。
[root@controller ~]# firewall-cmd --add-port 6783/tcp --add-port 6783/udp --add-port 6784/udp --permanent [root@controller ~]# firewall-cmd --reload [root@controller ~]# firewall-cmd --list-ports 6443/tcp 2379-2380/tcp 10250-10252/tcp 6783/tcp 6783/udp 6784/udp [root@worker-1 ~]# firewall-cmd --list-ports 10250/tcp 30000-32767/tcp 6783/tcp 6783/udp 6784/udp [root@worker-2 ~]# firewall-cmd --list-ports 10250/tcp 30000-32767/tcp 6783/tcp 6783/udp 6784/udp
守护程序还将TCP 6781/6782用作度量标准,但是如果我们希望从另一台主机收集度量标准,则只需要打开此端口即可。
我们可以从官方指南的控制平面节点上使用以下命令安装Pod网络添加组件:
[root@controller ~]# kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')" serviceaccount/weave-net created clusterrole.rbac.authorization.k8s.io/weave-net created clusterrolebinding.rbac.authorization.k8s.io/weave-net created role.rbac.authorization.k8s.io/weave-net created rolebinding.rbac.authorization.k8s.io/weave-net created daemonset.apps/weave-net created
每个集群只能安装一个Pod网络。
一旦安装了Pod网络,我们可以通过在Kubectl get pods --all-namespaces输出中检查CoreDNS Pod是否正在运行来确认它正在运行。目前,"编织网"正在创建容器,因此状态为" ContainerCreating",而" CoreDNS"的状态为"待处理":
[root@controller ~]# kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-f9fd979d6-4bdhv 0/1 Pending 0 3m37s kube-system coredns-f9fd979d6-nd44t 0/1 Pending 0 3m37s kube-system etcd-controller.example.com 1/1 Running 0 3m34s kube-system kube-apiserver-controller.example.com 1/1 Running 0 3m34s kube-system kube-controller-manager-controller.example.com 1/1 Running 0 3m34s kube-system kube-proxy-gcgj4 1/1 Running 0 3m37s kube-system kube-scheduler-controller.example.com 1/1 Running 0 3m34s kube-system weave-net-kgzwc 0/2 ContainerCreating 0 2s
我们还可以将相同的命令与-o wide一起使用,这将提供有关每个命名空间的更多详细信息。几分钟后再次检查状态,所有名称空间都应处于"正在运行"状态:
[root@controller ~]# kubectl get pods --all-namespaces -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES kube-system coredns-f9fd979d6-4bdhv 1/1 Running 0 5m19s 10.32.0.3 controller.example.com <none> <none> kube-system coredns-f9fd979d6-nd44t 1/1 Running 0 5m19s 10.32.0.2 controller.example.com <none> <none> kube-system etcd-controller.example.com 1/1 Running 0 5m16s 192.168.43.48 controller.example.com <none> <none> kube-system kube-apiserver-controller.example.com 1/1 Running 0 5m16s 192.168.43.48 controller.example.com <none> <none> kube-system kube-controller-manager-controller.example.com 1/1 Running 0 5m16s 192.168.43.48 controller.example.com <none> <none> kube-system kube-proxy-gcgj4 1/1 Running 0 5m19s 192.168.43.48 controller.example.com <none> <none> kube-system kube-scheduler-controller.example.com 1/1 Running 0 5m16s 192.168.43.48 controller.example.com <none> <none> kube-system weave-net-kgzwc 2/2 Running 0 104s 192.168.43.48 controller.example.com
接下来,检查群集的状态,如果我们还记得安装Pod网络插件之前该群集处于"未就绪"状态,但是现在群集处于"就绪"状态。
[root@controller ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION controller.example.com Ready master 85m v1.19.3
加入工作者节点
要将新节点添加到集群中,请通过SSH将其添加到所有工作节点,并以root用户身份执行从kubeadm init命令输出中保存的kubeadm join命令:
~]# kubeadm join 192.168.43.48:6443 --token rdet9b.4pugjes5hwq3aynk --discovery-token-ca-cert-hash sha256:d2408f85e478b5a9927f1fafd89630fb71a1ce07d5e26e0cf4c7ff4320d433a2
我的一个工作节点之一的输出示例:
接下来检查节点的状态,它应该处于" Ready"状态,如果状态显示为" NotReady",那么我们可以检查下一个命令中所示的pod的状态:
[root@controller ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION controller.example.com Ready master 161m v1.19.3 worker-2.example.com NotReady 17m v1.19.3
在所有可用的命名空间中检查Pod的状态,以确保新创建的Pod正在运行,当前正在为我们启动了join命令的工作节点上的各个Pod创建容器:
[root@controller ~]# kubectl get pods --all-namespaces -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES kube-system coredns-f9fd979d6-4bdhv 1/1 Running 0 27m 10.32.0.2 controller.example.com <none> <none> kube-system coredns-f9fd979d6-nd44t 1/1 Running 0 27m 10.32.0.3 controller.example.com <none> <none> kube-system etcd-controller.example.com 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system kube-apiserver-controller.example.com 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system kube-controller-manager-controller.example.com 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system kube-proxy-ffxx6 0/1 ContainerCreating 0 102s 192.168.43.50 worker-2.example.com <none> <none> kube-system kube-proxy-gcgj4 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system kube-scheduler-controller.example.com 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system weave-net-kgzwc 2/2 Running 0 22m 192.168.43.48 controller.example.com <none> <none> kube-system weave-net-wjzb7 0/2 ContainerCreating 0 102s 192.168.43.50 worker-2.example.com <none> <none>
同样,将worker-1.example.com
添加到集群中:
[root@worker-1 ~]# kubeadm join 192.168.43.48:6443 --token rdet9b.4pugjes5hwq3aynk --discovery-token-ca-cert-hash sha256:d2408f85e478b5a9927f1fafd89630fb71a1ce07d5e26e0cf4c7ff4320d433a2 [preflight] Running pre-flight checks [preflight] Reading configuration from the cluster... [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml' [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap... This node has joined the cluster: * Certificate signing request was sent to apiserver and a response was received. * The Kubelet was informed of the new secure connection details. Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
一旦添加了两个工作节点,所有的Pod都应处于运行状态:
[root@controller ~]# kubectl get pods --all-namespaces -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES kube-system coredns-f9fd979d6-4bdhv 1/1 Running 0 27m 10.32.0.2 controller.example.com <none> <none> kube-system coredns-f9fd979d6-nd44t 1/1 Running 0 27m 10.32.0.3 controller.example.com <none> <none> kube-system etcd-controller.example.com 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system kube-apiserver-controller.example.com 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system kube-controller-manager-controller.example.com 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system kube-proxy-ffxx6 1/1 Running 0 17m 192.168.43.50 worker-2.example.com <none> <none> kube-system kube-proxy-gcgj4 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system kube-proxy-xfzl7 1/1 Running 0 13m 192.168.43.49 worker-1.example.com <none> <none> kube-system kube-scheduler-controller.example.com 1/1 Running 0 27m 192.168.43.48 controller.example.com <none> <none> kube-system weave-net-62bq8 1/2 Running 3 13m 192.168.43.49 worker-1.example.com <none> <none> kube-system weave-net-kgzwc 2/2 Running 0 22m 192.168.43.48 controller.example.com <none> <none> kube-system weave-net-wjzb7 2/2 Running 1 17m 192.168.43.50 worker-2.example.com <none> <none>
还要验证控制器节点上的节点状态:
[root@controller ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION controller.example.com Ready master 53m v1.19.3 worker-1.example.com Ready 38m v1.19.3 worker-2.example.com Ready 42m v1.19.3
因此,现在所有节点的状态都处于"就绪"状态。
故障排除
在本节中,将介绍设置kubeadm集群时遇到的问题及其解决方案:
错误:失败:打开/run/systemd/resolve/resolv.conf:没有这样的文件或者目录"
我在缺少该目录/run/systemd/resolve的工作程序节点上遇到此错误。以下是日志中的完整错误代码段:
Error syncing pod 502bb214-5a88-4e72-b30b-7332e51642e3 ("weave-net-zkdbq_kube-system(502bb214-5a88-4e72-b30b-7332e51642e3)"), skipping: failed to "CreatePodSandbox" for "weave-net-zkdbq_kube-system(502bb214-5a88-4e72-b30b-7332e51642e3)" with CreatePodSandboxError: "GeneratePodSandboxConfig for pod \"weave-net-zkdbq_kube-system(502bb214-5a88-4e72-b30b-7332e51642e3)\" failed: open /run/systemd/resolve/resolv.conf: no such file or directory"
某些Linux发行版(例如Ubuntu)默认使用本地DNS解析器(systemd-resolved)。 Systemd-resolved移动并用存根文件替换了/etc/resolv.conf,当在上游服务器中解析名称时,该文件可能导致致命的转发循环。这可以通过使用kubelet的--resolv-conf标志来指向正确的resolv.conf来手动修复(对于systemd-resolved,它是/run/systemd/resolve/resolv.conf)。 kubeadm自动检测systemd-resolved,并相应地调整kubelet标志。
要解决此问题,请确保在相应的节点上运行" systemd-resolved":
[root@worker-2 ~]# systemctl status systemd-resolved ● systemd-resolved.service - Network Name Resolution Loaded: loaded (/usr/lib/systemd/system/systemd-resolved.service; disabled; vendor preset: disabled) Active: active (running) since Wed 2017-11-11 11:18:08 IST; 1s ago Docs: man:systemd-resolved.service(8) https://www.freedesktop.org/wiki/Software/systemd/resolved https://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients Main PID: 16964 (systemd-resolve) Status: "Processing requests..." Tasks: 1 (limit: 18135) Memory: 2.8M CGroup: /system.slice/systemd-resolved.service └─16964 /usr/lib/systemd/systemd-resolved Nov 11 11:18:08 worker-2.example.com systemd[1]: Starting Network Name Resolution...
如果它不处于运行状态,请手动启动该服务:
[root@worker-2 ~]# systemctl start systemd-resolved
并启用自动启动此服务:
[root@worker-2 ~]# systemctl enable systemd-resolved Created symlink /etc/systemd/system/dbus-org.freedesktop.resolve1.service → /usr/lib/systemd/system/systemd-resolved.service. Created symlink /etc/systemd/system/multi-user.target.wants/systemd-resolved.service → /usr/lib/systemd/system/systemd-resolved.service.
错误:辅助节点显示CrashLoopBackOff作为状态
以下是工作程序节点的日志:
Nov 11 11:26:05 worker-2.example.com kubelet[17992]: W1111 11:26:05.368907 17992 cni.go:239] Unable to update cni config: no networks found in /etc/cni/net.d Nov 11 11:26:07 worker-2.example.com kubelet[17992]: E1111 11:26:07.827786 17992 kubelet.go:2103] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
这些消息可能是通用消息,可能意味着不同的问题,首先请确保Linux服务器上使用的默认路由用于与Kubernetes Cluster节点进行通信,例如,其中我的默认网络通过192.168.43.0子网进行路由节点也可以在同一网络上访问。
~]# ip route default via 192.168.43.1 dev eth1 proto static metric 101 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100 10.32.0.0/12 dev weave proto kernel scope link src 10.36.0.0 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 192.168.43.0/24 dev eth1 proto kernel scope link src 192.168.43.49 metric 101 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
如果我们配置了多个默认路由(下面的示例输出),那么我们"必须指定在kubernetes控制节点初始化阶段要使用的地址"。
~]# ip route default via 10.0.2.2 dev eth0 proto dhcp metric 100 default via 192.168.43.1 dev eth1 proto static metric 101 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown ...
在我的尝试之一期间,我在Kubernetes集群的Oracle VirtualBox中使用了仅主机网络。因此,在控制节点初始化阶段,我使用了:
kubeadm init --apiserver-advertise-address=<control_node_ip>
由于kubeadm使用与默认网关关联的网络接口来设置此特定控制平面节点的API服务器的AD地址。因此,如果我们使用不同的网络进行内部通信,则必须指定--apiserver-advertise-address = <ip-address>
通过这种配置,初始化成功,但是后来在将工作节点加入Kubernetes集群后出现了同样的错误。后来经过大量调试,我找到了解决该问题的github页面。
原来iptables不允许kubernetes服务使用的默认子网
10.96.0.1`(可能会在新版本中修复)。
[root@controller ~]# kubectl get services --all-namespaces NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 10.96.0.1 443/TCP 4h23m kube-system kube-dns ClusterIP 10.96.0.10 53/UDP,53/TCP,9153/TCP 4h23m
因此,要启用此功能,我们必须在所有节点上添加以下规则。但是,只有在将工作节点添加到集群中并且创建了容器之后,才可以添加此规则,可以使用kubectl get pods -all-namespaces -o wide进行验证,否则我们将获得iptables:插入索引太大。
[root@controller ~]# iptables -t nat -I KUBE-SERVICES -d 10.96.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-MARK-MASQ
随后重新启动kubelet
服务以立即激活更改,或者我们也可以等待kubelet自动检测到新更改
~]# systemctl restart kubelet
。现在应该正确添加该节点,我们可以使用以下命令进行验证:
[root@controller ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION controller.example.com Ready master 4h28m v1.19.3 worker-2.example.com Ready 4h22m v1.19.3
"如果这些更改对问题有用,那么请仅执行后续步骤以使这些更改永久生效。"
现在这些更改将不会在重新启动后永久保留,因此我们将现有的iptables规则保存到一个新文件中。我正在使用/etc/my-iptables-rules
,但是我们可以选择此文件的任何其他位置和名称:
~]# iptables-save > /etc/my-iptables-rules
接下来创建一个systemd单元文件,该文件将在重启后执行iptables规则的恢复:
~]# cat /etc/systemd/system/my-iptables.service [Unit] Description=my-iptables - custom iptables daemon Before=network-pre.target Wants=network-pre.target After=kubelet.service Conflicts=iptables.service firewalld.service ip6tables.service ebtables.service ipset.service [Service] Type=oneshot ExecStart=/usr/sbin/iptables-restore -n /etc/my-iptables-rules RemainAfterExit=yes [Install] WantedBy=multi-user.target
加载更改:
~]# systemctl daemon-reload
其中我们正在使用systemd服务文件来恢复已保存的iptables规则。启用该服务以使其在每次重新启动后运行:
~]# systemctl enable my-iptables Created symlink /etc/systemd/system/multi-user.target.wants/my-iptables.service → /etc/systemd/system/my-iptables.service.
接下来,启动服务并检查状态以确保其正确启动:
~]# systemctl start my-iptables ~]# systemctl status my-iptables ● my-iptables.service - my-iptables - custom iptables daemon Loaded: loaded (/etc/systemd/system/my-iptables.service; disabled; vendor preset: disabled) Active: active (exited) since Thu 2017-11-26 11:41:50 IST; 1s ago Process: 11105 ExecStart=/usr/sbin/iptables-restore -n /etc/my-iptables-rules (code=exited, status=0/SUCCESS) Main PID: 11105 (code=exited, status=0/SUCCESS) Nov 26 11:41:50 worker-2.example.com systemd[1]: Starting my-iptables - custom iptables daemon... Nov 26 11:41:50 worker-2.example.com systemd[1]: Started my-iptables - custom iptables daemon.
因此,我们的服务已正常启动。现在,新添加的iptables规则将在每次重新启动后自动添加。
说明:
只有在kubernetes的上游发行版中未解决此问题后,才需要添加新iptables规则的技巧
在所有群集节点上重复这些步骤,然后重新启动kubelet服务。
错误:CoreDNS处于"挂起/失败"状态
通常,在控制节点初始化步骤之后,CoreDNS处于挂起状态。稍后,一旦我们安装了kubernetes网络插件,CoreDNS命名空间状态应更改为"正在运行",但是如果它仍处于"待处理/失败"状态,则几乎无需检查:
使用kubectl get pods命令验证DNS pod正在运行。
[root@controller ~]# kubectl get pods --namespace=kube-system -l k8s-app=kube-dns NAME READY STATUS RESTARTS AGE coredns-f9fd979d6-4bdhv 1/1 Running 0 4h53m coredns-f9fd979d6-nd44t 1/1 Running 0 4h53m
检查DNS容器的错误日志。
kubectl logs --namespace=kube-system -l k8s-app=kube-dns
确认DNS服务已启动并且正在运行,或者我们可以按照官方的疑难解答指南调试服务
[root@controller ~]# kubectl get svc --namespace=kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 53/UDP,53/TCP,9153/TCP 4h54m
日志文件位置
有两种类型的系统组件:在容器中运行的系统组件和不在容器中运行的系统组件。例如:
Kubernetes调度程序和kube-proxy在容器中运行。
kubelet和容器运行时(例如Docker)不在容器中运行。
在具有systemd的计算机上,kubelet和容器运行时将写入日志。
否则,它们将写入/var/log目录中的.log文件。容器内的系统组件总是绕过默认的日志记录机制,写入/var/log目录中的.log文件。
启用shell自动补全(可选)
现在这不是必须执行的步骤,但是仅通过按键盘上的TAB键,即可使用kubectl获取受支持的选项列表,这很有用。 kubectl为Bash和Zsh提供了自动补全支持,可以节省很多打字时间。要启用自动完成功能,我们必须首先在相应的节点上安装bash-completion。由于我们大多数时候都会使用主节点,因此我们将仅在控制器节点上安装此软件包:
[root@controller ~]# dnf -y install bash-completion
接下来执行kubectl完成bash以获取将对kubectl执行自动完成的脚本,这将在控制台上提供很长的输出
[root@controller ~]# kubectl completion bash
我们会将此命令的输出保存到root用户的~/.bashrc中。
[root@controller ~]# kubectl completion bash >> ~/.bashrc
如果希望所有其他用户都可以使用它,则可以在/etc/bash_completion.d/中创建一个新文件并保存内容:
[root@controller ~]# kubectl completion bash >> /etc/bash_completion.d/kubectl
下一步,重新加载shell,现在我们可以输入kubectl
,只需按TAB键即可为我们提供支持的选项列表:
[root@controller ~]# kubectl <press TAB on the keyboard> alpha attach completion create edit kustomize plugin run uncordon annotate auth config delete exec label port-forward scale version api-resources autoscale convert describe explain logs proxy set wait api-versions certificate cordon diff expose options replace taint apply cluster-info cp drain get patch rollout top