一、环境描述
主机 | IP 地址 | 节点 |
K8S-node1 | 192.168.66.13 | Master、node、etcd |
K8S-node2 | 192.168.66.14 | Master、Node、etcd |
K8S-node3 | 192.168.66.15 | Master、Node、etcd |
- 高可用一般建议大于等于3台master的奇数台
- 如果资源充足,那么使用多台主机搭建高可用集群
- 3台Master、3台ETCD、N台Node
- 注意:本文3Master,3Node节点,3ETCD集群节点都在上面提供的这三台服务器上搭建部署!!
1、核心组件
- etcd:保存整个集群的状态和数据
- kube-apiserver:提供资源操作的唯一入口,并提供认证、授权、访问控制、API注册和服务发现等机制
- kube-controller-manager:负责维护集群的状态,比如故障检测、自动扩容、滚动更新等
- kube-scheduler:负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上
- kubelet:负责维持容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理
- Container runtime:负责镜像管理以及Pod和容器的真正运行(CRI),默认运行容器时为Docker
- kube-proxy:负责为Service提供cluster内部的服务发现和负责均衡
2、推荐插件
- kube-dns 负责为整个集群提供 DNS 服务
- Ingress Controller 为服务提供外网入口
- Heapster 提供资源监控
- Dashboard 提供 GUI
- Federation 提供跨可用区的集群
- Fluentd-elasticsearch 提供集群日志采集、存储与查询
3、各节点组件
Master节点:
- kube-apiserver
- kube-Scheduler
- kube-controller-manager
- ETCD
- Flannel
- Docker
Node节点
- Docker
- kubelet
- kube-proxy
- ETCD
- Flannel
4、软件获取
- 由于咱们网络环境问题,有些包无法下载的话,我会提供百度网盘,将本文档中所需的所有程序包共享出来
4.1:k8S获取
- kubernetes-1.17.1:https://github.com/kubernetes/kubernetes/releases
- 注意:不管是Master节点还是Node节点,都使用的是Server端程序包
- 下载client程序包,安装kubectl命令行工具需要使用该程序包

4.2:ETCD获取
- etcd是一个开源的分布式键值存储,为Container Linux集群提供共享配置和服务发现
- etcd-3.3.18:https://github.com/etcd-io/etcd/releases

4.3:flannel获取
- Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。
- flannel-3.3.18:https://github.com/coreos/flannel/releases
5、架构规划
节点 | 主机名 | IP地址 | 所需程序 |
Master节点 | K8S-node1 | 192.168.66.13 | kube-apiserver、kube-Scheduler、kube-controller-manager、Flannel、Docker、ETCD |
Node节点1 | K8S-node2 | 192.168.66.14 | kubelet 、kube-proxy 、Flannel、Docker、ETCD |
Node节点2 | K8S-node3 | 192.168.66.15 | kubelet 、kube-proxy 、Flannel、Docker、ETCD |
6、新版变化
- K8S-v1.16以上版本中已经启用
extensions/v1beta1
- K8S-v1.16以上版本无法兼容Dashboardv1.10版本
- Kubernetes v1.16 将调整以下四种服务的 API:
- NetworkPolicy
- PodSecurityPolicy
- DaemonSet、Deployment、StatefulSet 和 ReplicaSet
- Ingress
NetworkPolicy
将不再从 v1.16 中的 extensions/v1beta1 提供服务- 迁移到从 v1.8 开始提供的 http://networking.k8s.io/v1 API。现有的持久化数据可以通过 http://networking.k8s.io/v1 API 检索、更新
PodSecurityPolicy
在 v1.16 中将不再从 extensions/v1beta1 提供服务- 迁移到从 v1.10 开始提供的 policy/v1beta1 API。现有的持久化数据可以通过 policy/v1beta1 API 检索、更新
DaemonSet、Deployment、StatefulSet
和ReplicaSet
在 v1.16 中将不再从extensions/v1beta1
、apps/v1beta1
或apps/v1beta2
提供服务- 迁移到从 v1.9 开始提供的
apps/v1 API
。现有的持久化数据可以通过apps/v1
API 检索、更新
- 迁移到从 v1.9 开始提供的
- Ingress 在 v1.16 中将不再从 extensions/v1beta1 提供服务
- 迁移到 http://networking.k8s.io/v1beta1 API。现有的持久化数据可以通过 http://networking.k8s.io/v1beta1 API 检索、更新
需要怎么做?
- 更改 YAML 文件以引用新的 API
- 更新自定义集成和控制器来调用新的 API
- 更新第三方工具(ingress controllers、持续交付系统)来调用新的 API
- 语法:
kubectl convert -f <file> --output-version <group>/<version>
- 例如:
apiVersion: extensions/v1beta1
--apiVersion: apps/v1beta1
- 例如,要将旧的 Deployment 转换为 apps/v1,可以执行:
kubectl convert -f ./my-deployment.yaml --output-version apps/v1
。注意,这可能使用非理想的默认值。
二、环境初始化
- 三台主机都需要分别执行环境初始化操作,这里以k8s-node1主机为例
- 三台主机网络配置为静态IP
- 如无特殊说明,以下操作均在K8S-node1节点操作
[root@localhost ~]# vi /etc/sysconfig/network-scripts/ifcfg-ens33
TYPE="Ethernet"
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="static"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="ens33"
UUID="b268c38c-17f1-432e-bb27-008614a40fb2"
DEVICE="ens33"
ONBOOT="yes"
IPADDR="192.168.66.13"
NETMASK="255.255.255.0"
GATEWAY="192.168.66.2"
DNS1="114.114.114.114"
[root@localhost ~]# systemctl restart network
1、主机名配置
[root@localhost ~]# hostnamectl set-hostname k8s-node1
2、HOSTS配置
[root@k8s-node1 ~]# vi /etc/hosts
192.168.66.13 k8s-node1
192.168.66.14 k8s-node2
192.168.66.15 k8s-node3
3、关闭swap
- 永久关闭,禁用swap这行
[root@k8s-node1 ~]# vi /etc/fstab
#/dev/mapper/centos-swap swap swap defaults 0 0
4、关闭防火墙
- 注意:一定确认关闭了firewalld或者iptables;因为K8S在后面部署过程中会自动生成一大堆的iptables规则
4.1:关闭firewalld
[root@k8s-node1 ~]# systemctl stop firewalld
[root@k8s-node1 ~]# systemctl disable firewall
4.2:关闭SELinux
# 查看SELinux状态
[root@k8s-node1 ~]# sestatus
[root@k8s-node1 ~]# setenforce 0
[root@k8s-node1 ~]# getenforce
[root@k8s-node1 ~]# sed -ri 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
5、常用命令
[root@k8s-node1 ~]# yum -y install gcc gcc-c++ libaio make cmake zlib-devel openssl-devel pcre pcre-devel wget git curl lynx lftp mailx mutt rsync ntp net-tools vim lrzsz screen sysstat yum-plugin-security yum-utils createrepo bash-completion zip unzip bzip2 tree tmpwatch pinfo man-pages lshw pciutils gdisk system-storage-manager git gdbm-devel sqlite-devel
6、时间同步
[root@k8s-node1 ~]# systemctl start ntpd
[root@k8s-node1 ~]# systemctl enable ntpd
[root@k8s-node1 ~]# echo "*/5 * * * * ntpdate time1.aliyun.com &> /dev/null && hwclock -w" >> /var/spool/cron/root
#调整系统 TimeZone
[root@k8s-node1 ~]# timedatectl set-timezone Asia/Shanghai
#将当前的 UTC 时间写入硬件时钟
[root@k8s-node1 ~]# timedatectl set-local-rtc 0
#重启依赖于系统时间的服务
[root@k8s-node1 ~]# systemctl restart rsyslog
[root@k8s-node1 ~]# systemctl restart crond
7、配置免密
- K8S-node1实现ssh免密登入其他节点
[root@k8s-node1 ~]# ssh-keygen -t rsa
[root@k8s-node1 ~]# ssh-copy-id root@192.168.66.13
[root@k8s-node1 ~]# ssh-copy-id root@192.168.66.14
[root@k8s-node1 ~]# ssh-copy-id root@192.168.66.15
[root@k8s-node1 ~]# ssh-copy-id k8s-node1
[root@k8s-node1 ~]# ssh-copy-id k8s-node2
[root@k8s-node1 ~]# ssh-copy-id k8s-node3
8、优化内核
- 所有节点执行加载内核模块
- 参数解释
net.bridge.bridge-nf-call-iptables=1
#如果net.bridge.bridge-nf-call-iptables=1,也就意味着二层的网桥在转发包时也会被iptables的FORWARD规则所过滤,这样就会出现L3层的iptables rules去过滤L2的帧的问题net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
#开启转发网络数据包net.ipv4.tcp_tw_recycle=0
vm.swappiness=0
#禁止使用 swap 空间,只有当系统 OOM 时才允许使用它vm.overcommit_memory=1
#不检查物理内存是否够用vm.panic_on_oom=0
#开启OOMfs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=1048576
fs.file-max=52706963
fs.nr_open=52706963
net.ipv6.conf.all.disable_ipv6=1
net.netfilter.nf_conntrack_max=2310720
#关闭防火墙会提示无该文件或目录,忽略即可
- 必须关闭
tcp_tw_recycle
,否则和 NAT 冲突,会导致服务不通;关闭 IPV6,防止触发 docker BUG;
[root@k8s-node1 ~]# cat > /etc/sysctl.d/kubernetes.conf <<EOF
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
net.ipv4.tcp_tw_recycle=0
vm.swappiness=0
vm.overcommit_memory=1
vm.panic_on_oom=0
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=1048576
fs.file-max=52706963
fs.nr_open=52706963
net.ipv6.conf.all.disable_ipv6=1
net.netfilter.nf_conntrack_max=2310720
EOF
#执行sysctl -p会出现如下报错:
sysctl: cannot stat /proc/sys/net/netfilter/nf_conntrack_max: 没有那个文件或目录
#解决方案:
[root@k8s-master1 ~]# cd /etc/modules-load.d/
[root@k8s-master1 ~]# vim netfilter.conf
nf_conntrack
[root@k8s-node1 ~]# for i in 192.168.66.{14,15};do scp /etc/modules-load.d/netfilter.conf root@$i:/etc/modules-load.d/;done
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do ssh root@$i "reboot";done
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "modprobe br_netfilter;sysctl -p /etc/sysctl.d/kubernetes.conf";done
9、设置 rsyslogd和systemd journald
- systemd 的 journald 是 Centos 7 缺省的日志记录工具,它记录了所有系统、内核、Service Unit 的日志。
- 相比 systemd,journald 记录的日志有如下优势:
- 1.可以记录到内存或文件系统;(默认记录到内存,对应的位置为
/run/log/jounal
) - 2.可以限制占用的磁盘空间、保证磁盘剩余空间;
- 3.可以限制日志文件大小、保存的时间;
- 1.可以记录到内存或文件系统;(默认记录到内存,对应的位置为
- journald 默认将日志转发给 rsyslog,这会导致日志写了多份,
/var/log/messages
中包含了太多无关日志,不方便后续查看,同时也影响系统性能。
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "mkdir /var/log/journal";done
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "mkdir /etc/systemd/journald.conf.d";done
[root@k8s-node1 ~]# cat > /etc/systemd/journald.conf.d/99-prophet.conf <<EOF
[Journal]
# 持久化保存到磁盘
Storage=persistent
# 压缩历史日志
Compress=yes
SyncIntervalSec=5m
RateLimitInterval=30s
RateLimitBurst=1000
# 最大占用空间 10G
SystemMaxUse=10G
# 单日志文件最大 200M
SystemMaxFileSize=200M
# 日志保存时间 2 周
MaxRetentionSec=2week
# 不将日志转发到 syslog
ForwardToSyslog=no
EOF
[root@k8s-node1 ~]# for i in 192.168.66.{14,15};do echo $i;scp /etc/systemd/journald.conf.d/99-prophet.conf root@$i:/etc/systemd/journald.conc.d/;done
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "systemctl restart systemd-journald";done
10、将可执行文件路径到PATH变量中
- 在每台机器上添加环境变量:/opt/k8s/bin
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "echo 'PATH=/opt/k8s/bin:$PATH' >>/root/.bashrc";done
11、创建相关目录
- 在每台机器上创建该目录
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "mkdir -p /opt/k8s/{bin,work}";done
12、升级内核
- 三节点分别执行
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm";done
- 安装完成后检查 /boot/grub2/grub.cfg 中对应内核 menuentry 中是否包含 initrd16 配置,如果没有,再安装一次!
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "yum --enablerepo=elrepo-kernel install -y kernel-lt";done
- 设置开机从新内核启动
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "grub2-set-default 0";done
13、关闭 NUMA
- 三节点都执行
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "cp /etc/default/grub{,.bak}";done
#在三节点的grub配置文件的最后添加以下参数进去
[root@k8s-node1 ~]# vim /etc/default/grub
#在GRUB_CMDLINE_LINUX 一行添加 numa=off 参数,如下所示:

- 三节点重新生成 grub2 配置文件:
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "cp /boot/grub2/grub.cfg{,.bak}";done
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "grub2-mkconfig -o /boot/grub2/grub.cfg
";done

14、添加Docker用户
- 在每台服务器上添加Docker用户
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "useradd -m docker";done
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "id docker";done
15、检查各节点是否适合运行Docker
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "curl https://raw.githubusercontent.com/docker/docker/master/contrib/check-config.sh > check-config.sh";done
[root@k8s-node1 ~]# for i in 192.168.66.{13..15};do echo $i;ssh root@$i "bash /root/check-config.sh";done
16、配置全局环境变量
- 注意:集群IP请根据自己服务器的实际IP进行修改,修改为自己的
[root@k8s-node1 ~]# vim /etc/profile
#文件最后添加进去
#----------------------------K8S-----------------------------#
# 生成 EncryptionConfig 所需的加密 key
export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
# Master集群各机器 IP 数组
export NODE_IPS=(192.168.66.13 192.168.66.14 192.168.66.15)
# Master集群各IP对应的 主机名数组
export NODE_NAMES=(k8s-node1 k8s-node2 k8s-node3)
# etcd 集群服务地址列表
export ETCD_ENDPOINTS="https://192.168.66.13:2379,https://192.168.66.14:2379,https://192.168.66.15:2379"
# etcd 集群间通信的 IP 和端口
export ETCD_NODES="k8s-node1=https://192.168.66.13:2380,k8s-node2=https://192.168.66.14:2380,k8s-node3=https://192.168.66.15:2380"
# kube-apiserver 的反向代理(kube-nginx)地址端口
export KUBE_APISERVER="https://127.0.0.1:8443"
# 节点间互联网络接口名称,根据实际服务器网卡名称配置
export IFACE="ens33"
# etcd 数据目录
export ETCD_DATA_DIR="/data/k8s/etcd/data"
# etcd WAL 目录,建议是 SSD 磁盘分区,或者和 ETCD_DATA_DIR 不同的磁盘分区
export ETCD_WAL_DIR="/data/k8s/etcd/wal"
# k8s 各组件数据目录
export K8S_DIR="/data/k8s/k8s"
# docker 数据目录
export DOCKER_DIR="/data/k8s/docker"
## 以下参数一般不需要修改
# TLS Bootstrapping 使用的 Token,可以使用命令 head -c 16 /dev/urandom | od -An -t x | tr -d ' ' 生成
BOOTSTRAP_TOKEN="b02ca253fcb632320613b00b6246a12c"
# 最好使用 当前未用的网段 来定义服务网段和 Pod 网段
# 服务网段,部署前路由不可达,部署后集群内路由可达(kube-proxy 保证)
SERVICE_CIDR="10.254.0.0/16"
# Pod 网段,建议 /16 段地址,部署前路由不可达,部署后集群内路由可达(flanneld 保证)
CLUSTER_CIDR="172.30.0.0/16"
# 服务端口范围 (NodePort Range)
export NODE_PORT_RANGE="30000-32767"
# flanneld 网络配置前缀
export FLANNEL_ETCD_PREFIX="/kubernetes/network"
# kubernetes 服务 IP (一般是 SERVICE_CIDR 中第一个IP)
export CLUSTER_KUBERNETES_SVC_IP="10.254.0.1"
# 集群 DNS 服务 IP (从 SERVICE_CIDR 中预分配)
export CLUSTER_DNS_SVC_IP="10.254.0.2"
# 集群 DNS 域名(末尾不带点号)
export CLUSTER_DNS_DOMAIN="cluster.local"
# 将二进制目录 /opt/k8s/bin 加到 PATH 中
export PATH=/opt/k8s/bin:$PATH
- 将环境变量文件传给其他2个节点
[root@k8s-node1 ~]# scp /etc/profile root@192.168.66.14:/etc/
[root@k8s-node1 ~]# scp /etc/profile root@192.168.66.15:/etc/
- 三台机器分别执行以下命令是全局变量生效
[root@k8s-node1 ~]# source /etc/profile
- 检查是否生效
[root@k8s-node1 ~]# echo ${PATH}
/opt/k8s/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@k8s-node1 ~]# echo ${CLUSTER_DNS_DOMAIN}
cluster.local
三、配置CA证书
- 为确保安全,
kubernetes
系统各组件需要使用 x509 证书对通信进行加密和认证。 - CA (Certificate Authority) 是自签名的根证书,用来签名后续创建的其它证书。
- 本文档使用 CloudFlare的 PKI 工具集创建所有证书。
- 注意:如果没有特殊指明,本文档的所有操作均在k8s-node1节点上执行,然后远程分发文件和执行命令。
1、安装cfssl工具集
[root@k8s-node1 ~]# mkdir -p /opt/k8s/cert && cd /opt/k8s
[root@k8s-node1 k8s]# wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
[root@k8s-node1 k8s]# mv cfssl_linux-amd64 /opt/k8s/bin/cfssl
[root@k8s-node1 k8s]# wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
[root@k8s-node1 k8s]# mv cfssljson_linux-amd64 /opt/k8s/bin/cfssljson
[root@k8s-node1 k8s]# wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
[root@k8s-node1 k8s]# mv cfssl-certinfo_linux-amd64 /opt/k8s/bin/cfssl-certinfo
[root@k8s-node1 k8s]# chmod +x /opt/k8s/bin/*
[root@k8s-node1 k8s]# export PATH=/opt/k8s/bin:$PATH
2、创建根证书(CA)
- CA 证书是集群所有节点共享的,只需要创建一个 CA 证书,后续创建的所有证书都由它签名。
2.1:创建配置文件
- CA 配置文件用于配置根证书的使用场景 (profile) 和具体参数 (usage,过期时间、服务端认证、客户端认证、加密等),后续在签名其它证书时需要指定特定场景。
[root@k8s-node1 k8s]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}
EOF
signing
:表示该证书可用于签名其它证书,生成的ca.pem
证书中CA=TRUE
;server auth
:表示 client 可以用该该证书对 server 提供的证书进行验证;client auth
:表示 server 可以用该该证书对 client 提供的证书进行验证;
2.2:创建证书签名请求文件
[root@k8s-node1 work]# cat > ca-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
- CN:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name),浏览器使用该字段验证网站是否合法;
- O:Organization,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);
- kube-apiserver 将提取的 User、Group 作为 RBAC 授权的用户标识;
2.3:生成CA证书和私钥
[root@k8s-node1 work]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2020/01/15 10:16:28 [INFO] generating a new CA key and certificate from CSR
2020/01/15 10:16:28 [INFO] generate received request
2020/01/15 10:16:28 [INFO] received CSR
2020/01/15 10:16:28 [INFO] generating key: rsa-2048
2020/01/15 10:16:29 [INFO] encoded CSR
2020/01/15 10:16:29 [INFO] signed certificate with serial number 6966905140966238303269956622170363884073553390
[root@k8s-node1 work]# ls ca*
ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem
3、分发证书文件
- 将生成的 CA 证书、秘钥文件、配置文件拷贝到所有节点的
/etc/kubernetes/cert
目录下:
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p /etc/kubernetes/cert"
scp ca*.pem ca-config.json root@${node_ip}:/etc/kubernetes/cert
done
四、部署kubectl命令行工具
- kubectl 是 kubernetes 集群的命令行管理工具,本文档介绍安装和配置它的步骤。部署在所有K8S-Master集群节点服务器上。
- kubectl 默认从
~/.kube/config
文件读取 kube-apiserver 地址、证书、用户名等信息,如果没有配置,执行 kubectl 命令时可能会出错: - 注意:
- 1、如果没有特殊指明,本文档的所有操作k8s-node1节点上执行,然后远程分发文件和执行命令。
- 2、本文档只需要部署一次,生成的 kubeconfig 文件是通用的,可以拷贝到需要执行 kubeclt 命令的机器上。
1、下载和分发 kubectl 二进制文件
- 我这里直接将下载好的上传至服务器中
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# tar -zxvf kubernetes-client-linux-amd64.tar.gz
- 分发到所有使用的kubectl节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kubernetes/client/bin/kubectl root@${node_ip}:/opt/k8s/bin/
ssh root@${node_ip} "chmod +x /opt/k8s/bin/*"
done
2、创建admin证书和私钥
- kubectl 与 apiserver https 安全端口通信,apiserver 对提供的证书进行认证和授权。
- kubectl 作为集群的管理工具,需要被授予最高权限。这里创建具有最高权限的 admin 证书。
2.1:创建证书签名请求:
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# cat > admin-csr.json <<EOF
{
"CN": "admin",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "system:masters",
"OU": "4Paradigm"
}
]
}
EOF
- 为 system:masters,kube-apiserver 收到该证书后将请求的 Group 设置为 system:masters;
- 预定义的 ClusterRoleBinding cluster-admin 将 Group system:masters 与 Role cluster-admin 绑定,该 Role 授予所有 API的权限;
- 该证书只会被 kubectl 当做 client 证书使用,所以 hosts 字段为空;
2.2:生成证书和私钥
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes admin-csr.json | cfssljson -bare admin

[root@k8s-node1 work]# ls admin*
admin.csr admin-csr.json admin-key.pem admin.pem
3、创建 kubeconfig 文件
- kubeconfig 为 kubectl 的配置文件,包含访问 apiserver 的所有信息,如 apiserver 地址、CA 证书和自身使用的证书;
- 注意:确认全局环境变量都是生效的
- 设置集群参数
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# kubectl config set-cluster kubernetes \
--certificate-authority=/opt/k8s/work/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kubectl.kubeconfig

- 设置客户端认证参数
[root@k8s-node1 work]# kubectl config set-credentials admin \
--client-certificate=/opt/k8s/work/admin.pem \
--client-key=/opt/k8s/work/admin-key.pem \
--embed-certs=true \
--kubeconfig=kubectl.kubeconfig

- 设置上下文参数
[root@k8s-node1 work]# kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=admin \
--kubeconfig=kubectl.kubeconfig

- 设置默认上下文
[root@k8s-node1 work]# kubectl config use-context kubernetes --kubeconfig=kubectl.kubeconfig
Switched to context "kubernetes".
--certificate-authority
:验证 kube-apiserver 证书的根证书;--client-certificate
、--client-key
:刚生成的 admin 证书和私钥,连接 kube-apiserver 时使用;--embed-certs=true
:将 ca.pem 和 admin.pem 证书内容嵌入到生成的 kubectl.kubeconfig 文件中(不加时,写入的是证书文件路径);
4、分发kubeconfig文件
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p ~/.kube"
scp kubectl.kubeconfig root@${node_ip}:~/.kube/config
done
- 保存到用户的
~/.kube/config
文件;
5、确认kubectl已经可以使用

五、部署ETCD集群
etcd 是基于 Raft 的分布式 key-value 存储系统,由 CoreOS 开发,常用于服务发现、共享配置以及并发控制(如 leader 选举、分布式锁等)。kubernetes 使用 etcd 存储所有运行数据。
本文档介绍部署一个三节点高可用 etcd 集群的步骤:
- 下载和分发 etcd 二进制文件;
- 创建 etcd 集群各节点的 x509 证书,用于加密客户端(如 etcdctl) 与 etcd 集群、etcd 集群之间的数据流;
- 创建 etcd 的 systemd unit 文件,配置服务参数;
- 检查集群工作状态;
etcd 集群各节点的名称和 IP 如下:
- k8s-node1:192.168.66.13
- k8s-node2:192.168.66.14
- k8s-node3:192.168.66.15
1、下载和分发 etcd 二进制文件
[root@k8s-node1 ~]# cd /opt/k8s/work
- ETCD仓库地址:https://github.com/etcd-io/etcd/releases
- 由于网络原因我这里直接下载好包上传至服务器
1.1:解压安装
[root@k8s-node1 work]# tar -xvf etcd-v3.3.18-linux-amd64.tar.gz
1.2:分发各ETCD节点
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp etcd-v3.3.18-linux-amd64/etcd* root@${node_ip}:/opt/k8s/bin
ssh root@${node_ip} "chmod +x /opt/k8s/bin/*"
done
2、创建 etcd 证书和私钥
2.1:创建证书签名请求
- 注意:证书文件中的hosts配置为自己实际的ETCD集群节点IP地址,如果配置错误可能启动ETCD时出现以下错误:
error "remote error: tls: bad certificate", ServerName ""
[root@k8s-node1 work]# cat > etcd-csr.json <<EOF
{
"CN": "etcd",
"hosts": [
"127.0.0.1",
"192.168.66.13",
"192.168.66.14",
"192.168.66.15"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
- hosts 字段指定授权使用该证书的 etcd 节点 IP 或域名列表,这里将 etcd 集群的三个节点 IP 都列在其中;
2.2:生成证书和私钥
[root@k8s-node1 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes etcd-csr.json | cfssljson -bare etcd
[root@k8s-node1 work]# ls etcd*pem
etcd-key.pem etcd.pem
2.3:分发证书和私钥至各etcd节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p /etc/etcd/cert"
scp etcd*.pem root@${node_ip}:/etc/etcd/cert/
done
3、创建 etcd 的 systemd unit 模板文件
[root@k8s-node1 work]# cat > etcd.service.template <<EOF
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos
[Service]
Type=notify
WorkingDirectory=${ETCD_DATA_DIR}
ExecStart=/opt/k8s/bin/etcd \\
--data-dir=${ETCD_DATA_DIR} \\
--wal-dir=${ETCD_WAL_DIR} \\
--name=##NODE_NAME## \\
--cert-file=/etc/etcd/cert/etcd.pem \\
--key-file=/etc/etcd/cert/etcd-key.pem \\
--trusted-ca-file=/etc/kubernetes/cert/ca.pem \\
--peer-cert-file=/etc/etcd/cert/etcd.pem \\
--peer-key-file=/etc/etcd/cert/etcd-key.pem \\
--peer-trusted-ca-file=/etc/kubernetes/cert/ca.pem \\
--peer-client-cert-auth \\
--client-cert-auth \\
--listen-peer-urls=https://##NODE_IP##:2380 \\
--initial-advertise-peer-urls=https://##NODE_IP##:2380 \\
--listen-client-urls=https://##NODE_IP##:2379,http://127.0.0.1:2379 \\
--advertise-client-urls=https://##NODE_IP##:2379 \\
--initial-cluster-token=etcd-cluster-0 \\
--initial-cluster=${ETCD_NODES} \\
--initial-cluster-state=new \\
--auto-compaction-mode=periodic \\
--auto-compaction-retention=1 \\
--max-request-bytes=33554432 \\
--quota-backend-bytes=6442450944 \\
--heartbeat-interval=250 \\
--election-timeout=2000
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
WorkingDirectory
、--data-dir
:指定工作目录和数据目录为 ${ETCD_DATA_DIR},需在启动服务前创建这个目录;--wal-dir
:指定 wal 目录,为了提高性能,一般使用 SSD 或者和--data-dir
不同的磁盘;--name
:指定节点名称,当--initial-cluster-state
值为 new 时,--name
的参数值必须位于--initial-cluster
列表中;--cert-file
、--key-file
:etcd server 与 client 通信时使用的证书和私钥;--trusted-ca-file
:签名 client 证书的 CA 证书,用于验证 client 证书;--peer-cert-file
、--peer-key-file
:etcd 与 peer 通信使用的证书和私钥;--peer-trusted-ca-file
:签名 peer 证书的 CA 证书,用于验证 peer 证书;
4、为各节点创建和分发 etcd systemd unit 文件
4.1:替换模板文件中的变量
- 替换变量为各节点创建 systemd unit 文件:
[root@k8s-node1 work]# for (( i=0; i < 3; i++ ))
do
sed -e "s/##NODE_NAME##/${NODE_NAMES[i]}/" -e "s/##NODE_IP##/${NODE_IPS[i]}/" etcd.service.template > etcd-${NODE_IPS[i]}.service
done
[root@k8s-node1 work]# ls *.service
etcd-192.168.66.13.service etcd-192.168.66.14.service etcd-192.168.66.15.service
- NODE_NAMES 和 NODE_IPS 为相同长度的 bash 数组,分别为节点名称和对应的 IP;
4.2:分发生成的 systemd unit 文件到各ETCD集群节点服务器
- 文件重命名为 etcd.service;
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp etcd-${node_ip}.service root@${node_ip}:/etc/systemd/system/etcd.service
done
4.3:检查配置文件
[root@k8s-node1 work]# ls /etc/systemd/system/etcd.service
/etc/systemd/system/etcd.service
[root@k8s-node1 work]# vim /etc/systemd/system/etcd.service
- 确认脚本文件中的IP地址和数据存储地址是否都正确
5、启动ETCD服务
- 必须创建 etcd 数据目录和工作目录;
- etcd 进程首次启动时会等待其它节点的 etcd 加入集群,命令 systemctl start etcd 会卡住一段时间,为正常现象。
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p ${ETCD_DATA_DIR} ${ETCD_WAL_DIR}"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable etcd && systemctl restart etcd "
done
6、检查启动结果
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl status etcd|grep Active"
done
>>> 192.168.66.13
Active: active (running) since 三 2020-01-15 12:25:28 CST; 8s ago
>>> 192.168.66.14
Active: active (running) since 三 2020-01-15 12:25:30 CST; 6s ago
>>> 192.168.66.15
Active: active (running) since 三 2020-01-15 12:25:31 CST; 5s ago
- 确保状态为 active (running),否则查看日志,确认原因:
[root@k8s-node1 work]# journalctl -u etcd
7、验证服务状态
7.1:任一etcd节点执行以下命令
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ETCDCTL_API=3 /opt/k8s/bin/etcdctl \
--endpoints=https://${node_ip}:2379 \
--cacert=/opt/k8s/work/ca.pem \
--cert=/etc/etcd/cert/etcd.pem \
--key=/etc/etcd/cert/etcd-key.pem endpoint health
done
7.2:预期输出内容
>>> 192.168.66.13
https://192.168.66.13:2379 is healthy: successfully committed proposal: took = 10.38829ms
>>> 192.168.66.14
https://192.168.66.14:2379 is healthy: successfully committed proposal: took = 10.101688ms
>>> 192.168.66.15
https://192.168.66.15:2379 is healthy: successfully committed proposal: took = 10.205109ms
- 各服务节点全部为healthy,则代码etcd集群状态正常
8、查看当前leader
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# ETCDCTL_API=3 /opt/k8s/bin/etcdctl \
-w table --cacert=/opt/k8s/work/ca.pem \
--cert=/etc/etcd/cert/etcd.pem \
--key=/etc/etcd/cert/etcd-key.pem \
--endpoints=${ETCD_ENDPOINTS} endpoint status

- 可以看到当前的leader为192.168.66.13这台etcd节点
六、部署 flannel 网络
- kubernetes 要求集群内各节点(包括 master 节点)能通过 Pod 网段互联互通。flannel 使用 vxlan 技术为各节点创建一个可以互通的 Pod 网络,使用的端口为 UDP 8472,需要开放该端口(如公有云 AWS 等)。
- flannel 第一次启动时,从 etcd 获取 Pod 网段信息,为本节点分配一个未使用的地址段,然后创建 flannedl.1(也可能是其它名称,如 flannel1 等) 接口。
- flannel 将分配的 Pod 网段信息写入
/run/flannel/docker
文件,docker 后续使用这个文件中的环境变量设置docker0
网桥。 - 注意:如果没有特殊指明,本文档的所有操作均在 k8s-node1节点上执行,然后远程分发文件和执行命令。
- K8S-Master集群节点以及所有K8S-Node节点都需要部署Flannel网络插件
1、下载和分发 flanneld 二进制文件
1.1:下载并解压安装二进制程序文件
- flanneld仓库地址:https://github.com/coreos/flannel/releases
- 目前最近版本0.11;时间:2020-01-15
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# mkdir flannel
[root@k8s-node1 work]# wget https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz
[root@k8s-node1 work]# tar -zxvf flannel-v0.11.0-linux-amd64.tar.gz -C flannel
flanneld
mk-docker-opts.sh
README.md
1.2:分发 flanneld 二进制文件到集群所有节点:
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp flannel/{flanneld,mk-docker-opts.sh} root@${node_ip}:/opt/k8s/bin/
ssh root@${node_ip} "chmod +x /opt/k8s/bin/*"
done
2、创建 flannel 证书和私钥
- flannel 从 etcd 集群存取网段分配信息,而 etcd 集群启用了双向 x509 证书认证,所以需要为 flanneld 生成证书和私钥。
2.1:创建证书签名请求
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > flanneld-csr.json <<EOF
{
"CN": "flanneld",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
- 该证书只会被 kubectl 当做 client 证书使用,所以 hosts 字段为空;
2.2:生成证书和私钥
[root@k8s-node1 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes flanneld-csr.json | cfssljson -bare flanneld
[root@k8s-node1 work]# ls flanneld*pem
flanneld-key.pem flanneld.pem
2.3:将生成的证书和私钥分发到所有节点(master 和 worker)
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p /etc/flanneld/cert"
scp flanneld*.pem root@${node_ip}:/etc/flanneld/cert
done
3、向 etcd 写入集群 Pod 网段信息
- 注意:本步骤只需执行一次。
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--ca-file=/opt/k8s/work/ca.pem \
--cert-file=/opt/k8s/work/flanneld.pem \
--key-file=/opt/k8s/work/flanneld-key.pem \
set ${FLANNEL_ETCD_PREFIX}/config '{"Network":"'${CLUSTER_CIDR}'", "SubnetLen": 21, "Backend": {"Type": "vxlan"}}'
- flanneld 当前版本 (v0.11.0) 不支持 etcd v3,故使用 etcd v2 API 写入配置 key 和网段数据;
- 写入的 Pod 网段 ${CLUSTER_CIDR} 地址段如 /16 必须小于 SubnetLen,必须与 kube-controller-manager 的
--cluster-cidr
参数值一致;
4、创建 flanneld 的 systemd unit 文件
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > flanneld.service << EOF
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service
[Service]
Type=notify
ExecStart=/opt/k8s/bin/flanneld \\
-etcd-cafile=/etc/kubernetes/cert/ca.pem \\
-etcd-certfile=/etc/flanneld/cert/flanneld.pem \\
-etcd-keyfile=/etc/flanneld/cert/flanneld-key.pem \\
-etcd-endpoints=${ETCD_ENDPOINTS} \\
-etcd-prefix=${FLANNEL_ETCD_PREFIX} \\
-iface=${IFACE} \\
-ip-masq
ExecStartPost=/opt/k8s/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
Restart=always
RestartSec=5
StartLimitInterval=0
[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
EOF
- mk-docker-opts.sh 脚本将分配给 flanneld 的 Pod 子网网段信息写入
/run/flannel/docker
文件,后续 docker 启动时使用这个文件中的环境变量配置 docker0 网桥; - flanneld 使用系统缺省路由所在的接口与其它节点通信,对于有多个网络接口(如内网和公网)的节点,可以用
--iface
参数指定通信接口; - flanneld 运行时需要 root 权限;
--ip-masq
: flanneld 为访问 Pod 网络外的流量设置 SNAT 规则,同时将传递给 Docker 的变量 –ip-masq(/run/flannel/docker 文件中)设置为 false,这样 Docker 将不再创建 SNAT 规则; Docker 的--ip-masq
为 true 时,创建的 SNAT 规则比较”暴力“:将所有本节点 Pod 发起的、访问非 docker0 接口的请求做 SNAT,这样访问其他节点 Pod 的请求来源 IP 会被设置为 flannel.1 接口的 IP,导致目的 Pod 看不到真实的来源 Pod IP。 flanneld 创建的 SNAT 规则比较温和,只对访问非 Pod 网段的请求做 SNAT。
5、分发 flanneld systemd unit 文件到所有节点
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp flanneld.service root@${node_ip}:/etc/systemd/system/
done
6、启动 flanneld 服务
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable flanneld && systemctl restart flanneld"
done
7、检查启动结果
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl status flanneld|grep Active"
done
>>> 192.168.66.13
Active: active (running) since 三 2020-01-15 12:53:23 CST; 22s ago
>>> 192.168.66.14
Active: active (running) since 三 2020-01-15 12:53:23 CST; 22s ago
>>> 192.168.66.15
Active: active (running) since 三 2020-01-15 12:53:24 CST; 22s ago
- 确保状态为
active (running)
,否则查看日志,确认原因:
8、检查分配给各 flanneld 的 Pod 网段信息
8.1:查看集群 Pod 网段(/16)
[root@k8s-node1 work]# etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--ca-file=/etc/kubernetes/cert/ca.pem \
--cert-file=/etc/flanneld/cert/flanneld.pem \
--key-file=/etc/flanneld/cert/flanneld-key.pem \
get ${FLANNEL_ETCD_PREFIX}/config

8.2:查看已分配的 Pod 子网段列表(/24)
[root@k8s-node1 work]# etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--ca-file=/etc/kubernetes/cert/ca.pem \
--cert-file=/etc/flanneld/cert/flanneld.pem \
--key-file=/etc/flanneld/cert/flanneld-key.pem \
ls ${FLANNEL_ETCD_PREFIX}/subnets

- 结果是部署情况而定,网段可能与上面不一样
8.3:查看某一 Pod 网段对应的节点 IP 和 flannel 接口地址
[root@k8s-node1 work]# etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--ca-file=/etc/kubernetes/cert/ca.pem \
--cert-file=/etc/flanneld/cert/flanneld.pem \
--key-file=/etc/flanneld/cert/flanneld-key.pem \
get ${FLANNEL_ETCD_PREFIX}/subnets/172.30.192.0-21

- 注意:这里的IP为上面中查看到的Pod 子网段列表(/24)中的其中一个
9、验证各节点能通过 Pod 网段互通
- 在各节点上部署flannel 后,检查是否创建了 flannel 接口(名称可能为 flannel0、flannel.0、flannel.1 等)
[root@k8s-node1 work]# pwd
/opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh ${node_ip} "/usr/sbin/ip addr show flannel.1|grep -w inet"
done
>>> 192.168.66.13
inet 172.30.192.0/32 scope global flannel.1
>>> 192.168.66.14
inet 172.30.128.0/32 scope global flannel.1
>>> 192.168.66.15
inet 172.30.240.0/32 scope global flannel.1
- 在各节点上 ping 所有 flannel 接口 IP,确保能通
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh ${node_ip} "ping -c 1 172.30.192.0"
ssh ${node_ip} "ping -c 1 172.30.128.0"
ssh ${node_ip} "ping -c 1 172.30.240.0"
done
- 注意:这里的IP一定要填写上面查到的,不然ping不通,根据自己实际查到的ip地址填写
七-(0)、kube-apiserver 高可用之 nginx 代理
- 本文档讲解使用 nginx 4 层透明代理功能实现 K8S 节点( master 节点和 worker 节点)高可用访问 kube-apiserver 的步骤。
- 注意:如果没有特殊指明,本文档的所有操作均在k8s-node1节点上执行,然后远程分发文件和执行命令。
1、基于 nginx 代理的 kube-apiserver 高可用方案
- 控制节点的 kube-controller-manager、kube-scheduler 是多实例部署,所以只要有一个实例正常,就可以保证高可用;
- 集群内的 Pod 使用 K8S 服务域名 kubernetes 访问 kube-apiserver, kube-dns 会自动解析出多个 kube-apiserver 节点的 IP,所以也是高可用的;
- 在每个节点起一个 nginx 进程,后端对接多个 apiserver 实例,nginx 对它们做健康检查和负载均衡;
- kubelet、kube-proxy、controller-manager、scheduler 通过本地的 nginx(监听 127.0.0.1)访问 kube-apiserver,从而实现 kube-apiserver 的高可用;
2、下载和编译 nginx
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# wget https://nginx.org/download/nginx-1.16.1.tar.gz
[root@k8s-node1 work]# yum -y install gcc-c++
[root@k8s-node1 work]# tar -zxvf nginx-1.16.1.tar.gz
[root@k8s-node1 work]# cd nginx-1.16.1/
[root@k8s-node1 nginx-1.16.1]# ./configure --with-stream --without-http --prefix=/usr/local/nginx --without-http_uwsgi_module --without-http_scgi_module --without-http_fastcgi_module
--with-stream
:开启 4 层透明转发(TCP Proxy)功能;--without-xxx
:关闭所有其他功能,这样生成的动态链接二进制程序依赖最小;

[root@k8s-node1 nginx-1.16.1]# make && make install
3、验证编译的Nginx
[root@k8s-node1 nginx-1.16.1]# cd /usr/local/nginx/
[root@k8s-node1 nginx]# ./sbin/nginx -v
nginx version: nginx/1.16.1
- 查看Nginx动态链接的库
[root@k8s-node1 nginx]# ldd /usr/local/nginx/sbin/nginx
linux-vdso.so.1 => (0x00007ffc76479000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f8b851ad000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f8b84f91000)
libc.so.6 => /lib64/libc.so.6 (0x00007f8b84bc3000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8b853b1000)
- 由于只开启了 4 层透明转发功能,所以除了依赖 libc 等操作系统核心 lib 库外,没有对其它 lib 的依赖(如 libz、libssl 等),这样可以方便部署到各版本操作系统中;
4、安装和部署 nginx
4.1:所有节点创建目录
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
mkdir -p /opt/k8s/kube-nginx/{conf,logs,sbin}
done
4.2:拷贝二进制程序
- 注意:根据自己配置的nginx编译安装的路径进行填写下面的路径
- 重命名二进制文件为 kube-nginx
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp /usr/local/nginx/sbin/nginx root@${node_ip}:/opt/k8s/kube-nginx/sbin/kube-nginx
ssh root@${node_ip} "chmod a+x /opt/k8s/kube-nginx/sbin/*"
ssh root@${node_ip} "mkdir -p /opt/k8s/kube-nginx/{conf,logs,sbin}"
done
4.3: 配置 nginx,开启 4 层透明转发功能
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > kube-nginx.conf <<EOF
worker_processes 1;
events {
worker_connections 1024;
}
stream {
upstream backend {
hash $remote_addr consistent;
server 192.168.66.13:6443 max_fails=3 fail_timeout=30s;
server 192.168.66.14:6443 max_fails=3 fail_timeout=30s;
server 192.168.66.15:6443 max_fails=3 fail_timeout=30s;
}
server {
listen 127.0.0.1:8443;
proxy_connect_timeout 1s;
proxy_pass backend;
}
}
EOF
- 注意:需要根据集群 kube-apiserver 的实际情况,替换 backend 中 server 列表
4.4:分发配置文件
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kube-nginx.conf root@${node_ip}:/opt/k8s/kube-nginx/conf/kube-nginx.conf
done
5、配置 systemd unit 文件,启动服务
5.1:配置 kube-nginx systemd unit 文件
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > kube-nginx.service <<EOF
[Unit]
Description=kube-apiserver nginx proxy
After=network.target
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
ExecStartPre=/opt/k8s/kube-nginx/sbin/kube-nginx -c /opt/k8s/kube-nginx/conf/kube-nginx.conf -p /opt/k8s/kube-nginx -t
ExecStart=/opt/k8s/kube-nginx/sbin/kube-nginx -c /opt/k8s/kube-nginx/conf/kube-nginx.conf -p /opt/k8s/kube-nginx
ExecReload=/opt/k8s/kube-nginx/sbin/kube-nginx -c /opt/k8s/kube-nginx/conf/kube-nginx.conf -p /opt/k8s/kube-nginx -s reload
PrivateTmp=true
Restart=always
RestartSec=5
StartLimitInterval=0
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
5.2:分发 systemd unit 文件
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-nginx && systemctl restart kube-nginx"
done
6、检查 kube-nginx 服务运行状态
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl status kube-nginx |grep 'Active:'"
done
#输出如下:
>>> 192.168.66.13
Active: active (running) since 三 2020-01-15 13:27:40 CST; 35s ago
>>> 192.168.66.14
Active: active (running) since 三 2020-01-15 13:27:41 CST; 35s ago
>>> 192.168.66.15
Active: active (running) since 三 2020-01-15 13:27:41 CST; 35s ago
#确保状态为 active (running),否则查看日志,确认原因:
[root@k8s-node1 work]# journalctl -u kube-nginx
七-(1)、部署 master 节点
- kubernetes master 节点运行如下组件:
- kube-apiserver
- kube-scheduler
- kube-controller-manager
- kube-nginx
- kube-scheduler 和 kube-controller-manager 可以以集群模式运行,通过 leader 选举产生一个工作进程,其它进程处于阻塞模式。
- 这两个组件需要高可用模式访问 kube-apiserver,所以也需要部署 kube-nginx 组件。
- 注意:如果没有特殊指明,本文档的所有操作均在 k8s-node1 节点上执行,然后远程分发文件和执行命令。
1、下载最新版本二进制文件
- 我这里直接在第一步环境配置时已经下载好了k8s-server的包,上传至服务器后直接解压安装;版本为最新的1.17.0
[root@k8s-node1 ~]# cd /opt/k8s/work/
[root@k8s-node1 work]# tar -zxvf kubernetes-server-linux-amd64.tar.gz
[root@k8s-node1 work]# cd kubernetes/
[root@k8s-node1 kubernetes]# tar -zxvf kubernetes-src.tar.gz
2、将二进制文件拷贝至所有节点
[root@k8s-node1 ~]# cd /opt/k8s/work/kubernetes/
[root@k8s-node1 kubernetes]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp server/bin/* root@${node_ip}:/opt/k8s/bin/
ssh root@${node_ip} "chmod +x /opt/k8s/bin/*"
done
七-(2)、部署高可用 kube-apiserver 集群
- 前提是该主机上已经部署了flannel网络,如果没有部署,参考本文档的第六步骤
1、创建 kubernetes 证书和私钥
1.1:创建证书签名请求
[root@k8s-node1 ~]# cd /opt/k8s/work/
[root@k8s-node1 work]# cat > kubernetes-csr.json <<EOF
{
"CN": "kubernetes",
"hosts": [
"127.0.0.1",
"192.168.66.15",
"192.168.66.14",
"192.168.66.13",
"${CLUSTER_KUBERNETES_SVC_IP}",
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
- hosts 字段指定授权使用该证书的 IP 或域名列表,这里列出了 VIP 、apiserver 节点 IP、kubernetes 服务 IP 和域名;
- 域名最后字符不能是 .(如不能为 kubernetes.defau
原文链接:https://www.dqzboy.com lt.svc.cluster.local.),否则解析时失败,提示:x509: cannot parse dnsName "kubernetes.default.svc.cluster.local"
; - 如果使用非 cluster.local 域名,如 opsnull.com,则需要修改域名列表中的最后两个域名为:kubernetes.default.svc.opsnull、kubernetes.default.svc.opsnull.com
- kubernetes 服务 IP 是 apiserver 自动创建的,一般是
--service-cluster-ip-range
参数指定的网段的第一个IP,后续可以通过如下命令获取:
1.2:生成证书和私钥
[root@k8s-node1 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes
[root@k8s-node1 work]# ls kubernetes*pem
kubernetes-key.pem kubernetes.pem
1.3:将生成的证书和私钥文件拷贝到 master 节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p /etc/kubernetes/cert"
scp kubernetes*.pem root@${node_ip}:/etc/kubernetes/cert/
done
2、创建加密配置文件
[root@k8s-node1 work]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${ENCRYPTION_KEY}
- identity: {}
EOF
2.1:将加密配置文件拷贝到 master 节点的 /etc/kubernetes
目录下
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp encryption-config.yaml root@${node_ip}:/etc/kubernetes/
done
3、创建 kube-apiserver systemd unit 模板文件
[root@k8s-node1 ~]# cd /opt/k8s/work/
[root@k8s-node1 work]# cat > kube-apiserver.service.template <<EOF
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
[Service]
WorkingDirectory=${K8S_DIR}/kube-apiserver
ExecStart=/opt/k8s/bin/kube-apiserver \\
--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml \\
--advertise-address=##NODE_IP## \\
--bind-address=##NODE_IP## \\
--authorization-mode=Node,RBAC \\
--enable-bootstrap-token-auth \\
--service-cluster-ip-range=${SERVICE_CIDR} \\
--service-node-port-range=${NODE_PORT_RANGE} \\
--tls-cert-file=/etc/kubernetes/cert/kubernetes.pem \\
--tls-private-key-file=/etc/kubernetes/cert/kubernetes-key.pem \\
--client-ca-file=/etc/kubernetes/cert/ca.pem \\
--kubelet-certificate-authority=/etc/kubernetes/cert/ca.pem \\
--kubelet-client-certificate=/etc/kubernetes/cert/kubernetes.pem \\
--kubelet-client-key=/etc/kubernetes/cert/kubernetes-key.pem \\
--kubelet-https=true \\
--service-account-key-file=/etc/kubernetes/cert/ca.pem \\
--etcd-cafile=/etc/kubernetes/cert/ca.pem \\
--etcd-certfile=/etc/kubernetes/cert/kubernetes.pem \\
--etcd-keyfile=/etc/kubernetes/cert/kubernetes-key.pem \\
--etcd-servers=${ETCD_ENDPOINTS} \\
--allow-privileged=true \\
--logtostderr=true \\
--v=4
Restart=on-failure
RestartSec=5
Type=notify
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
参数说明:
--authorization-mode=Node,RBAC
: 开启 Node 和 RBAC 授权模式,拒绝未授权的请求;--enable-admission-plugins
:启用 ServiceAccount 和 NodeRestriction;--service-account-key-file
:签名 ServiceAccount Token 的公钥文件,kube-controller-manager 的--service-account-private-key-file
指定私钥文件,两者配对使用;--tls--file
:指定 apiserver 使用的证书、私钥和 CA 文件。--client-ca-file
用于验证 client (kue-controller-manager、kube-scheduler、kubelet、kube-proxy 等)请求所带的证书;-
--kubelet-client-certificate
、--kubelet-client-key
:如果指定,则使用 https 访问 kubelet APIs;需要为证书对应的用户(上面 kubernetes.pem 证书的用户为 kubernetes) 用户定义 RBAC 规则,否则访问 kubelet API 时提示未授权; --bind-address
: 不能为 127.0.0.1,否则外界不能访问它的安全端口 6443;--service-cluster-ip-range
: 指定 Service Cluster IP 地址段;--service-node-port-range
: 指定 NodePort 的端口范围;--runtime-config=api/all=true
: 启用所有版本的 APIs,如 autoscaling/v2alpha1;--enable-bootstrap-token-auth
:启用 kubelet bootstrap 的 token 认证;--apiserver-count=3
:指定集群运行模式,多台 kube-apiserver 会通过 leader 选举产生一个工作节点,其它节点处于阻塞状态;
4、为各节点创建和分发 kube-apiserver systemd unit 文件
4.1:替换模板文件中的变量,为各节点创建 systemd unit 文件
[root@k8s-node1 work]# for (( i=0; i < 3; i++ ))
do
sed -e "s/##NODE_NAME##/${NODE_NAMES[i]}/" -e "s/##NODE_IP##/${NODE_IPS[i]}/" kube-apiserver.service.template > kube-apiserver-${NODE_IPS[i]}.service
done
[root@k8s-node1 work]# ls kube-apiserver*.service
- NODE_NAMES 和 NODE_IPS 为相同长度的 bash 数组,分别为节点名称和对应的 IP;
4.2:分发生成的 systemd unit 文件
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kube-apiserver-${node_ip}.service root@${node_ip}:/etc/systemd/system/kube-apiserver.service
done
- 文件重命名为 kube-apiserver.service;
5、启动 kube-apiserver 服务
- 必须创建工作目录
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kube-apiserver"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-apiserver && systemctl restart kube-apiserver"
done
6、检查 kube-apiserver 运行状态
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl status kube-apiserver |grep 'Active:'"
done
7、打印 kube-apiserver 写入 etcd 的数据
[root@k8s-node1 work]# ETCDCTL_API=3 etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--cacert=/opt/k8s/work/ca.pem \
--cert=/opt/k8s/work/etcd.pem \
--key=/opt/k8s/work/etcd-key.pem \
get /registry/ --prefix --keys-only

8、检查集群信息
[root@k8s-node1 work]# kubectl cluster-info

[root@k8s-node1 work]# kubectl get all --all-namespaces

[root@k8s-node1 work]# kubectl get componentstatuses

- 注意:
- 1、如果执行 kubectl 命令式时输出如下错误信息,则说明使用的
~/.kube/config
文件不对,请切换到正确的账户后再执行该命令:- The connection to the server localhost:8080 was refused – did you specify the right host or port?
- 2、执行 kubectl get componentstatuses 命令时,apiserver 默认向 127.0.0.1 发送请求。当 controller-manager、scheduler 以集群模式运行时,有可能和 kube-apiserver 不在一台机器上,这时 controller-manager 或 scheduler 的状态为 Unhealthy,但实际上它们工作正常。
- 1、如果执行 kubectl 命令式时输出如下错误信息,则说明使用的
9、检查 kube-apiserver 监听的端口
[root@k8s-node1 work]# netstat -lnpt|grep kube
tcp 0 0 192.168.66.13:6443 0.0.0.0:* LISTEN 10200/kube-apiserve
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 10200/kube-apiserve
- 6443: 接收 https 请求的安全端口,对所有请求做认证和授权;
- 由于关闭了非安全端口,故没有监听 8080;
10、授予 kubernetes 证书访问 kubelet API 的权限
- 在执行 kubectl exec、run、logs 等命令时,apiserver 会转发到 kubelet。这里定义 RBAC 规则,授权 apiserver 调用 kubelet API。
- 这里需要特别注意,为了方便管理集群,因此需要通过 kubectl logs 来查看,但由于 API 权限问题,故需要建立一个 RBAC Role 来获取访问权限,否则,这个不授权,kubectl 会无权访问pod 的log内容 会出现如下报错:
[root@k8s-node1 work]# kubectl logs coredns-6fcd79879-q425r -n kube-system
Error from server (Forbidden): Forbidden (user=kubernetes, verb=get, resource=nodes, subresource=proxy) ( pods/log coredns-6fcd79879-q425r)
[root@k8s-node1 work]# kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes
七-(3)、部署高可用 kube-controller-manager 集群
- 该集群包含 3 个节点,启动后将通过竞争选举机制产生一个 leader 节点,其它节点为阻塞状态。当 leader 节点不可用后,剩余节点将再次进行选举产生新的 leader 节点,从而保证服务的可用性。
- 为保证通信安全,本文档先生成 x509 证书和私钥,kube-controller-manager 在如下两种情况下使用该证书:
- 1.与 kube-apiserver 的安全端口通信时;
- 2.在安全端口(https,10252) 输出 prometheus 格式的 metrics;
- 注意:如果没有特殊指明,本文档的所有操作均在k8s-node1节点上执行,然后远程分发文件和执行命令。
1、创建 kube-controller-manager 证书和私钥
1.1:创建证书签名请求
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > kube-controller-manager-csr.json <<EOF
{
"CN": "system:kube-controller-manager",
"key": {
"algo": "rsa",
"size": 2048
},
"hosts": [
"127.0.0.1",
"192.168.66.15",
"192.168.66.14",
"192.168.66.13"
],
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "system:kube-controller-manager",
"OU": "4Paradigm"
}
]
}
EOF
- hosts 列表包含所有 kube-controller-manager 节点 IP;
- CN 为 system:kube-controller-manager、O 为 system:kube-controller-manager,kubernetes 内置的 ClusterRoleBindings system:kube-controller-manager 赋予 kube-controller-manager 工作所需的权限。
1.2:生成证书和私钥
[root@k8s-node1 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager
[root@k8s-node1 work]# ls kube-controller-manager*pem
1.3:将生成的证书和私钥分发到所有 master 节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kube-controller-manager*.pem root@${node_ip}:/etc/kubernetes/cert/
done
2、创建和分发 kubeconfig 文件
- kubeconfig 文件包含访问 apiserver 的所有信息,如 apiserver 地址、CA 证书和自身使用的证书;
2.1:配置kubeconfig文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# kubectl config set-cluster kubernetes \
--certificate-authority=/opt/k8s/work/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kube-controller-manager.kubeconfig

[root@k8s-node1 work]# kubectl config set-credentials system:kube-controller-manager \
--client-certificate=kube-controller-manager.pem \
--client-key=kube-controller-manager-key.pem \
--embed-certs=true \
--kubeconfig=kube-controller-manager.kubeconfig

[root@k8s-node1 work]# kubectl config set-context system:kube-controller-manager \
--cluster=kubernetes \
--user=system:kube-controller-manager \
--kubeconfig=kube-controller-manager.kubeconfig

[root@k8s-node1 work]# kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig
2.2:分发 kubeconfig 到所有 master 节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kube-controller-manager.kubeconfig root@${node_ip}:/etc/kubernetes/
done
3、创建和分发 kube-controller-manager systemd unit 文件
3.1:创建kube-controller-manager systemd模板文件
[root@k8s-node1 ~]# cd /opt/k8s/work/
[root@k8s-node1 work]# cat > kube-controller-manager.service <<EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
[Service]
WorkingDirectory=${K8S_DIR}/kube-controller-manager
ExecStart=/opt/k8s/bin/kube-controller-manager \\
--port=0 \\
--secure-port=10252 \\
--bind-address=127.0.0.1 \\
--kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
--authentication-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
--authorization-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
--service-cluster-ip-range=${SERVICE_CIDR} \\
--cluster-name=kubernetes \\
--cluster-signing-cert-file=/etc/kubernetes/cert/ca.pem \\
--cluster-signing-key-file=/etc/kubernetes/cert/ca-key.pem \\
--experimental-cluster-signing-duration=8760h \\
--root-ca-file=/etc/kubernetes/cert/ca.pem \\
--service-account-private-key-file=/etc/kubernetes/cert/ca-key.pem \\
--leader-elect=true \\
--controllers=*,bootstrapsigner,tokencleaner \\
--horizontal-pod-autoscaler-use-rest-clients=true \\
--horizontal-pod-autoscaler-sync-period=10s \\
--tls-cert-file=/etc/kubernetes/cert/kube-controller-manager.pem \\
--tls-private-key-file=/etc/kubernetes/cert/kube-controller-manager-key.pem \\
--use-service-account-credentials=true \\
--kube-api-qps=1000 \\
--kube-api-burst=2000 \\
--logtostderr=true \\
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
参数解释:
--port=0
:关闭监听 http /metrics 的请求,同时--address
参数无效,--bind-address
参数有效;--secure-port=10252
、--bind-address=0.0.0.0
: 在所有网络接口监听 10252 端口的 https /metrics 请求;--kubeconfig
:指定 kubeconfig 文件路径,kube-controller-manager 使用它连接和验证 kube-apiserver;--authentication-kubeconfig
和--authorization-kubeconfig
:kube-controller-manager 使用它连接 apiserver,对 client 的请求进行认证和授权。kube-controller-manager 不再使用 –tls-ca-file 对请求 https metrics 的 Client 证书进行校验。如果没有配置这两个 kubeconfig 参数,则 client 连接 kube-controller-manager https 端口的请求会被拒绝(提示权限不足)。--cluster-signing-file
:签名 TLS文章来源(Source):https://www.dqzboy.com Bootstrap 创建的证书;--experimental-cluster-signing-duration
:指定 TLS Bootstrap 证书的有效期;--root-ca-file
:放置到容器 ServiceAccount 中的 CA 证书,用来对 kube-apiserver 的证书进行校验;--service-account-private-key-file
:签名 ServiceAccount 中 Token 的私钥文件,必须和 kube-apiserver 的 –service-account-key-file 指定的公钥文件配对使用;--service-cluster-ip-range
:指定 Service Cluster IP 网段,必须和 kube-apiserver 中的同名参数一致;--leader-elect=true
:集群运行模式,启用选举功能;被选为 leader 的节点负责处理工作,其它节点为阻塞状态;--controllers=*,bootstrapsigner,tokencleaner
:启用的控制器列表,tokencleaner 用于自动清理过期的 Bootstrap token;--horizontal-pod-autoscaler-*
:custom metrics 相关参数,支持 autoscaling/v2alpha1;--tls-cert-file
、--tls-private-key-file
:使用 https 输出 metrics 时使用的 Server 证书和秘钥;--use-service-account-credentials=true
: kube-controller-manager 中各 controller 使用 serviceaccount 访问 kube-apiserver;
3.2:分发 systemd unit 文件到所有 master 节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kube-controller-manager.service root@${node_ip}:/etc/systemd/system/
done
4、kube-controller-manager 的权限
- ClusteRole: system:kube-controller-manager 的权限很小,只能创建 secret、serviceaccount 等资源对象,各 controller 的权限分散到 ClusterRole system:controller:XXX 中。
- 需要在 kube-controller-manager 的启动参数中添加
--use-service-account-credentials=true
参数,这样 main controller 会为各 controller 创建对应的 ServiceAccount XXX-controller。 - 内置的 ClusterRoleBinding system:controller:XXX 将赋予各 XXX-controller ServiceAccount 对应的 ClusterRole system:controller:XXX 权限。
- 另外,
--authentication-kubeconfig
和--authorization-kubeconfig
参数指定的证书需要有创建 “subjectaccessreviews” 的权限,否则提示:
Internal Server Error: "/metrics": subjectaccessreviews.authorization.k8s.io is forbidden: User "system:kube-controller-manager" cannot create resource "subjectaccessreviews" in API group "authorization.k8s.io" at the cluster scope
- 解决办法是创建一个 ClusterRoleBinding,赋予相应的权限
[root@k8s-node1 work]# kubectl create clusterrolebinding controller-manager:system:auth-delegator --user system:kube-controller-manager --clusterrole system:auth-delegator
clusterrolebinding.rbac.authorization.k8s.io/controller-manager:system:auth-delegator created
5、启动 kube-controller-manager 服务
- 必须创建工作目录;
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kube-controller-manager"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-controller-manager && systemctl restart kube-controller-manager"
done
6、检查服务运行状态
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl status kube-controller-manager|grep Active"
done
>>> 192.168.66.13
Active: active (running) since 三 2020-01-15 15:45:59 CST; 1min 1s ago
>>> 192.168.66.14
Active: active (running) since 三 2020-01-15 15:46:00 CST; 1min 1s ago
>>> 192.168.66.15
Active: active (running) since 三 2020-01-15 15:46:00 CST; 1min 1s ago
7、查看输出的 metric
- 注意:以下命令在
kube-control
文章来源(Source):https://www.dqzboy.com ler-manager 节点上执行。 - kube-controller-manager 监听 10252 端口,接收 https 请求:
[root@k8s-node1 work]# netstat -lnpt|grep kube-controll

[root@k8s-node1 work]# curl --cacert /opt/k8s/work/ca.pem --cert /opt/k8s/work/admin.pem --key /opt/k8s/work/admin-key.pem https://127.0.0.1:10252/metrics
8、查看当前的 leader
[root@k8s-node1 work]# kubectl get endpoints kube-controller-manager --namespace=kube-system -o yaml

- 可见上图红框中为当前的leader为k8s-node1节点
七-(4)、部署高可用 kube-scheduler 集群
1、创建 kube-scheduler 证书和私钥
1.1:创建证书签名请求
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > kube-scheduler-csr.json <<EOF
{
"CN": "system:kube-scheduler",
"hosts": [
"127.0.0.1",
"192.168.66.15",
"192.168.66.14",
"192.168.66.13"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "system:kube-scheduler",
"OU": "4Paradigm"
}
]
}
EOF
- hosts 列表包含所有 kube-scheduler 节点 IP;
- CN 为 system:kube-scheduler、O 为 system:kube-scheduler,kubernetes 内置的 ClusterRoleBindings system:kube-scheduler 将赋予 kube-scheduler 工作所需的权限。
1.2:生成证书和私钥
[root@k8s-node1 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes kube-scheduler-csr.json | cfssljson -bare kube-scheduler

[root@k8s-node1 work]# ls kube-scheduler*pem
kube-scheduler-key.pem kube-scheduler.pem
2、创建和分发 kubeconfig 文件
- kubeconfig 文件包含访问 apiserver 的所有信息,如 apiserver 地址、CA 证书和自身使用的证书;
2.1:创建kubeconfig文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# kubectl config set-cluster kubernetes \
--certificate-authority=/opt/k8s/work/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kube-scheduler.kubeconfig
[root@k8s-node1 work]# kubectl config set-credentials system:kube-scheduler \
--client-certificate=kube-scheduler.pem \
--client-key=kube-scheduler-key.pem \
--embed-certs=true \
--kubeconfig=kube-scheduler.kubeconfig
[root@k8s-node1 work]# kubectl config set-context system:kube-scheduler \
--cluster=kubernetes \
--user=system:kube-scheduler \
--kubeconfig=kube-scheduler.kubeconfig
[root@k8s-node1 work]# kubectl config use-context system:kube-scheduler --kubeconfig=kube-scheduler.kubeconfig
- 上一步创建的证书、私钥以及 kube-apiserver 地址被写入到 kubeconfig 文件中;
2.2:分发 kubeconfig 到所有 master 节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kube-scheduler.kubeconfig root@${node_ip}:/etc/kubernetes/
done
3、创建 kube-scheduler 配置文件
3.1:创建 kube-scheduler 配置文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat <<EOF | sudo tee kube-scheduler.yaml
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: "/etc/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
leaderElect: true
EOF

--kubeconfig
:指定 kubeconfig 文件路径,kube-scheduler 使用它连接和验证 kube-apiserver;--leader-elect=true
:集群运行模式,启用选举功能;被选为 leader 的节点负责处理工作,其它节点为阻塞状态;
3.2:分发 kube-scheduler 配置文件到所有 master 节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kube-scheduler.yaml root@${node_ip}:/etc/kubernetes/
done
4、创建和分发 kube-scheduler systemd unit 文件
4.1:创建kube-scheduler文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > kube-scheduler.service <<EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
[Service]
WorkingDirectory=${K8S_DIR}/kube-scheduler
ExecStart=/opt/k8s/bin/kube-scheduler \\
--config=/etc/kubernetes/kube-scheduler.yaml \\
--address=127.0.0.1 \\
--kube-api-qps=100 \\
--logtostderr=true \\
--v=2
Restart=always
RestartSec=5
StartLimitInterval=0
[Install]
WantedBy=multi-user.target
EOF
--address
:在 127.0.0.1:10251 端口接收 http /metrics 请求;kube-scheduler 目前还不支持接收 https 请求;
4.2:分发 systemd unit 文件到所有 master 节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp kube-scheduler.service root@${node_ip}:/etc/systemd/system/
done
5、启动 kube-scheduler 服务
- 必须先创建工作目录;
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kube-scheduler"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-scheduler && systemctl restart kube-scheduler"
done
6、检查服务运行状态
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl status kube-scheduler|grep Active"
done
>>> 192.168.66.13
Active: active (running) since 三 2020-01-15 16:38:43 CST; 34s ago
>>> 192.168.66.14
Active: active (running) since 三 2020-01-15 16:38:43 CST; 34s ago
>>> 192.168.66.15
Active: active (running) since 三 2020-01-15 16:38:44 CST; 34s ago
- 确保状态为
active (running)
,否则查看日志,确认原因:
7、查看输出的 metric
- 注意:以下命令在 kube-scheduler 节点上执行。
- kube-scheduler 监听 10251 端口,接收 http 请求:
[root@k8s-node1 work]# netstat -lnpt|grep kube-sche
[root@k8s-node1 work]# curl -s http://127.0.0.1:10251/metrics |head

8、查看当前的 leader
[root@k8s-node1 work]# kubectl get endpoints kube-scheduler --namespace=kube-system -o yaml

- 可看到当前leader为k8s-node2
八-(0)、部署 worker 节点
- kubernetes work 节点运行如下组件:
- docker
- kubelet
- kube-proxy
- flanneld
- kube-nginx
- 注意:如果没有特殊指明,本文档的所有操作均在k8s-node1节点上执行,然后远程分发文件和执行命令。
1、安装依赖包
[root@k8s-node1 ~]# cd /opt/k8s/work/
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "yum install -y epel-release"
ssh root@${node_ip} "yum install -y conntrack ipvsadm ipset jq iptables curl sysstat libseccomp && /usr/sbin/modprobe ip_vs "
done
八-(1)、部署docker
1、下载和分发 docker 二进制文件
1.1:下载程序包
- 下载地址:https://download.docker.com/linux/static/stable/x86_64/ 下载最新的二进制包
- 如果下载太慢,那么下载到本地电脑然后上传至服务器
[root@k8s-node1 ~]# cd /opt/k8s/work/
[root@k8s-node1 work]# wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.5.tgz
[root@k8s-node1 work]# tar -xzvf docker-19.03.5.tgz
1.2:分发程序包
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp docker/* root@${node_ip}:/opt/k8s/bin/
ssh root@${node_ip} "chmod +x /opt/k8s/bin/*"
done
2、创建和分发 systemd unit 文件
2.1:创建systemd unit 文件
[root@k8s-node1 work]# cat > docker.service <<"EOF"
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.io
[Service]
WorkingDirectory=##DOCKER_DIR##
Environment="PATH=/opt/k8s/bin:/bin:/sbin:/usr/bin:/usr/sbin"
EnvironmentFile=-/run/flannel/docker
ExecStart=/opt/k8s/bin/dockerd $DOCKER_NETWORK_OPTIONS
ExecReload=/bin/kill -s HUP $MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF
- EOF 前后有双引号,这样 bash 不会替换文档中的变量,如 $DOCKER_NETWORK_OPTIONS;
- dockerd 运行时会调用其它 docker 命令,如 docker-proxy,所以需要将 docker 命令所在的目录加到 PATH 环境变量中;
- flanneld 启动时将网络配置写入
/run/flannel/docker
文件中,dockerd 启动前读取该文件中的环境变量 DOCKER_NETWORK_OPTIONS ,然后设置 docker0 网桥网段; - 如果指定了多个 EnvironmentFile 选项,则必须将
/run/flannel/docker
放在最后(确保 docker0 使用 flanneld 生成的 bip 参数); - docker 需要以 root 用于运行;
- docker 从 1.13 版本开始,可能将 iptables FORWARD chain的默认策略设置为DROP,从而导致 ping 其它 Node 上的 Pod IP 失败,遇到这种情况时,需要手动设置策略为 ACCEPT
[root@k8s-node1 work]# iptables -P FORWARD ACCEPT
- 并且把以下命令写入
/etc/rc.local
文件中,防止节点重启iptables FORWARD chain的默认策略又还原为DROP
[root@k8s-node1 work]# vim /etc/rc.local
/sbin/iptables -P FORWARD ACCEPT
2.2:分发 systemd unit 文件到所有 worker 机器
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# sed -i -e "s|##DOCKER_DIR##|${DOCKER_DIR}|" docker.service
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
scp docker.service root@${node_ip}:/etc/systemd/system/
done
3、配置和分发 docker 配置文件
- 使用国内的仓库镜像服务器以加快 pull image 的速度,同时增加下载的并发数 (需要重启 dockerd 生效)
3.1:配置docker加速
- 由于网络环境原因。默认去下载官方的docker hub会下载失败所以使用阿里云的docker加速器
- 登入自己的阿里云生成docker.json,阿里云镜像地址
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > docker-daemon.json <<EOF
{
"registry-mirrors": ["https://a7ye1cuu.mirror.aliyuncs.com"]
}
EOF
3.2:分发至work节点
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p /etc/docker/ ${DOCKER_DIR}/{data,exec}"
scp docker-daemon.json root@${node_ip}:/etc/docker/daemon.json
done
4、启动 docker 服务
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl stop firewalld && systemctl disable firewalld"
ssh root@${node_ip} "/usr/sbin/iptables -F && /usr/sbin/iptables -X && /usr/sbin/iptables -F -t nat && /usr/sbin/iptables -X -t nat"
ssh root@${node_ip} "/usr/sbin/iptables -P FORWARD ACCEPT"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable docker && systemctl restart docker"
#ssh root@${node_ip} 'for intf in /sys/devices/virtual/net/docker0/brif/*; do echo 1 > $intf/hairpin_mode; done'
ssh root@${node_ip} "sudo sysctl -p /etc/sysctl.d/kubernetes.conf"
done
- 关闭 firewalld(centos7)/ufw(ubuntu16.04),否则可能会重复创建 iptables 规则;
- 清理旧的 iptables rules 和 chains 规则;
- 开启 docker0 网桥下虚拟网卡的 hairpin 模式;
5、检查服务运行状态
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl status docker|grep Active"
done
>>> 192.168.66.13
Active: active (running) since 三 2020-01-15 17:08:53 CST; 6min ago
>>> 192.168.66.14
Active: active (running) since 三 2020-01-15 17:08:55 CST; 6min ago
>>> 192.168.66.15
Active: active (running) since 三 2020-01-15 17:08:56 CST; 6min ago
6、检查 docker0 网桥
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "/usr/sbin/ip addr show flannel.1 && /usr/sbin/ip addr show docker0"
done
- 确认各 work 节点的 docker0 网桥和 flannel.1 接口的 IP 处于同一个网段中(如下 172.30.112.0/32 位于 172.30.112.1/21 中)
八-(2)、部署 kubelet 组件
- kubelet 运行在每个 worker 节点上,接收 kube-apiserver 发送的请求,管理 Pod 容器,执行交互式命令,如 exec、run、logs 等。
- kubelet 启动时自动向 kube-apiserver 注册节点信息,内置的 cadvisor 统计和监控节点的资源使用情况。
- 为确保安全,本文档只开启接收 https 请求的安全端口,对请求进行认证和授权,拒绝未授权的访问(如 apiserver、heapster)。
- 注意:如果没有特殊指明,本文档的所有操作均在k8s-node1节点上执行,然后远程分发文件和执行命令。
1、创建 kubelet bootstrap kubeconfig 文件
1.1:创建文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# for node_name in ${NODE_NAMES[@]}
do
echo ">>> ${node_name}"
# 创建 token
export BOOTSTRAP_TOKEN=$(kubeadm token create \
--description kubelet-bootstrap-token \
--groups system:bootstrappers:${node_name} \
--kubeconfig ~/.kube/config)
# 设置集群参数
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/cert/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
# 设置客户端认证参数
kubectl config set-credentials kubelet-bootstrap \
--token=${BOOTSTRAP_TOKEN} \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
# 设置上下文参数
kubectl config set-context default \
--cluster=kubernetes \
--user=kubelet-bootstrap \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
# 设置默认上下文
kubectl config use-context default --kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
done

- 证书中写入 Token 而非证书,证书后续由 kube-controller-manager 创建
1.2:查看 kubeadm 为各节点创建的 token
[root@k8s-node1 work]# kubeadm token list --kubeconfig ~/.kube/config

- 删除token命令:
[root@k8s-node1 work]# kubeadm token delete token_id
- 创建的 token 有效期为 1 天,超期后将不能再被使用,且会被 kube-controller-manager 的 tokencleaner 清理(如果启用该 controller 的话);
- kube-apiserver 接收 kubelet 的 bootstrap token 后,将请求的 user 设置为
system:bootstrap:<Token ID>
,group 设置为system:bootstrappers
;
1.3:查看各 token 关联的 Secret
[root@k8s-node1 work]# kubectl get secrets -n kube-system|grep bootstrap-token

2、分发 bootstrap kubeconfig 文件到所有 worker 节点
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# for node_name in ${NODE_NAMES[@]}
do
echo ">>> ${node_name}"
scp kubelet-bootstrap-${node_name}.kubeconfig root@${node_name}:/etc/kubernetes/kubelet-bootstrap.kubeconfig
done
3、创建和分发 kubelet 参数配置文件
3.1:创建 kubelet 参数配置模板文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat <<EOF | tee kubelet-config.yaml.template
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
enabled: true
x509:
clientCAFile: "/etc/kubernetes/cert/ca.pem"
authorization:
mode: Webhook
clusterDomain: "${CLUSTER_DNS_DOMAIN}"
clusterDNS:
- "${CLUSTER_DNS_SVC_IP}"
podCIDR: "${CLUSTER_CIDR}"
maxPods: 220
serializeImagePulls: false
hairpinMode: promiscuous-bridge
cgroupDriver: cgroupfs
runtimeRequestTimeout: "15m"
rotateCertificates: true
serverTLSBootstrap: true
readOnlyPort: 0
port: 10250
address: "##NODE_IP##"
EOF
address
:API 监听地址,不能为 127.0.0.1,否则 kube-apiserver、heapster 等不能调用 kubelet 的 API;readOnlyPort=0
:关闭只读端口(默认 10255),等效为未指定;authentication.anonymous.enabled
:设置为 false,不允许匿名访问 10250 端口;authentication.x509.clientCAFile
:指定签名客户端证书的 CA 证书,开启 HTTP 证书认证;authentication.webhook.enabled=true
:开启 HTTPs bearer token 认证;- 对于未通过 x509 证书和 webhook 认证的请求(kube-apiserver 或其他客户端),将被拒绝,提示 Unauthorized;
authroization.mode=Webhook
:kubelet 使用 SubjectAccessReview API 查询 kube-apiserver 某 user、group 是否具有操作资源的权限(RBAC);featureGates.RotateKubeletClientCertificate
、featureGates.RotateKubeletServerCertificate
:自动 rotate 证书,证书的有效期取决于 kube-controller-manager 的--experimental-cluster-signing-duration
参数;- 需要 root 账户运行;
3.2:为各节点创建和分发 kubelet 配置文件
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
sed -e "s/##NODE_IP##/${node_ip}/" kubelet-config.yaml.template > kubelet-config-${node_ip}.yaml.template
scp kubelet-config-${node_ip}.yaml.template root@${node_ip}:/etc/kubernetes/kubelet-config.yaml
done
4、创建和分发 kubelet systemd unit 文件
4.1:创建 kubelet systemd unit 文件模板
[root@k8s-node1 work]# cat > kubelet.service.template <<EOF
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service
[Service]
WorkingDirectory=${K8S_DIR}/kubelet
ExecStart=/opt/k8s/bin/kubelet \\
--root-dir=${K8S_DIR}/kubelet \\
--bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.kubeconfig \\
--cert-dir=/etc/kubernetes/cert \\
--kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
--config=/etc/kubernetes/kubelet-config.yaml \\
--hostname-override=##NODE_NAME## \\
--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause \\
--event-qps=0 \\
--kube-api-qps=1000 \\
--kube-api-burst=2000 \\
--registry-qps=0 \\
--image-pull-progress-deadline=30m \\
--logtostderr=true \\
--v=2
Restart=always
RestartSec=5
StartLimitInterval=0
[Install]
WantedBy=multi-user.target
EOF
参数说明:
- 如果设置了
--hostname-override
选项,则 kube-proxy 也需要设置该选项,否则会出现找不到 Node 的情况; --bootstrap-kubeconfig
:指向 bootstrap kubeconfig 文件,kubelet 使用该文件中的用户名和 token 向 kube-apiserver 发送 TLS Bootstrapping 请求;- K8S approve kubelet 的 csr 请求后,在
--cert-dir
目录创建证书和私钥文件,然后写入--kubeconfig
文件; --pod-infra-container-image
不使用 redhat 的 pod-infrastructure:latest 镜像,它不能回收容器的僵尸;这是Pause容器,又叫Infra容器,默认的镜像仓库地址网络原因无法下载,这里使用阿里云的,注意一定要选择国内的Pause源,不然后面无法启动Pod;阿里云Pause地址,最新版本3.1

4.2:为各节点创建和分发 kubelet systemd unit 文件
[root@k8s-node1 work]# for node_name in ${NODE_NAMES[@]}
do
echo ">>> ${node_name}"
sed -e "s/##NODE_NAME##/${node_name}/" kubelet.service.template > kubelet-${node_name}.service
scp kubelet-${node_name}.service root@${node_name}:/etc/systemd/system/kubelet.service
done
5、Bootstrap Token Auth 和授予权限
- kubelet 启动时查找配置的
--kubeletconfig
文件是否存在,如果不存在则使用--bootstrap-kubeconfig
向 kube-apiserver 发送证书签名请求 (CSR)。 - kube-apiserver 收到 CSR 请求后,对其中的 Token 进行
文章来源(Source):浅时光博客 认证(事先使用 kubeadm 创建的 token),认证通过后将请求的 user 设置为 system:bootstrap:<Token ID>,group 设置为 system:bootstrappers,这一过程称为 Bootstrap Token Auth。 - 默认情况下,这个 user 和 group 没有创建 CSR 的权限,kubelet 启动失败
- 解决办法是:创建一个 clusterrolebinding,将 group system:bootstrappers 和 clusterrole system:node-bootstrapper 绑定
[root@k8s-node1 work]# kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers
6、启动 kubelet 服务
- 必须创建工作目录;
- 关闭 swap 分区,否则 kubelet 会启动失败;
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kubelet"
ssh root@${node_ip} "/usr/sbin/swapoff -a"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kubelet && systemctl restart kubelet"
done
- kubelet 启动后使用
--bootstrap-kubeconfig
向 kube-apiserver 发送 CSR 请求,当这个 CSR 被 approve 后,kube-controller-manager 为 kubelet 创建 TLS 客户端证书、私钥和--kubeletconfig
文件。 - 注意:kube-controller-manager 需要配置
--cluster-signing-cert-file
和--cluster-signing-key-file
参数,才会为 TLS Bootstrap 创建证书和私钥。
[root@k8s-node1 work]# kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-bqr2j 101s system:node:k8s-node1 Pending
csr-gwxhn 48s system:node:k8s-node2 Pending
csr-hthvc 101s system:node:k8s-node3 Pending
- 三个 work 节点的 csr 均处于 pending 状态;
7、自动 approve CSR 请求
- 创建三个 ClusterRoleBinding,分别用于自动 approve client、renew client、renew server 证书
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > csr-crb.yaml <<EOF
# Approve all CSRs for the group "system:bootstrappers"
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: auto-approve-csrs-for-group
subjects:
- kind: Group
name: system:bootstrappers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
apiGroup: rbac.authorization.k8s.io
---
# To let a node of the group "system:nodes" renew its own credentials
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: node-client-cert-renewal
subjects:
- kind: Group
name: system:nodes
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
apiGroup: rbac.authorization.k8s.io
---
# A ClusterRole which instructs the CSR approver to approve a node requesting a
# serving cert matching its client cert.
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: approve-node-server-renewal-csr
rules:
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/selfnodeserver"]
verbs: ["create"]
---
# To let a node of the group "system:nodes" renew its own server credentials
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: node-server-cert-renewal
subjects:
- kind: Group
name: system:nodes
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: approve-node-server-renewal-csr
apiGroup: rbac.authorization.k8s.io
EOF
参数说明:
- auto-approve-csrs-for-group:自动 approve node 的第一次 CSR; 注意第一次 CSR 时,请求的 Group 为 system:bootstrappers;
- node-client-cert-renewal:自动 approve node 后续过期的 client 证书,自动生成的证书 Group 为 system:nodes;
- node-server-cert-renewal:自动 approve node 后续过期的 server 证书,自动生成的证书 Group 为 system:nodes;
7.2:生效配置
[root@k8s-node1 work]# kubectl apply -f csr-crb.yaml

7.3:查看 kubelet 的情况
- 等待一段时间(1-10 分钟),三个节点的 CSR 都被自动 approved
[root@k8s-node1 work]# kubectl get csr

- 所有节点均 ready
[root@k8s-node1 work]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-node1 Ready <none> 6m12s v1.17.1
k8s-node2 Ready <none> 6m12s v1.17.1
k8s-node3 Ready <none> 6m7s v1.17.1
- kube-controller-manager 为各 node 生成了 kubeconfig 文件和公私钥
[root@k8s-node1 work]# ls -l /etc/kubernetes/kubelet.kubeconfig
[root@k8s-node1 work]# ls -l /etc/kubernetes/cert/|grep kubelet
8、手动 approve server cert csr
- 基于安全性考虑,CSR approving controllers 默认不会自动 approve kubelet server 证书签名请求,需要手动 approve。
- 使用kubectl get csr查看各节点的证书文件名称,然后将节点的证书文件手动赞同
[root@k8s-node1 work]# kubectl get csr

[root@k8s-node1 work]# kubectl certificate approve <证书NAME>

9、kubelet 提供的 API 接口
[root@k8s-node1 work]# netstat -lnpt|grep kubelet

- 10248: healthz http 服务;
- 10250: https API 服务;注意:未开启只读端口 10255;
- 例如执行 kubectl exec -it nginx-ds-5rmws — sh 命令时,kube-apiserver 会向 kubelet 发送如下请求:
- 由于关闭了匿名认证,同时开启了 webhook 授权,所有访问 10250 端口 https API 的请求都需要被认证和授权。
- 预定义的 ClusterRole system:kubelet-api-admin 授予访问 kubelet 所有 API 的权限(kube-apiserver 使用的 kubernetes 证书 User 授予了该权限)
[root@k8s-node1 work]# kubectl describe clusterrole system:kubelet-api-admin

10、kubelet api 认证和授权
- kubelet 配置了如下认证参数:
authentication.anonymous.enabled
:设置为 false,不允许匿名访问 10250 端口;authentication.x509.clientCAFile
:指定签名客户端证书的 CA 证书,开启 HTTPs 证书认证;authentication.webhook.enabled=true
:开启 HTTPs bearer token 认证;
- 同时配置了如下授权参数:
authroization.mode=Webhook
:开启 RBAC 授权;- kubelet 收到请求后,使用 clientCAFile 对证书签名进行认证,或者查询 bearer token 是否有效。如果两者都没通过,则拒绝请求,提示 Unauthorized
- 在master主机访问work节点
[root@k8s-node1 work]# curl -s --cacert /etc/kubernetes/cert/ca.pem https://192.168.66.15:10250/metrics
Unauthorized
[root@k8s-node1 work]# curl -s --cacert /etc/kubernetes/cert/ca.pem -H "Authorization: Bearer 123456" https://192.168.66.15:10250/metrics
Unauthorized
- 通过认证后,kubelet 使用 SubjectAccessReview API 向 kube-apiserver 发送请求,查询证书或 token 对应的 user、group 是否有操作资源的权限(RBAC);
10.1:证书认证和授权
- 权限不足的证书;
[root@k8s-node1 work]# curl -s --cacert /etc/kubernetes/cert/ca.pem --cert /etc/kubernetes/cert/kube-controller-manager.pem --key /etc/kubernetes/cert/kube-controller-manager-key.pem https://192.168.66.15:10250/metrics
- 输出如下:
Forbidden (user=system:kube-controller-manager, verb=get, resource=nodes, subresource=metrics)
- 使用部署 kubectl 命令行工具时创建的、具有最高权限的 admin 证书;
[root@k8s-node1 work]# curl -s --cacert /etc/kubernetes/cert/ca.pem --cert /opt/k8s/work/admin.pem --key /opt/k8s/work/admin-key.pem https://192.168.66.15:10250/metrics|head
- 输出如下:

参数解释:
--cacert
、--cert
、--key
的参数值必须是文件路径,如上面的 ./admin.pem
不能省略 ./
,否则返回 401 Unauthorized;
10.2、bear token 认证和授权
- 创建一个 ServiceAccount,将它和 ClusterRole system:kubelet-api-admin 绑定,从而具有调用 kubelet API 的权限
[root@k8s-node1 work]# kubectl create sa kubelet-api-test
[root@k8s-node1 work]# kubectl create clusterrolebinding kubelet-api-test --clusterrole=system:kubelet-api-admin --serviceaccount=default:kubelet-api-test
[root@k8s-node1 work]# SECRET=$(kubectl get secrets | grep kubelet-api-test | awk '{print $1}')
[root@k8s-node1 work]# TOKEN=$(kubectl describe secret ${SECRET} | grep -E '^token' | awk '{print $2}')
[root@k8s-node1 work]# echo ${TOKEN}
[root@k8s-node1 work]# curl -s --cacert /etc/kubernetes/cert/ca.pem -H "Authorization: Bearer ${TOKEN}" https://192.168.66.15:10250/metrics|head

10.3、cadvisor 和 metrics
- cadvisor 统计所在节点各容器的资源(CPU、内存、磁盘、网卡)使用情况,分别在自己的 http web 页面(4194 端口)和 10250 以 promehteus metrics 的形式输出。
- 浏览器访问 https://192.168.66.15:10250/metrics和 https://192.168.66.15:10250/metrics/cadvisor 分别返回 kubelet 和 cadvisor 的 metrics。

- 注意:
- kubelet.config.json 设置 authentication.anonymous.enabled 为 false,不允许匿名证书访问 10250 的 https 服务;
11:获取kubelet配置
[root@k8s-node1 work]# curl -sSL --cacert /etc/kubernetes/cert/ca.pem --cert /opt/k8s/work/admin.pem --key /opt/k8s/work/admin-key.pem ${KUBE_APISERVER}/api/v1/nodes/k8s-node3/proxy/configz | jq \
'.kubeletconfig|.kind="KubeletConfiguration"|.apiVersion="kubelet.config.k8s.io/v1beta1"'
{
"syncFrequency": "1m0s",
"fileCheckFrequency": "20s",
"httpCheckFrequency": "20s",
"address": "10.18.197.124",
"port": 10250,
"rotateCertificates": true,
"serverTLSBootstrap": true,
"authentication": {
"x509": {
"clientCAFile": "/etc/kubernetes/cert/ca.pem"
},
"webhook": {
"enabled": true,
"cacheTTL": "2m0s"
},
"anonymous": {
"enabled": false
}
},
"authorization": {
"mode": "Webhook",
"webhook": {
"cacheAuthorizedTTL": "5m0s",
"cacheUnauthorizedTTL": "30s"
}
},
"registryPullQPS": 0,
"registryBurst": 10,
"eventRecordQPS": 0,
"eventBurst": 10,
"enableDebuggingHandlers": true,
"healthzPort": 10248,
"healthzBindAddress": "127.0.0.1",
"oomScoreAdj": -999,
"clusterDomain": "cluster.local",
"clusterDNS": [
"10.254.0.2"
],
"streamingConnectionIdleTimeout": "4h0m0s",
"nodeStatusUpdateFrequency": "10s",
"nodeStatusReportFrequency": "5m0s",
"nodeLeaseDurationSeconds": 40,
"imageMinimumGCAge": "2m0s",
"imageGCHighThresholdPercent": 85,
"imageGCLowThresholdPercent": 80,
"volumeStatsAggPeriod": "1m0s",
"cgroupsPerQOS": true,
"cgroupDriver": "cgroupfs",
"cpuManagerPolicy": "none",
"cpuManagerReconcilePeriod": "10s",
"topologyManagerPolicy": "none",
"runtimeRequestTimeout": "15m0s",
"hairpinMode": "promiscuous-bridge",
"maxPods": 220,
"podCIDR": "172.30.0.0/16",
"podPidsLimit": -1,
"resolvConf": "/etc/resolv.conf",
"cpuCFSQuota": true,
"cpuCFSQuotaPeriod": "100ms",
"maxOpenFiles": 1000000,
"contentType": "application/vnd.kubernetes.protobuf",
"kubeAPIQPS": 1000,
"kubeAPIBurst": 2000,
"serializeImagePulls": false,
"evictionHard": {
"imagefs.available": "15%",
"memory.available": "100Mi",
"nodefs.available": "10%",
"nodefs.inodesFree": "5%"
},
"evictionPressureTransitionPeriod": "5m0s",
"enableControllerAttachDetach": true,
"makeIPTablesUtilChains": true,
"iptablesMasqueradeBit": 14,
"iptablesDropBit": 15,
"failSwapOn": true,
"containerLogMaxSize": "10Mi",
"containerLogMaxFiles": 5,
"configMapAndSecretChangeDetectionStrategy": "Watch",
"enforceNodeAllocatable": [
"pods"
],
"kind": "KubeletConfiguration",
"apiVersion": "kubelet.config.k8s.io/v1beta1"
}
八-(3)、部署 kube-proxy 组件
- kube-proxy 运行在所有 worker 节点上,,它监听 apiserver 中 service 和 Endpoint 的变化情况,创建路由规则来进行服务负载均衡。
- 本文档讲解部署 kube-proxy 的部署,使用 ipvs 模式。
- 注意:如果没有特殊指明,本文档的所有操作均在 k8s-node1 节点上执行,然后远程分发文件和执行命令。
- 目前如果是按照该文档一步一步搭建过来的,那么所有节点都已经安装了kube-proxy
1、创建 kube-proxy 证书
1.1:创建证书签名请求
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > kube-proxy-csr.json <<EOF
{
"CN": "system:kube-proxy",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
- CN:指定该证书的 User 为
system:kube-proxy
; - 预定义的 RoleBinding
system:node-proxier
将Usersystem:kube-proxy
与 Rolesystem:node-proxier
绑定,该 Role 授予了调用kube-apiserver
Proxy 相关 API 的权限; - 该证书只会被 kube-proxy 当做 client 证书使用,所以 hosts 字段为空;
1.2:生成证书和私钥
[root@k8s-node1 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
ls kube-proxy*

[root@k8s-node1 work]# ls kube-proxy*
kube-proxy.csr kube-proxy-csr.json kube-proxy-key.pem kube-proxy.pem
2、创建和分发 kubeconfig 文件
2.1:创建kubeconfig 文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# kubectl config set-cluster kubernetes \
--certificate-authority=/opt/k8s/work/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kube-proxy.kubeconfig

[root@k8s-node1 work]# kubectl config set-credentials kube-proxy \
--client-certificate=kube-proxy.pem \
--client-key=kube-proxy-key.pem \
--embed-certs=true \
--kubeconfig=kube-proxy.kubeconfig

[root@k8s-node1 work]# kubectl config set-context default \
--cluster=kubernetes \
--user=kube-proxy \
--kubeconfig=kube-proxy.kubeconfig

[root@k8s-node1 work]# kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

参数说明:
--embed-certs=true
:将 ca.pem 和 admin.pem 证书内容嵌入到生成的 kubectl-proxy.kubeconfig 文件中(不加时,写入的是证书文件路径);
2.2:分发 kubeconfig 文件
[root@k8s-node1 work]# for node_name in ${NODE_NAMES[@]}
do
echo ">>> ${node_name}"
scp kube-proxy.kubeconfig root@${node_name}:/etc/kubernetes/
done
3、创建 kube-proxy 配置文件
- 从 v1.10 开始,kube-proxy 部分参数可以配置文件中配置。可以使用 –write-config-to 选项生成该配置文件
3.1:创建 kube-proxy 配置文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat <<EOF | tee kube-proxy-config.yaml.template
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
kubeconfig: "/etc/kubernetes/kube-proxy.kubeconfig"
bindAddress: ##NODE_IP##
clusterCIDR: ${CLUSTER_CIDR}
healthzBindAddress: ##NODE_IP##:10256
hostnameOverride: ##NODE_NAME##
metricsBindAddress: ##NODE_IP##:10249
mode: "ipvs"
EOF
bindAddress
: 监听地址;clientConnection.kubeconfig
: 连接 apiserver 的 kubeconfig 文件;clusterCIDR
: kube-proxy 根据--cluster-cidr
判断集群内部和外部流量,指定--cluster-cidr
或--masquerade-all
选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT;hostnameOverride
: 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 ipvs 规则;mode
: 使用 ipvs 模式;
3.2:为各节点创建和分发 kube-proxy 配置文件
[root@k8s-node1 work]# for (( i=0; i < 3; i++ ))
do
echo ">>> ${NODE_NAMES[i]}"
sed -e "s/##NODE_NAME##/${NODE_NAMES[i]}/" -e "s/##NODE_IP##/${NODE_IPS[i]}/" kube-proxy-config.yaml.template > kube-proxy-config-${NODE_NAMES[i]}.yaml.template
scp kube-proxy-config-${NODE_NAMES[i]}.yaml.template root@${NODE_NAMES[i]}:/etc/kubernetes/kube-proxy-config.yaml
done
4、创建和分发 kube-proxy systemd unit 文件
4.1:创建kube-proxy systemd unit 文件
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > kube-proxy.service <<EOF
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
[Service]
WorkingDirectory=${K8S_DIR}/kube-proxy
ExecStart=/opt/k8s/bin/kube-proxy \\
--config=/etc/kubernetes/kube-proxy-config.yaml \\
--logtostderr=true \\
--v=2
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
4.2:分发 kube-proxy systemd unit 文件
[root@k8s-node1 work]# for node_name in ${NODE_NAMES[@]}
do
echo ">>> ${node_name}"
scp kube-proxy.service root@${node_name}:/etc/systemd/system/
done
5、启动 kube-proxy 服务
- 必须先创建工作目录
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "mkdir -p ${K8S_DIR}/kube-proxy"
ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-proxy && systemctl restart kube-proxy"
done
6、检查启动结果
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "systemctl status kube-proxy|grep Active"
done
>>> 192.168.66.13
Active: active (running) since 四 2020-01-16 12:55:48 CST; 36s ago
>>> 192.168.66.14
Active: active (running) since 四 2020-01-16 12:55:48 CST; 36s ago
>>> 192.168.66.15
Active: active (running) since 四 2020-01-16 12:55:49 CST; 36s ago
#确保状态为 active (running),否则查看日志,确认原因:
[root@k8s-node1 work]# journalctl -u kube-proxy
7、查看监听端口和 metrics
[root@k8s-node1 work]# netstat -lnpt|grep kube-prox
tcp 0 0 192.168.66.13:10249 0.0.0.0:* LISTEN 8454/kube-proxy
tcp 0 0 192.168.66.13:10256 0.0.0.0:* LISTEN 8454/kube-proxy
# 10249:http prometheus metrics port;
# 10256:http healthz port;
8、查看 ipvs 路由规则
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "/usr/sbin/ipvsadm -ln"
done
- 预期输出:
>>> 192.168.66.13
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr
-> 192.168.66.13:6443 Masq 1 0 0
-> 192.168.66.14:6443 Masq 1 0 0
-> 192.168.66.15:6443 Masq 1 0 0
>>> 192.168.66.14
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr
-> 192.168.66.13:6443 Masq 1 0 0
-> 192.168.66.14:6443 Masq 1 0 0
-> 192.168.66.15:6443 Masq 1 0 0
>>> 192.168.66.15
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr
-> 192.168.66.13:6443 Masq 1 0 0
-> 192.168.66.14:6443 Masq 1 0 0
-> 192.168.66.15:6443 Masq 1 0 0
- 可见将所有到 kubernetes cluster ip 443 端口的请求都转发到 kube-apiserver 的 6443 端口;
九、验证集群功能
- 本文档使用 daemonset 验证 master 和 worker 节点是否工作正常。
- 注意:如果没有特殊指明,本文档的所有操作均在k8s-node1节点上执行,然后远程分发文件和执行命令。
1、检查节点状态
[root@k8s-node1 work]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-node1 Ready <none> 18h v1.17.1
k8s-node2 Ready <none> 19h v1.17.1
k8s-node3 Ready <none> 19h v1.17.1
- 都为
Ready
时正常
2、创建测试文件
- 通过YAML文件创建一个nginx的Deployment
[root@k8s-node1 ~]# cd /opt/k8s/work
[root@k8s-node1 work]# cat > nginx-test.yml <<EOF
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx
labels:
addonmanager.kubernetes.io/mode: Reconcile
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: my-nginx
image: nginx:1.16.1
ports:
- containerPort: 80
EOF
3、执行定义文件
[root@k8s-node1 work]# kubectl apply -f nginx-test.yml
deployment.apps/nginx-deployment created
- 查看Pod
[root@k8s-node1 work]# kubectl get pods -o wide|grep nginx

- 可以看到当前nginx的Pod IP分别是:172.30.240.3,172.30.128.2,172.30.192.2
- 在所有 Node 上分别 ping 这三个 IP,看是否连通:
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh ${node_ip} "ping -c 1 172.30.240.3"
ssh ${node_ip} "ping -c 1 172.30.128.2"
ssh ${node_ip} "ping -c 1 172.30.192.2"
done
- 确保各节点的Pod IP相互可以ping通
4、kubectl相关使用命令
- 查看Pod详细信息,如果Pod没有运行可以查看详细信息找原因
[root@k8s-node1 work]# kubectl describe pod nginx-mxmfj
- 删除Pod
[root@k8s-node1 work]# kubectl delete pods nginx-deployment-5d66cc795f-kdqhk
pod "nginx-deployment-5d66cc795f-kdqhk" deleted
- 删除整个Pod文件
[root@k8s-node1 work]# kubectl delete -f nginx-test.yml
deployment.apps "nginx-deployment" deleted
5、检查服务 IP 和端口可达性
5.1:查看nginx服务
[root@k8s-node1 work]# kubectl get svc |grep nginx
nginx NodePort 10.254.254.189 <none> 80:30064/TCP 61m
# Service Cluster IP:10.254.254.189
# 服务端口:80
# NodePort 端口:30064
5.2:在所有Node上curl Server IP
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh ${node_ip} "curl 10.254.254.189"
done
- 预期输出 nginx 欢迎页面内容

6、检查服务的 NodePort 可达性
- 在所有Node节点执行
[root@k8s-node1 work]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh ${node_ip} "curl ${node_ip}:30064"
done
- 浏览器访问
- 192.168.66.13:30064
- 192.168.66.14:30064
- 192.168.66.15:30064
声明:本文参考文章【和我一步步部署 kubernetes 集群】搭建部署。
必须 注册 为本站用户, 登录 后才可以发表评论!