centos搭建二进制k8s集群

参考文档:https://k8s-install.opsnull.com/

整体环境说明

主机名系统版本ip地址docker versionkubelet versionkubeadm versionkubectl versionflannel version备注
node01centos-release-7-7.1908.0.el7.centos.x86_64192.168.137.221Docker 18.09.6V1.14.2V1.14.2V1.14.2V0.11.0node节点
node02centos-release-7-7.1908.0.el7.centos.x86_64192.168.137.222Docker 18.09.6V1.14.2V1.14.2V1.14.2V0.11.0node节点
node03centos-release-7-7.1908.0.el7.centos.x86_64192.168.137.223Docker 18.09.6V1.14.2V1.14.2V1.14.2V0.11.0node节点

注意:

  1. 本文档中的 etcd 集群、master 节点、worker 节点均使用这三台机器;
  2. 需要在所有机器上执行本文档的初始化命令;
  3. 需要使用 root 账号执行这些命令;
  4. 如果没有特殊指明,本文档的所有操作均在 node01 节点上执行,然后远程分发文件和执行命令;

一、系统初始化

该章节所有内容需要在集群中的每个节点执行,以下是以node03节点做示例

1.工具安装

[root@centos7 ~]# yum install -y epel-release
[root@centos7 ~]# yum install -y vim conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget

2.修改主机名

[root@centos7 ~]# hostnamectl set-hostname node03
[root@centos7 ~]# cat /etc/hostname 
node03

重启服务器

3.修改ip地址(静态)

[root@node03 ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33 

TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static   ##改成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=2d7cb38b-cd33-4a42-b309-13f4fbe13650
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.137.223 ##ip地址
NETMASK=255.255.255.0
GATEWAY=192.168.137.2
DNS1=192.168.137.2
ZONE=public

重启服务器

4.关闭防火墙

[root@node03 ~]# systemctl stop firewalld   ##停止firewalld服务
[root@node03 ~]# systemctl disable firewalld  ##设置开机禁用防火墙
[root@node03 ~]# iptables -F && iptables -F -t nat  ##清除所有规则
[root@node03 ~]# iptables -X && iptables -X -t nat  ##清除用户自定义规则
[root@node03 ~]# iptables -P FORWARD ACCEPT  ##刷新Forward跟accept规则

5.swap 分区

如果开启了 swap 分区,kubelet 会启动失败(可以通过将参数 --fail-swap-on 设置为 false 来忽略 swap on),故需要在每台机器上关闭 swap 分区。同时注释 /etc/fstab 中相应的条目,防止开机自动挂载 swap 分区:

[root@node03 ~]# swapoff -a  ##禁用 /proc/swaps 中的所有交换区
[root@node03 ~]# sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab  ##注释掉/etc/fstab中所有 swap 的行

6.关闭 SELinux

关闭 SELinux,否则后续 K8S 挂载目录时可能报错 Permission denied:

[root@node03 ~]# setenforce 0  ##临时关闭
[root@node03 ~]# sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config  ##修改SELINUX=disabled 永久关闭

7.加载内核模块

[root@node03 ~]# modprobe ip_vs_rr
[root@node03 ~]# modprobe br_netfilter

8.优化内核参数

1.创建参数文件

[root@node03 ~]# mkdir -p /opt/k8s/work/ ##建一个文件夹用来存放k8s相关的文件
[root@node03 ~]# cat > /opt/k8s/work/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  ##必须关闭 tcp_tw_recycle,否则和 NAT 冲突,会导致服务不通;
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  ##关闭 IPV6,防止触发 docker BUG;
net.netfilter.nf_conntrack_max=2310720
EOF

2.移动参数文件

[root@node03 ~]# cp /opt/k8s/work/kubernetes.conf  /etc/sysctl.d/kubernetes.conf

3.读取配置文件,使参数生效

[root@node03 ~]# sysctl -p /etc/sysctl.d/kubernetes.conf

9.设置系统时区

[root@node03 ~]# timedatectl set-timezone Asia/Shanghai  ##调整系统 TimeZone
[root@node03 ~]# timedatectl set-local-rtc 0  ##将当前的 UTC 时间写入硬件时钟
[root@node03 ~]# systemctl restart rsyslog && systemctl restart crond  ##重启依赖于系统时间的服务

10.关闭无关的服务

[root@node03 ~]# systemctl stop postfix && systemctl disable postfix

11.设置 rsyslogd 和 systemd journald

systemd 的 journald 是 Centos 7 默认的日志记录工具,它记录了所有系统、内核、Service Unit 的日志。
相比 systemd,journald 记录的日志有如下优势:

  1. 可以记录到内存或文件系统;(默认记录到内存,对应的位置为 /run/log/jounal);
  2. 可以限制占用的磁盘空间、保证磁盘剩余空间;
  3. 可以限制日志文件大小、保存的时间;
    journald 默认将日志转发给 rsyslog,这会导致日志写了多份,/var/log/messages 中包含了太多无关日志,不方便后续查看,同时也影响系统性能。
[root@node03 ~]# mkdir /var/log/journal  ##持久化保存日志的目录
[root@node03 ~]# mkdir /etc/systemd/journald.conf.d
[root@node03 ~]# cat > /etc/systemd/journald.conf.d/99-prophet.conf <<EOF
[Journal]
# 持久化保存到磁盘
Storage=persistent

# 压缩历史日志
Compress=yes

SyncIntervalSec=5m
RateLimitInterval=30s
RateLimitBurst=1000

# 最大占用空间 10G
SystemMaxUse=10G

# 单日志文件最大 200M
SystemMaxFileSize=200M

# 日志保存时间 2 周
MaxRetentionSec=2week

# 不将日志转发到 syslog
ForwardToSyslog=no
EOF
[root@node03 ~]# systemctl restart systemd-journald  ##重启服务

12.创建相关目录

[root@node03 ~]# mkdir -p  /opt/k8s/{bin,work} /etc/{kubernetes,etcd}/cert

13.升级内核

CentOS 7.x 系统自带的 3.10.x 内核存在一些 Bugs,导致运行的 Docker、Kubernetes 不稳定,例如:

  1. 高版本的 docker(1.13 以后) 启用了 3.10 kernel 实验支持的 kernel memory account 功能(无法关闭),当节点压力大如频繁启动和停止容器时会导致 cgroup memory leak;
  2. 网络设备引用计数泄漏,会导致类似于报错:“kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1”;
    升级内核到 4.4.X 以上解决以上问题:
[root@node03 ~]# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm  ##安装完成后检查 /boot/grub2/grub.cfg 中对应内核 menuentry 中是否包含 initrd16 配置,如果没有,再安装一次!
[root@node03 ~]# yum --enablerepo=elrepo-kernel install -y kernel-lt
[root@node03 ~]# grub2-set-default 0  ##设置开机从新内核启动

重启服务器并查看内核版本

[root@node03 ~]# uname -a   ##第三列显示的是内核版本。如果是4.4.X就说明升级成功
Linux node03 4.4.196-1.el7.elrepo.x86_64 #1 SMP Mon Oct 7 16:17:40 EDT 2019 x86_64 x86_64 x86_64 GNU/Linux

14.关闭 NUMA

[root@node03 ~]# vim /etc/default/grub ##在 GRUB_CMDLINE_LINUX 一行添加 `numa=off` 参数,如下所示:

在这里插入图片描述
重新生成 grub2 配置文件:

[root@node03 ~]# cp /boot/grub2/grub.cfg{,.bak}
[root@node03 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg

输出:
在这里插入图片描述

15.修改hosts文件

需要在所有k8s节点中修改

[root@node03 ~]# vim /etc/hosts

在这里插入图片描述

16.无密码 ssh 登录其它节点

这个一般只需要在集群的 node01节点上执行就可以了,如果为了以后方便可以在每个机器上都执行,过程中需要输入各节点的root账号密码

[root@node01 ~]# ssh-keygen -t rsa
[root@node01 ~]# ssh-copy-id root@node01
[root@node01 ~]# ssh-copy-id root@node02
[root@node01 ~]# ssh-copy-id root@node03

在这里插入图片描述

17.创建docker账号

[root@node03 ~]# useradd -m docker

18.更新 PATH 变量

[root@node03 ~]# echo 'PATH=/opt/k8s/bin:$PATH' >>/root/.bashrc
[root@node03 ~]# source /root/.bashrc

19.创建集群配置参数脚本

该过程在node01节点上执行

[root@node01 ~]# vim environment.sh
#!/usr/bin/bash

# 生成 EncryptionConfig 所需的加密 key
export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)

# 集群各机器 IP 数组
export NODE_IPS=(192.168.137.221 192.168.137.222 192.168.137.223)

# 集群各 IP 对应的主机名数组
export NODE_NAMES=(node01 node02 node03)

# etcd 集群服务地址列表
export ETCD_ENDPOINTS="https://192.168.137.221:2379,https://192.168.137.222:2379,https://192.168.137.223:2379"

# etcd 集群间通信的 IP 和端口
export ETCD_NODES="node01=https://192.168.137.221:2380,node02=https://192.168.137.222:2380,node03=https://192.168.137.223: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="41f7e4ba8b7be874fcff18bf5cf41a7c"

# 最好使用 当前未用的网段 来定义服务网段和 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

拷贝environment.sh到每个节点的/opt/k8s/bin目录下

[root@node01 ~]# source environment.sh
[root@node01 ~]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp environment.sh root@${node_ip}:/opt/k8s/bin/
    ssh root@${node_ip} "chmod +x /opt/k8s/bin/*"
  done

二、创建 CA 证书和秘钥

注意:
如果没有特殊指明,本文档的所有操作均在node01节点上执行,然后远程分发文件和执行命令。

1.安装 cfssl 工具集

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

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

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

[root@node01 k8s]# chmod +x /opt/k8s/bin/*
[root@node01 k8s]# export PATH=/opt/k8s/bin:$PATH

2.创建根证书 (CA)

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

2.1 创建配置文件

[root@node01 k8s]# cd /opt/k8s/work
[root@node01 work]# cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "87600h"
      }
    }
  }
}
EOF

signing:表示该证书可用于签名其它证书,生成的 ca.pem 证书中 CA=TRUE
server auth:表示 client 可以用该该证书对 server 提供的证书进行验证;
client auth:表示 server 可以用该该证书对 client 提供的证书进行验证;

2.2 创建证书签名请求文件

[root@node01 work]# cat > ca-csr.json <<EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "GuangZhou",
      "L": "GuangZhou",
      "O": "k8s",
      "OU": "System"
    }
  ],
  "ca": {
    "expiry": "876000h"
 }
}
EOF

CN:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name),浏览器使用该字段验证网站是否合法;
O:Organization,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);
kube-apiserver 将提取的 User、Group 作为 RBAC 授权的用户标识;

2.3 生成 CA 证书和私钥

[root@node01 work]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca
[root@node01 work]# ls ca*

3.分发证书文件

将生成的 CA 证书、秘钥文件、配置文件拷贝到所有节点/etc/kubernetes/cert 目录下:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "mkdir -p /etc/kubernetes/cert"
    scp ca*.pem ca-config.json root@${node_ip}:/etc/kubernetes/cert
  done

三、部署 kubectl 命令行工具

注意:

  1. 如果没有特殊指明,本文档的所有操作均在node01节点上执行,然后远程分发文件和执行命令;
  2. 该章节只需要部署一次,生成的 kubeconfig 文件是通用的,可以拷贝到需要执行 kubectl 命令的机器,重命名为 ~/.kube/config

1.下载和分发 kubectl 二进制文件

下载和解压:

[root@node01 work]# cd /opt/k8s/work
[root@node01 work]# wget https://dl.k8s.io/v1.14.2/kubernetes-client-linux-amd64.tar.gz
[root@node01 work]# tar -xzvf kubernetes-client-linux-amd64.tar.gz

如果下载速度缓慢,或者下载不了的话,可以下载以下资源。

链接:https://pan.baidu.com/s/1rHde5cMh5gr_yjv_utyJag 
提取码:4scj

分发到所有使用 kubectl 的节点:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kubernetes/client/bin/kubectl root@${node_ip}:/opt/k8s/bin/
    ssh root@${node_ip} "chmod +x /opt/k8s/bin/*"
  done

2.创建 admin 证书和私钥

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

[root@node01 work]# cat > admin-csr.json <<EOF
{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "GuangZhou",
      "L": "GuangZhou",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
EOF

O 为 system:masters,kube-apiserver 收到该证书后将请求的 Group 设置为 system:masters
预定义的 ClusterRoleBinding cluster-admin 将 Group system:masters 与 Role cluster-admin 绑定,该 Role 授予所有 API的权限;
该证书只会被 kubectl 当做 client 证书使用,所以 hosts 字段为空;

生成证书和私钥:

[root@node01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
  -ca-key=/opt/k8s/work/ca-key.pem \
  -config=/opt/k8s/work/ca-config.json \
  -profile=kubernetes admin-csr.json | cfssljson -bare admin
[root@node01 work]# ls admin*

3.创建 kubeconfig 文件

[root@node01 work]# source /opt/k8s/bin/environment.sh

# 设置集群参数
[root@node01 work]# kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/k8s/work/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kubectl.kubeconfig

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

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

# 设置默认上下文
[root@node01 work]# kubectl config use-context kubernetes --kubeconfig=kubectl.kubeconfig

--certificate-authority:验证 kube-apiserver 证书的根证书;
--client-certificate--client-key:刚生成的 admin 证书和私钥,连接 kube-apiserver 时使用;
--embed-certs=true:将 ca.pem 和 admin.pem 证书内容嵌入到生成的 kubectl.kubeconfig 文件中(不加时,写入的是证书文件路径,后续拷贝 kubeconfig 到其它机器时,还需要单独拷贝证书文件,不方便。);

4.分发 kubeconfig 文件

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "mkdir -p ~/.kube"
    scp kubectl.kubeconfig root@${node_ip}:~/.kube/config
  done

四、部署 etcd 集群

注意:
如果没有特殊指明,该章节的所有操作均在node01节点上执行,然后远程分发文件和执行命令。

etcd 集群各节点的名称和 IP 如下:

  • node01:192.168.137.221
  • node02:192.168.137.222
  • node03:192.168.137.223

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

到 etcd 的 release 页面 下载最新版本的发布包:
这里使用的是v3.3.13版本

[root@node01 work]# cd /opt/k8s/work
[root@node01 work]# wget https://github.com/coreos/etcd/releases/download/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
[root@node01 work]# tar -xvf etcd-v3.3.13-linux-amd64.tar.gz

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

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp etcd-v3.3.13-linux-amd64/etcd* root@${node_ip}:/opt/k8s/bin
    ssh root@${node_ip} "chmod +x /opt/k8s/bin/*"
  done

2.创建 etcd 证书和私钥

创建证书签名请求:

[root@node01 work]# cat > etcd-csr.json <<EOF
{
  "CN": "etcd",
  "hosts": [
    "127.0.0.1",
    "172.27.137.240",
    "172.27.137.239",
    "172.27.137.238"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "GuangZhou",
      "L": "GuangZhou",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
EOF
  • hosts 字段指定授权使用该证书的 etcd 节点 IP 或域名列表,需要将 etcd 集群的三个节点 IP 都列在其中;

生成证书和私钥:

[root@node01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
    -ca-key=/opt/k8s/work/ca-key.pem \
    -config=/opt/k8s/work/ca-config.json \
    -profile=kubernetes etcd-csr.json | cfssljson -bare etcd
[root@node01 work]# ls etcd*pem

分发生成的证书和私钥到各 etcd 节点:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "mkdir -p /etc/etcd/cert"
    scp etcd*.pem root@${node_ip}:/etc/etcd/cert/
  done

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

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 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 文件

替换模板文件中的变量,为各节点创建 systemd unit 文件:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for (( i=0; i < 3; i++ ))
  do
    sed -e "s/##NODE_NAME##/${NODE_NAMES[i]}/" -e "s/##NODE_IP##/${NODE_IPS[i]}/" etcd.service.template > etcd-${NODE_IPS[i]}.service 
  done
[root@node01 work]# ls *.service
  • NODE_NAMESNODE_IPS 为相同长度的 bash 数组,分别为节点名称和对应的 IP;

分发生成的 systemd unit 文件:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp etcd-${node_ip}.service root@${node_ip}:/etc/systemd/system/etcd.service
  done

5.启动 etcd 服务

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

6.检查启动结果

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "systemctl status etcd"
  done

确保状态为 active (running),否则查看日志,确认原因:

journalctl -u etcd

7.验证服务状态

部署完 etcd 集群后,在任一 etcd 节点上执行如下命令:

[root@node01 work]# cd /opt/k8s/work
[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ETCDCTL_API=3 /opt/k8s/bin/etcdctl \
    --endpoints=https://${node_ip}:2379 \
    --cacert=/etc/kubernetes/cert/ca.pem \
    --cert=/etc/etcd/cert/etcd.pem \
    --key=/etc/etcd/cert/etcd-key.pem endpoint health
  done

输出均为 healthy 时表示集群服务正常。

8.查看当前的 leader

部署完 etcd 集群后,在任一 etcd 节点上执行如下命令:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# ETCDCTL_API=3 /opt/k8s/bin/etcdctl \
  -w table --cacert=/etc/kubernetes/cert/ca.pem \
  --cert=/etc/etcd/cert/etcd.pem \
  --key=/etc/etcd/cert/etcd-key.pem \
  --endpoints=${ETCD_ENDPOINTS} endpoint status

五、部署 flannel 网络

kubernetes 要求集群内各节点(包括 master 节点)能通过 Pod 网段互联互通。flannel 使用 vxlan 技术为各节点创建一个可以互通的 Pod 网络,使用的端口为 UDP 8472(需要开放该端口,如公有云 AWS 等)。

flanneld 第一次启动时,从 etcd 获取配置的 Pod 网段信息,为本节点分配一个未使用的地址段,然后创建 flannedl.1 网络接口(也可能是其它名称,如 flannel1 等)。

flannel 将分配给自己的 Pod 网段信息写入 /run/flannel/docker 文件,docker 后续使用这个文件中的环境变量设置 docker0 网桥,从而从这个地址段为本节点的所有 Pod 容器分配 IP。

注意:
如果没有特殊指明,本文档的所有操作均在node01节点上执行,然后远程分发文件和执行命令。

1.下载和分发 flanneld 二进制文件

从 flannel 的 release 页面 下载最新版本的安装包:

[root@node01 work]# cd /opt/k8s/work
[root@node01 work]# mkdir flannel
[root@node01 work]# wget https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz
[root@node01 work]# tar -xzvf flannel-v0.11.0-linux-amd64.tar.gz -C flannel

这里使用的是v0.11.0版本

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

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

2、创建 flannel 证书和私钥

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

创建证书签名请求:

[root@node01 work]# cat > flanneld-csr.json <<EOF
{
  "CN": "flanneld",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "GuangZhou",
      "L": "GuangZhou",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
EOF
  • 该证书只会被 kubectl 当做 client 证书使用,所以 hosts 字段为空;

生成证书和私钥:

[root@node01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
  -ca-key=/opt/k8s/work/ca-key.pem \
  -config=/opt/k8s/work/ca-config.json \
  -profile=kubernetes flanneld-csr.json | cfssljson -bare flanneld
[root@node01 work]# ls flanneld*pem

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

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "mkdir -p /etc/flanneld/cert"
    scp flanneld*.pem root@${node_ip}:/etc/flanneld/cert
  done

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

注意:本步骤只需执行一次。

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 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 \
  mk ${FLANNEL_ETCD_PREFIX}/config '{"Network":"'${CLUSTER_CIDR}'", "SubnetLen": 21, "Backend": {"Type": "vxlan"}}'
  • flanneld 当前版本 (v0.11.0) 不支持 etcd v3,故使用 etcd v2 API 写入配置 key 和网段数据;
  • 写入的 Pod 网段 ${CLUSTER_CIDR} 地址段(如 /16)必须小于 SubnetLen,必须与 kube-controller-manager 的 --cluster-cidr 参数值一致;

4.创建 flanneld 的 systemd unit 文件

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 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
  • docker 启动时使用这个文件中的环境变量配置 docker0 网桥;
  • flanneld 使用系统缺省路由所在的接口与其它节点通信,对于有多个网络接口(如内网和公网)的节点,可以用 -iface 参数指定通信接口;
  • flanneld 运行时需要 root 权限;
  • -ip-masq: flanneld 为访问 Pod 网络外的流量设置 SNAT 规则,同时将传递给 Docker 的变量 --ip-masq(/run/flannel/docker 文件中)设置为 false,这样 Docker 将不再创建 SNAT 规则; Docker 的 --ip-masq 为 true 时,创建的 SNAT 规则比较“暴力”:将所有本节点 Pod 发起的、访问非 docker0 接口的请求做 SNAT,这样访问其他节点 Pod 的请求来源 IP 会被设置为 flannel.1 接口的 IP,导致目的 Pod 看不到真实的来源 Pod IP。 flanneld 创建的 SNAT 规则比较温和,只对访问非 Pod 网段的请求做 SNAT。

5. 分发 flanneld systemd unit 文件到所有节点

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp flanneld.service root@${node_ip}:/etc/systemd/system/
  done

6.启动 flanneld 服务

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "systemctl daemon-reload && systemctl enable flanneld && systemctl restart flanneld"
  done

7.检查启动结果

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "systemctl status flanneld"
  done

确保状态为 active (running),否则查看日志,确认原因:

journalctl -u flanneld

8.检查分配给各 flanneld 的 Pod 网段信息

查看集群 Pod 网段(/16):

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 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

输出:

{“Network”:“172.30.0.0/16”, “SubnetLen”: 21, “Backend”: {“Type”: “vxlan”}}

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

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 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

输出(结果视部署情况而定):

/kubernetes/network/subnets/172.30.80.0-21
/kubernetes/network/subnets/172.30.32.0-21
/kubernetes/network/subnets/172.30.184.0-21

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

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 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.80.0-21

输出(结果视部署情况而定):

{"PublicIP":"172.27.137.240","BackendType":"vxlan","BackendData":{"VtepMAC":"ce:9c:a9:08:50:03"}}
  • 172.30.80.0/21 被分配给节点 zhangjun-k8s01(172.27.137.240);
  • VtepMAC 为 zhangjun-k8s01 节点的 flannel.1 网卡 MAC 地址;

9. 检查节点 flannel 网络信息

[root@node01 work]# ip addr show
  • flannel.1 网卡的地址为分配的 Pod 子网段的第一个 IP(.0),且是 /32 的地址;
[root@node01 work]# route show |grep flannel.1
  • 到其它节点 Pod 网段请求都被转发到 flannel.1 网卡;
  • flanneld 根据 etcd 中子网段的信息,如 ${FLANNEL_ETCD_PREFIX}/subnets/172.30.80.0-21 ,来决定进请求发送给哪个节点的互联 IP;

10.验证各节点能通过 Pod 网段互通

在各节点上部署 flannel 后,检查是否创建了 flannel 接口(名称可能为 flannel0、flannel.0、flannel.1 等):

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 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

在各节点上 ping 所有 flannel 接口 IP,确保能通:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh ${node_ip} "ping -c 1 172.30.80.0"
    ssh ${node_ip} "ping -c 1 172.30.32.0"
    ssh ${node_ip} "ping -c 1 172.30.184.0"
  done

六、kube-apiserver 高可用之 nginx 代理

注意:
如果没有特殊指明,本文档的所有操作均在 zhangjun-k8s01 节点上执行,然后远程分发文件和执行命令。

1.基于 nginx 代理的 kube-apiserver 高可用方案

  • 控制节点的 kube-controller-manager、kube-scheduler 是多实例部署,所以只要有一个实例正常,就可以保证高可用;
  • 集群内的 Pod 使用 K8S 服务域名 kubernetes 访问 kube-apiserver, kube-dns 会自动解析出多个 kube-apiserver 节点的 IP,所以也是高可用的;
  • 在每个节点起一个 nginx 进程,后端对接多个 apiserver 实例,nginx 对它们做健康检查和负载均衡;
  • kubelet、kube-proxy、controller-manager、scheduler 通过本地的 nginx(监听 127.0.0.1)访问 kube-apiserver,从而实现 kube-apiserver 的高可用;

2.下载和编译 nginx

下载源码:

[root@node01 work]# cd /opt/k8s/work
[root@node01 work]# wget http://nginx.org/download/nginx-1.15.3.tar.gz
[root@node01 work]# tar -xzvf nginx-1.15.3.tar.gz

配置编译参数:

[root@node01 work]# cd /opt/k8s/work/nginx-1.15.3
[root@node01 nginx-1.15.3]# mkdir nginx-prefix
[root@node01 nginx-1.15.3]# ./configure --with-stream --without-http --prefix=$(pwd)/nginx-prefix --without-http_uwsgi_module --without-http_scgi_module --without-http_fastcgi_module
  • --with-stream:开启 4 层透明转发(TCP Proxy)功能;
  • --without-xxx:关闭所有其他功能,这样生成的动态链接二进制程序依赖最小;

输出:

Configuration summary
  + PCRE library is not used
  + OpenSSL library is not used
  + zlib library is not used

  nginx path prefix: "/root/tmp/nginx-1.15.3/nginx-prefix"
  nginx binary file: "/root/tmp/nginx-1.15.3/nginx-prefix/sbin/nginx"
  nginx modules path: "/root/tmp/nginx-1.15.3/nginx-prefix/modules"
  nginx configuration prefix: "/root/tmp/nginx-1.15.3/nginx-prefix/conf"
  nginx configuration file: "/root/tmp/nginx-1.15.3/nginx-prefix/conf/nginx.conf"
  nginx pid file: "/root/tmp/nginx-1.15.3/nginx-prefix/logs/nginx.pid"
  nginx error log file: "/root/tmp/nginx-1.15.3/nginx-prefix/logs/error.log"
  nginx http access log file: "/root/tmp/nginx-1.15.3/nginx-prefix/logs/access.log"
  nginx http client request body temporary files: "client_body_temp"
  nginx http proxy temporary files: "proxy_temp"

编译和安装:

[root@node01 nginx-1.15.3]# cd /opt/k8s/work/nginx-1.15.3
[root@node01 nginx-1.15.3]# make && make install

3.验证编译的 nginx

[root@node01 nginx-1.15.3]# cd /opt/k8s/work/nginx-1.15.3
[root@node01 nginx-1.15.3]# ./nginx-prefix/sbin/nginx -v

输出:

nginx version: nginx/1.15.3

查看 nginx 动态链接的库:

[root@node01 nginx-1.15.3]# ldd ./nginx-prefix/sbin/nginx

输出:

		linux-vdso.so.1 =>  (0x00007ffc945e7000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f4385072000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f4384e56000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f4384a89000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f4385276000)
  • 由于只开启了 4 层透明转发功能,所以除了依赖 libc 等操作系统核心 lib 库外,没有对其它 lib 的依赖(如 libz、libssl 等),这样可以方便部署到各版本操作系统中;

4.安装和部署 nginx

创建目录结构:

[root@node01 nginx-1.15.3]# cd /opt/k8s/work
[root@node01 work]# source /opt/k8s/bin/environment.sh
for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "mkdir -p /opt/k8s/kube-nginx/{conf,logs,sbin}"
  done

拷贝二进制程序:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "mkdir -p /opt/k8s/kube-nginx/{conf,logs,sbin}"
    scp /opt/k8s/work/nginx-1.15.3/nginx-prefix/sbin/nginx  root@${node_ip}:/opt/k8s/kube-nginx/sbin/kube-nginx
    ssh root@${node_ip} "chmod a+x /opt/k8s/kube-nginx/sbin/*"
  done

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

[root@node01 work]# cat > kube-nginx.conf << \EOF
worker_processes 1;

events {
    worker_connections  1024;
}

stream {
    upstream backend {
        hash $remote_addr consistent;
        server 192.168.137.251:6443        max_fails=3 fail_timeout=30s;
        server 192.168.137.252:6443        max_fails=3 fail_timeout=30s;
        server 192.168.137.253: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 列表;

分发配置文件:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-nginx.conf  root@${node_ip}:/opt/k8s/kube-nginx/conf/kube-nginx.conf
  done

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

配置 kube-nginx systemd unit 文件:

[root@node01 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

分发 systemd unit 文件:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    scp kube-nginx.service  root@${node_ip}:/etc/systemd/system/
  done

启动 kube-nginx 服务:

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "systemctl daemon-reload && systemctl enable kube-nginx && systemctl restart kube-nginx"
  done

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

[root@node01 work]# source /opt/k8s/bin/environment.sh
[root@node01 work]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "systemctl status kube-nginx"
  done

确保状态为 active (running),否则查看日志,确认原因:

journalctl -u kube-nginx

参考文档:https://k8s-install.opsnull.com/

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐