Kubernetes
Kubernetes 是一个可移植的、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态系统。Kubernetes 的服务、支持和工具广泛可用。
Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。Kubernetes 建立在 Google 在大规模运行生产工作负载方面拥有十几年的经验 的基础上,结合了社区中最好的想法和实践。
Kubernetes 可以提供:
-
服务发现和负载均衡
Kubernetes 可以使用 DNS 名称或自己的 IP 地址公开容器,如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。
-
存储编排
Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。
-
自动部署和回滚
你可以使用 Kubernetes 描述已部署容器的所需状态,它可以以受控的速率将实际状态 更改为期望状态。例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。
-
自动完成装箱计算
Kubernetes 允许你指定每个容器所需 CPU 和内存(RAM)。 当容器指定了资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。
-
自我修复
Kubernetes 重新启动失败的容器、替换容器、杀死不响应用户定义的 运行状况检查的容器,并且在准备好服务之前不将其通告给客户端。
-
密钥与配置管理
Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。
几个概念
-
kubectl
Kubernetes 命令行工具,kubectl ,使得你可以对 Kubernetes 集群运行命令。 你可以使用 kubectl 来部署应用、监测和管理集群资源以及查看日志。
-
kubeadm
你可以使用 kubeadm 工具来 创建和管理 Kubernetes 集群。 该工具能够执行必要的动作并用一种用户友好的方式启动一个可用的、安全的集群。
-
kubelet
kubelet 是在每个 Node 节点上运行的主要 “节点代理”。它可以使用以下之一向 apiserver 注册: 主机名(hostname);覆盖主机名的参数;某云驱动的特定逻辑。
kubelet 是基于 PodSpec 来工作的。每个 PodSpec 是一个描述 Pod 的 YAML 或 JSON 对象。 kubelet 接受通过各种机制(主要是通过 apiserver)提供的一组 PodSpec,并确保这些 PodSpec 中描述的容器处于运行状态且运行状况良好。 kubelet 不管理不是由 Kubernetes 创建的容器。
架构规划
- 机器:Dell PowerEdge R620 机架式服务器,详细配置如下:
- CPU:
- 处理器类型:16 CPUs x Intel(R) Xeon(R) CPU E5-2650 0 @ 2.00GHz
- 逻辑处理器:32
- 插槽数:2
- 每个插槽内核数:8
- 内存:内存
- 磁盘:900G机械磁盘
- CPU:
- 操作系统:DellEMC Customized Image ESXi 6.7 A01 (based on ESXi VMKernel Release Build 8169922)
综上,机器总共有 32C 32G 900G 的配置,这里计划创建六个虚拟机,其中一个 nfs,五个 k8s 集群节点,操作系统都用 ubuntu-20.04.3-live-server-amd64,详细规划如下:
vm | resource | os | ip | subnet | gateway | name server |
---|---|---|---|---|---|---|
nfs | 4C4G350G | ubuntu 20.04.3 | 192.168.1.100 | 192.168.1.0/24 | 192.168.1.1 | 8.8.8.8,114.114.114.114 |
k8s01 | 4C4G50G | ubuntu 20.04.3 | 192.168.1.101 | 192.168.1.0/24 | 192.168.1.1 | 8.8.8.8,114.114.114.114 |
k8s02 | 4C4G50G | ubuntu 20.04.3 | 192.168.1.102 | 192.168.1.0/24 | 192.168.1.1 | 8.8.8.8,114.114.114.114 |
k8s03 | 4C4G50G | ubuntu 20.04.3 | 192.168.1.103 | 192.168.1.0/24 | 192.168.1.1 | 8.8.8.8,114.114.114.114 |
k8s04 | 4C4G50G | ubuntu 20.04.3 | 192.168.1.104 | 192.168.1.0/24 | 192.168.1.1 | 8.8.8.8,114.114.114.114 |
k8s05 | 4C4G50G | ubuntu 20.04.3 | 192.168.1.105 | 192.168.1.0/24 | 192.168.1.1 | 8.8.8.8,114.114.114.114 |
开始搭建之前一定要先规划好虚拟机以及对应的网关、IP等信息,后面配置的时候按照定好的填写就可以了,防止出错。
开始搭建
创建虚拟机
使用 Esxi 创建虚拟机的教程网上都有,常规创建即可,需要注意按照上图规划的配置进行虚拟机的创建,如下图所示:
安装操作系统
上一步已经选择了 ubuntu-20.04.3-live-server-amd64,创建好后启动虚拟机开始进行系统的安装,这里需要注意几点:
-
手动设置IP,按照配置分别填写子网、IP、网关、DNS等
-
更改镜像源为阿里云,或者其它你喜欢的国内源
-
更改磁盘挂载,k8s集群我是只挂载了 / 和 /boot,nfs 会多一个 /data
-
填写hostname和账号信息
-
安装 OpenSSH
到这里操作系统就安装好了,按照规划将虚拟机一个一个装好系统即可。
启用root及配置ssh
默认的操作系统有两个不方便的地方:
- 默认是是 ubuntu 账户(安装系统时你填写的账号),用这个账户搭建集群经常需要进行提权操作
- esxi web 访问shell 不方便
所以这里做几个个操作:
- 启用 root 账户
- 生成 ssh 秘钥,后续机器之间访问需要用到
- 关闭密码登录,启用秘钥登录,并将自己操作机器的公钥添加到 ~/.ssh/authorized_keys
脚本如下(需要替换自己的公钥):
#!/bin/bash
echo "为root用户设置密码"
sudo passwd root
echo "切换root用户"
su
echo "ssh设置"
ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
# 启用root用户、关闭密码登录、启用秘钥登录、调整客户端连接超时配置
sed -i -e 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' -e 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' -e 's/PasswordAuthentication yes/PasswordAuthentication no/' -e 's/#ClientAliveInterval 0/ClientAliveInterval 60/' -e 's/#ClientAliveCountMax 3/ClientAliveCountMax 30/' /etc/ssh/sshd_config
# 添加授权登陆的公钥
cat > ~/.ssh/authorized_keys <<EOF
# 这里替换为你自己的机器公钥
EOF
# 重启sshd
service sshd restart
在每个虚拟机上都创建脚本并执行,之后就可以在自己的机器上远程 root 访问虚拟机了。
初始化虚拟机
这里要执行几个操作:
- 设置时区为 Asia/Shanghai
- 切换阿里镜像源(如果安装系统时已经替换这里可以不执行)
- 关闭防火墙和Selinux,这一点很重要!!!
- 关闭swap,这一点很重要!!!
- 设置 hosts,这里有两点需要注意:
- 需要将提前规划好的虚拟机和IP都写入 hosts,IP 可以自定义,但一定要前后一致
- 新增 kuber4s.api 这个域名,用于后续集群内部访问 master api,这个域名需要指向你规划的master节点的IP
- 安装 docker及docker-compose
- 安装kubectl kubeadm kubelet,版本一定要一致,我安装的版本为 1.20.10-00,安装前最好先查一下 kubesphere支持的最新版本
#!/bin/bash
echo "设置时区为 Asia/Shanghai"
sudo timedatectl set-timezone Asia/Shanghai
echo "切换阿里源"
sudo cp -ra /etc/apt/sources.list /etc/apt/sources.list.bak
sudo sed -i -e 's#http://cn.archive.ubuntu.com/ubuntu#http://mirrors.aliyun.com/ubuntu#' /etc/apt/sources.list
sudo apt-get update
sudo apt-get upgrade
echo "关闭防火墙和Selinux"
sudo ufw disable
sudo ufw status
sudo apt install -y policycoreutils
sudo sestatus -v
echo "关闭swap"
swapoff -a
echo "设置 hosts"
sudo cat >> /etc/hosts <<EOF
192.168.1.101 kuber4s.api
192.168.1.100 nfs
192.168.1.101 k8s01
192.168.1.102 k8s02
192.168.1.103 k8s03
192.168.1.104 k8s04
192.168.1.105 k8s05
EOF
echo "设置 alias"
echo "alias ll=\"ls -la\"" >> ~/.bash_profile
source ~/.bash_profile
echo "安装 docker"
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt-get update
sudo apt-get install -y \
net-tools \
unzip \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
mkdir /etc/docker
sudo cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://mirror.ccs.tencentyun.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl enable docker
echo "安装 docker compose"
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
echo "安装kubectl kubeadm kubelet"
#!/bin/bash
# base
apt-get update && apt-get install -y apt-transport-https
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet=1.20.10-00 kubeadm=1.20.10-00 kubectl=1.20.10-00
kubelet --version && kubeadm version && kubectl version
到这里,每台机器的操作系统就都初始化完成了,接下来我们要安装下 nfs
安装 nfs
-
服务端
在规划的nfs机器上安装下nfs服务端,如果之前安装系统时没有挂载 /data目录的话需要手动创建下,当然,你可以替换为别的名称,但要修改下述脚本中对应的配置以及下面k8s相关的路径
#!/bin/bash # server sudo apt install -y nfs-kernel-server sudo systemctl start nfs-kernel-server.service sudo echo "/data *(rw,async,no_subtree_check,no_root_squash)" >> /etc/exports sudo exportfs -a
-
客户端
#!/bin/bash # client sudo apt install -y nfs-common # 下面是测试脚本 # 创建本地要挂载的目录 # sudo mkdir ~/test # 挂载nfs # sudo mount nfs:/data/test ~/test # 取消挂载 # sudo umount -f -l ~/test
安装 k8s 集群
这里有几点需要注意:
- 初始化指定的IP段地址为 10.10.0.0/16,如果要替换一定要将脚本中的相同内容都替换掉
- endpoint 指定的是kuber4s.api,如果要修改,记得也要将上述写入 hosts 中的域名更改掉
- 初始化成功后,要根据自己使用的账号选择不通的配置文件方案
- 网络使用的是 calico,这里同第一点,需要注意IP段地址的更改
- 初始化成功后,会将 /etc/kubernetes/admin.conf 拷贝到子节点,需要对应的修改子节点的名称
master 节点执行脚本:
#!/bin/bash
kubeadm init \
--apiserver-advertise-address 0.0.0.0 \
--apiserver-bind-port 6443 \
--cert-dir /etc/kubernetes/pki \
--control-plane-endpoint kuber4s.api \
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \
--kubernetes-version v1.20.10 \
--pod-network-cidr 10.10.0.0/16 \
--service-cidr 10.20.0.0/16 \
--service-dns-domain cluster.local \
--upload-certs
# 处理 scheduler 和 controller-manager unhealthy 问题
sed -i -e 's/- --port=0/# - --port=0/' /etc/kubernetes/manifests/kube-scheduler.yaml
sed -i -e 's/- --port=0/# - --port=0/' /etc/kubernetes/manifests/kube-controller-manager.yaml
systemctl restart kubelet.service
kubectl get pods --all-namespaces
# root用户
export KUBECONFIG=/etc/kubernetes/admin.conf
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
source ~/.bash_profile
# 普通用户
# mkdir -p $HOME/.kube
# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 检查
kubectl get cs
kubectl get pods -n kube-system -o wide
kubectl get pods --all-namespaces | grep coredns
wget https://docs.projectcalico.org/v3.8/manifests/calico.yaml
# 修改配置文件
# 找到 625 行左右的 192.168.0.0/16 ,并修改为我们初始化时配置的 10.10.0.0/16
sed -i -e 's#192.168.0.0/16#10.10.0.0/16#' calico.yaml
kubectl apply -f calico.yaml
# watch -n 2 kubectl get pods --all-namespaces -o wide
# 令牌过期,重新生成
# kubeadm token create
# 重新生成ca证书
# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
# 子节点加入
# kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>
# kubeadm join 192.168.1.101:6443 --token lo976n.rhnli6vci3jkj48l --discovery-token-ca-cert-hash sha256:806dfc49886cdb6d6d81f1b6fa2e1899d62da87fac3b532ba9503f3fd35e2fa6
# 复制到其它节点
scp /etc/kubernetes/admin.conf root@k8s02:/etc/kubernetes/admin.conf
scp /etc/kubernetes/admin.conf root@k8s03:/etc/kubernetes/admin.conf
scp /etc/kubernetes/admin.conf root@k8s04:/etc/kubernetes/admin.conf
scp /etc/kubernetes/admin.conf root@k8s05:/etc/kubernetes/admin.conf
# 在子节点上执行
# export KUBECONFIG=/etc/kubernetes/admin.conf
# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
子节点执行脚本:
#!/bin/bash
# master 结点初始化成功后可以看到一串 kubeadm join 打头的命令,格式类似于下方,拷贝它在子节点上执行,即可加入集群
# kubeadm join 192.168.1.101:6443 --token lo976n.rhnli6vci3jkj48l --discovery-token-ca-cert-hash sha256:806dfc49886cdb6d6d81f1b6fa2e1899d62da87fac3b532ba9503f3fd35e2fa6
# root用户
export KUBECONFIG=/etc/kubernetes/admin.conf
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
source ~/.bash_profile
# 普通用户
# mkdir -p $HOME/.kube
# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# sudo chown $(id -u):$(id -g) $HOME/.kube/config
设置默认的存储类型 nfs
需要修改 deployment.yaml 的 /data/k8s 为你实际规划的 k8s 挂载路径
-
rbac.yaml
apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-client-provisioner-runner rules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["create", "update", "patch"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: run-nfs-client-provisioner subjects: - kind: ServiceAccount name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default roleRef: kind: ClusterRole name: nfs-client-provisioner-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default subjects: - kind: ServiceAccount name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default roleRef: kind: Role name: leader-locking-nfs-client-provisioner apiGroup: rbac.authorization.k8s.io
-
storageclass.yaml
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: retain-nfs-storage annotations: storageclass.kubernetes.io/is-default-class: "true" provisioner: fuseim.pri/ifs parameters: archiveOnDelete: "true" reclaimPolicy: "Retain"
-
deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nfs-client-provisioner labels: app: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: nfs-client-provisioner template: metadata: labels: app: nfs-client-provisioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-provisioner image: quay.io/external_storage/nfs-client-provisioner:latest volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: fuseim.pri/ifs - name: NFS_SERVER # 这里根据实际情况更改,对应hosts中nfs的名称 value: nfs - name: NFS_PATH # 这里根据实际情况更改 value: /data/k8s volumes: - name: nfs-client-root nfs: # 这里根据实际情况更改,对应hosts中nfs的名称 server: nfs # 这里根据实际情况更改 path: /data/k8s
安装 kubesphere
这里按照官方文档 安装即可,需要注意几点:
- 检查当前支持的k8s版本
- CPU > 1 核,内存 > 2 GB
- 一定要设置默认的存储类型,也就是上一步内容1.
问题整理
-
scheduler 和 controller-manager 状态为 Unhealthy
k8s 主节点启动后执行 kubectl get cs 结果:
NAME STATUS MESSAGE ERROR scheduler Unhealthy Get "http://127.0.0.1:10251/healthz": dial tcp 127.0.0.1:10251: connect: connection refused etcd-0 Healthy {"health":"true","reason":""} controller-manager Healthy ok
原因:/etc/kubernetes/manifests/下的kube-controller-manager.yaml和kube-scheduler.yaml设置的默认端口是0
解决方法:
1. 注释掉这两个文件中的 port 2. master节点重启kubelet,systemctl restart kubelet.service
-
1 node(s) had taints that the pod didn’t tolerate.
原因:默认master节点无法部署pod
解决方法:
# 查询节点信息 ubectl get no -o yaml | grep taint -A 5 # node.kubernetes.io/not-ready 需要替换为对应报错的信息,后面一定要有 - kubectl taint nodes --all node.kubernetes.io/not-ready-
-
Kubernetes v1.20.0 报"unexpected error getting claim reference: selfLink was empty, can’t make reference"
解决方法:
1. 编辑 /etc/kubernetes/manifests/kube-apiserver.yaml 找到下述内容 spec: containers: - command: - kube-apiserver 添加一行 - --feature-gates=RemoveSelfLink=false 2. 应用 kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
-
子节点报证书不存在:Error: open /etc/kubernetes/pki/etcd/ca.crt: no such file or directory
解决方法:将主节点 /etc/kubernetes/pki/ 下的证书都拷贝到子节点的相同目录,包含 /etc/kubernetes/pki/etcd 目录。