1. 前期准备

[root@sea ~]# yum -y update 

# 说明: 如果没有关闭swap ,在k8s cluster 初始化或 worknode join cluster时都会失败;关闭 swap后在重启OS时,有可以会导致OS启动失败,具体原因待查;

[root@sea ~]# grep swapoff /etc/rc.local
swapoff -a
[root@sea ~]# swapoff -a     
[root@sea ~]# grep swap /etc/fstab
#/dev/mapper/centos-swap swap swap defaults 0 0

# 时钟同步
[root@sea ~]# yum -y install ntp ntpdate
[root@sea ~]# ntpdate cn.pool.ntp.org

# 关闭防火墙和 selinux
[root@sea ~]# systemctl stop firewalld && systemctl disable firewalld
[root@sea ~]# getenforce
Disabled

# 本地解析
[root@sea ~]# tail -1 /etc/hosts
10.168.25.62 sea

# 调整系统参数
[root@sea ~]# cat <<EOF >> /etc/sysctl.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
vm.swappiness=0
EOF

[root@sea ~]# sysctl -p
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
vm.swappiness = 0

# 确保iptables的参数都为1
[root@sea ~]# cat /proc/sys/net/bridge/bridge-nf-call-iptables 
1
[root@sea ~]# cat /proc/sys/net/bridge/bridge-nf-call-ip6tables 
1

# 关闭iptables转发,使用ipvs转发数据。
[root@sea ~]# cat > /etc/sysconfig/modules/ipvs.modules <<EOF
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF

[root@sea ~]# chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod |grep -e -i ip_vs -e nf_conntrack_ipv4

[root@sea ~]# reboot

2. 安装 Kubernetes v1.16.0 和 docker-ce v.19.03.12

# 导入 k8s 安装包所需要的证书
[root@sea ~]# wget https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
[root@sea ~]# wget https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
[root@sea ~]# rpm --import rpm-package-key.gpg 
[root@sea ~]# rpm --import yum-key.gpg

# 安装必要的一些系统工具
[root@sea ~]# yum install -y yum-utils

# 安装软件源信息
[root@sea ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@sea ~]# sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo

[root@sea ~]# cat /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1

# 更新并安装 kubernetes、docker-ce
[root@sea ~]# yum makecache fast
[root@sea ~]# yum install kubelet-1.16.0 kubeadm-1.16.0 kubectl-1.16.0 docker-ce-19.03.12 docker-ce-cli-19.03.12 containerd.io ipvsadm ipset
[root@sea ~]# systemctl enable docker && systemctl start docker
[root@sea ~]# systemctl enable kubelet

# 验证 docker-ce 的版本
[root@sea ~]# docker version
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:46:54 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       48a66213fe
  Built:            Mon Jun 22 15:45:28 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.5.11
  GitCommit:        3df54a852345ae127d1fa3092b95168e4a88e2f8
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683


# 验证 kubectl 的版本
[root@sea ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.0", GitCommit:"2bd9643cee5b3b3a5ecbd3af49d09018f0773c77", GitTreeState:"clean", BuildDate:"2019-09-18T14:36:53Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?


# 忽略swap
[root@sea ~]# cat /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS="--fail-swap-on=false"

3. 修改 docker 配置

# 设置 docker runtime 、cgroup 及其他配置

[root@sea ~]# cat /etc/docker/daemon.json
{
  "registry-mirrors": ["https://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn"],
  "storage-driver": "overlay2",
  "exec-opts": ["native.cgroupdriver=systemd"],
  "default-shm-size": "1G",
  "default-ulimits": {
    "memlock": {"name": "memlock", "soft": -1, "hard": -1},
    "stack": {"name": "stack", "soft": 67108864, "hard": 67108864},
    "nofile": {"name": "nofile", "soft": 65536, "hard": 65536}
  }
}

 # 重启 docker
[root@sea ~]# systemctl daemon-reload && systemctl restart docker

[root@sea ~]# systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since 四 2022-05-05 21:18:45 CST; 5s ago
     Docs: https://docs.docker.com
 Main PID: 8270 (dockerd)
    Tasks: 23
   Memory: 50.8M
   CGroup: /system.slice/docker.service
           └─8270 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

4. 部署 Kubernetes集群

# 生成集群初始化文件,修改配置选项
[root@sea ~]# kubeadm config print init-defaults > kubeadm-init.yaml

# 修改部分配置 (有注解的地方都是需要修改的)
[root@sea ~]# cat kubeadm-init.yaml
apiVersion: kubeadm.k8s.io/v1beta2
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.*************
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 10.***.**.**   # 当前主机、k8s master 的 IP
  bindPort: 6443
nodeRegistration:
  criSocket: /var/run/dockershim.sock
  name: sea
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
---
apiServer:
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta2
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns:
  type: CoreDNS
etcd:
  local:
    dataDir: /var/lib/etcd
imageRepository: k8s.gcr.io
kind: ClusterConfiguration
kubernetesVersion: v1.16.0
networking:
  dnsDomain: cluster.local
  podSubnet: 10.226.0.0/16        # pod ip网段
  serviceSubnet: 10.98.0.0/16     # service ip网段
scheduler: {}
---   # 增加以下三行,表示使用 ipvs mode
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"                      


# 下载 init.yaml 中所需 images
[root@sea ~]# kubeadm config images pull --config kubeadm-init.yaml
[root@sea ~]# docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
k8s.gcr.io/kube-apiserver            v1.16.0             b305571ca60a        2 years ago         217MB
k8s.gcr.io/kube-controller-manager   v1.16.0             06a629a7e51c        2 years ago         163MB
k8s.gcr.io/kube-proxy                v1.16.0             c21b0c7400f9        2 years ago         86.1MB
k8s.gcr.io/kube-scheduler            v1.16.0             301ddc62b80b        2 years ago         87.3MB
k8s.gcr.io/etcd                      3.3.15-0            b2756210eeab        2 years ago         247MB
k8s.gcr.io/coredns                   1.6.2               bf261d157914        2 years ago         44.1MB
k8s.gcr.io/pause                     3.1                 da86e6ba6ca1        4 years ago         742kB


# 无法下载国外镜像的话,可以改用 aliyun 的源先下载然后修改 tag 即可

# 先查看当前 kubeadm-init.yaml 中所需要的镜像及 tag
[root@sea ~]# kubeadm config images list --config kubeadm-init.yaml
k8s.gcr.io/kube-apiserver:v1.16.0
k8s.gcr.io/kube-controller-manager:v1.16.0
k8s.gcr.io/kube-scheduler:v1.16.0
k8s.gcr.io/kube-proxy:v1.16.0
k8s.gcr.io/pause:3.1
k8s.gcr.io/etcd:3.3.15-0
k8s.gcr.io/coredns:1.6.2


[root@sea ~]# images=(kube-apiserver:v1.16.0 kube-controller-manager:v1.16.0 kube-scheduler:v1.16.0 kube-proxy:v1.16.0 pause:3.1 etcd:3.3.15-0 coredns:1.6.2)

[root@sea ~]# for imageName in ${images[@]};do
> docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
> docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
> docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
> done


# 初始化 Kubernetes 集群
[root@sea ~]# kubeadm init --config kubeadm-init.yaml
......

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 10.***.**.**:6443 --token abcdef.***************** \
    --discovery-token-ca-cert-hash sha256:************************************



[root@sea ~]# mkdir -p $HOME/.kube
[root@sea ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@sea ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config

5. 去除 master 节点的污点,让 pod 可以在 master 节点运行

# 查看 master 节点的污点
[root@sea ~]# kubectl describe nodes sea |grep -i taints
Taints:              node-role-kubernetes.io/master:NoSchedule

# 删除 master 节点的污点
[root@sea ~]# kubectl taint node k8s-master node-role.kubernetes.io/master:NoSchedule-

# 再次查看 master 节点已经没有污点了
[root@sea ~]# kubectl describe nodes sea |grep -i taints
Taints:             <none>

6. 部署网络插件 flannel

说明:我这里由于不能下载 flannel 国外镜像源,所以找了一个之前用过的 kube-flannel.yaml 文件,如果可以下载国外镜像源,推荐使用以下命令安装 flannel
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
[root@sea ~]# cat kube-flannel.yml
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: psp.flannel.unprivileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
    seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
    apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
    apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
  privileged: false
  volumes:
  - configMap
  - secret
  - emptyDir
  - hostPath
  allowedHostPaths:
  - pathPrefix: "/etc/cni/net.d"
  - pathPrefix: "/etc/kube-flannel"
  - pathPrefix: "/run/flannel"
  readOnlyRootFilesystem: false
  # Users and groups
  runAsUser:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  # Privilege Escalation
  allowPrivilegeEscalation: false
  defaultAllowPrivilegeEscalation: false
  # Capabilities
  allowedCapabilities: ['NET_ADMIN', 'NET_RAW']
  defaultAddCapabilities: []
  requiredDropCapabilities: []
  # Host namespaces
  hostPID: false
  hostIPC: false
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  # SELinux
  seLinux:
    # SELinux is unused in CaaSP
    rule: 'RunAsAny'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
rules:
- apiGroups: ['extensions']
  resources: ['podsecuritypolicies']
  verbs: ['use']
  resourceNames: ['psp.flannel.unprivileged']
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: kube-flannel-cfg
  namespace: kube-system
  labels:
    tier: node
    app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-system
  labels:
    tier: node
    app: flannel
spec:
  selector:
    matchLabels:
      app: flannel
  template:
    metadata:
      labels:
        tier: node
        app: flannel
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
      hostNetwork: true
      priorityClassName: system-node-critical
      tolerations:
      - operator: Exists
        effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
      - name: install-cni
        image: quay.io/coreos/flannel:v0.14.0
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
        image: quay.io/coreos/flannel:v0.14.0
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: false
          capabilities:
            add: ["NET_ADMIN", "NET_RAW"]
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        volumeMounts:
        - name: run
          mountPath: /run/flannel
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      volumes:
      - name: run
        hostPath:
          path: /run/flannel
      - name: cni
        hostPath:
          path: /etc/cni/net.d
      - name: flannel-cfg
        configMap:
          name: kube-flannel-cfg


[root@sea ~]# kubectl create -f kube-flannel.yml

7. 验证安装

# 验证 flannel 及其他 Kubernetes 组件
[root@sea ~]# kubectl get all -n kube-system
NAME                              READY   STATUS    RESTARTS   AGE
pod/coredns-5644d7b6d9-96csd      1/1     Running   0          38m
pod/coredns-5644d7b6d9-t52z6      1/1     Running   0          38m
pod/etcd-sea                      1/1     Running   0          37m
pod/kube-apiserver-sea            1/1     Running   0          37m
pod/kube-controller-manager-sea   1/1     Running   0          37m
pod/kube-flannel-ds-zmprb         1/1     Running   0          16m
pod/kube-proxy-hh6nl              1/1     Running   0          38m
pod/kube-scheduler-sea            1/1     Running   0          37m

NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
service/kube-dns   ClusterIP   10.98.0.10   <none>        53/UDP,53/TCP,9153/TCP   38m

NAME                             DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                 AGE
daemonset.apps/kube-flannel-ds   1         1         1       1            1           <none>                        22m
daemonset.apps/kube-proxy        1         1         1       1            1           beta.kubernetes.io/os=linux   38m

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/coredns   2/2     2            2           38m

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/coredns-5644d7b6d9   2         2         2       38m


# 验证 Kubernetes master 的状态
[root@sea ~]# kubectl get node
NAME   STATUS   ROLES    AGE   VERSION
sea    Ready    master   39m   v1.16.0


# 验证 Kubernetes 核心组件的状态
[root@sea ~]# kubectl get cs -o yaml
apiVersion: v1
items:
- apiVersion: v1
  conditions:
  - message: ok
    status: "True"
    type: Healthy
  kind: ComponentStatus
  metadata:
    creationTimestamp: null
    name: controller-manager
    selfLink: /api/v1/componentstatuses/controller-manager
- apiVersion: v1
  conditions:
  - message: ok
    status: "True"
    type: Healthy
  kind: ComponentStatus
  metadata:
    creationTimestamp: null
    name: scheduler
    selfLink: /api/v1/componentstatuses/scheduler
- apiVersion: v1
  conditions:
  - message: '{"health":"true"}'
    status: "True"
    type: Healthy
  kind: ComponentStatus
  metadata:
    creationTimestamp: null
    name: etcd-0
    selfLink: /api/v1/componentstatuses/etcd-0
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""


# 将 k8s 和 docker-ce 的源重命名,避免 yum update 时更新版本
[root@sea ~]# mv /etc/yum.repos.d/docker-ce.repo{,.bak}
[root@sea ~]# mv /etc/yum.repos.d/kubernetes.repo{,.bak}
[root@sea ~]# ls /etc/yum.repos.d/
CentOS-Base.repo  CentOS-Debuginfo.repo  CentOS-Media.repo    CentOS-Vault.repo          docker-ce.repo.bak  kubernetes.repo.bak
CentOS-CR.repo    CentOS-fasttrack.repo  CentOS-Sources.repo  CentOS-x86_64-kernel.repo  elrepo.repo

8.上述资源都正常可重启 master 机器,并再次查看集群中的资源是否正常

[root@sea ~]# reboot
[root@sea ~]# kubectl get node
NAME   STATUS   ROLES    AGE   VERSION
sea    Ready    master   49m   v1.16.0


[root@sea ~]# kubectl get all -A
NAMESPACE     NAME                              READY   STATUS    RESTARTS   AGE
kube-system   pod/coredns-5644d7b6d9-96csd      1/1     Running   1          49m
kube-system   pod/coredns-5644d7b6d9-t52z6      1/1     Running   1          49m
kube-system   pod/etcd-sea                      1/1     Running   1          48m
kube-system   pod/kube-apiserver-sea            1/1     Running   1          48m
kube-system   pod/kube-controller-manager-sea   1/1     Running   1          48m
kube-system   pod/kube-flannel-ds-zmprb         1/1     Running   1          27m
kube-system   pod/kube-proxy-hh6nl              1/1     Running   1          49m
kube-system   pod/kube-scheduler-sea            1/1     Running   1          48m

NAMESPACE     NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
default       service/kubernetes   ClusterIP   10.98.0.1    <none>        443/TCP                  49m
kube-system   service/kube-dns     ClusterIP   10.98.0.10   <none>        53/UDP,53/TCP,9153/TCP   49m

NAMESPACE     NAME                             DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                 AGE
kube-system   daemonset.apps/kube-flannel-ds   1         1         1       1            1           <none>                        32m
kube-system   daemonset.apps/kube-proxy        1         1         1       1            1           beta.kubernetes.io/os=linux   49m

NAMESPACE     NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   deployment.apps/coredns   2/2     2            2           49m

NAMESPACE     NAME                                 DESIRED   CURRENT   READY   AGE
kube-system   replicaset.apps/coredns-5644d7b6d9   2         2         2       49m