Kubernetes集群部署

1. 准备工作

目前部署k8s集群社区有两种方式:

  • kubeadm

Kubeadm是一个k8s部署工具,提供kubeadm initkubeadm join,用于快速部署Kubernetes集群。

官方地址:https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/

  • 二进制包

github下载发行版的二进制包,手动部署每个组件,组成Kubernetes集群。

Kubeadm降低部署门槛,但屏蔽了很多细节,遇到问题很难排查。如果想更容易可控,推荐使用二进制包部署Kubernetes集群,虽然手动部署麻烦点,期间可以学习很多工作原理,也利于后期维护。

在开始之前,部署Kubernetes集群机器需要满足以下几个条件:

  • 一台或多台机器,操作系统 CentOS7.4及以上或Ubuntu 16.04及以上
  • 硬件配置:2GB或更多内存,2核CPU或多核CPU,硬盘30GB或更多
  • 可以访问外网,需要拉取镜像,如果服务器不能上网,需要提前下载镜像并导入节点
  • 禁用swap交换分区

集群规划

基本环境准备,以Ubuntu 16.04为例

(1)服务器规划

主机名 ip地址 规格 角色
sc-cc-k8s-master-001 172.17.40.129 2核4G etcd、kube-api-server、kube-controller-manager、kube-scheduler
sc-cc-k8s-node-001 172.17.40.135 2核4G etcd、kubelet、kube-proxy
sc-cc-k8s-node-002 172.17.40.139 2核4G etcd、kubelet、kube-proxy
sc-cc-k8s-node-003 172.17.40.142 2核4G kubelet、kube-proxy

(2)软件规划

软件 版本
Docker 19.03.12
kubernetes 1.18.14
etcd 3.4.9

初始化服务器

  • 关闭防火墙,CentOS还需关闭SELinux
  • 关闭swap交换分区
# 临时关闭
swapoff -a
# 永久关闭
sed -ri "s/(.*swap.*)/#\1/g" /etc/fstab
  • 时钟同步
# 设置时区
timedatectl set-timezone 'Asia/Shanghai'
# 更新源
apt update && apt install chrony
# 修改为阿里云的NTP服务器
vim /etc/chrony/chrony.conf
# Aliyun NTP server
server ntp.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp1.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp2.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp3.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp4.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp5.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp6.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp7.aliyun.com minpoll 4 maxpoll 10 iburst# 重启chrony服务
systemctl restart chrony
# 检查服务状态
systemctl status chrony
# 查看本机时间同步状态,用于验证服务是否已启动
chronyc activity
# 更新硬件时钟(RTC)
hwclock -w
# 查看时区
timedatectl status
  • 修改主机名
# 每台机器执行对应主机名修改
hostnamectl set-hostname sc-cc-k8s-master-001
hostnamectl set-hostname sc-cc-k8s-node-001
hostnamectl set-hostname sc-cc-k8s-node-002
hostnamectl set-hostname sc-cc-k8s-node-003
  • 修改本地解析
cat >>/etc/hosts <<EOF
# kubernetes 集群机器
172.17.40.129 sc-cc-k8s-master-001 master
172.17.40.135 sc-cc-k8s-node-001   node1
172.17.40.139 sc-cc-k8s-node-002   node2
172.17.40.142 sc-cc-k8s-node-003   node3
EOF
  • 内核优化
# 基础内核优化
cat >> /etc/sysctl.conf <EOF
vm.swappiness = 0
kernel.sysrq = 1
net.ipv4.neigh.default.gc_stale_time = 120
# see details in https://help.aliyun.com/knowledge_detail/39428.html
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_announce = 2
# see details in https://help.aliyun.com/knowledge_detail/41334.html
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2
# disable ipv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOFcat > /etc/sysctl.d/99-k8s.conf <<EOF
# sysctls for kubernetes node config
net.ipv4.tcp_slow_start_after_idle = 0
net.core.rmem_max = 16777216
kernel.softlockup_all_cpu_backtrace = 1
# kernel limits
kernel.pid_max = 4194303
kernel.threads-max = 30058
kernel.softlockup_panic = 1fs.file-max = 2097152
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 16384
fs.inotify.max_queued_events = 16384
vm.max_map_count = 262144
fs.may_detach_mounts = 1net.netfilter.nf_conntrack_max = 10485760
net.netfilter.nf_conntrack_tcp_timeout_established = 300
net.netfilter.nf_conntrack_buckets = 655360net.core.netdev_max_backlog = 16384
net.core.somaxconn = 32768
net.core.bpf_jit_enable = 1
net.core.bpf_jit_harden = 1
net.core.bpf_jit_kallsyms = 1
net.core.dev_weight_tx_bias = 1
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rps_sock_flow_entries = 8192net.ipv4.ip_forward = 1
net.ipv4.tcp_max_syn_backlog = 8096
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.tcp_rmem = 4096 12582912 16777216
net.ipv4.tcp_wmem = 4096 12582912 16777216
net.ipv4.tcp_max_orphans = 32768
net.ipv4.tcp_max_tw_buckets = 32768# 存在于 ARP 高速缓存中的最少层数,如果少于这个数,垃圾收集器将不会运行。缺省值是 128
net.ipv4.neigh.default.gc_thresh1 = 2048
# 保存在 ARP 高速缓存中的最多的记录软限制。垃圾收集器在开始收集前,允许记录数超过这个数字 5 秒。缺省值是 512
net.ipv4.neigh.default.gc_thresh2 = 4096
# 保存在 ARP 高速缓存中的最多记录的硬限制,一旦高速缓存中的数目高于此,垃圾收集器将马上运行。缺省值是 1024
net.ipv4.neigh.default.gc_thresh3 = 8192
EOF# 使参数生效
sysctl --system
  • 修改文件句柄数
sudo vim /etc/security/limits.conf
* hard nofile 655350
* soft nofile 655350
  • 配置免密登录
# 在 master 节点生成秘钥对
ssh-keygen -t rsa -b 1200
# 分发公钥给其他节点机器
ssh-copy-id -i ~/.ssh/id_rsa.pub  root@master
ssh-copy-id -i ~/.ssh/id_rsa.pub  root@node1
ssh-copy-id -i ~/.ssh/id_rsa.pub  root@node2
ssh-copy-id -i ~/.ssh/id_rsa.pub  root@node3

其他节点做同样的初始化。

2. 部署 Etcd

Etcd 是一个分布式键值存储系统,Kubernetes使用Etcd进行状态和数据存储,因此我们需要提前准备好Etcd,不过为解决Etcd单点故障问题,应采用集群方式部署,这里使用3台组建集群。

为了节约资源利用,我这里复用了两个node节点,这样子这三台主机便可组建一个集群。当然,建议是独立于k8s集群之外部署,毕竟数据很重要。

Etcd节点名称 IP
etcd-01 172.17.40.129
etcd-02 172.17.40.135
etcd-03 172.17.40.139

2.1 安装cfssl证书生成工具

在kubernetes中,使用openssl生成证书会及其麻烦,如果我们可以把预先的证书机构、使用期等时间写在json文件里面会更加高效和自动化。而cfssl就是这样的一款工具,cfssl采用go语言编写,是一个开源的证书管理工具,cfssljson用来从cfssl程序获取json输出,并将证书,密钥,csr和bundle写入文件中。

在 master 节点操作:

curl -L https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -o cfssl
curl -L https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -o cfssljson
curl -L https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -o cfssl-certinfosudo chmod a+x cfssl cfssljson cfssl-certinfo
sudo mv cfssl cfssljson cfssl-certinfo /usr/local/bin/

2.2 创建认证中心(根CA中心)

由于kubernetes各组件需要使用x509证书对通信进行加密和认证,所以需要创建一套CA(Certificate Autority),是自签名的根证书,用来签名后续需要创建的其它证书;

这里创建的CA是一个私有的内部认证中心,这个认证中心也需要一个CA证书和相应的CA私钥,CA私钥需要妥善保管,任何拥有它的人,都可以充当CA颁发证书。

(1)创建请求证书的json配置文件

CA证书的请求json文件是集群所有节点共享的,只需要创建一个,它后续创建的所有其它子CA证书的基础,子CA证书都会根据这里config中的profile段来生成证书的相关信息;

mkdir ~/ssl/etcd -p && cd ~/ssl/etcd
cat > ca-config.json << EOF
{"signing": {"default": {"expiry": "87600h"},"profiles": {"www": {"expiry": "87600h","usages": ["signing","key encipherment","server auth","client auth"]}}}
}
EOF

ca-config.json 这个配置文件只是告诉我们颁发有什么功能的证书,还有它用于配置证书的使用场景(profile),证书用途(usages)指定了证书可以用于签名、加密、服务端认证、客户端认证等。

default是默认策略,指定证书默认有效期是10年;

profiles是定义使用场景,这里只是kubernetes,其实可以定义多个场景,分别指定不同的过期时间,使用场景等参数,后续签名证书时使用某个profile;

signing: 表示该证书可用于签名其它证书,生成的ca.pem证书中的CA=TRUE;

server auth: 表示client 可以用该CA 对server 提供的证书进行校验;

client auth: 表示server 可以用该CA 对client 提供的证书进行验证。

(2)创建根 CA 证书签名请求文件

cat > ca-csr.json << EOF
{"CN": "etcd CA","key": {"algo": "rsa","size": 2048},"names": [{"C": "CN","L": "Hangzhou","ST": "Hangzhou"}]
}
EOF

(3)生成 CA 证书

root@sc-cc-k8s-master-001:~/ssl/etcd# cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
2021/02/03 20:28:03 [INFO] generating a new CA key and certificate from CSR
2021/02/03 20:28:03 [INFO] generate received request
2021/02/03 20:28:03 [INFO] received CSR
2021/02/03 20:28:03 [INFO] generating key: rsa-2048
2021/02/03 20:28:04 [INFO] encoded CSR
2021/02/03 20:28:04 [INFO] signed certificate with serial number 320097121816930102049763895739635481029580396735

gencert:生成新的key(密钥)和签名证书

–initca:初始化一个新ca

2.3 使用自签 CA 签发 Etcd 证书

(1)创建Etcd证书签名请求文件

cat > server-csr.json << EOF
{"CN": "etcd","hosts": ["172.17.40.129","172.17.40.135","172.17.40.139"],"key": {"algo": "rsa","size": 2048},"names": [{"C": "CN","L": "Hangzhou","ST": "Hangzhou"}]
}
EOF

hosts字段中IP为所有etcd节点的集群内部通信IP,有几个etcd节点,就写多少个IP。当然,为方便后期扩容可以多些几个预留的IP。

(2)生成 Etcd 证书

cfssl gencert -ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=www \
server-csr.json | cfssljson -bare server

gencert: 生成新的key(密钥)和签名证书

-initca:初始化一个新ca

-ca:指明ca的证书

-ca-key:指明ca的私钥文件

-config:指明证书签名请求文件

-profile:与config中的profile对应,是指根据config中的profile段来生成证书的相关信息

(3)证书分发至其他 etcd 服务器

将前面两步创建的证书都分发给其他etcd节点。

写一个shell脚本,先在目标主机创建存放etcd证书的目录,接着复制证书:

#!/bin/bash
for node in {master,node1,node2}
dossh root@${node} "[ -d /opt/etcd ] && rm -rfv /opt/etcd"ssh root@${node} "mkdir -pv /opt/etcd/ssl"scp -r ~/ssl/etcd/ca*pem root@${node}:/opt/etcd/ssl/scp -r ~/ssl/etcd/server*pem root@${node}:/opt/etcd/ssl/
done

补充:事实上只需要分发ca.pem公钥即可,ca-key.pem是私钥,很多组件不需要,除非你确保使用它,你才分发到服务器上面,以免造成私钥泄露。不过我们不需要考虑太多,所以把私钥也分发到了服务器上面。

2.4 部署 Etcd 集群

以下在节点1操作,部署完成后,将节点1生成的所有的文件拷贝到节点2和节点3

(1)下载解压Etcd包并拷贝二进制执行程序

  • 下载 release 包
wget -q --show-progress --https-only --timestamping \"https://github.com/etcd-io/etcd/releases/download/v3.4.9/etcd-v3.4.9-linux-amd64.tar.gz" -O ~/etcd/
  • 创建工作目录并解压二进制包
tar -xf etcd-v3.4.9-linux-amd64.tar.gz && cd etcd-v3.4.9-linux-amd64
# 分发二进制文件
for node in {master,node1,node2}; dossh root@${node} "mkdir -pv /opt/etcd/{bin,conf}"scp -r {etcd,etcdctl} root@${node}:/opt/etcd/bin/
done

(2)创建 Etcd 配置文件

# 每个节点按各自的IP地址修改
export IP="172.17.40.129"
export ETCD_NAME="etcd-01"
cat > /opt/etcd/conf/etcd.conf <<EOF
# [Member]
ETCD_NAME=${ETCD_NAME}
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://$IP:2380"
ETCD_LISTEN_CLIENT_URLS="https://$IP:2379"# [Cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://$IP:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://$IP:2379"
ETCD_INITIAL_CLUSTER="etcd-01=https://172.17.40.129:2380,etcd-02=https://172.17.40.135:2380,etcd-03=https://172.17.40.139:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-0"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_ENABLE_V2="true"
EOF

ETCD 集群每个成员都需要一个名字,这里第一个成员名字用 etcd-01,第二个成员可以用 etcd-02,以此类推。所有 ETCD 成员的名称和成员间通信的 https 监听地址为 ETCD_INITIAL_CLUSTER (注意是 2380 端口,不是 2379),其他配置项介绍如下:

  • ETCD_NAME:节点名称,集群中唯一
  • ETCD_DATA_DIR:数据目录
  • ETCD_LISTEN_PEER_URLS:集群通信监听地址
  • ETCD_LISTEN_CLIENT_URLS:客户端访问监听地址
  • ETCD_INITIAL_ADVERTISE_PEER_URLS:集群通告地址
  • ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址
  • ETCD_INITIAL_CLUSTER:集群节点地址
  • ETCD_INITIAL_CLUSTER_TOKEN:集群Token,整个集群中保持一致
  • ETCD_INITIAL_CLUSTER_STATE:加入集群的当前状态,new是新集群,existing表示加入已有集群

注意:这里的配置项仅仅是加载到 etcd 服务的环境变量

(3)编写启动脚本,由systemd管理etcd

cat > /lib/systemd/system/etcd.service << EOF
[Unit]
Description=Etcd Server
Documentation=https://github.com/etcd-io/etcd
After=network.target
After=network-online.target
Wants=network-online.target[Service]
Type=notify
EnvironmentFile=/opt/etcd/conf/etcd.conf
ExecStart=/opt/etcd/bin/etcd \\--cert-file=/opt/etcd/ssl/server.pem \\--key-file=/opt/etcd/ssl/server-key.pem \\--peer-cert-file=/opt/etcd/ssl/server.pem \\--peer-key-file=/opt/etcd/ssl/server-key.pem \\--trusted-ca-file=/opt/etcd/ssl/ca.pem \\--peer-trusted-ca-file=/opt/etcd/ssl/ca.pem \\--peer-client-cert-auth \\--client-cert-auth \\--logger=zap
Restart=on-failure
LimitNOFILE=65536[Install]
WantedBy=multi-user.target
EOF# 分发文件
for node in {node1,node2}; doscp -r /lib/systemd/system/etcd.service root@${node}:/lib/systemd/system/
done

(4)启动所有节点etcd服务

#! /bin/bash
for node in {master,node1,node2}
dossh root@${node} "systemctl daemon-reload && systemctl enable etcd; systemctl restart etcd &"
done

(5)检查etcd集群健康状态

  • 查看集群状态
root@sc-cc-k8s-master-001:~# ETCDCTL_API=3 /opt/etcd/bin/etcdctl \
--cacert=/opt/etcd/ssl/ca.pem \
--cert=/opt/etcd/ssl/server.pem \
--key=/opt/etcd/ssl/server-key.pem \
--endpoints="https://172.17.40.129:2379,https://172.17.40.135:2379,https://172.17.40.139:2379" \
endpoint health
https://172.17.40.129:2379 is healthy: successfully committed proposal: took = 18.728038ms
https://172.17.40.139:2379 is healthy: successfully committed proposal: took = 14.316256ms
https://172.17.40.135:2379 is healthy: successfully committed proposal: took = 26.027798ms

如果打印的每个etcd节点显示都为healthy,说明集群部署成功。如有问题就查syslog日志

  • 查看集群成员
root@sc-cc-k8s-master-001:~# ETCDCTL_API=3 /opt/etcd/bin/etcdctl \
--cacert=/opt/etcd/ssl/ca.pem \
--cert=/opt/etcd/ssl/server.pem \
--key=/opt/etcd/ssl/server-key.pem \
--endpoints="https://172.17.40.129:2379,https://172.17.40.135:2379,https://172.17.40.139:2379" \
member list
e7921bdd6ef73ff, started, etcd-01, https://172.17.40.129:2380, https://172.17.40.129:2379, false
1a3abe9dba6f8beb, started, etcd-03, https://172.17.40.139:2380, https://172.17.40.139:2379, false
370a495a140cf4b7, started, etcd-02, https://172.17.40.135:2380, https://172.17.40.135:2379, false

2.5 部署 docker-ce

所有node节点都部署docker服务

Ubuntu 14.04/16.04(使用 apt-get 进行安装)

# step 1: 安装必要的一些系统工具
sudo apt-get update
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# step 2: 安装GPG证书
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# Step 3: 写入软件源信息
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# Step 4: 更新并安装Docker-CE
sudo apt-get -y update
# sudo apt-get -y install docker-ce# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# apt-cache madison docker-ce
#   docker-ce | 17.03.1~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
#   docker-ce | 17.03.0~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
# Step 2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.1~ce-0~ubuntu-xenial)
# sudo apt-get -y install docker-ce=[VERSION]
sudo apt-get -y install docker-ce=5:19.03.12~3-0~ubuntu-xenial# Step 5: 配置镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<EOF
{"registry-mirrors": ["https://d94mqh7g.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker# Step 6: 安装检测
sudo docker version
Client: Docker Engine - CommunityVersion:           20.10.1API version:       1.40Go version:        go1.13.15Git commit:        831ebeaBuilt:             Tue Dec 15 04:35:01 2020OS/Arch:           linux/amd64Context:           defaultExperimental:      trueServer: Docker Engine - CommunityEngine:Version:          19.03.12API version:      1.40 (minimum version 1.12)Go version:       go1.13.10Git commit:       48a66213feBuilt:            Mon Jun 22 15:44:20 2020OS/Arch:          linux/amd64Experimental:     falsecontainerd:Version:          1.4.3GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939brunc:Version:          1.0.0-rc92GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7affdocker-init:Version:          0.18.0GitCommit:        fec3683

2.6 Etcd V2与V3版本API的区别

在 Kubernetes 中一共有两个服务用需要是通 etcd 用来协同和存储配置,分别是:

  • 网络插件 flannel、对于其它网络插件也需要用到 etcd 存储网络的配置信息
  • kubernetes 本身,包括各种对象的状态和元信息配置

Etcd V2 和 V3 之间的数据结构完全不同,互不兼容,也就是说使用 V2 版本的 API 创建的数据只能使用 V2 的 API 访问,V3 的版本的 API 创建的数据只能使用 V3 的 API 访问。这就造成我们访问 etcd 中保存的 flannel 的数据需要使用 etcdctl 的 V2 版 本的客户端,而访问 kubernetes 的数据需要设置 ETCDCTL_API=3 环境变量来指定 V3 版本的 API。所以 flannel 操作 etcdctl 时需要设置 ETCDCTL_API=2

查看Etcd中存储的flannel网络信息:

ETCDCTL_API=2 etcdctl --ca-file=/opt/etcd/ssl/ca.pem --cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem --endpoints="https://172.17.40.129:2379" ls /coreos.com/network -r
/coreos.com/network/config
/coreos.com/network/subnets
/coreos.com/network/subnets/172.31.92.0-24
/coreos.com/network/subnets/172.31.49.0-24
/coreos.com/network/subnets/172.31.57.0-24
/coreos.com/network/subnets/172.31.10.0-24

3. 部署 Master 节点

部署流程规划:

(1)制作集群证书

(2)部署kube-apiserver组件

(3)部署kube-controller-manager组件

(4)部署kube-scheduler组件

3.1 准备控制端证书

Master 节点的准备证书操作只需要做一次,将生成的证书拷到每个 Master 节点上以复用。

前提条件:

  • 签发证书需要用到生成 CA 证书时创建的 CA 证书及其密钥文件,确保它们在当前目录
  • 确保 cfssl 在当前环境已安装。

现在我们创建一套kubernetes集群的根CA证书。用于签发所有的k8s组件。

(1)创建证书请求json配置文件

mkdir -p ~/ssl/k8s && cd ~/ssl/k8s
cat > ca-config.json << EOF
{"signing": {"default": {"expiry": "87600h"},"profiles": {"kubernetes": {"expiry": "87600h","usages": ["signing","key encipherment","server auth","client auth"]}}}
}
EOF

(2)创建根CA证书签名请求文件

cat > ca-csr.json << EOF
{"CN": "kubernetes","key": {"algo": "rsa","size": 2048},"names": [{"C": "CN","L": "Hangzhou","ST": "Hangzhou","O": "k8s","OU": "System"}]
}
EOF

(3)生成证书文件

cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

3.2 使用自签CA签发kube-apiserver HTTPS证书

kube-apiserver 是 k8s 的访问核心,所有 K8S 组件和用户 kubectl 操作都会请求 kube-apiserver,通常启用 tls 证书认证,证书里面需要包含 kube-apiserver 可能被访问的地址,这样 client 校验 kube-apiserver 证书时才会通过,集群内的 Pod 一般通过 kube-apiserver 的 Service 名称访问,可能的 Service 名称有:

  • kubernetes
  • kubernetes.default
  • kubernetes.default.svc
  • kubernetes.default.svc.cluster
  • kubernetes.default.svc.cluster.local

通过集群外也可能访问 kube-apiserver,比如使用 kubectl,或者部署在集群外的服务会连 kube-apiserver (比如部署在集群外的 Promethues 采集集群指标做监控),这里列一下通过集群外连 kube-apiserver 有哪些可能地址:

  • 127.0.0.1: 在 Master 所在机器通过 127.0.0.1 访问本机 kube-apiserver
  • Service CIDR 的第一个 IP,比如 flanneld 以 daemonset 部署在每个节点,使用 hostNetwork 而不是集群网络,这时无法通过 service 名称访问 apiserver,因为使用 hostNetwork 无法解析 service 名称 (使用的 DNS 不是集群 DNS),它会使用 apiserver 内部的 CLUSTER IP 去请求 apiserver。 kube-controller-manager 的 --service-cluster-ip-range 启动参数是 10.32.0.0/16,那么第一个 IP 就是 10.32.0.1
  • 自定义域名: 配了 DNS,通过域名访问 kube-apiserver,也要将域名写入证书
  • SLB IP: 如果 Master 节点前面挂了一个负载均衡器,外界可以通过 SLB IP 来访问 kube-apiserver
  • Master 节点 IP: 如果没有 Master 负载均衡器,管理员在节点上执行 kubectl 通常使用 Master 节点 IP 访问 kube-apiserver

准备 CSR 文件

cat > apiserver-csr.json <<EOF
{"CN": "kubernetes","hosts": ["127.0.0.1","10.0.0.1","172.17.40.129","172.17.40.135","172.17.40.139","k8s-api.cluster.local","kubernetes","kubernetes.default","kubernetes.default.svc","kubernetes.default.svc.cluster","kubernetes.default.svc.cluster.local"],"key": {"algo": "rsa","size": 2048},"names": [{"C": "CN","L": "Hangzhou","ST": "Hangzhou","O": "k8s","OU": "System"}]
}
EOF

hosts 这里只准备了必要的,根据需求可增加,通常 Master 节点 IP 也都要加进去,你可以执行了上面的命令后再编辑一下 apiserver-csr.json,将需要 hosts 都加进去。k8s-api.cluster.local是自己添加的一个自定义域名,用于指定apiserver,这里需要写本地解析,shell脚本如下:

for node in {master,node1,node2}; dossh root@${node} "echo '# apiserver地址' >> /etc/hosts"ssh root@${node} "echo '172.17.40.129 k8s-api.cluster.local' >> /etc/hosts"
done

执行如下命令生成证书文件

cfssl gencert \-ca=ca.pem \-ca-key=ca-key.pem \-config=ca-config.json \-profile=kubernetes \apiserver-csr.json | cfssljson -bare apiserver

会生成下面两个重要的文件:

  • apiserver-key.pem: kube-apiserver 证书密钥
  • apiserver.pem: kube-apiserver 颁发的证书

将上面临时目录生成的证书都复制在kubernetes的证书目录下:

mkdir -pv /opt/kubernetes/ssl
cp -rp ./{ca*pem,apiserver*pem} /opt/kubernetes/ssl/

3.3 部署 kubectl 命令行工具

kubectl默认从~/.kube/config配置文件中获取访问kube-apiserver 地址、证书、用户名等信息,需要正确配置该文件才能正常使用kubectl命令。

需要将下载的kubectl 二进制文件和生产的~/.kube/config配置文件拷贝到需要使用kubectl 命令的机器上。

  • 设置环境变量
export KUBE_APISERVER="https://k8s-api.cluster.local:6443"

注意这里的KUBE_APISERVER地址,因为我们还没有安装haproxy,所以暂时需要手动指定使用apiserver的6443端口,等haproxy安装完成后就可以用使用443端口转发到6443端口去了。

  • 下载kubectl并放到/usr/local/bin/kubelet
  • 创建admin证书
cd ~/ssl/k8s
cat > admin-csr.json <<EOF
{"CN": "admin","hosts": [],"key": {"algo": "rsa","size": 2048},"names": [{"C": "CN","ST": "Hangzhou","L": "Hangzhou","O": "system:masters","OU": "System"}]
}
EOF
  • 后续kube-apiserver使用RBAC 对客户端(如kubelet、kube-proxy、Pod)请求进行授权
  • kube-apiserver 预定义了一些RBAC 使用的RoleBindings,如cluster-admin 将Group system:masters与Role cluster-admin绑定,该Role授予了调用kube-apiserver所有API的权限
  • O 指定了该证书的Group 为system:masters,kubectl使用该证书访问kube-apiserver时,由于证书被CA签名,所以认证通过,同时由于证书用户组为经过预授权的system:masters,所以被授予访问所有API的权限
  • hosts属性值为空列表

生成admin证书和私钥:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
--profile=kubernetes admin-csr.json |cfssljson -bare admin

创建kubectl kubeconfig文件:

# 设置集群参数
kubectl config set-cluster kubernetes \--certificate-authority=ca.pem \--embed-certs=true \--server=${KUBE_APISERVER}# 设置客户端认证参数
kubectl config set-credentials admin \--client-certificate=admin.pem \--client-key=admin-key.pem \--embed-certs=true# 设置上下文参数
kubectl config set-context kubernetes \--cluster=kubernetes \--user=admin# 设置默认上下文
kubectl config use-context kubernetes
  • admin.pem 证书 OU 字段值为 system:masterskube-apiserver 预定义的 RoleBinding cluster-admin 将 Group system:masters 与 Role cluster-admin 绑定,该 Role 授予了调用kube-apiserver 相关 API 的权限;
  • 生成的 kubeconfig 被保存到 ~/.kube/config 文件;
  • ~/.kube/config文件拥有对该集群的最高权限,请妥善保管。

3.4 部署kube-apiserver组件

  • 软件包下载

Kubernetes 在 Github 的 Release 页面其实已经有源码包、二进制包的下载方式:

See kubernetes-announce@. Additional binary downloads are linked in the CHANGELOG. 通过 Release 页面这句话的提示,我们可以从 CHANGELOG 页面获取下载软件包地址.

wget https://dl.k8s.io/v1.18.14/kubernetes-server-linux-amd64.tar.gz
tar -xf kubernetes-server-linux-amd64.tar.gz && cd ~/kubernetes/server/bin
chmod a+x {kube-apiserver,kube-controller-manager,kube-scheduler}
mkdir -p /opt/kubernetes/bin/
cp -rp {kube-apiserver,kube-controller-manager,kube-scheduler} /opt/kubernetes/bin/
  • kubernetes.tar.gz: Github Release 页面的压缩包(不含程序二进制)
  • kubernetes-server-linux-amd64.tar.gz: Kubernetes所需的程序二进制压缩包,大概三百多兆

只需要下载一个server包就够了,包含了Master和Worker Node二进制文件

(1)部署kube-apiserver

mkdir -p /opt/kubernetes/conf
export NODE="172.17.40.129"
cat > /opt/kubernetes/conf/kube-apiserver.conf << EOF
KUBE_APISERVER_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/var/log/kubernetes \\
--advertise-address=${NODE} \\
--default-not-ready-toleration-seconds=360 \\
--default-unreachable-toleration-seconds=360 \\
--max-mutating-requests-inflight=2000 \\
--max-requests-inflight=4000 \\
--default-watch-cache-size=200 \\
--delete-collection-workers=2 \\
--bind-address=${NODE} \\
--secure-port=6443 \\
--allow-privileged=true \\
--service-cluster-ip-range=10.0.0.0/24 \\
--service-node-port-range=1024-32767 \\
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \\
--authorization-mode=RBAC,Node \\
--enable-bootstrap-token-auth=true \\
--token-auth-file=/opt/kubernetes/ssl/token.csv \\
--kubelet-certificate-authority=/opt/kubernetes/ssl/ca.pem \\
--kubelet-client-certificate=/opt/kubernetes/ssl/apiserver.pem \\
--kubelet-client-key=/opt/kubernetes/ssl/apiserver-key.pem \\
--tls-cert-file=/opt/kubernetes/ssl/apiserver.pem  \\
--tls-private-key-file=/opt/kubernetes/ssl/apiserver-key.pem \\
--client-ca-file=/opt/kubernetes/ssl/ca.pem \\
--service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \\
--audit-log-maxage=30 \\
--audit-log-maxbackup=3 \\
--audit-log-maxsize=100 \\
--audit-log-path=/var/log/kubernetes/k8s-audit.log \\
--etcd-servers='https://172.17.40.129:2379,https://172.17.40.135:2379,https://172.17.40.139:2379' \\
--etcd-cafile=/opt/etcd/ssl/ca.pem \\
--etcd-certfile=/opt/etcd/ssl/server.pem \\
--etcd-keyfile=/opt/etcd/ssl/server-key.pem"
EOF

配置文件详细解释如下:

配置选项 选项说明
--logtostderr=false 输出日志到文件中(文件路径由--log-dir指定),不输出到标准错误控制台
--v=2 指定输出日志的级别
--advertise-address 向集群成员通知 apiserver 消息的 IP 地址,这个地址必须能够被集群中其他成员访问,如果 IP 地址为空,将会使用 --bind-address,如果未指定--bind-address,将会使用主机的默认接口地址
--etcd-servers 连接的 etcd 服务器列表 , 形式为(scheme://ip:port),使用逗号分隔
--etcd-cafile 用于etcd 通信的 SSL CA 文件
--etcd-certfile 用于 etcd 通信的的 SSL 证书文件
--etcd-keyfile 用于 etcd 通信的 SSL 密钥文件
--service-cluster-ip-range Service网络地址分配 ,CIDR 表示的 IP 范围,服务的 cluster ip 将从中分配, 一定不要和分配给 nodes 和 pods 的 IP 范围产生重叠
--bind-address 监听 --seure-port 的 IP 地址,被关联的接口必须能够被集群其它节点和 CLI/web 客户端访问,如果为空,则将使用所有接口(0.0.0.0
--secure-port=6443 用于监听具有认证授权功能的 HTTPS 协议的端口,默认值是6443
--allow-privileged 是否启用授权功能
--service-node-port-range Service使用的端口范围
--default-not-ready-toleration-seconds 表示 notReady状态的容忍度秒数
--default-unreachable-toleration-seconds 表示 unreachable状态的容忍度秒数:
--max-mutating-requests-inflight=2000 在给定时间内进行中可变请求的最大数量,当超过该值时,服务将拒绝所有请求,0 值表示没有限制(默认值 200)
--default-watch-cache-size=200 默认监视缓存大小,0 表示对于没有设置默认监视大小的资源,将禁用监视缓存
--delete-collection-workers=2 用于 DeleteCollection 调用的工作者数量,这被用于加速 namespace 的清理( 默认值 1)
--enable-admission-plugins 资源限制的相关配置
--authorization-mode 在安全端口上进行权限验证的插件的顺序列表,以逗号分隔的列表,包括:AlwaysAllow,AlwaysDeny,ABAC,Webhook,RBAC,Node.(默认值 “AlwaysAllow”)
--enable-bootstrap-token-auth 启用此选项以允许 ‘kube-system’ 命名空间中的'bootstrap.kubernetes.io/token' 类型密钥可以被用于 TLS 的启动认证
--token-auth-file 声明bootstrap token文件
--kubelet-certificate-authority 证书 authority 的文件路径
--kubelet-client-certificate 用于 TLS 的客户端证书文件路径
--kubelet-client-key 用于 TLS 的客户端证书密钥文件路径
--tls-private-key-file 包含匹配--tls-cert-file的 x509 证书私钥的文件
--service-account-key-file 包含 PEM 加密的 x509 RSA 或 ECDSA 私钥或公钥的文件,用于验证 ServiceAccount 令牌,如果设置该值,–tls-private-key-file 将会被使用,指定的文件可以包含多个密钥,并且这个标志可以和不同的文件一起多次使用
--audit-log-maxage 基于文件名中的时间戳,旧审计日志文件的最长保留天数
--audit-log-maxbackup 旧审计日志文件的最大保留个数
--audit-log-maxsize 审计日志被轮转前的最大兆字节数
--audit-log-path 如果设置,表示所有到apiserver的请求都会记录到这个文件中,‘-’表示写入标准输出

(2)启用TLS Bootstrapping机制

当集群开启了 TLS 认证后,每个节点的 kubelet 组件都要使用由 apiserver 使用的 CA 签发的有效证书才能与 apiserver 通讯;此时如果节点多了之后,为每个节点单独签署证书将是一件非常繁琐的事情;

TLS bootstrapping 功能就是让 node节点上的kubelet组件先使用一个预定的低权限用户连接到 apiserver,然后向 apiserver 申请证书,kubelet 的证书由 apiserver 动态签署;

  • 生成token值
head -c 16 /dev/urandom | od -An -t x | tr -d ' '
daaaae1a90e44c98d214405fed1cfed0
  • 生成token文件
cat > /opt/kubernetes/ssl/token.csv << EOF
daaaae1a90e44c98d214405fed1cfed0,kubelet-bootstrap,10001,"system:node-bootstrapper"
EOF

(3)创建kube-apiserver启动脚本

cat > /lib/systemd/system/kube-apiserver.service << EOF
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target[Service]
EnvironmentFile=/opt/kubernetes/conf/kube-apiserver.conf
ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS
Restart=on-failure
RestartSec=10
Type=notify
LimitNOFILE=65536[Install]
WantedBy=multi-user.target
EOF

(4)启动并设置开机启动

systemctl daemon-reload
systemctl enable kube-apiserver
systemctl start kube-apiserver

(5)授权kubelet-bootstrap用户允许请求证书

kubectl create clusterrolebinding kubelet-bootstrap \
--clusterrole=system:node-bootstrapper \
--user=kubelet-bootstrap

3.5 部署kube-controller-manager

kube-controller-manager(k8s控制器管理器)是一个守护进程,它通过kube-apiserver监视集群的共享状态(kube-apiserver收集或监视到的一些集群资源状态,供kube-controller-manager或其它客户端watch), 控制器管理器并尝试将当前的状态向所定义的状态迁移(移动、靠近),它本身是有状态的,会修改集群状态信息,如果多个控制器管理器同时生效,则会有一致性问题,所以kube-controller-manager的高可用,只能是主备模式,而kubernetes集群是采用租赁锁实现leader选举,需要在启动参数中加入--leader-elect=true

(1)创建controller-manager配置文件

cat > /opt/kubernetes/conf/kube-controller-manager.conf << EOF
KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/var/log/kubernetes \\
--leader-elect=true \\
--master=127.0.0.1:8080 \\
--bind-address=127.0.0.1 \\
--allocate-node-cidrs=true \\
--cluster-cidr=10.245.0.0/16 \\
--service-cluster-ip-range=10.0.0.0/24 \\
--cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \\
--cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem  \\
--root-ca-file=/opt/kubernetes/ssl/ca.pem \\
--service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \\
--experimental-cluster-signing-duration=87600h0m0s"
EOF

配置文件详细解释如下:

配置选项 选项意义
--leader-elect 高可用时启用选举功能。这里开启方便以后扩展
--master 通过本地非安全本地端口8080连接apiserver
--bind-address 监控地址
--allocate-node-cidrs 是否应在node节点上分配和设置Pod的CIDR
--cluster-cidr Controller Manager在启动时如果设置了–cluster-cidr参数,那么为每个没有设置Spec.PodCIDR的Node节点生成一个CIDR地址,并用该CIDR地址设置节点的Spec.PodCIDR属性,防止不同的节点的CIDR地址发生冲突
--service-cluster-ip-range 参数指定 Cluster 中 Service 的CIDR范围,该网络在各 Node 间必须路由不可达,必须和 kube-apiserver 中的参数一致
--cluster-signing-cert-file 指定用于集群签发的所有集群范围内证书文件(根证书文件)
--cluster-signing-key-file 指定集群签发证书的key
--root-ca-file 用来对 kube-apiserver 证书进行校验,指定该参数后,才会在Pod 容器的 ServiceAccount 中放置该 CA 证书文件;
--service-account-private-key-file 包含用于签署service account token的PEM编码RSA或者ECDSA私钥的文件名
--experimental-cluster-signing-duration 证书签发时间

(2)创建controller-manager启动脚本

cat > /lib/systemd/system/kube-controller-manager.service << EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
After=network.target[Service]
EnvironmentFile=/opt/kubernetes/conf/kube-controller-manager.conf
ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS
Restart=on-failure
RestartSec=5
LimitNOFILE=65536[Install]
WantedBy=multi-user.target
EOF

(3)启动并设置开机启动

systemctl daemon-reload
systemctl enable kube-controller-manager
systemctl start kube-controller-manager

3.6 部署 kube-scheduler

调度器的职责主要是为新创建的pod在集群中寻找最合适的node,并将pod调度到Node上,Scheduler调度器运行在master节点,它的核心功能是监听apiserver来获取节点上为空的pod,然后为pod创建一个binding指示pod应该调度到哪个节点上,调度结果写入apiserver

(1)创建scheduler配置文件

cat > /opt/kubernetes/conf/kube-scheduler.conf << EOF
KUBE_SCHEDULER_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/var/log/kubernetes \\
--leader-elect=true \\
--master=http://127.0.0.1:8080 \\
--bind-address=127.0.0.1 \\
--address=127.0.0.1"
EOF

(2)创建scheduler服务启动脚本

cat > /lib/systemd/system/kube-scheduler.service << EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
After=network.target[Service]
EnvironmentFile=/opt/kubernetes/conf/kube-scheduler.conf
ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS
Restart=on-failure
RestartSec=5
LimitNOFILE=65536[Install]
WantedBy=multi-user.target
EOF

(3)启动并设置开机启动

systemctl daemon-reload
systemctl enable kube-scheduler
systemctl start kube-scheduler

3.7 查看集群状态

所有组件都已经启动成功,通过kubectl工具查看当前集群组件状态:

root@sc-cc-k8s-master-001:~# kubectl get cs
NAME                 STATUS    MESSAGE             ERROR
scheduler            Healthy   ok
controller-manager   Healthy   ok
etcd-0               Healthy   {"health":"true"}
etcd-1               Healthy   {"health":"true"}
etcd-2               Healthy   {"health":"true"}

看到第二列的状态值都是Healthy,说明Master节点组件运行正常。

4 部署Flannel网络插件

4.1 Flannel简介

Flannel 是 CoreOS 团队针对 Kubernetes 设计的一个覆盖网络(Overlay Network)工具,其目的在于帮助每一个使用 Kuberentes 的 CoreOS 主机拥有一个完整的子网。Flannel通过给每台宿主机分配一个子网的方式为容器提供虚拟网络,它基于Linux TUN/TAP,使用UDP封装IP包来创建overlay网络,并借助etcd维护网络的分配情况。

Flannel是作为一个二进制文件的方式部署在每个node上,主要实现两个功能:

  • 为每个node分配subnet,容器将自动从该子网中获取IP地址
  • 当有node加入到网络中时,为每个node增加路由配置

下面是使用host-gw backend的flannel网络架构图:

4.2 Flannel 部署

所有的node节点都需要安装网络插件才能让所有的Pod加入到同一个局域网中。以下是二进制部署方式:

(1)下载flannel二进制插件

[ ! -d ~/flannel ] && mkdir -pv ~/flannel
wget https://github.com/coreos/flannel/releases/download/v0.13.0/flannel-v0.13.0-linux-amd64.tar.gz -O ~/flannel

(2)解压并拷贝

# master 节点操作
cd ~/flannel && tar xf flannel-v0.13.0-linux-amd64.tar.gz
mkdir -pv /opt/flannel/{bin,cfg,ssl}
cp -rpv {flanneld,mk-docker-opts.sh} /opt/flannel/bin/
cp -rpv ~/ssl/etcd/{ca*pem,server*pem} /opt/flannel/ssl/

(3)创建flannel配置文件

# node节点操作
export ETCD_ENDPOINTS="https://172.17.40.129:2379,https://172.17.40.135:2379,https://172.17.40.139:2379"
export IFACE="ens160"
cat > /opt/flannel/cfg/flanneld.conf << EOF
FLANNEL_OPTS="-etcd-endpoints=${ETCD_ENDPOINTS} \\
-etcd-cafile=/opt/flannel/ssl/ca.pem \\
-etcd-certfile=/opt/flannel/ssl/server.pem \\
-etcd-keyfile=/opt/flannel/ssl/server-key.pem \\
-etcd-prefix=/kube-flannel/network \\
-ip-masq \\
-iface=${IFACE}"
EOF
配置项 说明
-etcd-endpoints 指定etcd的endpoint地址
-ip-masq 外部网络流量的IP伪装规则
-iface 用于主机间通信的接口(IP或名称)。可以多次指定以按顺序检查每个选项。返回找到的第一个匹配项。

(4)在etcd中创建网络配置

# master节点操作,只操作一次
# 注意 Network 的值是 Pod 集群网段
cat > ~/flannel/flannel-network.json << EOF
{"Network": "10.245.0.0/16","SubnetLen": 24,"Backend": {"Type": "vxlan"}
}
EOF# 创建/kube-flannel/network目录
export ETCD_ENDPOINTS="https://172.17.40.129:2379,https://172.17.40.135:2379,https://172.17.40.139:2379"
ETCDCTL_API=2 /opt/etcd/bin/etcdctl --ca-file=/opt/etcd/ssl/ca.pem \
--cert-file=/opt/etcd/ssl/server.pem \
--key-file=/opt/etcd/ssl/server-key.pem \
--endpoints=${ETCD_ENDPOINTS} \
mkdir /kube-flannel/network# 在etcd中初始化flannel网络数据
ETCDCTL_API=2 etcdctl --ca-file=/opt/etcd/ssl/ca.pem \
--cert-file=/opt/etcd/ssl/server.pem \
--key-file=/opt/etcd/ssl/server-key.pem \
--endpoints=${ETCD_ENDPOINTS} \
mk /kube-flannel/network/config "$(cat ~/flannel/flannel-network.json)"

(5)设置systemd管理

cat > /lib/systemd/system/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
EnvironmentFile=/opt/flannel/cfg/flanneld.conf
ExecStart=/opt/flannel/bin/flanneld \$FLANNEL_OPTS
ExecStartPost=/opt/flannel/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
Restart=on-failure[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
EOF

启动flannel服务

systemctl daemon-reload
systemctl enable flanneld.service
systemctl start flanneld.service

还有一个ExecStartPost=/opt/flannel/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker,其中的ExecStartPost=/opt/flannel/bin/mk-docker-opts.sh脚本是在flanneld启动后运行,将会生成两个环境变量配置文件:

  • /run/flannel/docker
  • /run/flannel/subnet.env
# 指定Docker的配置参数
cat /run/flannel/docker
DOCKER_OPT_BIP="--bip=10.245.3.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=false"
DOCKER_OPT_MTU="--mtu=1450"
DOCKER_NETWORK_OPTIONS=" --bip=10.245.3.1/24 --ip-masq=false --mtu=1450"
# Flannel子网分配结果
cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.245.0.0/16
FLANNEL_SUBNET=10.245.3.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

(6)修改docker systemd配置并重启

sed -ri "/EnvironmentFile=*flannel*/d" /lib/systemd/system/docker.service
sed -ri '/ExecStart/i\EnvironmentFile=/run/flannel/docker' /lib/systemd/system/docker.service
sed -ri "/ExecStart=.*$/s#(.*)#\1 \$DOCKER_NETWORK_OPTIONS#" /lib/systemd/system/docker.service# 重启docker
systemctl daemon-reload && systemctl restart docker

(7)查看路由信息

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.40.1     0.0.0.0         UG    0      0        0 ens160
10.245.3.0      0.0.0.0         255.255.255.0   U     0      0        0 cni0
10.245.3.0      0.0.0.0         255.255.255.0   U     0      0        0 docker0
10.245.4.0      10.245.4.0      255.255.255.0   UG    0      0        0 flannel.1
10.245.5.0      10.245.5.0      255.255.255.0   UG    0      0        0 flannel.1
10.245.6.0      10.245.6.0      255.255.255.0   UG    0      0        0 flannel.1
172.17.40.0     0.0.0.0         255.255.254.0   U     0      0        0 ens160

可以看到路由规则已经创建,说明flannel安装成功。

5. 部署 Node 节点

拷贝kubelet、kube-proxy二进制包到/opt/worker/bin下:

mkdir -pv /opt/worker/{bin,conf,ssl}
cd ~/kubernetes/server/bin
cp -rp {kubelet,kube-proxy} /opt/worker/bin/
# 分发ca到/opt/worker/ssl
cd ~/ssl/k8s && cp -rp ca.pem /opt/worker/ssl/

5.1 部署 kubelet 服务

(1)创建 kubelet.conf 配置文件

cat > /opt/worker/conf/kubelet.conf << EOF
KUBELET_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/var/log/kubernetes \\
--hostname-override=node0 \\
--container-runtime=docker \\
--network-plugin=cni \\
--kubeconfig=/opt/worker/conf/kubelet.kubeconfig \\
--bootstrap-kubeconfig=/opt/worker/conf/bootstrap.kubeconfig \\
--config=/opt/worker/conf/kubelet-config.yml \\
--cert-dir=/opt/worker/ssl \\
--image-pull-progress-deadline=15m \\
--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1"
EOF

配置文件解释说明

配置选项 选项意义
--hostname-override 用来配置该节点在集群中显示的主机名,kubelet设置了-–hostname-override参数后,kube-proxy也需要设置,否则会出现找不到Node的情况
--container-runtime 指定容器运行时引擎
--network-plugin 启用CNI网络插件
--kubeconfig kubelet作为客户端使用的kubeconfig认证文件,此文件是由kube-controller-mananger生成的
--bootstrap-kubeconfig 指定令牌认证文件
--config 指定kubelet配置文件
--cert-dir 设置kube-controller-manager生成证书和私钥的目录
--image-pull-progress-deadline 镜像拉取进度最大时间,如果在这段时间拉取镜像没有任何进展,将取消拉取,默认:1m0s
--pod-infra-container-image 每个pod中的network/ipc名称空间容器将使用的镜像

(2)kubelet-config.yml配置参数文件

cat > /opt/worker/conf/kubelet-config.yml << EOF
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: 0.0.0.0
port: 10250
readOnlyPort: 10255
cgroupDriver: cgroupfs
clusterDNS:
- 10.0.0.2
clusterDomain: cluster.local
failSwapOn: false
authentication:anonymous:enabled: falsewebhook:cacheTTL: 2m0senabled: truex509:clientCAFile: /opt/worker/ssl/ca.pem
authorization:mode: Webhookwebhook:cacheAuthorizedTTL: 5m0scacheUnauthorizedTTL: 30s
evictionHard:imagefs.available: 15%memory.available: 100Minodefs.available: 10%nodefs.inodesFree: 5%
maxOpenFiles: 1000000
maxPods: 110
EOF

简单说几个比较重要的选项配置意义

配置选项 选项意义
address kubelet 服务监听的地址
port: 10250 kubelet 服务的端口,默认 10250
readOnlyPort 没有认证/授权的只读 kubelet 服务端口 ,设置为 0 表示禁用,默认 10255
clusterDNS DNS 服务器的IP地址列表
clusterDomain 集群域名, kubelet 将配置所有容器除了主机搜索域还将搜索当前域

(3)生成bootstrap.kubeconfig文件

cd /opt/worker/conf
export KUBE_APISERVER="https://k8s-api.cluster.local:6443"kubectl config set-cluster kubernetes \
--certificate-authority=/opt/worker/ssl/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=bootstrap.kubeconfig# token 与 token.csv 里保持一致
kubectl config set-credentials "kubelet-bootstrap" \
--token=daaaae1a90e44c98d214405fed1cfed0 \
--kubeconfig=bootstrap.kubeconfig                          kubectl config set-context default \
--cluster=kubernetes \
--user="kubelet-bootstrap" \
--kubeconfig=bootstrap.kubeconfigkubectl config use-context default --kubeconfig=bootstrap.kubeconfig

(4)创建kubelet服务启动脚本

cat > /lib/systemd/system/kubelet.service << EOF
[Unit]
Description=Kubernetes Kubelet
After=docker.service[Service]
EnvironmentFile=/opt/worker/conf/kubelet.conf
ExecStart=/opt/worker/bin/kubelet \$KUBELET_OPTS
Restart=on-failure
RestartSec=10
LimitNOFILE=65536[Install]
WantedBy=multi-user.target
EOF

(5)启动并设置开机启动

systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet

(6)部署CNI容器网络

CNI,全名叫做:容器网络接口。是CNCF旗下的一个项目,由一组用于配置容器的网络接口的规范和库组成。CNI主要用于解决容器网络互联的配置并支持多种网络模型。

[root@master ~]# cd ~
[root@master ~]# wget https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz
[root@master ~]# mkdir -pv /opt/cni/bin
[root@master ~]# tar xf cni-plugins-linux-amd64-v0.8.6.tgz -C /opt/cni/bin
[root@master ~]# ls /opt/cni/bin
bandwidth  bridge  dhcp  firewall  flannel  host-device  host-local  ipvlan  loopback  macvlan  portmap  ptp  sbr  static  tuning  vlan

5.2 批准kubelet证书申请并加入集群

(1)查看kubelet证书请求

root@sc-cc-k8s-master-001:~# kubectl get csr
NAME                                                   AGE     SIGNERNAME                                    REQUESTOR           CONDITION
node-csr-ig5V2hNrx3WpZ3gTcoT5nWL-SIRsdcmOSsFac-S3Pew   6m25s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Pending

此命令可以看到所有请求,所有为pending状态,则是需要批准的。

(2)批准申请

[root@master ~]# kubectl certificate approve `kubectl get csr | grep "Pending" | awk '{print $1}'`
certificatesigningrequest.certificates.k8s.io/node-csr-ig5V2hNrx3WpZ3gTcoT5nWL-SIRsdcmOSsFac-S3Pew approved

(3)查看节点

root@sc-cc-k8s-master-001:~# kubectl get node
NAME    STATUS     ROLES    AGE   VERSION
node0   NotReady   <none>   29s   v1.18.14

注意:这里的STATUS状态值还是notReady,是因为网络插件还没有部署好。接着往下即可

5.3 部署kube-proxy服务

kube-proxy是什么,这里就不得不提前说下service,service是一组Pod的抽象集合,它相当于一组Pod的负载均衡器,负责将请求分发到对应的pod,kube-proxy就是负责service的实现的,当请求到达service时,它通过label关联到后端并转发到某个Pod;kube-proxy提供了三种负载均衡模式:用户空间、iptables、ipvs,我们采用的是iptables负载均衡模式。

(1)创建kube-proxy配置文件

cat > /opt/worker/conf/kube-proxy.conf << EOF
KUBE_PROXY_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/var/log/kubernetes \\
--config=/opt/worker/conf/kube-proxy-config.yml"
EOF

(2)kube-proxy-config.yml配置参数文件

cat > /opt/worker/conf/kube-proxy-config.yml << EOF
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
healthzBindAddress: 0.0.0.0:10256
metricsBindAddress: 0.0.0.0:10249
clientConnection:burst: 200kubeconfig: /opt/worker/conf/kube-proxy.kubeconfigqps: 100
hostnameOverride: node0
clusterCIDR: 10.0.0.0/24
EOF

简单说一下上面配置的选项意义

选项配置 选项意义
clientConnection 与kube-apiserver交互时的参数设置
burst: 200 临时允许该事件记录值超过qps设定值
kubeconfig kube-proxy 客户端连接 kube-apiserver 的 kubeconfig 文件路径设置
qps: 100 与kube-apiserver交互时的QPS,默认值5
bindAddress kube-proxy监听地址
healthzBindAddress 用于检查服务的IP地址和端口
metricsBindAddress metrics服务的ip地址和端口。默认:127.0.0.1:10249
clusterCIDR kube-proxy 根据 --cluster-cidr 判断集群内部和外部流量,指定 --cluster-cidr 或 --masquerade-all 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT
hostnameOverride 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 ipvs 规则;

(3)生成kube-proxy.kubeconfig证书

创建证书请求文件

cd ~/ssl/k8s
cat > kube-proxy-csr.json << EOF
{"CN": "system:kube-proxy","hosts": [],"key": {"algo": "rsa","size": 2048},"names": [{"C": "CN","L": "Hangzhou","ST": "Hangzhou","O": "k8s","OU": "System"}]
}
EOF

生成证书

cfssl gencert -ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes \
kube-proxy-csr.json | cfssljson -bare kube-proxy

(4)生成kubeconfig文件

kube-proxy是作为kube-apiserver的客户端,由于我们启用了TLS,所以需要认证访问,这里我们需要使用到之前生成的证书。

cd ~/ssl/k8s
export KUBE_APISERVER="https://k8s-api.cluster.local:6443"
kubectl config set-cluster kubernetes \--certificate-authority=./ca.pem \--embed-certs=true \--server=${KUBE_APISERVER} \--kubeconfig=kube-proxy.kubeconfigkubectl config set-credentials kube-proxy \--client-certificate=./kube-proxy.pem \--client-key=./kube-proxy-key.pem \--embed-certs=true \--kubeconfig=kube-proxy.kubeconfigkubectl config set-context default \--cluster=kubernetes \--user=kube-proxy \--kubeconfig=kube-proxy.kubeconfigkubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
cp -r kube-proxy.kubeconfig /opt/worker/conf/

(5)创建kube-proxy服务启动脚本

cat > /lib/systemd/system/kube-proxy.service << EOF
[Unit]
Description=Kubernetes Proxy
After=network.target[Service]
EnvironmentFile=/opt/worker/conf/kube-proxy.conf
ExecStart=/opt/worker/bin/kube-proxy \$KUBE_PROXY_OPTS
Restart=on-failure
RestartSec=10
LimitNOFILE=65536[Install]
WantedBy=multi-user.target
EOF

(6)启动并设置开机启动

systemctl daemon-reload
systemctl enable kube-proxy
systemctl start kube-proxy

5.4 授权apiserver访问kubelet

在执行kubectl exec、run、logs 等命令时,apiserver 会转发到 kubelet。这里定义 RBAC规则,授权apiserver调用kubelet API"

mkdir -p ~/yaml
cat > ~/yaml/apiserver-to-kubelet-rbac.yaml << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:annotations:rbac.authorization.kubernetes.io/autoupdate: "true"labels:kubernetes.io/bootstrapping: rbac-defaultsname: system:kube-apiserver-to-kubelet
rules:- apiGroups:- ""resources:- nodes/proxy- nodes/stats- nodes/log- nodes/spec- nodes/metrics- pods/logverbs:- "*"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:name: system:kube-apiservernamespace: ""
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: system:kube-apiserver-to-kubelet
subjects:- apiGroup: rbac.authorization.k8s.iokind: Username: kubernetes
EOF

访问授权

root@sc-cc-k8s-master-001:~# kubectl apply -f ~/yaml/apiserver-to-kubelet-rbac.yaml
clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created
clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created

5.5 增加work节点

(1)拷贝部署好的node相关文件到新节点

# master节点操作
export NODE_NAME=node1
scp -r /lib/systemd/system/kubelet.service root@${NODE_NAME}:/lib/systemd/system/kubelet.service
scp -r /lib/systemd/system/kube-proxy.service root@${NODE_NAME}:/lib/systemd/system/kube-proxy.service
scp -r /opt/worker root@${NODE_NAME}:/opt/
scp -r /opt/cni root@${NODE_NAME}:/opt/

(2)删除kubelet证书和kubeconfig

# node节点操作
cd /opt/worker/ssl && rm -rf kubelet*

(3)修改配置文件

# node节点操作
export NODE_NAME=node1
cd /opt/worker/conf
sed -ri "/hostname-override/{s/node0/$NODE_NAME/}" kubelet.conf
sed -ri "/ostnameOverride/{s/node0/$NODE_NAME/}" kube-proxy-config.yml

(4)启动并设置开机启动

# node节点操作
systemctl daemon-reload
systemctl enable --now kubelet kube-proxy
systemctl start kubelet kube-proxy

(5)批准node节点申请

# master节点操作
root@sc-cc-k8s-master-001:/opt# kubectl get csr
NAME                                                   AGE   SIGNERNAME                                    REQUESTOR           CONDITION
node-csr-sBR41BWRZ1uCnw73zStIFP_osT9f10-jQBnUsTkuWdE   33s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Pendingroot@sc-cc-k8s-master-001:/opt# kubectl certificate approve node-csr-sBR41BWRZ1uCnw73zStIFP_osT9f10-jQBnUsTkuWdE
certificatesigningrequest.certificates.k8s.io/node-csr-sBR41BWRZ1uCnw73zStIFP_osT9f10-jQBnUsTkuWdE approved

(6)查看node状态

root@sc-cc-k8s-master-001:~/yaml# kubectl get nodes
NAME    STATUS   ROLES    AGE   VERSION
node0   Ready    <none>   86s   v1.18.14
node1   Ready    <none>   90s   v1.18.14

Kubernetes从零开始搭建相关推荐

  1. 容器编排技术 -- Kubernetes从零开始搭建自定义集群

    容器编排技术 -- Kubernetes从零开始搭建自定义集群 1 设计和准备 1.1 学习 1.2 Cloud Provider 1.3 节点 1.4 网络 1.4.1 网络连接 1.4.2 网络策 ...

  2. 从零开始搭建Kubernetes集群(三、搭建K8S集群)

    一.前言 在上一篇文章 从零开始搭建Kubernetes 1.10.0 集群(二.搭建虚拟机环境)中,我们已经搭建好了基础的虚拟机环境.现在,我们可以开启我们真正的K8S之旅. 我们将现有的虚拟机称之 ...

  3. 从零开始搭建创业公司后台技术栈!

    原文 : http://ju.outofmemory.cn/entry/351897 前言 说到后台技术栈,脑海中是不是浮现的是这样一幅图? 图 1 有点眼晕,以下只是我们会用到的一些语言的合集,而且 ...

  4. 从零开始搭建公司后台技术栈

    有点眼晕,以下只是我们会用到的一些语言的合集,而且只是语言层面的一部分,就整个后台技术栈来说,这只是一个开始,从语言开始,还有很多很多的内容.今天要说的后台是大后台的概念,放在服务器上的东西都属于后台 ...

  5. 从零开始搭建创业公司全新技术栈

    目录 **1.项目管理/Bug管理/问题管理** **2.DNS** **3.LB(负载均衡)** **4.CDN** **5.RPC 框架** **6.名字发现/服务发现** **7.关系数据库** ...

  6. 从零开始搭建创业公司后台技术栈

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 后台回复"k8s",可领取k8s资料 -     前言 ...

  7. 从零开始搭建K8S--搭建K8S Ingress

    Ingress是个什么鬼,网上资料很多(推荐官方),大家自行研究.简单来讲,就是一个负载均衡的玩意,其主要用来解决使用NodePort暴露Service的端口时Node IP会漂移的问题.同时,若大量 ...

  8. ssm radis mysql_从零开始搭建框架SSM+Redis+Mysql(一)之摘要

    从零开始搭建框架SSM+Redis+Mysql(一)之摘要 本文章为本人实际的操作后的回忆笔记,如果有步骤错漏,希望来信307793969@qq.com或者评论指出. 本文章只体现过程,仅体现操作流程 ...

  9. 从零开始搭建一个vue项目 -- vue-cli/cooking-cli(一)

    从零开始搭建一个vue项目 -- vue-cli/cooking-cli(一) 1.vue-cli搭建一个可靠成熟的项目 1.介绍 vue-cli 我是去年六月份接触的vue1.0,当时还是个菜逼,当 ...

最新文章

  1. Cstring的使用
  2. 第9章 数据字典(选项)管理
  3. ubuntu 终端常用命令
  4. Java 里的泛型简介.
  5. 深入浅出学Hive:Hive参数
  6. MediaWiKi简明安装与配置笔记
  7. C# 控制台 模拟时间一秒一秒走动,直到按Esc键,时间静止,退出!
  8. [转+整理]十道海量数据处理面试题与十个方法大总结
  9. python中的列表和元组_python中列表和元组的区别
  10. python open函数 创建变量文件_python的open函数怎么用
  11. ASCII、ANSI、UNICODE及UTF-8编码
  12. 干货 | attention超全综述
  13. C语言开定时器做呼吸灯程序,单片机制作呼吸灯的C语言程序怎么样编写
  14. layui date插件设置不能跨月查询
  15. matlab互相关函数并画图,自相关函数和互相关函数的matlab计算和作图
  16. 毕业设计 STM32单片机的空气质量检测系统
  17. 码教授|面试官:你还有什么要问我的吗?
  18. 很迷茫,30岁,大专学历,没有一技之长,负债累累,怎么翻身?
  19. 网络数据包的封包格式
  20. 《AMNet: Deep Atrous Multiscale Stereo Disparity Estimation Networks》

热门文章

  1. jmeter 添加html断言,JMeter-断言
  2. chrom插件使用方法
  3. 深切缅怀一位国产数据库的先驱者:崔维力先生
  4. HTTP错误代码大全
  5. 你有没有使用java生成过二维码?(二)
  6. 李宏毅2020机器学习作业2-Classification:年收入二分类
  7. 城市治理复杂性需要大数据
  8. 【JavaWeb】web开发中的绝对路径和相对路径
  9. MBI5024驱动程序
  10. redis集群搭建(非常详细,适合新手)