CentOS 8安装多节点Kubernetes Cluster

时间:2020-01-09 10:40:57  来源:igfitidea点击:

我们必须问一个问题:"如何设置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,MinikubeKind可以在几分钟内启动Kubernetes集群。

因此,如我们所见,部署第一批Kubernetes集群有很多选择。我将介绍"使用kubeadm在CentOS 8服务器上安装Kubernetes CLuster"的步骤。

使用kubeadm构建多节点Kubernetes集群

在本节中,我们将安装具有多个节点的Kubernetes集群。我将通过Oracle VirtualBox运行虚拟机内部的节点,但是我们也可以使用其他虚拟化工具或者裸机。要同时设置主节点和工作节点,我们将使用" kubeadm"工具。

我们计划做什么

在继续进行安装之前,让我简要概述一下本节将要执行的操作,以便我们对下一步有一个基本的了解。如果我们已经阅读了上一教程,那么我们必须熟悉Kubernetes集群的体系结构,我将再次放置该图像以供参考:

我们会看到在主节点和工作节点上安装了不同的组件,现在手动安装这些组件,修改配置文件等可能是非常繁琐的任务。这就是我们使用kubeadm的原因,因为它将简化构建Kubernetes集群并在相应节点上启动所有组件的过程。

以下是简要介绍的步骤(图中从下至上):

  • 创建虚拟机或者安排裸机服务器来设置集群

  • 在所有主机上安装操作系统

  • 在所有主机上安装容器运行时,以便我们可以在所有主机上运行容器,我们将使用Docker

  • 在所有节点上安装kubeadm

  • 初始化主服务器,在此过程中,所有必需的组件都已在主服务器上安装和配置

  • Kubernetes在主节点和工作节点之间需要一个特殊的网络,即Pod网络或者集群网络。

  • 将主节点加入工作节点

实验室环境

我将有一个控制器或者主节点,以及两个工作节点。

资源控制器worker-1worker-2
操作系统CentOS 8CentOS 8CentOS 8
主机名控制器worker-1worker-2
FQDNcontroller.example.comworker-1. example.comworker-2. example.com
存储容量20GB20GB20GB
vCPU222
内存6GB6GB6GB
适配器-1(桥接)192.168.43.48192.168.43.49192.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更新libseccomprpm。

在所有节点上使用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