参考文章:https://www.cnblogs.com/chiangchou/p/k8s-1.html
一、简介
1、Kubernetes 是什么
Kubernetes 是一个全新的基于容器技术的分布式架构解决方案,是 Google 开源的一个容器集群管理系统,Kubernetes 简称 K8S。
Kubernetes 是一个一站式的完备的分布式系统开发和支撑平台,更是一个开放平台,对现有的编程语言、编程框架、中间件没有任何侵入性。
Kubernetes 提供了完善的管理工具,这些工具涵盖了开发、部署测试、运维监控在内的各个环节。
Kubernetes 具有完备的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制、多粒度的资源配额管理能力。
Kubernetes 官方文档:https://kubernetes.io/zh/
2、Kubernetes 特性
① 自我修复
在节点故障时,重新启动失败的容器,替换和重新部署,保证预期的副本数量;杀死健康检查失败的容器,并且在未准备好之前不会处理用户的请求,确保线上服务不中断。
② 弹性伸缩
使用命令、UI或者基于CPU使用情况自动快速扩容和缩容应用程序实例,保证应用业务高峰并发时的高可用性;业务低峰时回收资源,以最小成本运行服务。
③ 自动部署和回滚
K8S采用滚动更新策略更新应用,一次更新一个Pod,而不是同时删除所有Pod,如果更新过程中出现问题,将回滚更改,确保升级不影响业务。
④ 服务发现和负载均衡
K8S为多个容器提供一个统一访问入口(内部IP地址和一个DNS名称),并且负载均衡关联的所有容器,使得用户无需考虑容器IP问题。
⑤ 机密和配置管理
管理机密数据和应用程序配置,而不需要把敏感数据暴露在镜像里,提高敏感数据安全性。并可以将一些常用的配置存储在K8S中,方便应用程序使用。
⑥ 存储编排
挂载外部存储系统,无论是来自本地存储,公有云,还是网络存储,都作为集群资源的一部分使用,极大提高存储使用灵活性。
⑦ 批处理
提供一次性任务,定时任务;满足批量数据处理和分析的场景。
二、集群架构与组件
Kubernetes 集群架构以及相关的核心组件如下图所示:一个 Kubernetes 集群一般包含一个 Master 节点和多个 Node 节点,一个节点可以看成是一台物理机或虚拟机。
1、Master
Master 是 K8S 的集群控制节点,每个 K8S 集群里需要有一个 Master 节点来负责整个集群的管理和控制,基本上 K8S 所有的控制命令都是发给它,它来负责具体的执行过程。Master 节点通常会占据一个独立的服务器,因为它太重要了,如果它不可用,那么所有的控制命令都将失效。
Master 节点上运行着以下关键组件:
① kube-apiserver
是集群的统一入口,各组件协调者,以 HTTP Rest 提供接口服务,所有对象资源的增、删、改、查和监听操作都交给 apiserver 处理后再提交给 Etcd 存储。
② kube-controller-manager
是 K8S 里所有资源对象的自动化控制中心,处理集群中常规后台任务,一个资源对应一个控制器,而 controller-manager 就是负责管理这些控制器的。
③ kube-scheduler
根据调度算法为新创建的 Pod 选择一个 Node 节点,可以任意部署,可以部署在同一个节点上,也可以部署在不同的节点上。
④ etcd
是一个分布式的,一致的 key-value 存储,主要用途是共享配置和服务发现,保存集群状态数据,比如 Pod、Service 等对象信息。
2、Node
除了 Master,K8S 集群中的其它机器被称为 Node 节点,Node 节点是 K8S 集群中的工作负载节点,每个 Node 都会被 Master 分配一些工作负载,当某个 Node 宕机时,其上的工作负载会被 Master 自动转移到其它节点上去。
每个 Node 节点上都运行着以下关键组件:
① kubelet
kubelet 是 Master 在 Node 节点上的 Agent(代理),与 Master 密切协作,管理本机运行容器的生命周期,负责 Pod 对应的容器的创建、启停等任务,实现集群管理的基本功能。
② kube-proxy
在 Node 节点上实现 Pod 网络代理,实现 Kubernetes Service 的通信,维护网络规则和四层负载均衡工作。
③ docker engine
Docker 引擎,负责本机的容器创建和管理工作。
Node 节点可以在运行期间动态增加到 K8S 集群中,前提是这个节点上已经正确安装、配置和启动了上述关键组件。在默认情况下 kubelet 会向 Master 注册自己,一旦 Node 被纳入集群管理范围,kubelet 就会定时向 Master 节点汇报自身的情况,例如操作系统、Docker 版本、机器的 CPU 和内存情况,以及之前有哪些 Pod 在运行等,这样 Master 可以获知每个 Node 的资源使用情况,并实现高效均衡的资源调度策略。而某个 Node 超过指定时间不上报信息时,会被 Master 判定为“失联”,Node 的状态被标记为不可用(Not Ready),随后 Master 会触发“工作负载大转移”的自动流程。
三、核心概念
1、Pod
Pod 是 K8S 中最重要也是最基本的概念,Pod 是最小的部署单元,是一组容器的集合。每个 Pod 都由一个特殊的根容器 Pause 容器,以及一个或多个紧密相关的用户业务容器组成。
Pause 容器作为 Pod 的根容器,以它的状态代表整个容器组的状态。K8S 为每个 Pod 都分配了唯一的 IP 地址,称之为 Pod IP。Pod 里的多个业务容器共享 Pause 容器的IP,共享 Pause 容器挂载的 Volume。
2、Label
标签,附加到某个资源上,用于关联对象、查询和筛选。一个 Label 是一个 key=value 的键值对,key 与 value 由用户自己指定。Label 可以附加到各种资源上,一个资源对象可以定义任意数量的 Label,同一个 Label 也可以被添加到任意数量的资源上。
我们可以通过给指定的资源对象捆绑一个或多个不同的 Label 来实现多维度的资源分组管理功能,以便于灵活、方便地进行资源分配、调度、配置、部署等工作。
K8S 通过 Label Selector(标签选择器)来查询和筛选拥有某些 Label 的资源对象。Label Selector 有基于等式( name=label1 )和基于集合( name in (label1, label2) )的两种方式。
3、ReplicaSet(RC)
ReplicaSet 用来确保预期的 Pod 副本数量,如果有过多的 Pod 副本在运行,系统就会停掉一些 Pod,否则系统就会再自动创建一些 Pod。
我们很少单独使用 ReplicaSet,它主要被 Deployment 这个更高层的资源对象使用,从而形成一整套 Pod 创建、删除、更新的编排机制。
4、Deployment
Deployment 用于部署无状态应用,Deployment 为 Pod 和 ReplicaSet 提供声明式更新,只需要在 Deployment 描述想要的目标状态,Deployment 就会将 Pod 和 ReplicaSet 的实际状态改变到目标状态。
5、Horizontal Pod Autoscaler(HPA)
HPA 为 Pod 横向自动扩容,也是 K8S 的一种资源对象。HPA 通过追踪分析 RC 的所有目标 Pod 的负载变化情况,来确定是否需要针对性调整目标 Pod 的副本数量。
6、Service
Service 定义了一个服务的访问入口,通过 Label Selector 与 Pod 副本集群之间“无缝对接”,定义了一组 Pod 的访问策略,防止 Pod 失联。
创建 Service 时,K8S会自动为它分配一个全局唯一的虚拟 IP 地址,即 Cluster IP。服务发现就是通过 Service 的 Name 和 Service 的 ClusterIP 地址做一个 DNS 域名映射来解决的。
7、Namespace
命名空间,Namespace 多用于实现多租户的资源隔离。Namespace 通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组。
K8S 集群在启动后,会创建一个名为 default 的 Namespace,如果不特别指明 Namespace,创建的 Pod、RC、Service 都将被创建到 default 下。
当我们给每个租户创建一个 Namespace 来实现多租户的资源隔离时,还可以结合 K8S 的资源配额管理,限定不同租户能占用的资源,例如 CPU 使用量、内存使用量等。
四、集群搭建 —— 平台规划
1、生产环境 K8S 平台规划
K8S 环境有两种架构方式,单 Master 集群和多 Master 集群,将先搭建起单 Master 集群,再扩展为多 Master 集群。开发、测试环境可以部署单 Master 集群,生产环境为了保证高可用需部署多 Master 集群。
① 单 Master 集群架构
单 Master 集群架构相比于多 Master 集群架构无法保证集群的高可用,因为 master 节点一旦宕机就无法进行集群的管理工作了。单 master 集群主要包含一台 Master 节点,及多个 Node 工作节点、多个 Etcd 数据库节点。
Etcd 是 K8S 集群的数据库,可以安装在任何地方,也可以与 Master 节点在同一台机器上,只要 K8S 能连通 Etcd。
② 多 Master 集群架构
多 Master 集群能保证集群的高可用,相比单 Master 架构,需要一个额外的负载均衡器来负载多个 Master 节点,Node 节点从连接 Master 改成连接 LB 负载均衡器
③ 集群规划
角色 | IP | 主机 | 组件 |
---|---|---|---|
k8s-master-1 | 192.168.137.14 | k8s-master-1 | kube-apiserver,kube-controller-manager,kube-scheduler,etcd |
k8s-master-1 | 192.168.137.15 | k8s-master-2 | kube-apiserver,kube-controller-manager,kube-scheduler |
k8s-node-1 | 192.168.137.16 | k8s-node-1 | kubelet,kube-proxy,docker,etcd |
k8s-node-1 | 192.168.137.17 | k8s-node-2 | kubelet,kube-proxy,docker,etcd |
Load Balancer(master) | 192.168.137.18 | k8s-lb-master | Nginx L4 |
Load Balancer(backup) | 192.168.137.19 | k8s-lb-backup | Nginx L4 |
④ 服务器硬件配置推荐
测试环境与生产环境服务器配置推荐如下,本地虚拟机的配置将按照本地测试环境的配置来创建虚拟机。
2、操作系统初始化
接下来将基于二进制包的方式,手动部署每个组件,来组成 K8S 高可用集群。通过手动部署每个组件,一步步熟悉每个组件的配置、组件之间的通信等,深层次的理解和掌握 K8S。
首先做的是每台服务器的配置初始化,依次按如下步骤初始化。
① 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
② 关闭 selinux
临时生效 setenforce 0
,永久生效 sed -i 's/enforcing/disabled/' /etc/selinux/config
③ 关闭 swap
临时关闭 swapoff -a
永久生效
vim /etc/fstab
#将 [/dev/mapper/centos-swap swap swap defaults 0 0] 这一行注释掉
④ 添加 hosts
vim /etc/hosts
192.168.137.14 k8s-master-1
192.168.137.15 k8s-master-2
192.168.137.16 k8s-node-1
192.168.137.17 k8s-node-2
192.168.137.18 k8s-lb-master
192.168.137.19 k8s-lb-backup
⑤ 同步系统时间
各个节点之间需保持时间一致,因为自签证书是根据时间校验证书有效性,如果时间不一致,将校验不通过。
# #联网情况可使用如下命令
# ntpdate time.windows.com
# #如果不能联外网可使用 date 命令设置时间
五、集群搭建 —— 部署Etcd集群
1、自签证书
K8S 集群安装配置过程中,会使用各种证书,目的是为了加强集群安全性。K8S 提供了基于 CA 签名的双向数字证书认证方式和简单的基于 http base 或 token 的认证方式,其中 CA 证书方式的安全性最高。每个K8S集群都有一个集群根证书颁发机构(CA),集群中的组件通常使用CA来验证API server的证书,由API服务器验证kubelet客户端证书等。
证书生成操作可以在master节点上执行,证书只需要创建一次,以后在向集群中添加新节点时只要将证书拷贝到新节点上,并做一定的配置即可。下面就在 k8s-master-1 节点上来创建证书,详细的介绍也可以参考官方文档:分发自签名-CA-证书
① K8S 证书
如下是 K8S 各个组件需要使用的证书
② 准备 cfssl 工具
我是使用 cfssl 工具来生成证书,首先下载 cfssl 工具。依次执行如下命令:
curl -L https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -o /usr/local/bin/cfssl
curl -L https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -o /usr/local/bin/cfssljson
curl -L https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -o /usr/local/bin/cfssl-certinfo
chmod +x /usr/local/bin/cfssl*
2、自签 Etcd SSL 证书
我们首先为 etcd 签发一套SSL证书,通过如下命令创建几个目录,/k8s/etcd/ssl 用于存放 etcd 自签证书,/k8s/etcd/cfg 用于存放 etcd 配置文件,/k8s/etcd/bin 用于存放 etcd 执行程序。
mkdir -p /k8s/etcd/{ssl,cfg,bin}
进入 etcd 目录:
cd /k8s/etcd/ssl
① 创建 CA 配置文件:ca-config.json
执行如下命令创建 ca-config.json
cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"etcd": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}
EOF
说明:
- signing:表示该证书可用于签名其它证书;生成的 ca.pem 证书中 CA=TRUE;
- profiles:可以定义多个 profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile;
- expiry:证书过期时间
- server auth:表示client可以用该 CA 对server提供的证书进行验证;
- client auth:表示server可以用该CA对client提供的证书进行验证;
② 创建 CA 证书签名请求文件:ca-csr.json
执行如下命令创建 ca-csr.json:
cat > ca-csr.json <<EOF
{
"CN": "etcd",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Shanghai",
"L": "Shanghai",
"O": "etcd",
"OU": "System"
}
],
"ca": {
"expiry": "87600h"
}
}
EOF
说明:
- CN:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name);浏览器使用该字段验证网站是否合法;
- key:加密算法
- C:国家
- ST:地区
- L:城市
- O:组织,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);
- OU:组织单位
③ 生成 CA 证书和私钥
[root@localhost ssl]# ll
总用量 8
-rw-r--r--. 1 root root 286 9月 17 21:34 ca-config.json
-rw-r--r--. 1 root root 250 9月 17 21:36 ca-csr.json
[root@localhost ssl]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2023/09/17 21:40:55 [INFO] generating a new CA key and certificate from CSR
2023/09/17 21:40:55 [INFO] generate received request
2023/09/17 21:40:55 [INFO] received CSR
2023/09/17 21:40:55 [INFO] generating key: rsa-2048
2023/09/17 21:40:55 [INFO] encoded CSR
2023/09/17 21:40:55 [INFO] signed certificate with serial number 701481595761330401456406283469574221010089175792
[root@localhost ssl]# ls
ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem
说明:
- ca-key.pem:CA 私钥
- ca.pem:CA 数字证书
④ 创建证书签名请求文件:etcd-csr.json
执行如下命令创建 etcd-csr.json:
cat > etcd-csr.json <<EOF
{
"CN": "etcd",
"hosts": [
"192.168.137.14",
"192.168.137.16",
"192.168.137.17"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "etcd",
"OU": "System"
}
]
}
EOF
说明:
- hosts:需要指定授权使用该证书的 IP 或域名列表,这里配置所有 etcd 的IP地址。
- key:加密算法及长度
⑤ 为 etcd 生成证书和私钥
[root@localhost ssl]# ls
ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem etcd-csr.json
[root@localhost ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=etcd etcd-csr.json | cfssljson -bare etcd
2023/09/17 21:46:49 [INFO] generate received request
2023/09/17 21:46:49 [INFO] received CSR
2023/09/17 21:46:49 [INFO] generating key: rsa-2048
2023/09/17 21:46:49 [INFO] encoded CSR
2023/09/17 21:46:49 [INFO] signed certificate with serial number 350599829265555004142693609766831172405337534555
2023/09/17 21:46:49 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
[root@localhost ssl]# ls
ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem etcd.csr etcd-csr.json etcd-key.pem etcd.pem
参数说明:
- ca:指定 CA 数字证书
- ca-key:指定 CA 私钥
- config:CA 配置文件
- profile:指定环境
- bare:指定证书名前缀
文件说明:
- etcd-key.pem:etcd 私钥
- etcd.pem:etcd 数字证书
证书生成完成,后面部署 Etcd 时主要会用到如下几个证书:
[root@localhost ssl]# pwd
/k8s/etcd/ssl
[root@localhost ssl]# ls *.pem
ca-key.pem ca.pem etcd-key.pem etcd.pem
3、Etcd 数据库集群部署
etcd 集群采用主从架构模式(一主多从)部署,集群通过选举产生 leader,因此需要部署奇数个节点(3/5/7)才能正常工作。etcd使用raft一致性算法保证每个节点的一致性。
① 下载 etcd
从 github 上下载合适版本的 etcd
cd /k8s/etcd
wget https://github.com/coreos/etcd/releases/download/v3.5.0/etcd-v3.5.0-linux-amd64.tar.gz
解压:
tar zxf etcd-v3.5.0-linux-amd64.tar.gz
将 etcd 复制到 /usr/local/bin 下:
cp etcd-v3.5.0-linux-amd64/{etcd,etcdctl} /k8s/etcd/bin
rm -rf etcd-v3.5.0-linux-amd64*
② 创建 etcd 配置文件:etcd.conf
cat > /k8s/etcd/cfg/etcd.conf <<EOF
# [member]
ETCD_NAME=etcd-1
ETCD_DATA_DIR=/k8s/data/default.etcd
ETCD_LISTEN_PEER_URLS=https://192.168.137.14:2380
ETCD_LISTEN_CLIENT_URLS=https://192.168.137.14:2379
# [cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS=https://192.168.137.14:2380
ETCD_ADVERTISE_CLIENT_URLS=https://192.168.137.14:2379
ETCD_INITIAL_CLUSTER=etcd-1=https://192.168.137.14:2380,etcd-2=https://192.168.137.16:2380,etcd-3=https://192.168.137.17:2380
ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
ETCD_INITIAL_CLUSTER_STATE=new
# [security]
ETCD_CERT_FILE=/k8s/etcd/ssl/etcd.pem
ETCD_KEY_FILE=/k8s/etcd/ssl/etcd-key.pem
ETCD_TRUSTED_CA_FILE=/k8s/etcd/ssl/ca.pem
ETCD_PEER_CERT_FILE=/k8s/etcd/ssl/etcd.pem
ETCD_PEER_KEY_FILE=/k8s/etcd/ssl/etcd-key.pem
ETCD_PEER_TRUSTED_CA_FILE=/k8s/etcd/ssl/ca.pem
EOF
说明:
- ETCD_NAME:etcd在集群中的唯一名称
- ETCD_DATA_DIR:etcd数据存放目录
- ETCD_LISTEN_PEER_URLS:etcd集群间通讯的地址,设置为本机IP
- ETCD_LISTEN_CLIENT_URLS:客户端访问的地址,设置为本机IP
- ETCD_INITIAL_ADVERTISE_PEER_URLS:初始集群通告地址,集群内部通讯地址,设置为本机IP
- ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址,设置为本机IP
- ETCD_INITIAL_CLUSTER:集群节点地址,以 key=value 的形式添加各个 etcd 的地址
- ETCD_INITIAL_CLUSTER_TOKEN:集群令牌,用于集群间做简单的认证
- ETCD_INITIAL_CLUSTER_STATE:集群状态
- ETCD_CERT_FILE:客户端 etcd 数字证书路径
- ETCD_KEY_FILE:客户端 etcd 私钥路径
- ETCD_TRUSTED_CA_FILE:客户端 CA 证书路径
- ETCD_PEER_CERT_FILE:集群间通讯etcd数字证书路径
- ETCD_PEER_KEY_FILE:集群间通讯etcd私钥路径
- ETCD_PEER_TRUSTED_CA_FILE:集群间通讯CA证书路径
③ 创建 etcd 服务:etcd.service
通过EnvironmentFile指定 etcd.conf 作为环境配置文件
cat > /k8s/etcd/etcd.service <<'EOF'
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
EnvironmentFile=/k8s/etcd/cfg/etcd.conf
WorkingDirectory=${ETCD_DATA_DIR}
ExecStart=/k8s/etcd/bin/etcd \
--name=${ETCD_NAME} \
--data-dir=${ETCD_DATA_DIR} \
--listen-peer-urls=${ETCD_LISTEN_PEER_URLS} \
--listen-client-urls=${ETCD_LISTEN_CLIENT_URLS},http://127.0.0.1:2379 \
--initial-advertise-peer-urls=${ETCD_INITIAL_ADVERTISE_PEER_URLS} \
--advertise-client-urls=${ETCD_ADVERTISE_CLIENT_URLS} \
--initial-cluster=${ETCD_INITIAL_CLUSTER} \
--initial-cluster-token=${ETCD_INITIAL_CLUSTER_TOKEN} \
--initial-cluster-state=${ETCD_INITIAL_CLUSTER_STATE} \
--cert-file=${ETCD_CERT_FILE} \
--key-file=${ETCD_KEY_FILE} \
--trusted-ca-file=${ETCD_TRUSTED_CA_FILE} \
--peer-cert-file=${ETCD_PEER_CERT_FILE} \
--peer-key-file=${ETCD_PEER_KEY_FILE} \
--peer-trusted-ca-file=${ETCD_PEER_TRUSTED_CA_FILE}
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
etcd.service 更多的配置以及说明可以通过如下命令查看:/k8s/etcd/bin/etcd --help
ETCD3.4版本会自动读取环境变量的参数,所以EnvironmentFile文件中有的参数,不需要再次在ExecStart启动参数中添加,二选一,如同时配置,会触发以下类似报错
etcd: conflicting environment variable "ETCD_NAME" is shadowed by corresponding command-line flag (either unset environment variable or disable flag)
④ 将 etcd 目录拷贝到另外两个节点
[root@localhost etcd]# scp -r /k8s/ root@k8s-node-1:/k8s
The authenticity of host 'k8s-node-1 (192.168.137.16)' can't be established.
ECDSA key fingerprint is SHA256:8NHw5kqsFAXGzdOH4LTN6v9ps+Bl3C9k9sEUf6ZEfVw.
ECDSA key fingerprint is MD5:57:b3:24:da:12:ad:bb:37:23:12:57:6f:d0:87:0f:58.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'k8s-node-1,192.168.137.16' (ECDSA) to the list of known hosts.
root@k8s-node-1's password:
ca-config.json 100% 286 680.0KB/s 00:00
ca-csr.json 100% 250 504.9KB/s 00:00
ca.pem 100% 1350 3.0MB/s 00:00
ca-key.pem 100% 1679 4.0MB/s 00:00
ca.csr 100% 997 2.6MB/s 00:00
etcd-csr.json 100% 352 930.6KB/s 00:00
etcd.pem 100% 1424 3.0MB/s 00:00
etcd-key.pem 100% 1675 4.1MB/s 00:00
etcd.csr 100% 1058 2.7MB/s 00:00
etcd.conf 100% 764 2.0MB/s 00:00
etcd 100% 22MB 256.0MB/s 00:00
etcdctl 100% 17MB 262.4MB/s 00:00
etcd.service 100% 1033 3.0MB/s 00:00
[root@localhost etcd]# scp -r /k8s/ root@k8s-node-2:/k8s
The authenticity of host 'k8s-node-2 (192.168.137.17)' can't be established.
ECDSA key fingerprint is SHA256:zdJng7VI65mj43rSyJ+/FhSxmlI07PTpWTG8H5JDsAI.
ECDSA key fingerprint is MD5:f4:87:57:e4:91:de:8a:9d:16:83:50:7e:03:ca:2d:4e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'k8s-node-2,192.168.137.17' (ECDSA) to the list of known hosts.
root@k8s-node-2's password:
ca-config.json 100% 286 480.1KB/s 00:00
ca-csr.json 100% 250 578.8KB/s 00:00
ca.pem 100% 1350 3.3MB/s 00:00
ca-key.pem 100% 1679 4.0MB/s 00:00
ca.csr 100% 997 2.1MB/s 00:00
etcd-csr.json 100% 352 1.0MB/s 00:00
etcd.pem 100% 1424 3.2MB/s 00:00
etcd-key.pem 100% 1675 4.8MB/s 00:00
etcd.csr 100% 1058 2.7MB/s 00:00
etcd.conf 100% 764 2.0MB/s 00:00
etcd 100% 22MB 242.6MB/s 00:00
etcdctl 100% 17MB 262.6MB/s 00:00
etcd.service
⑤ 修改两个节点配置文件
修改 k8s-node-1 节点的 /k8s/etcd/cfg/etcd.conf:
# [member]
ETCD_NAME=etcd-2
ETCD_LISTEN_PEER_URLS=https://192.168.31.35:2380
ETCD_LISTEN_CLIENT_URLS=https://192.168.31.35:2379
# [cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS=https://192.168.31.35:2380
ETCD_ADVERTISE_CLIENT_URLS=https://192.168.31.35:2379
修改 k8s-node-2 节点的 /k8s/etcd/cfg/etcd.conf:
# [member]
ETCD_NAME=etcd-3
ETCD_LISTEN_PEER_URLS=https://192.168.31.71:2380
ETCD_LISTEN_CLIENT_URLS=https://192.168.31.71:2379
# [cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS=https://192.168.31.71:2380
ETCD_ADVERTISE_CLIENT_URLS=https://192.168.31.71:2379
⑥ 启动 etcd 服务
首先在三个节点将 etcd.service 拷贝到 /usr/lib/systemd/system/ 下
cp /k8s/etcd/etcd.service /usr/lib/systemd/system/
systemctl daemon-reload
在三个节点启动 etcd 服务 systemctl start etcd
设置开机启动 systemctl enable etcd
查看 etcd 的日志 tail -f /var/log/messages
注意:如果日志中出现连接异常信息,请确认所有节点防火墙是否开放2379,2380端口,或者直接关闭防火墙。
查看 etcd 集群状态
/k8s/etcd/bin/etcdctl \
--cacert=/k8s/etcd/ssl/ca.pem \
--key=/k8s/etcd/ssl/etcd-key.pem \
--cert=/k8s/etcd/ssl/etcd.pem \
--endpoints=https://192.168.137.14:2379,https://192.168.137.16:2379,https://192.168.137.17:2379 \
--write-out=table \
endpoint health
执行结果
[root@localhost etcd]# /k8s/etcd/bin/etcdctl \
> --cacert=/k8s/etcd/ssl/ca.pem \
> --key=/k8s/etcd/ssl/etcd-key.pem \
> --cert=/k8s/etcd/ssl/etcd.pem \
> --endpoints=https://192.168.137.14:2379,https://192.168.137.16:2379,https://192.168.137.17:2379 \
> --write-out=table \
> endpoint health
+-----------------------------+--------+-------------+-------+
| ENDPOINT | HEALTH | TOOK | ERROR |
+-----------------------------+--------+-------------+-------+
| https://192.168.137.14:2379 | true | 6.79061ms | |
| https://192.168.137.17:2379 | true | 11.304482ms | |
| https://192.168.137.16:2379 | true | 11.374983ms | |
+-----------------------------+--------+-------------+-------+
六、集群搭建 —— 部署Master组件
1、自签 ApiServer SSL 证书
K8S 集群中所有资源的访问和变更都是通过 kube-apiserver 的 REST API 来实现的,首先在 master 节点上部署 kube-apiserver 组件。
我们首先为 apiserver 签发一套SSL证书,过程与 etcd 自签SSL证书类似。通过如下命令创建几个目录,ssl 用于存放自签证书,cfg 用于存放配置文件,bin 用于存放执行程序,logs 存放日志文件。
mkdir -p /k8s/kubernetes/{ssl,cfg,bin,logs}
进入 kubernetes 目录:
cd /k8s/kubernetes/ssl
① 创建 CA 配置文件:ca-config.json
执行如下命令创建 ca-config.json
cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}
EOF
② 创建 CA 证书签名请求文件:ca-csr.json
执行如下命令创建 ca-csr.json:
cat > ca-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Shanghai",
"L": "Shanghai",
"O": "kubernetes",
"OU": "System"
}
],
"ca": {
"expiry": "87600h"
}
}
EOF
③ 生成 CA 证书和私钥
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
④ 创建证书签名请求文件:kubernetes-csr.json
执行如下命令创建 kubernetes-csr.json:
cat > kubernetes-csr.json <<EOF
{
"CN": "kubernetes",
"hosts": [
"127.0.0.1",
"10.0.0.1",
"192.168.137.14",
"192.168.137.15",
"192.168.137.16",
"192.168.137.17",
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "kubernetes",
"OU": "System"
}
]
}
EOF
说明:
- hosts:指定会直接访问 apiserver 的IP列表,一般需指定 etcd 集群、kubernetes master 集群的主机 IP 和 kubernetes 服务的服务 IP,Node 的IP一般不需要加入。
⑤ 为 kubernetes 生成证书和私钥
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes
2、部署 kube-apiserver 组件
① 下载二进制包
通过 kubernetes Github 下载安装用的二进制包,server 二进制包已经包含了 master、node 上的各个组件,下载 server 二进制包即可。
cd /data/soft
wget https://dl.k8s.io/v1.16.2/kubernetes-server-linux-amd64.tar.gz --no-check-certificate
解压:
tar -zxf kubernetes-server-linux-amd64.tar.gz
先将 master 节点上部署的组件拷贝到 /k8s/kubernetes/bin 目录下:
cp -p /data/soft/kubernetes/server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler} /k8s/kubernetes/bin/
cp -p /data/soft/kubernetes/server/bin/kubectl /usr/local/bin/
② 创建 Node 令牌文件:token.csv
Master apiserver 启用 TLS 认证后,Node节点 kubelet 组件想要加入集群,必须使用CA签发的有效证书才能与apiserver通信,当Node节点很多时,签署证书是一件很繁琐的事情,因此有了 TLS Bootstrap 机制,kubelet 会以一个低权限用户自动向 apiserver 申请证书,kubelet 的证书由 apiserver 动态签署。因此先为 apiserver 生成一个令牌文件,令牌之后会在 Node 中用到。
生成 token,一个随机字符串,可使用如下命令生成 token:apiserver 配置的 token 必须与 Node 节点 bootstrap.kubeconfig 配置保持一致。
[root@k8s-master-1 kubernetes]# head -c 16 /dev/urandom | od -An -t x | tr -d ' '
b9187da74c444ee060a1c6053eab97c7
创建 token.csv,格式:token,用户,UID,用户组
cat > /k8s/kubernetes/cfg/token.csv <<'EOF'
b9187da74c444ee060a1c6053eab97c7,kubelet-bootstrap,10001,"system:node-bootstrapper"
EOF
③ 创建 kube-apiserver 配置文件:kube-apiserver.conf
kube-apiserver 有很多配置项,可以参考官方文档查看每个配置项的用途:kube-apiserver
注意:踩的一个坑,“\” 后面不要有空格,不要有多余的换行,否则启动失败。
cat > /k8s/kubernetes/cfg/kube-apiserver.conf <<'EOF'
KUBE_APISERVER_OPTS="--etcd-servers=https://192.168.137.14:2379,https://192.168.137.16:2379,https://192.168.137.17:2379 \
--bind-address=192.168.137.14 \
--secure-port=6443 \
--advertise-address=192.168.137.14 \
--allow-privileged=true \
--service-cluster-ip-range=10.0.0.0/24 \
--service-node-port-range=30000-32767 \
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \
--authorization-mode=RBAC,Node \
--enable-bootstrap-token-auth=true \
--token-auth-file=/k8s/kubernetes/cfg/token.csv \
--kubelet-client-certificate=/k8s/kubernetes/ssl/kubernetes.pem \
--kubelet-client-key=/k8s/kubernetes/ssl/kubernetes-key.pem \
--tls-cert-file=/k8s/kubernetes/ssl/kubernetes.pem \
--tls-private-key-file=/k8s/kubernetes/ssl/kubernetes-key.pem \
--client-ca-file=/k8s/kubernetes/ssl/ca.pem \
--service-account-key-file=/k8s/kubernetes/ssl/ca-key.pem \
--service-account-signing-key-file=/k8s/kubernetes/ssl/kubernetes-key.pem \
--service-account-issuer=https://kubernetes.service.account.issuer \
--etcd-cafile=/k8s/etcd/ssl/ca.pem \
--etcd-certfile=/k8s/etcd/ssl/etcd.pem \
--etcd-keyfile=/k8s/etcd/ssl/etcd-key.pem \
--v=2 \
--logtostderr=false \
--log-dir=/k8s/kubernetes/logs \
--audit-log-maxage=30 \
--audit-log-maxbackup=3 \
--audit-log-maxsize=100 \
--audit-log-path=/k8s/kubernetes/logs/k8s-audit.log"
EOF
重点配置说明:
- --etcd-servers:etcd 集群地址
- --bind-address:apiserver 监听的地址,一般配主机IP
- --secure-port:监听的端口
- --advertise-address:集群通告地址,其它Node节点通过这个地址连接 apiserver,不配置则使用 --bind-address
- --service-cluster-ip-range:Service 的 虚拟IP范围,以CIDR格式标识,该IP范围不能与物理机的真实IP段有重合。
- --service-node-port-range:Service 可映射的物理机端口范围,默认30000-32767
- --admission-control:集群的准入控制设置,各控制模块以插件的形式依次生效,启用RBAC授权和节点自管理
- --authorization-mode:授权模式,包括:AlwaysAllow,AlwaysDeny,ABAC(基于属性的访问控制),Webhook,RBAC(基于角色的访问控制),Node(专门授权由 kubelet 发出的API请求)。(默认值"AlwaysAllow")。
- --enable-bootstrap-token-auth:启用TLS bootstrap功能
- --token-auth-file:这个文件将被用于通过令牌认证来保护API服务的安全端口。
- --v:指定日志级别,0~8,越大日志越详细
④ 创建 apiserver 服务:kube-apiserver.service
cat > /usr/lib/systemd/system/kube-apiserver.service <<'EOF'
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
[Service]
EnvironmentFile=/k8s/kubernetes/cfg/kube-apiserver.conf
ExecStart=/k8s/kubernetes/bin/kube-apiserver $KUBE_APISERVER_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
⑤ 启动 kube-apiserver 组件
启动组件
systemctl daemon-reload
systemctl start kube-apiserver
systemctl enable kube-apiserver
检查启动状态
systemctl status kube-apiserver.service
查看启动日志
less /k8s/kubernetes/logs/kube-apiserver.INFO
⑥ 将 kubelet-bootstrap 用户绑定到系统集群角色,之后便于 Node 使用token请求证书
kubectl create clusterrolebinding kubelet-bootstrap \
--clusterrole=system:node-bootstrapper \
--user=kubelet-bootstrap
3、部署 kube-controller-manager 组件
① 创建 kube-controller-manager 配置文件:kube-controller-manager.conf
详细的配置可参考官方文档:kube-controller-manager
cat > /k8s/kubernetes/cfg/kube-controller-manager.conf <<'EOF'
KUBE_CONTROLLER_MANAGER_OPTS="--leader-elect=true \
--master=127.0.0.1:8080 \
--address=127.0.0.1 \
--allocate-node-cidrs=true \
--cluster-cidr=10.244.0.0/16 \
--service-cluster-ip-range=10.0.0.0/24 \
--cluster-signing-cert-file=/k8s/kubernetes/ssl/ca.pem \
--cluster-signing-key-file=/k8s/kubernetes/ssl/ca-key.pem \
--root-ca-file=/k8s/kubernetes/ssl/ca.pem \
--service-account-private-key-file=/k8s/kubernetes/ssl/ca-key.pem \
--experimental-cluster-signing-duration=87600h0m0s \
--v=2 \
--logtostderr=false \
--log-dir=/k8s/kubernetes/logs"
EOF
重点配置说明:
- --leader-elect:当该组件启动多个时,自动选举,默认true
- --master:连接本地apiserver,apiserver 默认会监听本地8080端口
- --allocate-node-cidrs:是否分配和设置Pod的CDIR
- --service-cluster-ip-range:Service 集群IP段
② 创建 kube-controller-manager 服务:kube-controller-manager.service
cat > /usr/lib/systemd/system/kube-controller-manager.service <<'EOF'
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
[Service]
EnvironmentFile=/k8s/kubernetes/cfg/kube-controller-manager.conf
ExecStart=/k8s/kubernetes/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
③ 启动 kube-controller-manager 组件
启动组件
systemctl daemon-reload
systemctl start kube-controller-manager
systemctl enable kube-controller-manager
4、部署 kube-scheduler 组件
① 创建 kube-scheduler 配置文件:kube-scheduler.conf
cat > /k8s/kubernetes/cfg/kube-scheduler.conf <<'EOF'
KUBE_SCHEDULER_OPTS="--leader-elect=true \
--master=127.0.0.1:8080 \
--address=127.0.0.1 \
--v=2 \
--logtostderr=false \
--log-dir=/k8s/kubernetes/logs"
EOF
② 创建 kube-scheduler 服务:kube-scheduler.service
cat > /usr/lib/systemd/system/kube-scheduler.service <<'EOF'
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
[Service]
EnvironmentFile=/k8s/kubernetes/cfg/kube-scheduler.conf
ExecStart=/k8s/kubernetes/bin/kube-scheduler $KUBE_SCHEDULER_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
③ 启动 kube-scheduler 组件
启动组件
systemctl daemon-reload
systemctl start kube-scheduler
systemctl enable kube-scheduler
查看启动状态
systemctl status kube-scheduler.service
查看启动日志
less /k8s/kubernetes/logs/kube-scheduler.INFO
5、查看集群状态
① 查看组件状态
kubectl get cs
七、集群搭建 —— 部署Node组件
1、安装 Docker
CentOS 安装参考官方文档:https://docs.docker.com/install/linux/docker-ce/centos/
k8s-node-1,k8s-node-2 安装docker
① 卸载旧版本
yum remove docker docker-common docker-selinux
② 安装依赖包
yum install -y yum-utils device-mapper-persistent-data lvm2
③ 安装 Docker 软件包源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
④ 安装 Docker CE
yum install docker-ce
⑤ 启动 Docker 服务
systemctl start docker
⑥ 设置开机启动
systemctl enable docker
⑦ 验证安装是否成功
docker -v
docker info
2、Node 节点证书
① 创建 Node 节点的证书签名请求文件:kube-proxy-csr.json
首先在 k8s-master-1 节点上,通过颁发的 CA 证书先创建好 Node 节点要使用的证书,先创建证书签名请求文件:kube-proxy-csr.json:
cat > /k8s/kubernetes/ssl/kube-proxy-csr.json <<EOF
{
"CN": "system:kube-proxy",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "kubernetes",
"OU": "System"
}
]
}
EOF
② 为 kube-proxy 生成证书和私钥
cd /k8s/kubernetes/ssl
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
③ node 节点创建工作目录
在 k8s-node-1 节点上创建 k8s 目录
mkdir -p /k8s/kubernetes/{bin,cfg,logs,ssl}
④ 将 k8s-master-1 节点的文件拷贝到 node 节点
将 kubelet、kube-proxy 拷贝到 node 节点上:
scp -r /data/soft/kubernetes/server/bin/{kubelet,kube-proxy} root@k8s-node-1:/k8s/kubernetes/bin/
将证书拷贝到 k8s-node-1 节点上:
scp -r /k8s/kubernetes/ssl/{ca.pem,kube-proxy.pem,kube-proxy-key.pem} root@k8s-node-1:/k8s/kubernetes/ssl/
3、安装 kubelet
① 创建请求证书的配置文件:bootstrap.kubeconfig
bootstrap.kubeconfig 将用于向 apiserver 请求证书,apiserver 会验证 token、证书 是否有效,验证通过则自动颁发证书。
cat > /k8s/kubernetes/cfg/bootstrap.kubeconfig <<'EOF'
apiVersion: v1
clusters:
- cluster:
certificate-authority: /k8s/kubernetes/ssl/ca.pem
server: https://192.168.137.14:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubelet-bootstrap
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: kubelet-bootstrap
user:
token: b9187da74c444ee060a1c6053eab97c7
EOF
说明:
- certificate-authority:CA 证书
- server:master 地址
- token:master 上 token.csv 中配置的 token
② 创建 kubelet 配置文件:kubelet-config.yml
为了安全性,kubelet 禁止匿名访问,必须授权才可以,通过 kubelet-config.yml 授权 apiserver 访问 kubelet。
cat > /k8s/kubernetes/cfg/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: /k8s/kubernetes/ssl/ca.pem
authorization:
mode: Webhook
webhook:
cacheAuthroizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
evictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
maxOpenFiles: 100000
maxPods: 110
EOF
说明:
- address:kubelet 监听地址
- port:kubelet 的端口
- cgroupDriver:cgroup 驱动,与 docker 的 cgroup 驱动一致
- authentication:访问 kubelet 的授权信息
- authorization:认证相关信息
- evictionHard:垃圾回收策略
- maxPods:最大pod数
③ 创建 kubelet 服务配置文件:kubelet.conf
cat > /k8s/kubernetes/cfg/kubelet.conf <<'EOF'
KUBELET_OPTS="--hostname-override=k8s-node-1 \
--network-plugin=cni \
--cni-bin-dir=/opt/cni/bin \
--cni-conf-dir=/etc/cni/net.d \
--cgroups-per-qos=false \
--enforce-node-allocatable="" \
--kubeconfig=/k8s/kubernetes/cfg/kubelet.kubeconfig \
--bootstrap-kubeconfig=/k8s/kubernetes/cfg/bootstrap.kubeconfig \
--config=/k8s/kubernetes/cfg/kubelet-config.yml \
--cert-dir=/k8s/kubernetes/ssl \
--pod-infra-container-image=kubernetes/pause:latest \
--v=2 \
--logtostderr=false \
--log-dir=/k8s/kubernetes/logs"
EOF
说明:
- --hostname-override:当前节点注册到K8S中显示的名称,默认为主机 hostname
- --network-plugin:启用 CNI 网络插件
- --cni-bin-dir:CNI 插件可执行文件位置,默认在 /opt/cni/bin 下
- --cni-conf-dir:CNI 插件配置文件位置,默认在 /etc/cni/net.d 下
- --cgroups-per-qos:必须加上这个参数和--enforce-node-allocatable,否则报错 [Failed to start ContainerManager failed to initialize top level QOS containers.......]
- --kubeconfig:会自动生成 kubelet.kubeconfig,用于连接 apiserver
- --bootstrap-kubeconfig:指定 bootstrap.kubeconfig 文件
- --config:kubelet 配置文件
- --cert-dir:证书目录
- --pod-infra-container-image:管理Pod网络的镜像,基础的 Pause 容器,默认是 k8s.gcr.io/pause:3.1
④ 创建 kubelet 服务:kubelet.service
cat > /usr/lib/systemd/system/kubelet.service <<'EOF'
[Unit]
Description=Kubernetes Kubelet
After=docker.service
Before=docker.service
[Service]
EnvironmentFile=/k8s/kubernetes/cfg/kubelet.conf
ExecStart=/k8s/kubernetes/bin/kubelet $KUBELET_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
⑤ 启动 kubelet
systemctl daemon-reload
systemctl start kubelet
开机启动:
systemctl enable kubelet
查看启动日志:
tail -f /k8s/kubernetes/logs/kubelet.INFO
⑥ master 给 node 授权
kubelet 启动后,还没加入到集群中,会向 apiserver 请求证书,需手动在 k8s-master-1 上对 node 授权。
通过如下命令查看是否有新的客户端请求颁发证书:
[root@k8s-master-1 ssl]# kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr-6jk-QVHhy1BdMfqAZ6znhkgmFocCk69nSozPR59QBMI 60s kubelet-bootstrap Pending
给客户端颁发证书,允许客户端加入集群:
[root@k8s-master-1 ssl]# kubectl certificate approve node-csr-6jk-QVHhy1BdMfqAZ6znhkgmFocCk69nSozPR59QBMI
certificatesigningrequest.certificates.k8s.io/node-csr-6jk-QVHhy1BdMfqAZ6znhkgmFocCk69nSozPR59QBMI approved
⑦ 授权成功
查看 node 是否加入集群(此时的 node 还处于未就绪的状态,因为还没有安装 CNI 组件):
[root@k8s-master-1 ssl]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-node-1 NotReady <none> 2s v1.16.2
颁发证书后,可以在 /k8s/kubenetes/ssl 下看到 master 为 kubelet 颁发的证书:
[root@k8s-node-1 ~]# ls /k8s/kubernetes/ssl/
ca.pem kubelet-client-2023-09-18-17-15-25.pem kubelet-client-current.pem kubelet.crt kubelet.key kube-proxy-key.pem kube-proxy.pem
在 /k8s/kubenetes/cfg 下可以看到自动生成的 kubelet.kubeconfig 配置文件:
[root@k8s-node-1 ~]# ls /k8s/kubernetes/cfg
bootstrap.kubeconfig kubelet.conf kubelet-config.yml kubelet.kubeconfig
4、安装 kube-proxy
① 创建 kube-proxy 连接 apiserver 的配置文件:kube-proxy.kubeconfig
cat > /k8s/kubernetes/cfg/kube-proxy.kubeconfig <<'EOF'
apiVersion: v1
clusters:
- cluster:
certificate-authority: /k8s/kubernetes/ssl/ca.pem
server: https://192.168.137.14:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kube-proxy
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: kube-proxy
user:
client-certificate: /k8s/kubernetes/ssl/kube-proxy.pem
client-key: /k8s/kubernetes/ssl/kube-proxy-key.pem
EOF
② 创建 kube-proxy 配置文件:kube-proxy-config.yml
cat > /k8s/kubernetes/cfg/kube-proxy-config.yml <<'EOF'
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
address: 0.0.0.0
metrisBindAddress: 0.0.0.0:10249
clientConnection:
kubeconfig: /k8s/kubernetes/cfg/kube-proxy.kubeconfig
hostnameOverride: k8s-node-1
clusterCIDR: 10.0.0.0/24
mode: ipvs
ipvs:
scheduler: "rr"
iptables:
masqueradeAll: true
EOF
说明:
- metrisBindAddress:采集指标暴露的地址端口,便于监控系统,采集数据
- clusterCIDR:集群 Service 网段
③ 创建 kube-proxy 配置文件:kube-proxy.conf
cat > /k8s/kubernetes/cfg/kube-proxy.conf <<'EOF'
KUBE_PROXY_OPTS="--config=/k8s/kubernetes/cfg/kube-proxy-config.yml \
--v=2 \
--logtostderr=false \
--log-dir=/k8s/kubernetes/logs"
EOF
④ 创建 kube-proxy 服务:kube-proxy.service
cat > /usr/lib/systemd/system/kube-proxy.service <<'EOF'
[Unit]
Description=Kubernetes Proxy
After=network.target
[Service]
EnvironmentFile=/k8s/kubernetes/cfg/kube-proxy.conf
ExecStart=/k8s/kubernetes/bin/kube-proxy $KUBE_PROXY_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
⑤ 启动 kube-proxy
启动服务:
systemctl daemon-reload
systemctl start kube-proxy
开机启动:
systemctl enable kube-proxy
查看启动日志:
tail -f /k8s/kubernetes/logs/kube-proxy.INFO
5、部署其它Node节点
部署其它 Node 节点基于与上述流程一致,只需将配置文件中 k8s-node-1 改为 k8s-node-2 即可。
[root@k8s-master-1 ssl]# kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-node-1 NotReady <none> 19m v1.16.2 192.168.137.16 <none> CentOS Linux 7 (Core) 3.10.0-1127.el7.x86_64 docker://24.0.6
k8s-node-2 NotReady <none> 2m57s v1.16.2 192.168.137.17 <none> CentOS Linux 7 (Core) 3.10.0-1127.el7.x86_64 docker://24.0.6
6、部署K8S容器集群网络(Flannel)
① K8S 集群网络
Kubernetes 项目并没有使用 Docker 的网络模型,kubernetes 是通过一个 CNI 接口维护一个单独的网桥来代替 docker0,这个网桥默认叫 cni0。
CNI(Container Network Interface)是CNCF旗下的一个项目,由一组用于配置 Linux 容器的网络接口的规范和库组成,同时还包含了一些插件。CNI仅关心容器创建时的网络分配,和当容器被删除时释放网络资源。
Flannel 是 CNI 的一个插件,可以看做是 CNI 接口的一种实现。Flannel 是针对 Kubernetes 设计的一个网络规划服务,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
Flannel 网络架构请参考:flannel 网络架构
② 创建 CNI 工作目录
通过给 kubelet 传递 --network-plugin=cni 命令行选项来启用 CNI 插件。 kubelet 从 --cni-conf-dir (默认是 /etc/cni/net.d)读取配置文件并使用该文件中的 CNI 配置来设置每个 pod 的网络。CNI 配置文件必须与 CNI 规约匹配,并且配置引用的任何所需的 CNI 插件都必须存在于 --cni-bin-dir(默认是 /opt/cni/bin)指定的目录。
由于前面部署 kubelet 服务时,指定了 --cni-conf-dir=/etc/cni/net.d,--cni-bin-dir=/opt/cni/bin,因此首先在node节点上创建这两个目录:
mkdir -p /opt/cni/bin /etc/cni/net.d
③ 装 CNI 插件
可以从 github 上下载 CNI 插件:下载 CNI 插件 。
mkdir -p /data/soft && cd /data/soft/
wget https://github.com/containernetworking/plugins/releases/download/v1.1.1/cni-plugins-linux-amd64-v1.1.1.tgz
tar zxf cni-plugins-linux-amd64-v1.1.1.tgz -C /opt/cni/bin/
cd /opt/cni/bin/
[root@k8s-node-1 bin]# ls
bandwidth bridge dhcp firewall host-device host-local ipvlan loopback macvlan portmap ptp sbr static tuning vlan vrf
④ 部署 Flannel
下载 flannel 配置文件:
cat >> /etc/hosts << EOF
185.199.110.133 raw.githubusercontent.com
EOF
cd /k8s/kubernetes/cfg
wget https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml
注意如下配置:Network 的地址需与** kube-controller-manager.conf** 中的 --cluster-cidr=10.244.0.0/16 保持一致。
在 k8s-master-1 节点上部署 Flannel:
[root@k8s-master-1 cfg]# kubectl apply -f kube-flannel.yml
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds-amd64 created
daemonset.apps/kube-flannel-ds-arm64 created
daemonset.apps/kube-flannel-ds-arm created
daemonset.apps/kube-flannel-ds-ppc64le created
daemonset.apps/kube-flannel-ds-s390x created
⑤ 检查部署状态
Flannel 会在 Node 上起一个 Flannel 的 Pod,可以查看 pod 的状态看 flannel 是否启动成功:
[root@k8s-master-1 cfg]# kubectl get pods -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-flannel-ds-amd64-k77nl 0/1 Init:0/1 0 3m41s 192.168.137.17 k8s-node-2 <none> <none>
kube-flannel-ds-amd64-s7b7b 0/1 Init:0/1 0 3m41s 192.168.137.16 k8s-node-1 <none> <none>
观察 k8s-node-1/2 上面的 kubelet 的输出,存在报错,kubernetes/pause:latest
镜像无法拉取
[root@k8s-node-1 ~]# journalctl -f -u kubelet
-- Logs begin at 一 2023-09-18 14:01:17 CST. --
9月 18 22:56:03 k8s-node-1 kubelet[29325]: E0918 22:56:03.561630 29325 kubelet.go:2187] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
9月 18 22:56:08 k8s-node-1 kubelet[29325]: E0918 22:56:08.570365 29325 kubelet.go:2187] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
9月 18 22:56:13 k8s-node-1 kubelet[29325]: E0918 22:56:13.583713 29325 kubelet.go:2187] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
9月 18 22:56:14 k8s-node-1 kubelet[29325]: E0918 22:56:14.011477 29325 aws_credentials.go:77] while getting AWS credentials NoCredentialProviders: no valid providers in chain. Deprecated.
9月 18 22:56:14 k8s-node-1 kubelet[29325]: For verbose messaging see aws.Config.CredentialsChainVerboseErrors
9月 18 22:56:18 k8s-node-1 kubelet[29325]: E0918 22:56:18.312985 29325 remote_runtime.go:105] RunPodSandbox from runtime service failed: rpc error: code = Unknown desc = failed pulling image "kubernetes/pause:latest": Error response from daemon: manifest for kubernetes/pause:latest not found: manifest unknown: manifest unknown
9月 18 22:56:18 k8s-node-1 kubelet[29325]: E0918 22:56:18.313023 29325 kuberuntime_sandbox.go:68] CreatePodSandbox for pod "kube-flannel-ds-amd64-s7b7b_kube-system(71fc2598-b9e6-401f-a4ba-795184198586)" failed: rpc error: code = Unknown desc = failed pulling image "kubernetes/pause:latest": Error response from daemon: manifest for kubernetes/pause:latest not found: manifest unknown: manifest unknown
9月 18 22:56:18 k8s-node-1 kubelet[29325]: E0918 22:56:18.313039 29325 kuberuntime_manager.go:710] createPodSandbox for pod "kube-flannel-ds-amd64-s7b7b_kube-system(71fc2598-b9e6-401f-a4ba-795184198586)" failed: rpc error: code = Unknown desc = failed pulling image "kubernetes/pause:latest": Error response from daemon: manifest for kubernetes/pause:latest not found: manifest unknown: manifest unknown
9月 18 22:56:18 k8s-node-1 kubelet[29325]: E0918 22:56:18.313084 29325 pod_workers.go:191] Error syncing pod 71fc2598-b9e6-401f-a4ba-795184198586 ("kube-flannel-ds-amd64-s7b7b_kube-system(71fc2598-b9e6-401f-a4ba-795184198586)"), skipping: failed to "CreatePodSandbox" for "kube-flannel-ds-amd64-s7b7b_kube-system(71fc2598-b9e6-401f-a4ba-795184198586)" with CreatePodSandboxError: "CreatePodSandbox for pod \"kube-flannel-ds-amd64-s7b7b_kube-system(71fc2598-b9e6-401f-a4ba-795184198586)\" failed: rpc error: code = Unknown desc = failed pulling image \"kubernetes/pause:latest\": Error response from daemon: manifest for kubernetes/pause:latest not found: manifest unknown: manifest unknown"
9月 18 22:56:18 k8s-node-1 kubelet[29325]: E0918 22:56:18.589370 29325 kubelet.go:2187] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
9月 18 22:56:23 k8s-node-1 kubelet[29325]: E0918 22:56:23.594946 29325 kubelet.go:2187] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
k8s-node-1/2 上不知道什么原因,无法拉取
[root@k8s-node-2 ~]# docker search kubernetes/pause
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
kubernetes/pause restore from backup of kubernetes/pause 13
[root@k8s-node-2 ~]# docker pull kubernetes/pause
Using default tag: latest
Error response from daemon: manifest for kubernetes/pause:latest not found: manifest unknown: manifest unknown
为了解决错误,手动拉取其他镜像,然后重新打tag来处理,这样就没问题了
[root@k8s-node-1 ~]# docker pull mirrorgooglecontainers/pause:3.1
3.1: Pulling from mirrorgooglecontainers/pause
67ddbfb20a22: Pull complete
Digest: sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610
Status: Downloaded newer image for mirrorgooglecontainers/pause:3.1
docker.io/mirrorgooglecontainers/pause:3.1
[root@k8s-node-1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mirrorgooglecontainers/pause 3.1 da86e6ba6ca1 5 years ago 742kB
[root@k8s-node-1 ~]# docker tag mirrorgooglecontainers/pause:3.1 kubernetes/pause:latest
[root@k8s-node-1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kubernetes/pause latest da86e6ba6ca1 5 years ago 742kB
mirrorgooglecontainers/pause 3.1 da86e6ba6ca1 5 years ago 742kB
同理,k8s-node-2 一样处理,查看 pod 的状态看 flannel 是否启动成功
[root@k8s-master-1 cfg]# kubectl get pods -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-flannel-ds-amd64-k77nl 1/1 Running 0 34m 192.168.137.17 k8s-node-2 <none> <none>
kube-flannel-ds-amd64-s7b7b 1/1 Running 0 34m 192.168.137.16 k8s-node-1 <none> <none>
Flannel 部署成功后,就可以看 Node 是否就绪:
[root@k8s-master-1 cfg]# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-node-1 Ready <none> 5h55m v1.16.2 192.168.137.16 <none> CentOS Linux 7 (Core) 3.10.0-1127.el7.x86_64 docker://24.0.6
k8s-node-2 Ready <none> 5h39m v1.16.2 192.168.137.17 <none> CentOS Linux 7 (Core) 3.10.0-1127.el7.x86_64 docker://24.0.6
在 Node 上查看网络配置,可以看到多了一个 flannel.1 的虚拟网卡,这块网卡用于接收 Pod 的流量并转发出去。
⑥ 测试创建 Pod
例如创建一个 Nginx 服务:
[root@k8s-master-1 cfg]# kubectl create deployment web --image=nginx
deployment.apps/web created
查看 Pod 状态,容器被部署到k8s-node-1:
[root@k8s-master-1 cfg]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-d86c95cc9-wcjp6 0/1 ContainerCreating 0 32s <none> k8s-node-1 <none> <none>
在对应的k8s-node-1节点上可以看到部署的容器:
[root@k8s-node-1 soft]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7068e2c4b1bd nginx "/docker-entrypoint.…" About a minute ago Up About a minute k8s_nginx_web-d86c95cc9-wcjp6_default_08f51dcd-0d1d-4a52-8cc1-9673a8ab2581_0
57c6c3eebb5f kubernetes/pause:latest "/pause" 2 minutes ago Up 2 minutes k8s_POD_web-d86c95cc9-wcjp6_default_08f51dcd-0d1d-4a52-8cc1-9673a8ab2581_0
f7b02f620190 ff281650a721 "/opt/bin/flanneld -…" 19 minutes ago Up 19 minutes k8s_kube-flannel_kube-flannel-ds-amd64-s7b7b_kube-system_71fc2598-b9e6-401f-a4ba-795184198586_0
62f71925e927 quay.io/coreos/flannel "cp -f /etc/kube-fla…" 19 minutes ago Exited (0) 19 minutes ago k8s_install-cni_kube-flannel-ds-amd64-s7b7b_kube-system_71fc2598-b9e6-401f-a4ba-795184198586_0
2bc5e3a5f596 kubernetes/pause:latest "/pause" 20 minutes ago Up 20 minutes k8s_POD_kube-flannel-ds-amd64-s7b7b_kube-system_71fc2598-b9e6-401f-a4ba-795184198586_0
容器创建成功后,再在 Node 上查看网络配置,又多了一块 cni0 的虚拟网卡,cni0 用于 pod 本地通信使用
暴露端口并访问 Nginx:
[root@k8s-master-1 cfg]# kubectl expose deployment web --port=80 --type=NodePort
service/web exposed
[root@k8s-master-1 cfg]# kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/web-d86c95cc9-wcjp6 1/1 Running 0 9m35s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 8h
service/web NodePort 10.0.0.141 <none> 80:31896/TCP 15s
[root@k8s-master-1 cfg]# curl k8s-node-1:31896
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
7、部署内部 DNS 服务
在Kubernetes集群推荐使用Service Name作为服务的访问地址,因此需要一个Kubernetes集群范围的DNS服务实现从Service Name到Cluster IP的解析,这就是Kubernetes基于DNS的服务发现功能。
① 部署 CoreDNS
[root@k8s-master-1 cfg]# cat coredns.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
kubernetes.io/bootstrapping: rbac-defaults
addonmanager.kubernetes.io/mode: Reconcile
name: system:coredns
rules:
- apiGroups:
- ""
resources:
- endpoints
- services
- pods
- namespaces
verbs:
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
labels:
kubernetes.io/bootstrapping: rbac-defaults
addonmanager.kubernetes.io/mode: EnsureExists
name: system:coredns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:coredns
subjects:
- kind: ServiceAccount
name: coredns
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: EnsureExists
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
proxy . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "CoreDNS"
spec:
# replicas: not specified here:
# 1. In order to make Addon Manager do not reconcile this replicas parameter.
# 2. Default is 1.
# 3. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
annotations:
seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
spec:
serviceAccountName: coredns
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
- key: "CriticalAddonsOnly"
operator: "Exists"
containers:
- name: coredns
image: coredns/coredns:1.2.6
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
args: [ "-conf", "/etc/coredns/Corefile" ]
volumeMounts:
- name: config-volume
mountPath: /etc/coredns
readOnly: true
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- all
readOnlyRootFilesystem: true
dnsPolicy: Default
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
---
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
annotations:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "CoreDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.0.0.2
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
注意如下 clusterIP 一定要与 kubelet-config.yaml 中的 clusterDNS 保持一致
部署 CoreDNS:
[root@k8s-master-1 cfg]# kubectl apply -f coredns.yaml
serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
configmap/coredns created
deployment.apps/coredns created
service/kube-dns created
② 验证 CoreDNS
创建 busybox 服务:
cat > busybox.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
dnsPolicy: ClusterFirst
containers:
- name: busybox
image: busybox:1.28.4
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
EOF
kubectl apply -f busybox.yaml
验证是否安装成功:
kubectl exec -ti busybox sh
提示错误:
[root@k8s-master-1 cfg]# kubectl exec -ti pod/busybox sh
error: unable to upgrade connection: Forbidden (user=kubernetes, verb=create, resource=nodes, subresource=proxy)
权限问题,参考文章 https://www.kancloud.cn/idzqj/customer/1873079
[root@k8s-master-1 cfg]# kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes
clusterrolebinding.rbac.authorization.k8s.io/kube-apiserver:kubelet-apis created
[root@k8s-master-1 cfg]# kubectl exec -ti pod/busybox sh
/ # nslookup web
Server: 10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local
Name: web
Address 1: 10.0.0.141 web.default.svc.cluster.local
/ # exit
八、集群搭建 —— 部署 Dashboard
K8S 提供了一个 Web 版 Dashboard,用户可以用 dashboard 部署容器化的应用、监控应用的状态,能够创建和修改各种 K8S 资源,比如 Deployment、Job、DaemonSet 等。用户可以 Scale Up/Down Deployment、执行 Rolling Update、重启某个 Pod 或者通过向导部署新的应用。Dashboard 能显示集群中各种资源的状态以及日志信息。Kubernetes Dashboard 提供了 kubectl 的绝大部分功能。
1、部署 K8S Dashboard
通过此地址下载 dashboard yaml文件:https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta5/aio/deploy/recommended.yaml
因为本次kubernetes
版本对应的是1.16.2,所以dashboard
需要是这个版本对应匹配的版本【v2.0.0-beta5】,一定要对的上,否则会有其他奇奇怪怪的错误
下载下来之后,需更新如下内容:通过 Node 暴露端口访问 dashboard
namespace: kubernetes-dashboard
全部替换为 namespace: kube-system
部署 dashboard:
[root@k8s-master-1 cfg]# kubectl apply -f kubernetes-dashboard.yaml
namespace/kubernetes-dashboard unchanged
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard unchanged
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard configured
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created
查看是否部署成功:
[root@k8s-master-1 cfg]# kubectl get pods,svc -n kube-system
NAME READY STATUS RESTARTS AGE
pod/coredns-84d8b55f97-cdjwv 1/1 Running 0 70m
pod/dashboard-metrics-scraper-76585494d8-lhlhl 1/1 Running 0 7m23s
pod/kube-flannel-ds-amd64-k77nl 1/1 Running 0 6d
pod/kube-flannel-ds-amd64-s7b7b 1/1 Running 0 6d
pod/kubernetes-dashboard-5c68c48b47-dfgth 1/1 Running 0 7m23s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/dashboard-metrics-scraper ClusterIP 10.0.0.3 <none> 8000/TCP 7m23s
service/kube-dns ClusterIP 10.0.0.2 <none> 53/UDP,53/TCP 70m
service/kubernetes-dashboard NodePort 10.0.0.134 <none> 443:30005/TCP 17m
通过 https 访问 dashboard(ip 地址为 node1 或者node2):
如果提示
解决办法:在chrome该页面上,在当前页面(也就是上图页面)注意是在当前页面打开的时候点网页任意地方,不是输入框,直接输入thisisunsafe回车,直接就打开页面了
2、登录授权
Dashboard 支持 Kubeconfig 和 Token 两种认证方式,为了简化配置,我们通过配置文件为 Dashboard 默认用户赋予 admin 权限。
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kube-system
EOF
授权:
[root@k8s-master-1 cfg]# kubectl apply -f kubernetes-adminuser.yaml
serviceaccount/admin-user created
clusterrolebinding.rbac.authorization.k8s.io/admin-user created
获取登录的 token:
[root@k8s-master-1 cfg]# kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk ' {print $1}')
Name: admin-user-token-hrdpx
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name: admin-user
kubernetes.io/service-account.uid: 5d0b81f2-5b15-4ceb-97e1-f0b35d05b9ce
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1383 bytes
namespace: 11 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6InBXa2VKS0Y4TGpFUFFZN2M3c1M1djdNcDhEU09YZnZ4c1dYOENLRWd4ZG8ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLWhyZHB4Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI1ZDBiODFmMi01YjE1LTRjZWItOTdlMS1mMGIzNWQwNWI5Y2UiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.BpsqqIRD6jUwGm9Luya_3Y38ApE__VqmOSQe9tBWMvJ6Uh5uJVyZrWT52mPpTudNJoGSAYqtVPuZIpBZzabyAGeC1BIt0unBHj6B0hEI2Yc6WetfPeHgPhmHQ6K_2W9VQKTW497ZZXuu1A5T-XI7ompWPWF0gC1ZQgZkeVoFaZvRLjrX02e0lFyP_EInL_Wss0-5gh2qDzY1vbocCUaR87Hq8dEpkTBhR9paRwqJaTOf5vtwpcS6I0dk2eivWVmVcDQ8a3s08xdQD1Fd4oRxlqKrMcK9BEFW6WTUjEqgrMCByn3T0fVr9J9rgUfH9WYDXqTUdJevDNeA_0n-7AXzog
通过token登录进 dashboard,就可以查看集群的信息:
九、集群搭建 —— 多 Master 部署
1、部署Master2组件
① 将 k8s-master-1 上相关文件拷贝到 k8s-master-2 上
创建k8s工作目录:
[root@k8s-master-2 ~]# mkdir -p /k8s/kubernetes
[root@k8s-master-2 ~]# mkdir -p /k8s/etcd
[root@k8s-master-2 ~]# mkdir -p /data/soft
拷贝 k8s 配置文件、执行文件、证书:
k8s-master-1 上执行
scp -r /k8s/kubernetes/{cfg,ssl,bin} root@k8s-master-2:/k8s/kubernetes
scp -r /data/soft/ root@k8s-master-2:/data/
k8s-master-2 上执行
cp -p /data/soft/kubernetes/server/bin/kubectl /usr/local/bin/
拷贝 etcd 证书:
[root@k8s-master-1 cfg]# scp -r /k8s/etcd/ssl root@k8s-master-2:/k8s/etcd
拷贝 k8s 服务的service文件:
[root@k8s-master-1 cfg]# scp /usr/lib/systemd/system/kube-* root@k8s-master-2:/usr/lib/systemd/system
② 修改 k8s-master-2 上的配置文件
修改 kube-apiserver.conf,修改IP为本机IP
③ 启动 k8s-master-2 组件
重新加载配置:
systemctl daemon-reload
启动 kube-apiserver:
systemctl start kube-apiserver
systemctl enable kube-apiserver
启动 kube-controller-manager:
systemctl start kube-controller-manager
systemctl enable kube-controller-manager
部署 kube-scheduler:
systemctl start kube-scheduler
systemctl enable kube-scheduler
④ 验证
[root@k8s-master-2 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-node-1 Ready <none> 6d6h v1.16.2
k8s-node-2 Ready <none> 6d6h v1.16.2
[root@k8s-master-2 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 1 107m
web-d86c95cc9-wcjp6 1/1 Running 0 6d
2、部署 Nginx 负载均衡
为了保证 k8s master 的高可用,将使用 k8s-lb-master 和 k8s-lb-backup 这两台机器来部署负载均衡。这里使用 nginx 做负载均衡器,下面分别在 k8s-lb-master 和 k8s-lb-backup 这两台机器上部署 nginx。
① gcc等环境安装,后续有些软件安装需要这些基础环境
# gcc安装:
yum install gcc-c++
# PCRE pcre-devel 安装:
yum install -y pcre pcre-devel
# zlib 安装:
yum install -y zlib zlib-devel
#OpenSSL 安装:
yum install -y openssl openssl-devel
② 安装nginx
rpm -ivh https://nginx.org/packages/rhel/7/x86_64/RPMS/nginx-1.16.1-1.el7.ngx.x86_64.rpm
③ apiserver 负载配置
vim /etc/nginx/nginx.conf
增加如下配置:
stream {
log_format main '$remote_addr $upstream_addr - [$time_local] $status $upstream_bytes_sent';
access_log /var/log/nginx/k8s-access.log main;
upstream k8s-apiserver {
server 192.168.137.14:6443;
server 192.168.137.15:6443;
}
server {
listen 6443;
proxy_pass k8s-apiserver;
}
}
④ 启动 nginx
systemctl start nginx
systemctl enable nginx
3、部署 KeepAlive
为了保证 nginx 的高可用,还需要部署 keepalive,keepalive 主要负责 nginx 的健康检查和故障转移。
① 分别在 k8s-lb-master 和 k8s-lb-backup 这两台机器上安装 keepalive
yum install keepalived -y
② master 启动 keepalived
修改 k8s-lb-master keepalived 配置文件
cat > /etc/keepalived/keepalived.conf <<'EOF'
global_defs {
script_user root
enable_script_security
notification_email {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id NGINX_MASTER
}
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
}
# vrrp实例
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.137.100/24
}
track_script {
check_nginx
}
}
EOF
配置说明:
- vrrp_script:用于健康检查nginx状态,如果nginx没有正常工作,就会进行故障漂移,使备节点接管VIP,这里通过 shell 脚本来检查nginx状态
- state:keepalived 角色,主节点为 MASTER,备节点为 BACKUP
- interface:接口,配置本地网卡名,keepalived 会将虚拟IP绑定到这个网卡上
- virtual_router_id:#VRRP 路由ID实例,每个实例是唯一的
- priority:优先级,备服务器设置90
- advert_int:指定VRRP心跳包通告间隔时间,默认1秒
- virtual_ipaddress:VIP,要与当前机器在同一网段,keepalived 会在网卡上附加这个IP,之后通过这个IP来访问Nginx,当nginx不可用时,会将此虚拟IP漂移到备节点上。
增加 check_nginx.sh 脚本,通过此脚本判断 nginx 是否正常:
cat > /etc/keepalived/check_nginx.sh <<'EOF'
#!/bin/bash
count=$(ps -ef | grep nginx | egrep -cv "grep|$$")
if [ "$count" -eq 0 ];then
exit 1;
else
exit 0;
fi
EOF
增加可执行权限:
chmod +x /etc/keepalived/check_nginx.sh
启动 keepalived:
systemctl start keepalived
systemctl enable keepalived
③ backup 启动 keepalived
修改 k8s-lb-backup keepalived 配置文件
cat > /etc/keepalived/keepalived.conf <<'EOF'
global_defs {
script_user root
enable_script_security
notification_email {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id NGINX_BACKUP
}
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
}
# vrrp实例
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.137.100/24
}
track_script {
check_nginx
}
}
EOF
增加 check_nginx.sh 脚本:
cat > /etc/keepalived/check_nginx.sh <<'EOF'
#!/bin/bash
count=$(ps -ef | grep nginx | egrep -cv "grep|$$")
if [ "$count" -eq 0 ];then
exit 1;
else
exit 0;
fi
EOF
增加可执行权限:
chmod +x /etc/keepalived/check_nginx.sh
启动 keepalived:
systemctl start keepalived
systemctl enable keepalived
注意:本地网卡名可以先通过
ip a
查看下,按自己实际的来,我的虚拟机是eth0
的
④ 验证负载均衡
keepalived 已经将VIP附加到MASTER所在的网卡上
[root@k8s-lb-master ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:0a:0e:0d brd ff:ff:ff:ff:ff:ff
inet 192.168.137.18/24 brd 192.168.137.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet 192.168.137.100/24 scope global secondary eth0
valid_lft forever preferred_lft forever
inet6 fe80::fdca:50bb:5250:8fce/64 scope link noprefixroute
valid_lft forever preferred_lft forever
BACKUP节点上并没有
[root@k8s-lb-backup nginx]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:0a:0e:0e brd ff:ff:ff:ff:ff:ff
inet 192.168.137.19/24 brd 192.168.137.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 fe80::195d:5816:625e:75f/64 scope link noprefixroute
valid_lft forever preferred_lft forever
关闭 k8s-lb-master 上的nginx,可看到VIP已经不在了
[root@k8s-lb-master ~]# systemctl stop nginx
[root@k8s-lb-master ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:0a:0e:0d brd ff:ff:ff:ff:ff:ff
inet 192.168.137.18/24 brd 192.168.137.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 fe80::fdca:50bb:5250:8fce/64 scope link noprefixroute
valid_lft forever preferred_lft forever
可以看到已经漂移到备节点上了,如果再重启 MASTER 上的 Ngnix,VIP又会漂移到主节点上。
[root@k8s-lb-backup nginx]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:0a:0e:0e brd ff:ff:ff:ff:ff:ff
inet 192.168.137.19/24 brd 192.168.137.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet 192.168.137.100/24 scope global secondary eth0
valid_lft forever preferred_lft forever
inet6 fe80::195d:5816:625e:75f/64 scope link noprefixroute
valid_lft forever preferred_lft forever
访问虚拟IP还是可以访问的
[root@k8s-master-1 ~]# curl 192.168.137.100
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
4、Node节点连接VIP
① 修改 node 节点的配置文件中连接 k8s-master 的IP为VIP
node1
[root@k8s-node-1 cfg]# pwd
/k8s/kubernetes/cfg
[root@k8s-node-1 cfg]# grep 192 *
bootstrap.kubeconfig: server: https://192.168.137.14:6443
kubelet.kubeconfig: server: https://192.168.137.14:6443
kube-proxy.kubeconfig: server: https://192.168.137.14:6443
[root@k8s-node-1 cfg]# sed -i 's#192.168.137.14#192.168.137.100#' *
[root@k8s-node-1 cfg]# grep 192 *
bootstrap.kubeconfig: server: https://192.168.137.100:6443
kubelet.kubeconfig: server: https://192.168.137.100:6443
kube-proxy.kubeconfig: server: https://192.168.137.100:6443
② 重启 kubelet 和 kube-proxy
systemctl restart kubelet
systemctl restart kube-proxy
node2同样操作
③ 在 node 节点上访问VIP调用 apiserver 验证
Authorization 的token 是前面生成 token.csv 中的令牌。
[root@k8s-node-1 cfg]# ssh root@k8s-master-1 'cat /k8s/kubernetes/cfg/token.csv'
root@k8s-master-1's password:
b9187da74c444ee060a1c6053eab97c7,kubelet-bootstrap,10001,"system:node-bootstrapper"
[root@k8s-node-1 cfg]# curl -k --header "Authorization: Bearer b9187da74c444ee060a1c6053eab97c7" https://192.168.137.100:6443/version
{
"major": "1",
"minor": "16",
"gitVersion": "v1.16.2",
"gitCommit": "c97fe5036ef3df2967d086711e6c0c405941e14b",
"gitTreeState": "clean",
"buildDate": "2019-10-15T19:09:08Z",
"goVersion": "go1.12.10",
"compiler": "gc",
"platform": "linux/amd64"