Kubernetes集群部署
1. 准备工作
目前部署k8s集群社区有两种方式:
- kubeadm
Kubeadm
是一个k8s部署工具,提供kubeadm init
和kubeadm 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
EOF
cat > /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 = 1
fs.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 = 1
net.netfilter.nf_conntrack_max = 10485760
net.netfilter.nf_conntrack_tcp_timeout_established = 300
net.netfilter.nf_conntrack_buckets = 655360
net.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 = 8192
net.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-certinfo
sudo 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}
do
ssh 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}; do
ssh 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}; do
scp -r /lib/systemd/system/etcd.service root@${node}:/lib/systemd/system/
done
(4)启动所有节点etcd服务
#! /bin/bash
for node in {master,node1,node2}
do
ssh 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 - Community
Version: 20.10.1
API version: 1.40
Go version: go1.13.15
Git commit: 831ebea
Built: Tue Dec 15 04:35:01 2020
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 19.03.12
API version: 1.40 (minimum version 1.12)
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:44:20 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.4.3
GitCommit: 269548fa27e0089a8b8278fc4fc781d7f65a939b
runc:
Version: 1.0.0-rc92
GitCommit: ff819c7e9184c13b7c2607fe6c30ae19403a7aff
docker-init:
Version: 0.18.0
GitCommit: 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-apiserverService 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}; do
ssh 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 将Groupsystem:masters
与Rolecluster-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:masters
,kube-apiserver
预定义的 RoleBindingcluster-admin
将 Groupsystem:masters
与 Rolecluster-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: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /opt/worker/ssl/ca.pem
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
evictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.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.kubeconfig
kubectl 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: 200
kubeconfig: /opt/worker/conf/kube-proxy.kubeconfig
qps: 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.kubeconfig
kubectl config set-credentials kube-proxy \
--client-certificate=./kube-proxy.pem \
--client-key=./kube-proxy-key.pem \
--embed-certs=true \
--kubeconfig=kube-proxy.kubeconfig
kubectl config set-context default \
--cluster=kubernetes \
--user=kube-proxy \
--kubeconfig=kube-proxy.kubeconfig
kubectl 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-defaults
name: system:kube-apiserver-to-kubelet
rules:
- apiGroups:
- ""
resources:
- nodes/proxy
- nodes/stats
- nodes/log
- nodes/spec
- nodes/metrics
- pods/log
verbs:
- "*"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:kube-apiserver
namespace: ""
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:kube-apiserver-to-kubelet
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: 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 Pending
root@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