总体流程一览

主要流程如下:
1.准备云主机,升级CentOS系统到7.9
2.所有节点上安装Docker和Kubeadm,拉取相关镜像
3.在Master节点初始化集群,包括kubectl和部署CN容器网络插件
4.把Node节点加入k8s集群

可视化界面和私有镜像仓库请参考其他文章
1.部署Dashboard Web 页面,可视化查看Kubernetes资源,看我下一篇文章:k8s dashboard安装

2.部署Harbor私有仓库,存放镜像资源(非必要,省略介绍)

下面,开始介绍各流程详细配置步骤。

环境准备

云主机

云服务器

区域

CentOS

节点类型

配置

公网IP

安装工具

腾讯云

上海三区

7.9

master01

2C4G

101.34.112.190

docker、kubeadm、kubelet、kubectl、flannel

同上

上海二区

7.9

node01

1C2G

81.68.126.69

同上

同上

上海二区

7.9

node02

1C2G

81.68.92.49

同上

CentOS升级

如果低于CentOS 7.9,请先升级:

$ yum update -y
$ cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

PS:请务必不要跳过这步,低版本的CentOS安装Kubeadm时很可能会失败。作者就是失败之后升级CentOS才成功了!!

所有节点CentOS设置

基础设置

PS:该步骤基于:CentOS Linux release 7.9.2009 (Core) ,7.2 - 7.6 版本好像会失败,如果中途不成功,请考虑升级更换CentOS版本!

可以创建 k8s-pre-install-centos.sh 脚本,一键设置:

$ vim k8s-pre-install-centos.sh

#!/bin/sh

function set_base(){
  # 关闭防火墙,PS:如果使用云服务器,还需要在云服务器的控制台中把防火墙关闭了或者允许所有端口。
  systemctl stop firewalld
  systemctl disable firewalld

  # 关闭SELinux,这样做的目的是:为了让容器能读取主机文件系统。
  setenforce 0

  # 永久关闭swap分区交换,kubeadm规定,一定要关闭
  swapoff -a
  sed -ri 's/.*swap.*/#&/' /etc/fstab

  # iptables配置
  cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

  cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

  # iptables生效参数
  sysctl --system
}

set_base

执行上述脚本:

$ chmod 777 k8s-pre-install-centos.sh && ./k8s-pre-install-centos.sh

主机名设置

修改主机名

master执行:

hostnamectl set-hostname master01

节点1执行:

hostnamectl set-hostname node01

节点2执行:

hostnamectl set-hostname node02
修改hosts文件

每台机器上都要执行:

$ vim /etc/hosts 
101.34.112.190 master01 
81.68.126.69 node01 
81.68.92.49 node02

PS:请更换为自己的公网IP(注意是公网IP,不是内网)!

所有节点安装Docker

k8s支持 3种容器运行时,这里我们优先使用熟悉的 Docker 作为容器运行时。请确保CentOS 7以上,最新要求以 官方 为主。

安装yum仓库

$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

安装docker

包括cli、engine、docker compose等

$ sudo yum install docker-ce-20.10.14-3.el7 docker-ce-cli-20.10.14-3.el7 containerd.io docker-compose-plugin
ps:本教程使用的是docker版本:20.10.14

配置Docker守护程序

尤其是使用 systemd 来管理容器的 cgroup,另外还要配置阿里云镜像源,加快拉取速度!

$ sudo mkdir /etc/docker
$ cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "registry-mirrors": ["https://6ijb8ubo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF
  • registry-mirrors:镜像加速。
  • cgroupdriver:使用systemd。
  • log-driver:使用json日志,大小为100m。

启动docker,并设置为开机启动

$ sudo systemctl enable docker
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
$ systemctl status docker # 确保是running状态

确认Cgroup Driver为systemd

$ docker info | grep "Cgroup Driver"
 Cgroup Driver: systemd

PS:因为k8s是默认systemd作为cgroup driver,如果Docker使用另外的驱动,则可能出现不稳定的情况。

所有节点安装kubeadm

为保证不过期,请最终以 官方文档 为主:

配置yum源(使用aliyun,google你知道的)

$ cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

$ yum makecache # 更新yum

安装kubeadm, kubelet, kubectl

$ sudo yum install -y kubelet-1.23.6 kubeadm-1.23.6 kubectl-1.23.6 --disableexcludes=kubernetes

PS:k8s升级很快,为了保证教程正确,请使用相同版本。

启动klubelet,并设置为开机启动

$ sudo systemctl start kubelet
$ sudo systemctl enable kubelet

PS:kubeadm 将使用 kubelet 服务以容器方式部署和启动 Kubemetes 的主要服务。

所有节点拉取Docker镜像

ps:该1.23.6版本,需要docker为20,超过的不保证成功
拉取Docker镜像

查看初始化需要的镜像

$ kubeadm config images list

k8s.gcr.io/kube-apiserver:v1.23.6
k8s.gcr.io/kube-controller-manager:v1.23.6
k8s.gcr.io/kube-scheduler:v1.23.6
k8s.gcr.io/kube-proxy:v1.23.6
k8s.gcr.io/pause:3.6
k8s.gcr.io/etcd:3.5.1-0
k8s.gcr.io/coredns/coredns:v1.8.6

替换k8s镜像源

k8s模式镜像仓库是 k8s.gcr.io,由于众所周知的原因是无法访问的。

故这里需要创建配置 kubeadm-config-image.yaml 替换成阿里云的源:

$ vim kubeadm-config-image.yaml

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
# 默认为k8s.gcr.io,但是网络不通,所以要替换为阿里云镜像
imageRepository: registry.aliyuncs.com/google_containers

确认镜像仓库改变

再查看,发现镜像的地址变了,才能执行下一步:

$ kubeadm config images list --config kubeadm-config-image.yaml

registry.aliyuncs.com/google_containers/kube-apiserver:v1.23.6
registry.aliyuncs.com/google_containers/kube-controller-manager:v1.23.6
registry.aliyuncs.com/google_containers/kube-scheduler:v1.23.6
registry.aliyuncs.com/google_containers/kube-proxy:v1.23.6
registry.aliyuncs.com/google_containers/pause:3.6
registry.aliyuncs.com/google_containers/etcd:3.5.1-0
registry.aliyuncs.com/google_containers/coredns:v1.8.6

拉取镜像

$ kubeadm config images pull --config kubeadm-config-image.yaml

所有机器 上执行,把这些镜像提前拉好。

Master节点初始化集群

生成默认配置 kubeadm-config.yaml

并更改下面几项:

$ kubeadm config print init-defaults > kubeadm-config.yaml
  • kubernetes-version:集群版本,上面安装的kubeadm版本必须小于等于这里的
  • pod-network-cidr:pod资源的网段,需与pod网络插件的值设置一致。通常,Flannel网络插件的默认为10.244.0.0/16,Calico插件的默认值为192.168.0.0/16;
  • api-server:使用Master作为api-server,所以就是master机器的IP地址。
  • image-repository:拉取镜像的镜像仓库,默认是k8s.gcr.io。
  • nodeRegistration.name:改成master01
    最终如下:
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: 101.34.112.190 # 指定master节点的IP地址(公网)
  bindPort: 6443
nodeRegistration:
  criSocket: /var/run/dockershim.sock
  imagePullPolicy: IfNotPresent
  name: master01  # 改成master的主机名
  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.aliyuncs.com/google_containers  # 默认为k8s.gcr.io,但是网络不通,所以要替换为阿里云镜像
kind: ClusterConfiguration
kubernetesVersion: 1.23.6  # 指定kubernetes版本号,使用kubeadm config print init-defaults生成的即可
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
  podSubnet: 10.244.0.0/16  # 指定pod网段,10.244.0.0/16用于匹配flannel默认网段
scheduler: {}

上面的配置等价于:

$ kubeadm init \
--kubernetes-version=v1.23.6 \
--image-repository registry.aliyuncs.com/google_containers \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=101.34.112.190 --ignore-preflight-errors=Swap

或者1核CPU Master初始化(--ignore-preflight-errors=NumCPU这个如果是1核的ECS服务器一定要添加,不然会报错,因为K8S要求最低核数是2核)::

$ kubeadm init \
--kubernetes-version=v1.23.6 \
--apiserver-advertise-address=101.34.112.190 
--ignore-preflight-errors=NumCPU \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16 \
--image-repository registry.aliyuncs.com/google_containers \
--v=6

PS:建议通过配置文件的方式来操作,命令行不直观。

检查环境

$ kubeadm init phase preflight --config=kubeadm-config.yaml

这个命令会检查配置文件是否正确,以及系统环境是否支持kubeadm的安装。

初始化kubeadm集群

只需要在master上执行如下命令:

$ kubeadm init --config=kubeadm-config.yaml

PS:这里是最难的,作者卡在这里卡了一整天,查阅各种资料才解决,所以如果你也失败了,比较正常,这里是相比于内网部署k8s,公网最麻烦也是最难的点,这一步成功了,后面也没啥了。

到这里,会有2种结果:

  • 如果是内网,上面的docker版本,kubeadm版本没错的话,会成功,直接跳到4步骤。
  • 如果在云服务器(腾讯云,阿里云)上,一定会失败(原因和办法在这里)
[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”
// ...
[kubelet-check] Initial timeout of 40s passed.

提示:请一定先执行上面的初始化(目的是为了生成k8s的配置文件,否则下面的步骤中你会找不到etcd.yaml),失败后再执行下面的步骤!!

云服务器初始化失败解决版本

1)编辑etcd配置文件
配置文件位置:/etc/kubernetes/manifests/etcd.yaml

- --listen-client-urls=https://127.0.0.1:2379,https://101.34.112.190:2379
    - --listen-peer-urls=https://101.34.112.190:2380

改成

- --listen-client-urls=https://127.0.0.1:2379
    - --listen-peer-urls=https://127.0.0.1:2380

引用 在腾讯云安装K8S集群 :
此处"118.195.137.68"为腾讯云公网ip,要关注的是"–listen-client-urls"和"–listen-peer-urls"。需要把–listen-client-urls后面的公网IP删除,把–listen-peer-urls改成127.0.0.1:2380
原因是因为腾讯云只要选择VPC网络均是采用NAT方式将公网IP映射到私人网卡的,有兴趣的同学可以了解下NAT。这也就是为什么很多同事无法在腾讯云或阿里云上安装k8s集群的原因

2)手工停止已启动的进程

# 先停止kubelet
$ systemctl stop kubelet 
# 把所有kube的进程杀掉
$ netstat -anp |grep kube

请注意,不要执行 kubeadm reset,先 systemctl stop kubelet ,然后手动通过 netstat -anp |grep kube 来找pid,再通过 kill -9 pid 强杀。否则又会生成错误的etcd配置文件,这里非常关键!!!

3)重新初始化,但是跳过etcd文件已经存在的检查:

# 重新启动kubelet
$ systemctl start kubelet
# 重新初始化,跳过配置文件生成环节,不要etcd的修改要被覆盖掉
$ kubeadm init --config=kubeadm-config.yaml --skip-phases=preflight,certs,kubeconfig,kubelet-start,control-plane,etcd

成功初始化

如果所有配置都正常,很快会输出下面的信息(秒级别)代表了成功,否则大概率是失败(由于网络超时等):

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

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

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.1.200:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:af2a6e096cb404da729ef3802e77482f0a8a579fa602d7c071ef5c5415aac748

保存上面输出的token和sha256值。

也就是下面这一段,这段命令主要是让node节点加入k8s集群:

kubeadm join 101.34.112.190:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:af2a6e096cb404da729ef3802e77482f0a8a579fa602d7c071ef5c5415aac748

常见错误

Initial timeout of 40s passed
  • 可能1:检查镜像版本,可能是不匹配或者本地替换tag出错造成的了,或者是因为公网IP ETCD无法启动造成的。执行:journalctl -xeu kubelet 查看具体错误,或者时候 journalctl -f -u kubelet 查看初始化的实时输出,下次初始化之前执行 kubeadm reset 重置。
  • 可能2:CentOS版本太低,推荐7.8以上。我在7.2和7.5都失败了,执行 yum update -y升级到7.9才成功。
证书忘记
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2> /dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
token忘记
kubeadm token list

配置kubectl(master)

准备配置文件

kubectl需经由API server认证及授权后方能执行相应的管理操作,kubeadm 部署的集群为其生成了一个具有管理员权限的认证配置文件 /etc/kubernetes/admin.conf,它可由 kubectl 通过默认的 “$HOME/.kube/config” 的路径进行加载。

拷贝配置文件到kubectl默认加载路径:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
使用kubectl查看集群信息

在Master节点上执行,输出集群信息:

$ kubectl get nodes
NAME       STATUS     ROLES                  AGE   VERSION
master01   NotReady   control-plane,master   15m   v1.23.6

$ kubectl get cs
etcd-0               Healthy   {"health":"true","reason":""}
controller-manager   Healthy   ok
scheduler            Healthy   ok

这里STATUS是NotReady是因为还没有配置网络的原因,接下来会介绍。

安装CN网络(master)

$ curl https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml>>kube-flannel.yml
$ chmod 777 kube-flannel.yml 
$ kubectl apply -f kube-flannel.yml

等待几分钟,再查看Master节点状态,由NotRead变成了Ready状态:

$ kubectl get nodes
NAME       STATUS   ROLES                  AGE   VERSION
master01   Ready    control-plane,master   15m   v1.23.6

允许master节点部署pod

此时k8s master节点安装完毕(为了不浪费云服务器资源,需要让master节点能部署pod,需要运行以下命令)。

  1. 查看调度策略
$ kubectl describe node|grep -E "Name:|Taints:"

Name:               master01
Taints:             node-role.kubernetes.io/master:NoSchedule
  • NoSchedule: 一定不能被调度
  • PreferNoSchedule: 尽量不要调度
  • NoExecute: 不仅不会调度, 还会驱逐Node上已有的Pod
  1. 更改master节点可被部署pod
$ kubectl taint nodes --all node-role.kubernetes.io/master-
  1. 查看是否生效
$ kubectl describe node|grep -E "Name:|Taints:"
Name:               master01
Taints:             <none>

把Node节点加入集群

在node上执行上面kubeadm输出的命令(注意token和sha256值不同):

$ kubeadm join 192.168.1.200:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:af2a6e096cb404da729ef3802e77482f0a8a579fa602d7c071ef5c5415aac748

[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 -o yaml'
[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.

此时,在master执行以下命令,可以看到node节点已经加入成功了:

$ kubectl get nodes
NAME       STATUS     ROLES                  AGE   VERSION
master01   Ready   control-plane,master   60m   v1.23.6
node01     NotReady   <none>                 54s   v1.23.6

等待5分钟左右,node01的状态变成Ready。

另外一台Node节点机器,重复改步骤加入集群即可!

测试集群

创建个nginx Pod

在master节点运行以下命令:

$ kubectl run --image=nginx nginx-app --port=80
$ kubectl run --image=nginx nginx-app1 --port=81

然后再运行:

$ kubectl get pods
NAME        READY   STATUS              RESTARTS   AGE
nginx-app   0/1     ContainerCreating   0          18s

$ kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
nginx-app   1/1     Running   0          26s

$ kubectl get pods -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
nginx-app   1/1     Running   0          57s   10.244.1.2   node01   <none>           <none>

可以看到2个pod已经是运行状态,证明k8s集群成功安装

Dashboard可视化界面安装