From 88e195fdbecb0c2734d571fa5bc6e1959ad8afb0 Mon Sep 17 00:00:00 2001 From: yangpeng Date: Tue, 19 May 2020 15:35:25 +0800 Subject: [PATCH] =?UTF-8?q?kubernetes=20v1.18.2=E4=BA=8C=E8=BF=9B=E5=88=B6?= =?UTF-8?q?=E9=AB=98=E5=8F=AF=E7=94=A8=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- img/bootstrap-1.png | Bin 0 -> 49615 bytes ...57\347\224\250\351\203\250\347\275\262.md" | 1476 +++++++++++++++++ 2 files changed, 1476 insertions(+) create mode 100644 img/bootstrap-1.png create mode 100644 "kubernetes/kubernetes-v1.18.2\344\272\214\350\277\233\345\210\266\351\253\230\345\217\257\347\224\250\351\203\250\347\275\262.md" diff --git a/img/bootstrap-1.png b/img/bootstrap-1.png new file mode 100644 index 0000000000000000000000000000000000000000..169e2788adea05906d947f137e7a10aeef2d4a8c GIT binary patch literal 49615 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 literal 0 HcmV?d00001 diff --git "a/kubernetes/kubernetes-v1.18.2\344\272\214\350\277\233\345\210\266\351\253\230\345\217\257\347\224\250\351\203\250\347\275\262.md" "b/kubernetes/kubernetes-v1.18.2\344\272\214\350\277\233\345\210\266\351\253\230\345\217\257\347\224\250\351\203\250\347\275\262.md" new file mode 100644 index 0000000..25f5d3e --- /dev/null +++ "b/kubernetes/kubernetes-v1.18.2\344\272\214\350\277\233\345\210\266\351\253\230\345\217\257\347\224\250\351\203\250\347\275\262.md" @@ -0,0 +1,1476 @@ +## 一、环境 + +### 服务器信息 + +主机名 | IP | 备注 +---|---|--- +k8s-master1 | 192.168.0.216 | 主集群1,etcd1,node节点 +k8s-master2 | 192.168.0.217 | 主集群1,etcd1,node节点 +k8s-master3 | 192.168.0.218 | 主集群1,etcd1,node节点 +slb | lb.ypvip.com.cn | 外网阿里slb域名 + +### 服务版本与K8S集群说明 + +- `阿里slb`设置`TCP监听`,监听6443端口(通过四层负载到master apiserver)。 +- 所有`阿里云ECS主机`使用 `CentOS 7.6.1810` 版本,并且内核都升到到`5.x`版本。 +- K8S 集群使用 `Iptables 模式`(kube-proxy 注释中预留 `Ipvs` 模式配置) +- Calico 使用 `IPIP` 模式 +- 集群使用默认 `svc.cluster.local` +- `10.10.0.1` 为集群 kubernetes svc 解析ip +- Docker CE version 19.03.6 +- Kubernetes Version 1.18.2 +- Etcd Version v3.4.7 +- Calico Version v3.14.0 +- Coredns Version 1.6.7 +- Metrics-Server Version v0.3.6 + +```bash +$ kubectl get svc + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +kubernetes ClusterIP 10.10.0.1 443/TCP 6d23h +``` + +> PS:上面服务版本都是使用`当前最新版本` + +### Service 和 Pods Ip 段划分 + +名称 | IP网段 | 备注 +---|---|--- +service-cluster-ip | 10.10.0.0/16 | 可用地址 65534 +pods-ip | 10.20.0.0/16 | 可用地址 65534 +集群dns | 10.10.0.2 | 用于集群service域名解析 +k8s svc | 10.10.0.1 | 集群 kubernetes svc 解析ip + +## 二、环境初始化 + +> 所有集群服务器都需要初始化 + +### 2.1 停止所有机器 firewalld 防火墙 + +```bash +$ systemctl stop firewalld +$ systemctl disable firewalld +``` + +### 2.2 关闭 swap + +```bash +$ swapoff -a +$ sed -i 's/.*swap.*/#&/' /etc/fstab +``` + +### 2.3 关闭 Selinux + +```bash +$ setenforce 0 +$ sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/sysconfig/selinux +$ sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config +$ sed -i "s/^SELINUX=permissive/SELINUX=disabled/g" /etc/sysconfig/selinux +$ sed -i "s/^SELINUX=permissive/SELINUX=disabled/g" /etc/selinux/config +``` + +### 2.4 设置主机名、升级内核、安装 Docker ce + +运行下面 `init.sh` shell 脚本,脚本完成下面四项任务: + +- 设置服务器 `hostname` +- 安装 `k8s依赖环境` +- `升级系统内核`(升级Centos7系统内核,解决Docker-ce版本兼容问题) +- 安装 `docker ce` 19.03.6 版本 + +在每台机器上运行 init.sh 脚本,示例如下: + +> Ps:init.sh 脚本只用于 `Centos`,支持 `重复运行`。 + +```bash +# k8s-master1 机器运行,init.sh 后面接的参数是设置 k8s-master1 服务器主机名 +$ chmod +x init.sh && ./init.sh k8s-master1 + +# 执行完 init.sh 脚本,请重启服务器 +$ reboot +``` + +```bash +#!/usr/bin/env bash + +function Check_linux_system(){ + linux_version=`cat /etc/redhat-release` + if [[ ${linux_version} =~ "CentOS" ]];then + echo -e "\033[32;32m 系统为 ${linux_version} \033[0m \n" + else + echo -e "\033[32;32m 系统不是CentOS,该脚本只支持CentOS环境\033[0m \n" + exit 1 + fi +} + +function Set_hostname(){ + if [ -n "$HostName" ];then + grep $HostName /etc/hostname && echo -e "\033[32;32m 主机名已设置,退出设置主机名步骤 \033[0m \n" && return + case $HostName in + help) + echo -e "\033[32;32m bash init.sh 主机名 \033[0m \n" + exit 1 + ;; + *) + hostname $HostName + echo "$HostName" > /etc/hostname + echo "`ifconfig eth0 | grep inet | awk '{print $2}'` $HostName" >> /etc/hosts + ;; + esac + else + echo -e "\033[32;32m 输入为空,请参照 bash init.sh 主机名 \033[0m \n" + exit 1 + fi +} + +function Install_depend_environment(){ + rpm -qa | grep nfs-utils &> /dev/null && echo -e "\033[32;32m 已完成依赖环境安装,退出依赖环境安装步骤 \033[0m \n" && return + yum install -y nfs-utils curl yum-utils device-mapper-persistent-data lvm2 net-tools conntrack-tools wget vim ntpdate libseccomp libtool-ltdl telnet + echo -e "\033[32;32m 升级Centos7系统内核到5版本,解决Docker-ce版本兼容问题\033[0m \n" + rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org && \ + rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm && \ + yum --disablerepo=\* --enablerepo=elrepo-kernel repolist && \ + yum --disablerepo=\* --enablerepo=elrepo-kernel install -y kernel-ml.x86_64 && \ + yum remove -y kernel-tools-libs.x86_64 kernel-tools.x86_64 && \ + yum --disablerepo=\* --enablerepo=elrepo-kernel install -y kernel-ml-tools.x86_64 && \ + grub2-set-default 0 + modprobe br_netfilter + cat < /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 + sysctl -p /etc/sysctl.d/k8s.conf + ls /proc/sys/net/bridge +} + +function Install_docker(){ + rpm -qa | grep docker && echo -e "\033[32;32m 已安装docker,退出安装docker步骤 \033[0m \n" && return + yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo + yum makecache fast + yum -y install docker-ce-19.03.6 docker-ce-cli-19.03.6 + systemctl enable docker.service + systemctl start docker.service + systemctl stop docker.service + echo '{"registry-mirrors": ["https://4xr1qpsp.mirror.aliyuncs.com"], "log-opts": {"max-size":"500m", "max-file":"3"}}' > /etc/docker/daemon.json + systemctl daemon-reload + systemctl start docker +} + +# 初始化顺序 +HostName=$1 +Check_linux_system && \ +Set_hostname && \ +Install_depend_environment && \ +Install_docker +``` + +## 三、Kubernetes 部署 + +### 部署顺序 + +1、自签TLS证书 +2、部署Etcd集群 +3、创建 metrics-server 证书 +4、获取K8S二进制包 +5、创建Node节点kubeconfig文件 +6、配置Master组件并运行 +7、配置kubelet证书自动续期 +8、配置Node组件并运行 +9、安装calico网络,使用IPIP模式 +10、集群CoreDNS部署 +11、部署集群监控服务 Metrics Server +12、部署 Kubernetes Dashboard + +### 3.1 自签TLS证书 + +> 在 `k8s-master1` 安装证书生成工具 cfssl,并生成相关证书 + +```bash +# 创建目录用于存放 SSL 证书 +$ mkdir /data/ssl -p + +# 下载生成证书命令 +$ wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 +$ wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 +$ wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 + +# 添加执行权限 +$ chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 cfssl-certinfo_linux-amd64 + +# 移动到 /usr/local/bin 目录下 +$ mv cfssl_linux-amd64 /usr/local/bin/cfssl +$ mv cfssljson_linux-amd64 /usr/local/bin/cfssljson +$ mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo +``` + +```bash +# 进入证书目录 +$ cd /data/ssl/ + +# 创建 certificate.sh 脚本 +$ vim certificate.sh +``` + +> PS:证书有效期为 `10年` + +```bash +cat > ca-config.json < ca-csr.json < server-csr.json < admin-csr.json < kube-proxy-csr.json < k8s-master1 机器上操作,把执行文件copy到 k8s-master2 k8s-master3 + +二进制包下载地址:https://github.com/etcd-io/etcd/releases/download/v3.4.7/etcd-v3.4.7-linux-amd64.tar.gz + +```bash +# 创建存储etcd数据目录 +$ mkdir /data/etcd/ + +# 创建 k8s 集群配置目录 +$ mkdir /opt/kubernetes/{bin,cfg,ssl} -p + +# 下载二进制etcd包,并把执行文件放到 /opt/kubernetes/bin/ 目录 +$ cd /data/etcd/ +$ wget https://github.com/etcd-io/etcd/releases/download/v3.4.7/etcd-v3.4.7-linux-amd64.tar.gz +$ tar zxvf etcd-v3.4.7-linux-amd64.tar.gz +$ cd etcd-v3.4.7-linux-amd64 +$ cp -a etcd etcdctl /opt/kubernetes/bin/ + +# 把 /opt/kubernetes/bin 目录加入到 PATH +$ echo 'export PATH=$PATH:/opt/kubernetes/bin' >> /etc/profile +$ source /etc/profile +``` + +> 登陆到 `k8s-master2` 和 `k8s-master3` 服务器上操作 + +```bash +# 创建 k8s 集群配置目录 +$ mkdir /data/etcd +$ mkdir /opt/kubernetes/{bin,cfg,ssl} -p + +# 把 /opt/kubernetes/bin 目录加入到 PATH +$ echo 'export PATH=$PATH:/opt/kubernetes/bin' >> /etc/profile +$ source /etc/profile +``` + +> 登陆到 `k8s-master1` 操作 + +```bash +# 进入 K8S 集群证书目录 +$ cd /data/ssl + +# 把证书 copy 到 k8s-master1 机器 /opt/kubernetes/ssl/ 目录 +$ cp ca*pem server*pem /opt/kubernetes/ssl/ + +# 把etcd执行文件与证书 copy 到 k8s-master2 k8s-master3 机器 +scp -r /opt/kubernetes/* root@k8s-master2:/opt/kubernetes +scp -r /opt/kubernetes/* root@k8s-master3:/opt/kubernetes +``` + +```bash +$ cd /data/etcd + +# 编写 etcd 配置文件脚本 +$ vim etcd.sh +``` + +```bash +#!/bin/bash + +ETCD_NAME=${1:-"etcd01"} +ETCD_IP=${2:-"127.0.0.1"} +ETCD_CLUSTER=${3:-"etcd01=https://127.0.0.1:2379"} + +cat </opt/kubernetes/cfg/etcd.yml +name: ${ETCD_NAME} +data-dir: /var/lib/etcd/default.etcd +listen-peer-urls: https://${ETCD_IP}:2380 +listen-client-urls: https://${ETCD_IP}:2379,https://127.0.0.1:2379 + +advertise-client-urls: https://${ETCD_IP}:2379 +initial-advertise-peer-urls: https://${ETCD_IP}:2380 +initial-cluster: ${ETCD_CLUSTER} +initial-cluster-token: etcd-cluster +initial-cluster-state: new + +client-transport-security: + cert-file: /opt/kubernetes/ssl/server.pem + key-file: /opt/kubernetes/ssl/server-key.pem + client-cert-auth: false + trusted-ca-file: /opt/kubernetes/ssl/ca.pem + auto-tls: false + +peer-transport-security: + cert-file: /opt/kubernetes/ssl/server.pem + key-file: /opt/kubernetes/ssl/server-key.pem + client-cert-auth: false + trusted-ca-file: /opt/kubernetes/ssl/ca.pem + auto-tls: false + +debug: false +logger: zap +log-outputs: [stderr] +EOF + +cat </usr/lib/systemd/system/etcd.service +[Unit] +Description=Etcd Server +Documentation=https://github.com/etcd-io/etcd +Conflicts=etcd.service +After=network.target +After=network-online.target +Wants=network-online.target + +[Service] +Type=notify +LimitNOFILE=65536 +Restart=on-failure +RestartSec=5s +TimeoutStartSec=0 +ExecStart=/opt/kubernetes/bin/etcd --config-file=/opt/kubernetes/cfg/etcd.yml + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable etcd +systemctl restart etcd +``` + +```bash +# 执行 etcd.sh 生成配置脚本 +$ chmod +x etcd.sh +$ ./etcd.sh etcd01 192.168.0.216 etcd01=https://192.168.0.216:2380,etcd02=https://192.168.0.217:2380,etcd03=https://192.168.0.218:2380 + +# 查看 etcd 是否启动正常 +$ ps -ef | grep etcd +$ netstat -ntplu | grep etcd + +tcp 0 0 192.168.0.216:2379 0.0.0.0:* LISTEN 1558/etcd +tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 1558/etcd +tcp 0 0 192.168.0.216:2380 0.0.0.0:* LISTEN 1558/etcd + +# 把 etcd.sh 脚本 copy 到 k8s-master2 k8s-master3 机器上 +$ scp /data/etcd/etcd.sh root@k8s-master2:/data/etcd/ +$ scp /data/etcd/etcd.sh root@k8s-master3:/data/etcd/ +``` + +> 登陆到 `k8s-master2` 操作 + +```bash +# 执行 etcd.sh 生成配置脚本 +$ chmod +x etcd.sh +$ ./etcd.sh etcd02 192.168.0.217 etcd01=https://192.168.0.216:2380,etcd02=https://192.168.0.217:2380,etcd03=https://192.168.0.218:2380 + +# 查看 etcd 是否启动正常 +$ ps -ef | grep etcd +$ netstat -ntplu | grep etcd +``` + +> 登陆到 `k8s-master3` 操作 + +```bash +# 执行 etcd.sh 生成配置脚本 +$ chmod +x etcd.sh +$ ./etcd.sh etcd03 192.168.0.218 etcd01=https://192.168.0.216:2380,etcd02=https://192.168.0.217:2380,etcd03=https://192.168.0.218:2380 + +# 查看 etcd 是否启动正常 +$ ps -ef | grep etcd +$ netstat -ntplu | grep etcd +``` + +```bash +# 随便登陆一台master机器,查看 etcd 集群是否正常 +$ ETCDCTL_API=3 etcdctl --write-out=table \ +--cacert=/opt/kubernetes/ssl/ca.pem --cert=/opt/kubernetes/ssl/server.pem --key=/opt/kubernetes/ssl/server-key.pem \ +--endpoints=https://192.168.0.216:2379,https://192.168.0.217:2379,https://192.168.0.218:2379 endpoint health + ++---------------------------------+--------+-------------+-------+ +| ENDPOINT | HEALTH | TOOK | ERROR | ++---------------------------------+--------+-------------+-------+ +| https://192.168.0.216:2379 | true | 38.721248ms | | +| https://192.168.0.217:2379 | true | 38.621248ms | | +| https://192.168.0.218:2379 | true | 38.821248ms | | ++---------------------------------+--------+-------------+-------+ +``` + +### 3.3 创建 metrics-server 证书 + +创建 metrics-server 使用的证书 + +> 登陆到 `k8s-master1` 操作 + +```bash +$ cd /data/ssl/ + +# 注意: "CN": "system:metrics-server" 一定是这个,因为后面授权时用到这个名称,否则会报禁止匿名访问 +$ cat > metrics-server-csr.json < 登陆到 `k8s-master1` 操作 + +> v1.18 下载页面 https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.18.md + +```bash +# 创建存放 k8s 二进制包目录 +$ mkdir /data/k8s-package +$ cd /data/k8s-package + +# 下载 v1.18.2 二进制包 +$ wget https://dl.k8s.io/v1.18.2/kubernetes-server-linux-amd64.tar.gz + +$ tar xf kubernetes-server-linux-amd64.tar.gz +``` + +master 节点需要用到: +- kubectl +- kube-scheduler +- kube-apiserver +- kube-controller-manager + +node 节点需要用到: +- kubelet +- kube-proxy + +> PS:本文master节点也做为一个node节点,所以需要用到 kubelet kube-proxy 执行文件 + +```bash +# 进入解压出来二进制包bin目录 +$ cd /data/k8s-package/kubernetes/server/bin + +# cpoy 执行文件到 /opt/kubernetes/bin 目录 +$ cp -a kube-apiserver kube-controller-manager kube-scheduler kubectl kubelet kube-proxy /opt/kubernetes/bin + +# copy 执行文件到 k8s-master2 k8s-master3 机器 /opt/kubernetes/bin 目录 +$ scp kube-apiserver kube-controller-manager kube-scheduler kubectl kubelet kube-proxy root@k8s-master2:/opt/kubernetes/bin/ +$ scp kube-apiserver kube-controller-manager kube-scheduler kubectl kubelet kube-proxy root@k8s-master3:/opt/kubernetes/bin/ +``` + +### 3.5 创建Node节点kubeconfig文件 + +> 登陆到 `k8s-master1` 操作 + +- 创建TLS Bootstrapping Token +- 创建kubelet kubeconfig +- 创建kube-proxy kubeconfig + +```bash +$ cd /data/ssl/ + +# 修改第10行 KUBE_APISERVER 地址 +$ vim kubeconfig.sh +``` + +```bash +# 创建 TLS Bootstrapping Token +export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ') +cat > token.csv < 登陆到 `k8s-master1` `k8s-master2` `k8s-master3` 操作 + +```bash +# 创建 /data/k8s-master 目录,用于存放 master 配置执行脚本 +$ mkdir /data/k8s-master +``` + +> 登陆到 `k8s-master1` + +```bash +$ cd /data/k8s-master + +# 创建生成 kube-apiserver 配置文件脚本 +$ vim apiserver.sh +``` + +```bash +#!/bin/bash + +MASTER_ADDRESS=${1:-"192.168.0.216"} +ETCD_SERVERS=${2:-"http://127.0.0.1:2379"} + +cat </opt/kubernetes/cfg/kube-apiserver +KUBE_APISERVER_OPTS="--logtostderr=false \\ +--v=2 \\ +--log-dir=/var/log/kubernetes \\ +--etcd-servers=${ETCD_SERVERS} \\ +--bind-address=0.0.0.0 \\ +--secure-port=6443 \\ +--advertise-address=${MASTER_ADDRESS} \\ +--allow-privileged=true \\ +--service-cluster-ip-range=10.10.0.0/16 \\ +--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction \\ +--authorization-mode=RBAC,Node \\ +--kubelet-https=true \\ +--enable-bootstrap-token-auth=true \\ +--token-auth-file=/opt/kubernetes/cfg/token.csv \\ +--service-node-port-range=30000-50000 \\ +--kubelet-client-certificate=/opt/kubernetes/ssl/server.pem \\ +--kubelet-client-key=/opt/kubernetes/ssl/server-key.pem \\ +--tls-cert-file=/opt/kubernetes/ssl/server.pem \\ +--tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \\ +--client-ca-file=/opt/kubernetes/ssl/ca.pem \\ +--service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \\ +--etcd-cafile=/opt/kubernetes/ssl/ca.pem \\ +--etcd-certfile=/opt/kubernetes/ssl/server.pem \\ +--etcd-keyfile=/opt/kubernetes/ssl/server-key.pem \\ +--requestheader-client-ca-file=/opt/kubernetes/ssl/ca.pem \\ +--requestheader-extra-headers-prefix=X-Remote-Extra- \\ +--requestheader-group-headers=X-Remote-Group \\ +--requestheader-username-headers=X-Remote-User \\ +--proxy-client-cert-file=/opt/kubernetes/ssl/metrics-server.pem \\ +--proxy-client-key-file=/opt/kubernetes/ssl/metrics-server-key.pem \\ +--runtime-config=api/all=true \\ +--audit-log-maxage=30 \\ +--audit-log-maxbackup=3 \\ +--audit-log-maxsize=100 \\ +--audit-log-truncate-enabled=true \\ +--audit-log-path=/var/log/kubernetes/k8s-audit.log" +EOF + +cat </usr/lib/systemd/system/kube-apiserver.service +[Unit] +Description=Kubernetes API Server +Documentation=https://github.com/kubernetes/kubernetes + +[Service] +EnvironmentFile=-/opt/kubernetes/cfg/kube-apiserver +ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable kube-apiserver +systemctl restart kube-apiserver +``` + +```bash +# 创建生成 kube-controller-manager 配置文件脚本 +$ vim controller-manager.sh +``` + +```bash +#!/bin/bash + +MASTER_ADDRESS=${1:-"127.0.0.1"} + +cat </opt/kubernetes/cfg/kube-controller-manager +KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=true \\ +--v=2 \\ +--master=${MASTER_ADDRESS}:8080 \\ +--leader-elect=true \\ +--bind-address=0.0.0.0 \\ +--service-cluster-ip-range=10.10.0.0/16 \\ +--cluster-name=kubernetes \\ +--cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \\ +--cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem \\ +--service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \\ +--experimental-cluster-signing-duration=87600h0m0s \\ +--feature-gates=RotateKubeletServerCertificate=true \\ +--feature-gates=RotateKubeletClientCertificate=true \\ +--allocate-node-cidrs=true \\ +--cluster-cidr=10.20.0.0/16 \\ +--root-ca-file=/opt/kubernetes/ssl/ca.pem" +EOF + +cat </usr/lib/systemd/system/kube-controller-manager.service +[Unit] +Description=Kubernetes Controller Manager +Documentation=https://github.com/kubernetes/kubernetes + +[Service] +EnvironmentFile=-/opt/kubernetes/cfg/kube-controller-manager +ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable kube-controller-manager +systemctl restart kube-controller-manager +``` + +```bash +# 创建生成 kube-scheduler 配置文件脚本 +$ vim scheduler.sh +``` + +```bash +#!/bin/bash + +MASTER_ADDRESS=${1:-"127.0.0.1"} + +cat </opt/kubernetes/cfg/kube-scheduler +KUBE_SCHEDULER_OPTS="--logtostderr=true \\ +--v=2 \\ +--master=${MASTER_ADDRESS}:8080 \\ +--address=0.0.0.0 \\ +--leader-elect" +EOF + +cat </usr/lib/systemd/system/kube-scheduler.service +[Unit] +Description=Kubernetes Scheduler +Documentation=https://github.com/kubernetes/kubernetes + +[Service] +EnvironmentFile=-/opt/kubernetes/cfg/kube-scheduler +ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable kube-scheduler +systemctl restart kube-scheduler +``` + +```bash +# 添加执行权限 +$ chmod +x *.sh + +$ cp /data/ssl/token.csv /opt/kubernetes/cfg/ + +# copy token.csv 和 master 配置到 k8s-master2 k8s-master3 机器上 +$ scp /data/ssl/token.csv root@k8s-master2:/opt/kubernetes/cfg +$ scp /data/ssl/token.csv root@k8s-master3:/opt/kubernetes/cfg +$ scp apiserver.sh controller-manager.sh scheduler.sh root@k8s-master2:/data/k8s-master +$ scp apiserver.sh controller-manager.sh scheduler.sh root@k8s-master3:/data/k8s-master + +# 生成 master配置文件并运行 +$ ./apiserver.sh 192.168.0.216 https://192.168.0.216:2379,https://192.168.0.217:2379,https://192.168.0.218:2379 +$ ./controller-manager.sh 127.0.0.1 +$ ./scheduler.sh 127.0.0.1 + +# 查看master三个服务是否正常运行 +$ ps -ef | grep kube +$ netstat -ntpl | grep kube- +``` + +> 登陆到 `k8s-master2` 操作 + +```bash +$ cd /data/k8s-master + +# 生成 master配置文件并运行 +$ ./apiserver.sh 192.168.0.217 https://192.168.0.216:2379,https://192.168.0.217:2379,https://192.168.0.218:2379 +$ ./controller-manager.sh 127.0.0.1 +$ ./scheduler.sh 127.0.0.1 + +# 查看master三个服务是否正常运行 +$ ps -ef | grep kube +$ netstat -ntpl | grep kube- +``` + +> 登陆到 `k8s-master3` 操作 + +```bash +$ cd /data/k8s-master + +# 生成 master配置文件并运行 +$ ./apiserver.sh 192.168.0.218 https://192.168.0.216:2379,https://192.168.0.217:2379,https://192.168.0.218:2379 +$ ./controller-manager.sh 127.0.0.1 +$ ./scheduler.sh 127.0.0.1 + +# 查看master三个服务是否正常运行 +$ ps -ef | grep kube +$ netstat -ntpl | grep kube- +``` + +```bash +# 随便登陆一台master查看集群健康状态 +$ kubectl get cs + +NAME STATUS MESSAGE ERROR +scheduler Healthy ok +controller-manager Healthy ok +etcd-2 Healthy {"health":"true"} +etcd-1 Healthy {"health":"true"} +etcd-0 Healthy {"health":"true"} +``` + +### 3.7 配置kubelet证书自动续期 + +创建自动批准相关 CSR 请求的 ClusterRole + +```bash +$ vim tls-instructs-csr.yaml +``` + +```yaml +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: system:certificates.k8s.io:certificatesigningrequests:selfnodeserver +rules: +- apiGroups: ["certificates.k8s.io"] + resources: ["certificatesigningrequests/selfnodeserver"] + verbs: ["create"] +``` + +```bash +# 部署 +$ kubectl apply -f tls-instructs-csr.yaml +``` + +自动批准 kubelet-bootstrap 用户 TLS bootstrapping 首次申请证书的 CSR 请求 + +```bash +$ kubectl create clusterrolebinding node-client-auto-approve-csr --clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient --user=kubelet-bootstrap +``` + +自动批准 system:nodes 组用户更新 kubelet 自身与 apiserver 通讯证书的 CSR 请求 + +```bash +$ kubectl create clusterrolebinding node-client-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient --group=system:nodes +``` + +自动批准 system:nodes 组用户更新 kubelet 10250 api 端口证书的 CSR 请求 + +```bash +$ kubectl create clusterrolebinding node-server-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeserver --group=system:nodes +``` + +### 3.8 配置Node组件并运行 + +首先我们先了解下 `kubelet` 中 `kubelet.kubeconfig` 配置是如何生成? + +`kubelet.kubeconfig` 配置是通过 `TLS Bootstrapping` 机制生成,下面是生成的流程图。 + +![](/img/bootstrap-1.png) + +> 登陆到 `k8s-master1` `k8s-master2` `k8s-master3` 操作 + +```bash +# 创建 node 节点生成配置脚本目录 +$ mkdir /data/k8s-node +``` + +> 登陆到 `k8s-master1` 操作 + +```bash +# 创建生成 kubelet 配置脚本 +$ vim kubelet.sh +``` + +```bash +#!/bin/bash + +DNS_SERVER_IP=${1:-"10.10.0.2"} +HOSTNAME=${2:-"`hostname`"} +CLUETERDOMAIN=${3:-"cluster.local"} + +cat </opt/kubernetes/cfg/kubelet.conf +KUBELET_OPTS="--logtostderr=true \\ +--v=2 \\ +--hostname-override=${HOSTNAME} \\ +--kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \\ +--bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \\ +--config=/opt/kubernetes/cfg/kubelet-config.yml \\ +--cert-dir=/opt/kubernetes/ssl \\ +--network-plugin=cni \\ +--cni-conf-dir=/etc/cni/net.d \\ +--cni-bin-dir=/opt/cni/bin \\ +--pod-infra-container-image=yangpeng2468/google_containers-pause-amd64:3.2" +EOF + +cat </opt/kubernetes/cfg/kubelet-config.yml +kind: KubeletConfiguration # 使用对象 +apiVersion: kubelet.config.k8s.io/v1beta1 # api版本 +address: 0.0.0.0 # 监听地址 +port: 10250 # 当前kubelet的端口 +readOnlyPort: 10255 # kubelet暴露的端口 +cgroupDriver: cgroupfs # 驱动,要于docker info显示的驱动一致 +clusterDNS: + - ${DNS_SERVER_IP} +clusterDomain: ${CLUETERDOMAIN} # 集群域 +failSwapOn: false # 关闭swap + +# 身份验证 +authentication: + anonymous: + enabled: false + webhook: + cacheTTL: 2m0s + enabled: true + x509: + clientCAFile: /opt/kubernetes/ssl/ca.pem + +# 授权 +authorization: + mode: Webhook + webhook: + cacheAuthorizedTTL: 5m0s + cacheUnauthorizedTTL: 30s + +# Node 资源保留 +evictionHard: + imagefs.available: 15% + memory.available: 1G + nodefs.available: 10% + nodefs.inodesFree: 5% +evictionPressureTransitionPeriod: 5m0s + +# 镜像删除策略 +imageGCHighThresholdPercent: 85 +imageGCLowThresholdPercent: 80 +imageMinimumGCAge: 2m0s + +# 旋转证书 +rotateCertificates: true # 旋转kubelet client 证书 +featureGates: + RotateKubeletServerCertificate: true + RotateKubeletClientCertificate: true + +maxOpenFiles: 1000000 +maxPods: 110 +EOF + +cat </usr/lib/systemd/system/kubelet.service +[Unit] +Description=Kubernetes Kubelet +After=docker.service +Requires=docker.service + +[Service] +EnvironmentFile=-/opt/kubernetes/cfg/kubelet.conf +ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS +Restart=on-failure +KillMode=process + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable kubelet +systemctl restart kubelet +``` + +```bash +# 创建生成 kube-proxy 配置脚本 +$ vim proxy.sh +``` + +```bash +#!/bin/bash + +HOSTNAME=${1:-"`hostname`"} + +cat </opt/kubernetes/cfg/kube-proxy.conf +KUBE_PROXY_OPTS="--logtostderr=true \\ +--v=2 \\ +--config=/opt/kubernetes/cfg/kube-proxy-config.yml" +EOF + +cat </opt/kubernetes/cfg/kube-proxy-config.yml +kind: KubeProxyConfiguration +apiVersion: kubeproxy.config.k8s.io/v1alpha1 +address: 0.0.0.0 # 监听地址 +metricsBindAddress: 0.0.0.0:10249 # 监控指标地址,监控获取相关信息 就从这里获取 +clientConnection: + kubeconfig: /opt/kubernetes/cfg/kube-proxy.kubeconfig # 读取配置文件 +hostnameOverride: ${HOSTNAME} # 注册到k8s的节点名称唯一 +clusterCIDR: 10.10.0.0/16 # service IP范围 +mode: iptables # 使用iptables模式 + +# 使用 ipvs 模式 +#mode: ipvs # ipvs 模式 +#ipvs: +# scheduler: "rr" +#iptables: +# masqueradeAll: true +EOF + +cat </usr/lib/systemd/system/kube-proxy.service +[Unit] +Description=Kubernetes Proxy +After=network.target + +[Service] +EnvironmentFile=-/opt/kubernetes/cfg/kube-proxy.conf +ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable kube-proxy +systemctl restart kube-proxy +``` + +```bash +# 生成 node 配置文件 +$ ./kubelet.sh 10.10.0.2 k8s-master1 cluster.local +$ ./proxy.sh k8s-master1 + +# 查看服务是否启动 +$ netstat -ntpl | egrep "kubelet|kube-proxy" + +# copy kubelet.sh proxy.sh 脚本到 k8s-master2 k8s-master3 机器上 +$ scp kubelet.sh proxy.sh root@k8s-master2:/data/k8s-node +$ scp kubelet.sh proxy.sh root@k8s-master3:/data/k8s-node +``` + +> 登陆到 `k8s-master2` 操作 + +```bash +$ cd /data/k8s-node + +# 生成 node 配置文件 +$ ./kubelet.sh 10.10.0.2 k8s-master2 cluster.local +$ ./proxy.sh k8s-master2 + +# 查看服务是否启动 +$ netstat -ntpl | egrep "kubelet|kube-proxy" +``` + +> 登陆到 `k8s-master3` 操作 + +```bash +$ cd /data/k8s-node + +# 生成 node 配置文件 +$ ./kubelet.sh 10.10.0.2 k8s-master3 cluster.local +$ ./proxy.sh k8s-master3 + +# 查看服务是否启动 +$ netstat -ntpl | egrep "kubelet|kube-proxy" +``` + +```bash +# 随便登陆一台master机器查看node节点是否添加成功 +$ kubectl get node + +NAME STATUS ROLES AGE VERSION +k8s-master1 NoReady 4d4h v1.18.2 +k8s-master2 NoReady 4d4h v1.18.2 +k8s-master3 NoReady 4d4h v1.18.2 +``` +> 上面 Node 节点处理 `NoReady` 状态,是因为目前还没有安装网络组件,下文安装网络组件。 + +### 3.9 安装calico网络,使用IPIP模式 + +> 登陆到 `k8s-master1` 操作 + +下载 `Calico Version v3.14.0` Yaml 文件 + +```bash +# 存放etcd yaml文件 +$ mkdir -p ~/yaml/calico + +$ cd ~/yaml/calico + +# 注意:下面是基于自建etcd做为存储的配置文件 +$ curl https://docs.projectcalico.org/manifests/calico-etcd.yaml -O +``` + +#### `calico-etcd.yaml` 需要修改如下配置: + +##### Secret 配置修改 + +```yaml +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: calico-etcd-secrets + namespace: kube-system +data: + etcd-key: (cat /opt/kubernetes/ssl/server-key.pem | base64 -w 0) # 将输出结果填写在这里 + etcd-cert: (cat /opt/kubernetes/ssl/server.pem | base64 -w 0) # 将输出结果填写在这里 + etcd-ca: (cat /opt/kubernetes/ssl/ca.pem | base64 -w 0) # 将输出结果填写在这里 +``` + +##### `ConfigMap` 配置修改 + +```yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: calico-config + namespace: kube-system +data: + etcd_endpoints: "https://192.168.0.216:2379,https://192.168.0.217:2379,https://192.168.0.218:2379" + etcd_ca: "/calico-secrets/etcd-ca" + etcd_cert: "/calico-secrets/etcd-cert" + etcd_key: "/calico-secrets/etcd-key" +``` + +关于ConfigMap部分主要参数如下: +- `etcd_endpoints`:Calico使用etcd来保存网络拓扑和状态,该参数指定etcd的地址,可以使用K8S Master所用的etcd,也可以另外搭建。 +- `calico_backend`:Calico的后端,默认为bird。 +- `cni_network_config`:符合CNI规范的网络配置,其中type=calico表示,Kubelet从 CNI_PATH(默认为/opt/cni/bin)找名为calico的可执行文件,用于容器IP地址的分配。 +- etcd 如果配置`TLS安全认证`,则还需要指定相应的`ca`、`cert`、`key`等文件 + +##### 修改 Pods 使用的 `IP 网段`,默认使用 `192.168.0.0/16` 网段 + +```yaml + - name: CALICO_IPV4POOL_CIDR + value: "10.20.0.0/16" +``` + +##### 配置网卡自动发现规则 + +在 DaemonSet calico-node `env` 中添加网卡发现规则 + +```yaml + # 定义ipv4自动发现网卡规则 + - name: IP_AUTODETECTION_METHOD + value: "interface=eth.*" + # 定义ipv6自动发现网卡规则 + - name: IP6_AUTODETECTION_METHOD + value: "interface=eth.*" +``` + +##### Calico 模式设置 + +```yaml + # Enable IPIP + - name: CALICO_IPV4POOL_IPIP + value: "Always" +``` + +Calico 有两种网络模式:`BGP` 和 `IPIP` +- 使用 `IPIP` 模式时,设置 `CALICO_IPV4POOL_IPIP="always"`,IPIP 是一种将各Node的路由之间做一个`tunnel`,再把两个网络连接起来的模式,启用IPIP模式时,Calico将在各Node上创建一个名为 `tunl0` 的虚拟网络接口。 +- 使用 `BGP` 模式时,设置 `CALICO_IPV4POOL_IPIP="off"` + +##### 错误解决方法 + +`错误`: [ERROR][8] startup/startup.go 146: failed to query kubeadm's config map error=Get https://10.10.0.1:443/api/v1/namespaces/kube-system/configmaps/kubeadm-config?timeout=2s: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) + +`原因`:Node工作节点连接不到 `apiserver` 地址,检查一下calico配置文件,要把apiserver的IP和端口配置上,如果不配置的话,calico默认将设置默认的calico网段和443端口。字段名:`KUBERNETES_SERVICE_HOST`、`KUBERNETES_SERVICE_PORT`、`KUBERNETES_SERVICE_PORT_HTTPS`。 + +`解决方法`: + +在 DaemonSet calico-node `env` 中添加环境变量 + +```yaml + - name: KUBERNETES_SERVICE_HOST + value: "lb.ypvip.com.cn" + - name: KUBERNETES_SERVICE_PORT + value: "6443" + - name: KUBERNETES_SERVICE_PORT_HTTPS + value: "6443" +``` + +修改完 `calico-etcd.yaml` 后,执行部署 + +```bash +# 部署 +$ kubectl apply -f calico-etcd.yaml + +# 查看 calico pods +$ kubectl get pods -n kube-system | grep calico + +# 查看 node 是否正常,现在 node 服务正常了 +$ kubectl get node + +NAME STATUS ROLES AGE VERSION +k8s-master1 Ready 4d4h v1.18.2 +k8s-master2 Ready 4d4h v1.18.2 +k8s-master3 Ready 4d4h v1.18.2 +``` + +### 3.10 集群CoreDNS部署 + +> 登陆到 `k8s-master1` 操作 + +`deploy.sh` 是一个便捷的脚本,用于生成coredns yaml 配置。 + +```bash +# 安装依赖 jq 命令 +$ yum install jq -y + +$ cd ~/yaml + +# 下载 CoreDNS 项目 +$ git clone https://github.com/coredns/deployment.git +$ cd coredns/deployment/kubernetes +``` + +默认情况下 `CLUSTER_DNS_IP` 是自动获取kube-dns的集群ip的,但是由于没有部署kube-dns所以只能手动指定一个集群ip。 + +```yaml +111 if [[ -z $CLUSTER_DNS_IP ]]; then +112 # Default IP to kube-dns IP +113 # CLUSTER_DNS_IP=$(kubectl get service --namespace kube-system kube-dns -o jsonpath="{.spec.clusterIP}") +114 CLUSTER_DNS_IP=10.10.0.2 +``` + +```bash +# 查看执行效果,并未开始部署 +$ ./deploy.sh + +# 执行部署 +$ ./deploy.sh | kubectl apply -f - + +# 查看 Coredns +$ kubectl get svc,pods -n kube-system| grep coredns +``` + +测试 Coredns 解析 + +```bash +# 创建一个 busybox Pod +$ vim busybox.yaml +``` + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: busybox + namespace: default +spec: + containers: + - name: busybox + image: busybox:1.28.4 + command: + - sleep + - "3600" + imagePullPolicy: IfNotPresent + restartPolicy: Always +``` + +```bash +# 部署 +$ kubectl apply -f busybox.yaml + +# 测试解析,下面是解析正常 +$ kubectl exec -i busybox -n default nslookup kubernetes + +Server: 10.10.0.2 +Address 1: 10.10.0.2 kube-dns.kube-system.svc.cluster.local + +Name: kubernetes +Address 1: 10.10.0.1 kubernetes.default.svc.cluster.local +``` + +### 3.11 部署集群监控服务 Metrics Server + +> 登陆到 `k8s-master1` 操作 + +```bash +$ cd ~/yaml + +# 拉取 v0.3.6 版本 +$ git clone https://github.com/kubernetes-sigs/metrics-server.git -b v0.3.6 +$ cd metrics-server/deploy/1.8+ +``` + +只修改 `metrics-server-deployment.yaml` 配置文件 + +```bash +# 下面是修改前后比较差异 +$ git diff metrics-server-deployment.yaml + +diff --git a/deploy/1.8+/metrics-server-deployment.yaml b/deploy/1.8+/metrics-server-deployment.yaml +index 2393e75..2139e4a 100644 +--- a/deploy/1.8+/metrics-server-deployment.yaml ++++ b/deploy/1.8+/metrics-server-deployment.yaml +@@ -29,8 +29,19 @@ spec: + emptyDir: {} + containers: + - name: metrics-server +- image: k8s.gcr.io/metrics-server-amd64:v0.3.6 +- imagePullPolicy: Always ++ image: yangpeng2468/metrics-server-amd64:v0.3.6 ++ imagePullPolicy: IfNotPresent ++ resources: ++ limits: ++ cpu: 400m ++ memory: 1024Mi ++ requests: ++ cpu: 50m ++ memory: 50Mi ++ command: ++ - /metrics-server ++ - --kubelet-insecure-tls ++ - --kubelet-preferred-address-types=InternalIP + volumeMounts: + - name: tmp-dir + mountPath: /tmp +``` + +```bash +# 部署 +$ kubectl apply -f . + +# 验证 +$ kubectl top node + +NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% +k8s-master1 72m 7% 1002Mi 53% +k8s-master2 121m 3% 1852Mi 12% +k8s-master3 300m 3% 1852Mi 20% + +# 内存单位 Mi=1024*1024字节 M=1000*1000字节 +# CPU单位 1核=1000m 即 250m=1/4核 +``` + +### 3.12 部署 Kubernetes Dashboard + +Kubernetes Dashboard 部署,请参考 [K8S Dashboard 2.0 部署](https://www.yp14.cn/2020/05/16/K8S-Dashboard-2-0-%E9%83%A8%E7%BD%B2%E5%B9%B6%E4%BD%BF%E7%94%A8-Ingress-Nginx-%E6%8F%90%E4%BE%9B%E8%AE%BF%E9%97%AE%E5%85%A5%E5%8F%A3/) 文章。 + +## 结束语 + +Kubernetes v1.18.2 二进制部署,作者测试过无坑。这篇部署文章完成可以直接用于生产环境部署。全方位包含整个 Kubernetes 组件部署。 \ No newline at end of file