Kubernetes / 云计算

二进制搭建高可用K8S集群-v1.17

浅时光 · 3月3日 · 2020年 12834次已读

一、环境描述

主机 IP 地址 节点
K8S-node1 192.168.66.13 文章来源(Source):https://www.dqzboy.com 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:保文章来源(Source):https://www.dqzboy.com存整个集群的状态和数据
  • kube-apiserver:提供资源操作的唯一入口,并提供认证、授权、访问控制、API注册和服务发现等机制
  • kube-c文章来源(Source):https://www.dqzboy.comontroller-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获取

二进制搭建高可用K8S集群-v1.17-浅时光博客
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 注意:不管是Master节点还是Node节点,都使用的是Server端程序包
  • 下载client程序包,安装kubectl命令行工具需要使用该程序包
二进制搭建高可用K8S集群-v1.17-浅时光博客

4.2:ETCD获取

二进制搭建高可用K8S集群-v1.17-浅时光博客

4.3:flannel获取

  • Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。
  • flannel-3.3.18:https://github.com/coreos/flannel/releases
二进制搭建高可用K8S集群-v1.17-浅时光博客

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/v1beta1apps/v1beta1apps/v1beta2 提供服务
    • 迁移到从 v1.9 开始提供的 apps/v1 API。现有的持久化数据可以通过 apps/v1 API 检索、更新
  • 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节点操作
[[email protected] ~]# 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"

[[email protected] ~]# systemctl restart network

1、主机名配置

[[email protected] ~]# hostnamectl set-hostname k8s-node1

2、HOSTS配置

[[email protected] ~]# vi /etc/hosts
192.168.66.13   k8s-node1
192.168.66.14   k8s-node2
192.168.66.15   k8s-node3

3、关闭swap

  • 永久关闭,禁用swap这行
[[email protected] ~]# vi /etc/fstab
#/dev/mapper/centos-swap swap        swap    defaults        0 0

4、关闭防火墙

  • 注意:一定确认关闭了firewalld或者iptables;因为K8S在后面部署过程中会自动生成一大堆的iptables规则

4.1:关闭firewalld

[[email protected] ~]# systemctl stop firewalld
[[email protected] ~]# systemctl disable firewall

4.2:关闭SELinux

# 查看SELinux状态
[[email protected] ~]# sestatus
[[email protected] ~]# setenforce 0
[[email protected] ~]# getenforce
[[email protected] ~]# sed -ri 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

5、常用命令

[[email protected] ~]# 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、时间同步

[[email protected] ~]# systemctl start ntpd
[[email protected] ~]# systemctl enable ntpd
[[email protected] ~]# echo "*/5 * * * *  ntpdate time1.aliyun.com &> /dev/null && hwclock -w" >> /var/spool/cron/root

#调整系统 TimeZone
[[email protected] ~]# timedatectl set-timezone Asia/Shanghai

#将当前的 UTC 时间写入硬件时钟
[[email protected] ~]# timedatectl set-local-rtc 0

#重启依赖于系统时间的服务
[[email protected] ~]# systemctl restart rsyslog 
[[email protected] ~]# systemctl restart crond

7、配置免密

  • K8S-node1实现ssh免密登入其他节点
[[email protected] ~]# ssh-keygen -t rsa
[[email protected] ~]# ssh-copy-id [email protected]
[[email protected] ~]# ssh-copy-id [email protected]
[[email protected] ~]# ssh-copy-id [email protected]
[[email protected] ~]# ssh-copy-id k8s-node1
[[email protected] ~]# ssh-copy-id k8s-node2
[[email protected] ~]# 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  #开启OOM
    • 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 #关闭防火墙会提示无该文件或目录,忽略即可
  • 必须关闭 tcp_tw_recycle,否则和 NAT 冲突,会导致服务不通;关闭 IPV6,防止触发 docker BUG;
[[email protected] ~]# 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: 没有那个文件或目录
#解决方案:
[[email protected] ~]# cd /etc/modules-load.d/
[[email protected] ~]# vim netfilter.conf
nf_conntrack

[[email protected] ~]# for i in 192.168.66.{14,15};do scp /etc/modules-load.d/netfilter.conf [email protected]$i:/etc/modules-load.d/;done
[[email protected] ~]# for i in 192.168.66.{13..15};do ssh [email protected]$i "reboot";done
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$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.可以限制日志文件大小、保存的时间;
  • journald 默认将日志转发给 rsyslog,这会导致日志写了多份,/var/log/messages 中包含了太多无关日志,不方便后续查看,同时也影响系统性能。
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "mkdir /var/log/journal";done
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "mkdir /etc/systemd/journald.conf.d";done

[[email protected] ~]# 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

[[email protected] ~]# for i in 192.168.66.{14,15};do echo $i;scp /etc/systemd/journald.conf.d/99-prophet.conf [email protected]$i:/etc/systemd/journald.conc.d/;done
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "systemctl restart systemd-journald";done

10、将可执行文件路径到PATH变量中

  • 在每台机器上添加环境变量:/opt/k8s/bin
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "echo 'PATH=/opt/k8s/bin:$PATH' >>/root/.bashrc";done

11、创建相关目录

  • 在每台机器上创建该目录
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "mkdir -p  /opt/k8s/{bin,work}";done

12、升级内核

  • 三节点分别执行
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm";done
  • 安装完成后检查 /boot/grub2/grub.cfg 中对应内核 menuentry 中是否包含 initrd16 配置,如果没有,再安装一次!
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "yum --enablerepo=elrepo-kernel install -y kernel-lt";done
  • 设置开机从新内核启动
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "grub2-set-default 0";done

13、关闭 NUMA

  • 三节点都执行
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "cp /etc/default/grub{,.bak}";done

#在三节点的grub配置文件的最后添加以下参数进去
[[email protected] ~]# vim /etc/default/grub   
#在GRUB_CMDLINE_LINUX 一行添加 numa=off 参数,如下所示:  
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 三节点重新生成 grub2 配置文件:
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "cp /boot/grub2/grub.cfg{,.bak}";done

[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "grub2-mkconfig -o /boot/grub2/grub.cfg
";done
二进制搭建高可用K8S集群-v1.17-浅时光博客

14、添加Docker用户

  • 在每台服务器上添加Docker用户
[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "useradd -m docker";done

[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "id docker";done

15、检查各节点是否适合运行Docker

[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "curl https://raw.githubusercontent.com/docker/docker/master/contrib/check-config.sh > check-config.sh";done

[[email protected] ~]# for i in 192.168.66.{13..15};do echo $i;ssh [email protected]$i "bash /root/check-config.sh";done

16、配置全局环境变量

  • 注意:集群IP请根据自己服务器的实际IP进行修改,修改为自己的
[[email protected] ~]# 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个节点
[[email protected] ~]# scp /etc/profile [email protected]:/etc/
[[email protected] ~]# scp /etc/profile [email protected]:/etc/
  • 三台机器分别执行以下命令是全局变量生效
[[email protected] ~]# source /etc/profile
  • 检查是否生效
[[email protected] ~]# echo ${PATH}
/opt/k8s/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[[email protected] ~]# echo ${CLUSTER_DNS_DOMAIN}
cluster.local

三、配置CA证书

  • 为确保安全,kubernetes系统各组件需要使用 x509 证书对通信进行加密和认证。
  • CA (Certificate Authority) 是自签名的根证书,用来签名后续创建的其它证书。
  • 本文档使用 CloudFlare的 PKI 工具集创建所有证书。
  • 注意:如果没有特殊指明,本文档的所有操作均在k8s-node1节点上执行,然后远程分发文件和执行命令。

1、安装cfssl工具集

[[email protected] ~]# mkdir -p /opt/k8s/cert && cd /opt/k8s
[[email protected] k8s]# wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
[[email protected] k8s]# mv cfssl_linux-amd64 /opt/k8s/bin/cfssl

[[email protected] k8s]# wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
[[email protected] k8s]# mv cfssljson_linux-amd64 /opt/k8s/bin/cfssljson

[[email protected] k8s]# wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
[[email protected] k8s]# mv cfssl-certinfo_linux-amd64 /opt/k8s/bin/cfssl-certinfo

[[email protected] k8s]# chmod +x /opt/k8s/bin/*
[[email protected] k8s]# export PATH=/opt/k8s/bin:$PATH

2、创建根证书(CA)

  • CA 证书是集群所有节点共享的,只需要创建一个 CA 证书,后续创建的所有证书都由它签名。

2.1:创建配置文件

  • CA 配置文件用于配置根证书的使用场景 (profile) 和具体参数 (usage,过期时间、服务端认证、客户端认证、加密等),后续在签名其它证书时需要指定特定场景。
[[email protected] k8s]# cd /opt/k8s/work
[[email protected] 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:创建证书签名请求文件

[[email protected] 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证书和私钥

[[email protected] 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

[[email protected] work]# ls ca*
ca-config.json  ca.csr  ca-csr.json  ca-key.pem  ca.pem

3、分发证书文件

  • 将生成的 CA 证书、秘钥文件、配置文件拷贝到所有节点的 /etc/kubernetes/cert 目录下:
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
   do
     echo ">>> ${node_ip}"
     ssh [email protected]${node_ip} "mkdir -p /etc/kubernetes/cert"
     scp ca*.pem ca-config.json [email protected]${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 二进制文件

  • 我这里直接将下载好的上传至服务器中
[[email protected] work]# pwd
/opt/k8s/work
[[email protected] work]# tar -zxvf kubernetes-client-linux-amd64.tar.gz
  • 分发到所有使用的kubectl节点
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kubernetes/client/bin/kubectl [email protected]${node_ip}:/opt/k8s/bin/
    ssh [email protected]${node_ip} "chmod +x /opt/k8s/bin/*"
  done

2、创建admin证书和私钥

  • kubectl 与 apiserver https 安全端口通信,apiserver 对提供的证书进行认证和授权。
  • kubectl 作为集群的管理工具,需要被授予最高权限。这里创建具有最高权限的 admin 证书。

2.1:创建证书签名请求:

[[email protected] work]# pwd
/opt/k8s/work
[[email protected] 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:生成证书和私钥

[[email protected] work]# pwd
/opt/k8s/work
[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# ls admin*
admin.csr  admin-csr.json  admin-key.pem  admin.pem

3、创建 kubeconfig 文件

  • kubeconfig 为 kubectl 的配置文件,包含访问 apiserver 的所有信息,如 apiserver 地址、CA 证书和自身使用的证书;
  • 注意:确认全局环境变量都是生效的
  • 设置集群参数
[[email protected] work]# pwd
/opt/k8s/work

[[email protected] work]# kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/k8s/work/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kubectl.kubeconfig
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 设置客户端认证参数
[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 设置上下文参数
[[email protected] work]# kubectl config set-context kubernetes \
  --cluster=kubernetes \
  --user=admin \
  --kubeconfig=kubectl.kubeconfig
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 设置默认上下文
[[email protected] 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文件

[[email protected] work]#  for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p ~/.kube"
    scp kubectl.kubeconfig [email protected]${node_ip}:~/.kube/config
  done
  • 保存到用户的 ~/.kube/config 文件;

5、确认kubectl已经可以使用

二进制搭建高可用K8S集群-v1.17-浅时光博客

五、部署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.1文章来源(Source):https://www.dqzboy.com5

1、下载和分发 etcd 二进制文件

[[email protected] ~]# cd /opt/k8s/work

1.1:解压安装

[[email protected] work]# tar -xvf etcd-v3.3.18-linux-amd64.tar.gz

1.2:分发各ETCD节点

[[email protected] work]# pwd
/opt/k8s/work
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp etcd-v3.3.18-linux-amd64/etcd* [email protected]${node_ip}:/opt/k8s/bin
    ssh [email protected]${node_ip} "chmod +x /opt/k8s/bin/*"
  done

2、创建 etcd 证书和私钥

2.1:创建证书签名请求

  • 注意:证书文件中的hosts配置为自己实际的ETCD集群节点IP地址,如果配置错误可能启动ETCD时出现以下错误:error "remote error: tls: bad certificate", ServerName ""
[[email protected] 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:生成证书和私钥

[[email protected] 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

[[email protected] work]# ls etcd*pem
etcd-key.pem  etcd.pem

2.3:分发证书和私钥至各etcd节点

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p /etc/etcd/cert"
    scp etcd*.pem [email protected]${node_ip}:/etc/etcd/cert/
  done

3、创建 etcd 的 systemd unit 模板文件

[[email protected] 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 文件:
[[email protected] 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

[[email protected] 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;
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp etcd-${node_ip}.service [email protected]${node_ip}:/etc/systemd/system/etcd.service
  done

4.3:检查配置文件

[[email protected] work]# ls /etc/systemd/system/etcd.service 
/etc/systemd/system/etcd.service
[[email protected] work]# vim /etc/systemd/system/etcd.service
  • 确认脚本文件中的IP地址和数据存储地址是否都正确

5、启动ETCD服务

  • 必须创建 etcd 数据目录和工作目录;
  • etcd 进程首次启动时会等待其它节点的 etcd 加入集群,命令 systemctl start etcd 会卡住一段时间,为正常现象。
[[email protected] work]# pwd
/opt/k8s/work
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p ${ETCD_DATA_DIR} ${ETCD_WAL_DIR}"
    ssh [email protected]${node_ip} "systemctl daemon-reload && systemctl enable etcd && systemctl restart etcd "
  done

6、检查启动结果

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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),否则查看日志,确认原因:
[[email protected] work]# journalctl -u etcd

7、验证服务状态

7.1:任一etcd节点执行以下命令

[[email protected] work]# pwd
/opt/k8s/work
[[email protected] 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

[[email protected] work]# pwd
/opt/k8s/work
[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 可以看到当前的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:下载并解压安装二进制程序文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# mkdir flannel
[[email protected] work]# wget https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz

[[email protected] work]# tar -zxvf flannel-v0.11.0-linux-amd64.tar.gz -C flannel
flanneld
mk-docker-opts.sh
README.md

1.2:分发 flanneld 二进制文件到集群所有节点:

[[email protected] work]# pwd
/opt/k8s/work
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp flannel/{flanneld,mk-docker-opts.sh} [email protected]${node_ip}:/opt/k8s/bin/
    ssh [email protected]${node_ip} "chmod +x /opt/k8s/bin/*"
  done

2、创建 flannel 证书和私钥

  • flannel 从 etcd 集群存取网段分配信息,而 etcd 集群启用了双向 x509 证书认证,所以需要为 flanneld 生成证书和私钥。

2.1:创建证书签名请求

[[email protected] work]# cd /opt/k8s/work
[[email protected] 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:生成证书和私钥

[[email protected] 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

[[email protected] work]# ls flanneld*pem
flanneld-key.pem  flanneld.pem

2.3:将生成的证书和私钥分发到所有节点(master 和 worker)

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p /etc/flanneld/cert"
    scp flanneld*.pem [email protected]${node_ip}:/etc/flanneld/cert
  done

3、向 etcd 写入集群 Pod 网段信息

  • 注意:本步骤只需执行一次。
[[email protected] work]# cd /opt/k8s/work
[[email protected] 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 文件

[[email protected] work]# cd /opt/k8s/work
[[email protected] 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 文件到所有节点

[[email protected] work]# cd /opt/k8s/work
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp flanneld.service [email protected]${node_ip}:/etc/systemd/system/
  done

6、启动 flanneld 服务

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "systemctl daemon-reload && systemctl enable flanneld && systemctl restart flanneld"
  done

7、检查启动结果

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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)

[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客

8.2:查看已分配的 Pod 子网段列表(/24)

[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 结果是部署情况而定,网段可能与上面不一样

8.3:查看某一 Pod 网段对应的节点 IP 和 flannel 接口地址

[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 注意:这里的IP为上面中查看到的Pod 子网段列表(/24)中的其中一个

9、验证各节点能通过 Pod 网段互通

  • 在各节点上部署flannel 后,检查是否创建了 flannel 接口(名称可能为 flannel0、flannel.0、flannel.1 等)
[[email protected] work]# pwd
/opt/k8s/work
[[email protected] 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,确保能通
[[email protected] 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

[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# wget https://nginx.org/download/nginx-1.16.1.tar.gz

[[email protected] work]# yum -y install gcc-c++
[[email protected] work]# tar -zxvf nginx-1.16.1.tar.gz

[[email protected] work]# cd nginx-1.16.1/
[[email protected] 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:关闭所有其他功能,这样生成的动态链接二进制程序依赖最小;
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] nginx-1.16.1]# make && make install

3、验证编译的Nginx

[[email protected] nginx-1.16.1]# cd /usr/local/nginx/
[[email protected] nginx]# ./sbin/nginx -v
nginx version: nginx/1.16.1
  • 查看Nginx动态链接的库
[[email protected] 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:所有节点创建目录

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp /usr/local/nginx/sbin/nginx [email protected]${node_ip}:/opt/k8s/kube-nginx/sbin/kube-nginx
    ssh [email protected]${node_ip} "chmod a+x /opt/k8s/kube-nginx/sbin/*"
    ssh [email protected]${node_ip} "mkdir -p /opt/k8s/kube-nginx/{conf,logs,sbin}"
  done

4.3: 配置 nginx,开启 4 层透明转发功能

[[email protected] work]#  cd /opt/k8s/work
[[email protected] 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:分发配置文件

[[email protected] work]# cd /opt/k8s/work
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-nginx.conf  [email protected]${node_ip}:/opt/k8s/kube-nginx/conf/kube-nginx.conf
  done

5、配置 systemd unit 文件,启动服务

5.1:配置 kube-nginx systemd unit 文件

[[email protected] work]# cd /opt/k8s/work
[[email protected] 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 文件

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "systemctl daemon-reload && systemctl enable kube-nginx && systemctl restart kube-nginx"
  done

6、检查 kube-nginx 服务运行状态

[[email protected] work]# cd /opt/k8s/work
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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),否则查看日志,确认原因:
[[email protected] 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
[[email protected] ~]# cd /opt/k8s/work/
[[email protected] work]# tar -zxvf kubernetes-server-linux-amd64.tar.gz
[[email protected] work]# cd kubernetes/
 
[[email protected] kubernetes]# tar -zxvf kubernetes-src.tar.gz

2、将二进制文件拷贝至所有节点

[[email protected] ~]# cd /opt/k8s/work/kubernetes/
[[email protected] kubernetes]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp server/bin/* [email protected]${node_ip}:/opt/k8s/bin/
    ssh [email protected]${node_ip} "chmod +x /opt/k8s/bin/*"
  done

七-(2)、部署高可用 kube-apiserver 集群

  • 前提是该主机上已经部署了flannel网络,如果没有部署,参考本文档的第六步骤

1、创建 kubernetes 证书和私钥

1.1:创建证书签名请求

[[email protected] ~]# cd /opt/k8s/work/
[[email protected] 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.default.svc.文章来源(Source):https://www.dqzboy.comcluster.local.),否则解析时失败,提示:x509: cannot parse dnsName "kubernetes.default.svc.cluster.local"
  • 如果使用非 cluster.local 域名,如 opsnull.com,则需要修改域名列表中的最后两个域名为:kubernetes.default.svc.opsnullkubernetes.default.svc.opsnull.com
  • kubernetes 服务 IP 是 apiserver 自动创建的,一般是 --service-cluster-ip-range 参数指定的网段的第一个IP,后续可以通过如下命令获取:

1.2:生成证书和私钥

[[email protected] 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

[[email protected] work]# ls kubernetes*pem
kubernetes-key.pem  kubernetes.pem

1.3:将生成的证书和私钥文件拷贝到 master 节点

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p /etc/kubernetes/cert"
    scp kubernetes*.pem [email protected]${node_ip}:/etc/kubernetes/cert/
  done

2、创建加密配置文件

[[email protected] work]# cd /opt/k8s/work
[[email protected] 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 目录下

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp encryption-config.yaml [email protected]${node_ip}:/etc/kubernetes/
  done

3、创建 kube-apiserver systemd unit 模板文件

[[email protected] ~]# cd /opt/k8s/work/
[[email protected] 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 文件

[[email protected] 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

[[email protected] work]# ls kube-apiserver*.service
  • NODE_NAMES 和 NODE_IPS 为相同长度的 bash 数组,分别为节点名称和对应的 IP;

4.2:分发生成的 systemd unit 文件

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-apiserver-${node_ip}.service [email protected]${node_ip}:/etc/systemd/system/kube-apiserver.service
  done
  • 文件重命名为 kube-apiserver.service;

5、启动 kube-apiserver 服务

  • 必须创建工作目录
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p ${K8S_DIR}/kube-apiserver"
    ssh [email protected]${node_ip} "systemctl daemon-reload && systemctl enable kube-apiserver && systemctl restart kube-apiserver"
  done

6、检查 kube-apiserver 运行状态

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "systemctl status kube-apiserver |grep 'Active:'"
  done

7、打印 kube-apiserver 写入 etcd 的数据

[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客

8、检查集群信息

[[email protected] work]#  kubectl cluster-info
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# kubectl get all --all-namespaces
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# kubectl get componentstatuses
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 注意:
    • 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,但实际上它们工作正常。

9、检查 kube-apiserver 监听的端口

[[email protected] 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内容 会出现如下报错:
[[email protected] 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)

[[email protected] 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:创建证书签名请求

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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:生成证书和私钥

[[email protected] 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

[[email protected] work]# ls kube-controller-manager*pem

1.3:将生成的证书和私钥分发到所有 master 节点

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-controller-manager*.pem [email protected]${node_ip}:/etc/kubernetes/cert/
  done

2、创建和分发 kubeconfig 文件

  • kubeconfig 文件包含访问 apiserver 的所有信息,如 apiserver 地址、CA 证书和自身使用的证书;

2.1:配置kubeconfig文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/k8s/work/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kube-controller-manager.kubeconfig
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# kubectl config set-context system:kube-controller-manager \
  --cluster=kubernetes \
  --user=system:kube-controller-manager \
  --kubeconfig=kube-controller-manager.kubeconfig
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig

2.2:分发 kubeconfig 到所有 master 节点

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-controller-manager.kubeconfig [email protected]${node_ip}:/etc/kubernetes/
  done

3、创建和分发 kube-controller-manager systemd unit 文件

3.1:创建kube-controller-manager systemd模板文件

[[email protected] ~]# cd /opt/k8s/work/
[[email protected] 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 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 节点

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-controller-manager.service [email protected]${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,赋予相应的权限
[[email protected] 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 服务

  • 必须创建工作目录;
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p ${K8S_DIR}/kube-controller-manager"
    ssh [email protected]${node_ip} "systemctl daemon-reload && systemctl enable kube-controller-manager && systemctl restart kube-controller-manager"
  done

6、检查服务运行状态

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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-controller-manager 节点上执行。
  • kube-controller-manager 监听 10252 端口,接收 https 请求:
[[email protected] work]# netstat -lnpt|grep kube-controll
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] 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

[[email protected] work]# kubectl get endpoints kube-controller-manager --namespace=kube-system  -o yaml
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 可见上图红框中为当前的leader为k8s-node1节点

七-(4)、部署高可用 kube-scheduler 集群

1、创建 kube-sch文章来源(Source):https://www.dqzboy.comeduler 证书和私钥

1.1:创建证书签名请求

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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:生成证书和私钥

[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# ls kube-scheduler*pem
kube-scheduler-key.pem  kube-scheduler.pem

2、创建和分发 kubeconfig 文件

  • kubeconfig 文件包含访问 apiserver 的所有信息,如 apiserver 地址、CA 证书和自身使用的证书;

2.1:创建kubeconfig文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/k8s/work/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kube-scheduler.kubeconfig


[[email protected] 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


[[email protected] work]# kubectl config set-context system:kube-scheduler \
  --cluster=kubernetes \
  --user=system:kube-scheduler \
  --kubeconfig=kube-scheduler.kubeconfig


[[email protected] work]# kubectl config use-context system:kube-scheduler --kubeconfig=kube-scheduler.kubeconfig
  • 上一步创建的证书、私钥以及 kube-apiserver 地址被写入到 kubeconfig 文件中;

2.2:分发 kubeconfig 到所有 master 节点

[[email protected] work]#  for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-scheduler.kubeconfig [email protected]${node_ip}:/etc/kubernetes/
  done

3、创建 kube-scheduler 配置文件

3.1:创建 kube-scheduler 配置文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • --kubeconfig:指定 kubeconfig 文件路径,kube-scheduler 使用它连接和验证 kube-apiserver;
  • --leader-elect=true:集群运行模式,启用选举功能;被选为 leader 的节点负责处理工作,其它节点为阻塞状态;

3.2:分发 kube-scheduler 配置文件到所有 master 节点

[[email protected] work]#  for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-scheduler.yaml [email protected]${node_ip}:/etc/kubernetes/
  done

4、创建和分发 kube-scheduler systemd unit 文件

4.1:创建kube-scheduler文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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 节点

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-scheduler.service [email protected]${node_ip}:/etc/systemd/system/
  done

5、启动 kube-scheduler 服务

  • 必须先创建工作目录;
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p ${K8S_DIR}/kube-scheduler"
    ssh [email protected]${node_ip} "systemctl daemon-reload && systemctl enable kube-scheduler && systemctl restart kube-scheduler"
  done

6、检查服务运行状态

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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 请求:
[[email protected] work]# netstat -lnpt|grep kube-sche
[[email protected] work]# curl -s http://127.0.0.1:10251/metrics |head
二进制搭建高可用K8S集群-v1.17-浅时光博客

8、查看当前的 leader

[[email protected] work]# kubectl get endpoints kube-scheduler --namespace=kube-system  -o yaml
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 可看到当前leader为k8s-node2

八-(0)、部署 worker 节点

  • kubernetes work 节点运行如下组件:
  • docker
  • kubelet
  • kube-proxy
  • flanneld
  • kube-nginx
  • 注意:如果没有特殊指明,本文档的所有操作均在k8s-node1节点上执行,然后远程分发文件和执行命令。

1、安装依赖包

[[email protected] ~]# cd /opt/k8s/work/
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "yum install -y epel-release"
    ssh [email protected]${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:下载程序包

[[email protected] ~]# cd /opt/k8s/work/
[[email protected] work]# wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.5.tgz

[[email protected] work]# tar -xzvf docker-19.03.5.tgz

1.2:分发程序包

[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp docker/*  [email protected]${node_ip}:/opt/k8s/bin/
    ssh [email protected]${node_ip} "chmod +x /opt/k8s/bin/*"
  done

2、创建和分发 systemd unit 文件

2.1:创建systemd unit 文件

[[email protected] 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
[[email protected] work]# iptables -P FORWARD ACCEPT
  • 并且把以下命令写入 /etc/rc.local文件中,防止节点重启iptables FORWARD chain的默认策略又还原为DROP
[[email protected] work]# vim /etc/rc.local
/sbin/iptables -P FORWARD ACCEPT

2.2:分发 systemd unit 文件到所有 worker 机器

[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# sed -i -e "s|##DOCKER_DIR##|${DOCKER_DIR}|" docker.service
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp docker.service [email protected]${node_ip}:/etc/systemd/system/
  done

3、配置和分发 docker 配置文件

  • 使用国内的仓库镜像服务器以加快 pull image 的速度,同时增加下载的并发数 (需要重启 dockerd 生效)

3.1:配置docker加速

  • 由于网络环境原因。默认去下载官方的docker hub会下载失败所以使用阿里云的docker加速器
  • 登入自己的阿里云生成docker.json,阿里云镜像地址
[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# cat > docker-daemon.json <<EOF
{
  "registry-mirrors": ["https://a7ye1cuu.mirror.aliyuncs.com"]
}
EOF

3.2:分发至work节点

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p  /etc/docker/ ${DOCKER_DIR}/{data,exec}"
    scp docker-daemon.json [email protected]${node_ip}:/etc/docker/daemon.json
  done

4、启动 docker 服务

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "systemctl stop firewalld && systemctl disable firewalld"
    ssh [email protected]${node_ip} "/usr/sbin/iptables -F && /usr/sbin/iptables -X && /usr/sbin/iptables -F -t nat && /usr/sbin/iptables -X -t nat"
    ssh [email protected]${node_ip} "/usr/sbin/iptables -P FORWARD ACCEPT"
    ssh [email protected]${node_ip} "systemctl daemon-reload && systemctl enable docker && systemctl restart docker"
    #ssh [email protected]${node_ip} 'for intf in /sys/devices/virtual/net/docker0/brif/*; do echo 1 > $intf/hairpin_mode; done'
    ssh [email protected]${node_ip} "sudo sysctl -p /etc/sysctl.d/kubernetes.conf"
  done
  • 关闭 firewalld(centos7)/ufw(ubuntu16.04),否则可能会重复创建 iptables 规则;
  • 清理旧的 iptables rules 和 chains 规则;
  • 开启 docker0 网桥下虚拟网卡的 hairpin 模式;

5、检查服务运行状态

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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 网桥

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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:创建文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 证书中写入 Token 而非证书,证书后续由 kube-controller-manager 创建

1.2:查看 kubeadm 为各节点创建的 token

[[email protected] work]# kubeadm token list --kubeconfig ~/.kube/config
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 删除token命令:
[[email protected] 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

[[email protected] work]# kubectl get secrets  -n kube-system|grep bootstrap-token
二进制搭建高可用K8S集群-v1.17-浅时光博客

2、分发 bootstrap kubeconfig 文件到所有 worker 节点

[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# for node_name in ${NODE_NAMES[@]}
  do
    echo ">>> ${node_name}"
    scp kubelet-bootstrap-${node_name}.kubeconfig [email protected]${node_name}:/etc/kubernetes/kubelet-bootstrap.kubeconfig
  done

3、创建和分发 kubelet 参数配置文件

3.1:创建 kubelet 参数配置模板文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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.RotateKubeletClientCertificatefeatureGates.RotateKubeletServerCertificate:自动 rotate 证书,证书的有效期取决于 kube-controller-manager 的 --experimental-cluster-signing-duration 参数;
  • 需要 root 账户运行;

3.2:为各节点创建和分发 kubelet 配置文件

[[email protected] 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 [email protected]${node_ip}:/etc/kubernetes/kubelet-config.yaml
  done

4、创建和分发 kubelet systemd unit 文件

4.1:创建 kubelet systemd unit 文件模板

[[email protected] 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 镜像,它不能回收容器的僵尸;文章来源(Source):https://www.dqzboy.com这是Pause容器,又叫Infra容器,默认的镜像仓库地址网络原因无法下载,这里使用阿里云的,注意一定要选择国内的Pause源,不然后面无法启动Pod;阿里云Pause地址,最新版本3.1
二进制搭建高可用K8S集群-v1.17-浅时光博客

4.2:为各节点创建和分发 kubelet systemd unit 文件

[[email protected] 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 [email protected]${node_name}:/etc/systemd/system/kubelet.service
  done

5、Bootstrap Token Auth 和授予权限

  • kubelet 启动时查找配置的 --kubeletconfig 文件是否存在,如果不存在则使用 --bootstrap-kubeconfig 向 kube-apiserver 发送证书签名请求 (CSR)。
  • kube-apiserver 收到 CSR 请求后,对其中的 Token 进行认证(事先使用 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 绑定
[[email protected] work]# kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers

6、启动 kubelet 服务

  • 必须创建工作目录;
  • 关闭 swap 分区,否则 kubelet 会启动失败;
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p ${K8S_DIR}/kubelet"
    ssh [email protected]${node_ip} "/usr/sbin/swapoff -a"
    ssh [email protected]${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 创建证书和私钥。
[[email protected] 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 证书
[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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-cli文章来源(Source):https://www.dqzboy.coment-cert-renewal:自动 approve 文章来源(Source):https://www.dqzboy.comnode 后续过期的 client 证书,自动生成的证书 Group 为 system:nodes;
  • node-server-cert-renewal:自动 approve node 后续过期的 server 证书,自动生成的证书 Group 为 system:nodes;

7.2:生效配置

[[email protected] work]# kubectl apply -f csr-crb.yaml
二进制搭建高可用K8S集群-v1.17-浅时光博客

7.3:查看 kubelet 的情况

  • 等待一段时间(1-10 分钟),三个节点的 CSR 都被自动 approved
[[email protected] work]# kubectl get csr
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 所有节点均 ready
[[email protected] 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 文件和公私钥
[[email protected] work]# ls -l /etc/kubernetes/kubelet.kubeconfig
[[email protected] work]# ls -l /etc/kubernetes/cert/|grep kubelet

8、手动 approve server cert csr

  • 基于安全性考虑,CSR approving controllers 默认不会自动 approve kubelet server 证书签名请求,需要手动 approve。
  • 使用kubectl get csr查看各节点的证书文件名称,然后将节点的证书文件手动赞同
[[email protected] work]# kubectl get csr
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# kubectl certificate approve <证书NAME>
二进制搭建高可用K8S集群-v1.17-浅时光博客

9、kubelet 提供的 API 接口

[[email protected] work]# netstat -lnpt|grep kubelet
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 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 授予了该权限)
[[email protected] work]# kubectl describe clusterrole system:kubelet-api-admin
二进制搭建高可用K8S集群-v1.17-浅时光博客

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节点
[[email protected] work]# curl -s --cacert /etc/kubernetes/cert/ca.pem https://192.168.66.15:10250/metrics

Unauthorized

[[email protected] 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:证书认证和授权

  • 权限不足的证书;
[[email protected] 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 证书;
[[email protected] 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
  • 输出如下:
二进制搭建高可用K8S集群-v1.17-浅时光博客

参数解释:

--cacert--cert--key 的参数值必须是文件路径,如上面的 ./admin.pem 不能省略 ./,否则返回 401 Unauthorized;

10.2、bear token 认证和授权

  • 创建一个 ServiceAccount,将它和 ClusterRole system:kubelet-api-admin 绑定,从而具有调用 kubelet API 的权限

[[email protected] work]# kubectl create sa kubelet-api-test
[[email protected] work]# kubectl create clusterrolebinding kubelet-api-test --clusterrole=system:kubelet-api-admin --serviceaccount=default:kubelet-api-test

[[email protected] work]# SECRET=$(kubectl get secrets | grep kubelet-api-test | awk '{print $1}')
[[email protected] work]# TOKEN=$(kubectl describe secret ${SECRET} | grep -E '^token' | awk '{print $2}')
[[email protected] work]# echo ${TOKEN}

[[email protected] work]# curl -s --cacert /etc/kubernetes/cert/ca.pem -H "Authorization: Bearer ${TOKEN}" https://192.168.66.15:10250/metrics|head
二进制搭建高可用K8S集群-v1.17-浅时光博客

10.3、cadvisor 和 metrics

二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 注意:
    • kubelet.config.json 设置 authentication.anonymous.enabled 为 false,不允许匿名证书访问 10250 的 https 服务;

11:获取kubelet配置

[[email protected] 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:创建证书签名请求

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;
  • 该证书只会被 kube-proxy 当做 文章来源(Source):https://www.dqzboy.comclient 证书使用,所以 hosts 字段为空;

1.2:生成证书和私钥

[[email protected] 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*
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# ls kube-proxy*
kube-proxy.csr  kube-proxy-csr.json  kube-proxy-key.pem  kube-proxy.pem

2、创建和分发 kubeconfig 文件

2.1:创建kubeconfig 文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] work]# kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/k8s/work/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kube-proxy.kubeconfig
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# kubectl config set-context default \
  --cluster=kubernetes \
  --user=kube-proxy \
  --kubeconfig=kube-proxy.kubeconfig
二进制搭建高可用K8S集群-v1.17-浅时光博客
[[email protected] work]# kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
二进制搭建高可用K8S集群-v1.17-浅时光博客

参数说明:

--embed-certs=true:将 ca.pem 和 admin.pem 证书内容嵌入到生成的 kubectl-proxy.kubeconfig 文件中(不加时,写入的是证书文件路径);

2.2:分发 kubeconfig 文件

[[email protected] work]# for node_name in ${NODE_NAMES[@]}
  do
    echo ">>> ${node_name}"
    scp kube-proxy.kubeconfig [email protected]${node_name}:/etc/kubernetes/
  done

3、创建 kube-proxy 配置文件

  • 从 v1.10 开始,kube-proxy 部分参数可以配置文件中配置。可以使用 –write-config-to 选项生成该配置文件

3.1:创建 kube-proxy 配置文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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 配置文件

[[email protected] 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 [email protected]${NODE_NAMES[i]}:/etc/kubernetes/kube-proxy-config.yaml
  done

4、创建和分发 kube-proxy systemd unit 文件

4.1:创建kube-proxy systemd unit 文件

[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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 文件

[[email protected] work]# for node_name in ${NODE_NAMES[@]}
  do 
    echo ">>> ${node_name}"
    scp kube-proxy.service [email protected]${node_name}:/etc/systemd/system/
  done

5、启动 kube-proxy 服务

  • 必须先创建工作目录
[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${node_ip} "mkdir -p ${K8S_DIR}/kube-proxy"
    ssh [email protected]${node_ip} "systemctl daemon-reload && systemctl enable kube-proxy && systemctl restart kube-proxy"
  done

6、检查启动结果

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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),否则查看日志,确认原因:
[[email protected] work]# journalctl -u kube-proxy

7、查看监听端口和 metrics

[[email protected] 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 路由规则

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh [email protected]${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、检查节点状态

[[email protected] 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
[[email protected] ~]# cd /opt/k8s/work
[[email protected] 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、执行定义文件

[[email protected] work]# kubectl apply -f nginx-test.yml 
deployment.apps/nginx-deployment created
  • 查看Pod
[[email protected] work]# kubectl get pods -o wide|grep nginx
二进制搭建高可用K8S集群-v1.17-浅时光博客
  • 可以看到当前nginx的Pod IP分别是:172.30.240.3,172.30.128.2,172.30.192.2
  • 在所有 Node 上分别 ping 这三个 IP,看是否连通:
[[email protected] 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没有运行可以查看详细信息找原因
[[email protected] work]# kubectl describe pod nginx-mxmfj
  • 删除Pod
[[email protected] work]# kubectl delete pods nginx-deployment-5d66cc795f-kdqhk

pod "nginx-deployment-5d66cc795f-kdqhk" deleted
  • 删除整个Pod文件
[[email protected] work]# kubectl delete -f nginx-test.yml 
deployment.apps "nginx-deployment" deleted

5、检查服务 IP 和端口可达性

5.1:查看nginx服务

[[email protected] 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

[[email protected] work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh ${node_ip} "curl 10.254.254.189"
  done
  • 预期输出 nginx 欢迎页面内容
二进制搭建高可用K8S集群-v1.17-浅时光博客

6、检查服务的 NodePort 可达性

  • 在所有Node节点执行
[[email protected] 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
二进制搭建高可用K8S集群-v1.17-浅时光博客
二进制搭建高可用K8S集群-v1.17-浅时光博客
二进制搭建高可用K8S集群-v1.17-浅时光博客

声明:本文参考文章【和我一步步部署 kubernetes 集群】搭建部署。

二进制搭建高可用K8S集群-v1.17-浅时光博客
0 条回应
    本站已安全运行: | 耗时 0.584 秒 | 查询 101 次 | 内存 37.49 MB