K8S实战进阶
一、搭建Kubernetes集群
1.1 搭建方案
1.1.1 minikube
minikube 是一个工具, 能让你在本地运行 Kubernetes。 minikube 在你的个人计算机(包括 Windows、macOS 和 Linux PC)上运行一个一体化(all-in-one)或多节点的本地 Kubernetes 集群,以便你来尝试 Kubernetes 或者开展每天的开发工作。
1.1.2 kubeadm
你可以使用 kubeadm 工具来创建和管理 Kubernetes 集群。 该工具能够执行必要的动作并用一种用户友好的方式启动一个可用的、安全的集群。
安装 kubeadm 展示了如何安装 kubeadm 的过程。一旦安装了 kubeadm, 你就可以使用它来创建一个集群。
服务器要求
IP地址:
- k8s-master:192.168.122.100
- k8s-node1:192.168.122.110
- k8s-node2:192.168.122.120
最低配置:2核、2G内存、20G硬盘
需要联网,不能联网的话需要提供对应镜像的私有仓库
软件环境
操作系统:Debian 12
Docker版本:Docker version 26.0.0
K8S版本:1.23.17(1.24移除了docker支持)
安装步骤
初始操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32#关闭防火墙
systemctl stop firewalld #停止firewalld服务
systemctl disable firewalld #firewalld禁止自启
ufw disable #ufw禁用
#关闭selinux
sed -i 's/enforcing/disabled/' /etc/selinux/config #永久
setenforce 0 #临时
#关闭swap(关闭swap后,需要重启主机)
swapoff -a #临时
sed -ri 's/.*swap.*/#&/' /etc/fstab #永久
free -h #查看内存
#根据规划设置主机名
vim /etc/hostname
#将桥接的IPv4流量传递到iptablesd的链路
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system #生效
#时间同步
yum install ntpdate -y
ntpdate ntp.aliyun.com
apt install -y ntp
ntpdate ntp.aliyun.com安装基础软件(所有节点)
安装docker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32#卸载旧版本docker
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
#设置Docker的存储库
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
#安装Docker包
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
#配置docker镜像加速
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://URL.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
#测试
sudo docker run hello-world添加阿里云软件源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#设置kubernetes存储库
apt-get update && apt-get install -y apt-transport-https
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
# 排错:
# 执行curl命令报错:curl: (23) Failed writing body
# 解决方案:curl -s -N https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
# 执行命令报错:E: gnupg, gnupg2 and gnupg1 do not seem to be installed, but one of them is required for this operation
# 解决方案:apt-get install -y gnupg2
# 弹出警告:Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).(apt-key已弃用)
# 解决方案:建议无视或者curl -s https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/apt-key.gpg --import
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF安装kubeadm、kubelet、kubectl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#安装kubernetes
apt-get update
apt-cache madison kubelet #查询版本号
apt-get install -y kubelet=1.23.17-00 kubeadm=1.23.17-00 kubectl=1.23.17-00
#测试
kubectl version
#设置开机自启
systemctl enable kubelet.service
# 查看docker设备信息
docker info | grep Driver
Cgroup Driver: systemd
# 如果显示以下信息则需要更改
docker info | grep Driver
Cgroup Driver: cgroupfs
# 配置关闭 Docker 的 cgroups
vim /etc/docker/daemon.json 加入以下内容
"exec-opts": ["native.cgroupdriver=systemd"]
# 重启 docker
systemctl daemon-reload
systemctl restart docker
部署Kubernetes Master
1
2
3
4
5
6
7
8
9
10
11
12
13#初始化master节点
kubeadm init \
--apiserver-advertise-address=192.168.122.100 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.23.17 \
--service-cidr=10.96.0.0/12 \
--pod-network-cidr=10.244.0.0/16
#安装成功后,复制如下配置并执行
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl get nodes加入Kubernetes Node
1
2
3
4
5
6
7
8
9
10
11
12
13
14#分别在 k8s-node1 和 k8s-node2 执行
kubeadm join 192.168.122.100:6443 --token 47pvys.orya0t0d8fjcqhwj \
--discovery-token-ca-cert-hash sha256:314d290d2734a305e4a7220067f38f34dd0f6cdf349b983f09dba99a7843ce6f
# 如果初始化的 token 不小心清空了,可以通过如下命令获取或者重新申请
# 如果 token 已经过期,就重新申请
kubeadm token create
# token 没有过期可以通过如下命令获取
kubeadm token list
# 获取 --discovery-token-ca-cert-hash 值,得到值后需要在前面拼接上 sha256:
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'部署CNI网络插件
可选择Flannel、Calico、Canal 和 Weave
1
2
3
4
5
6
7
8
9
10
11#在master节点上执行
#下载 calico 配置文件(也可以使用Flannel、Canal和Weave)
curl https://calico-v3-25.netlify.app/archive/v3.25/manifests/calico.yaml -O
# 修改 calico.yaml 文件中的 CALICO_IPV4POOL_CIDR 配置,修改为与初始化的 cidr 相同(默认注释状态,会使用初始化时指定pod网络状态的配置)
# Calico默认会拉取docker.io中的镜像,可以删除镜像 docker.io/ 前缀,避免下载过慢导致失败
sed -i 's#docker.io/##g' calico.yaml
#构建Calico网络
kubectl apply -f calico.yaml测试Kubernetes集群
1
2
3
4
5
6
7
8
9
10#创建部署
kubectl create deployment nginx --image=nginx
#暴露端口
kubectl expose deployment nginx --port=80 --type=NodePort
#查看 pod 以及服务信息
kubectl get pod,svc
#宿主机访问测试
1.1.3 二进制安装
利用 k8s 官方 github 仓库下载二进制包安装,安装过程较复杂,但相对较为稳定,推荐生产环境使用。
1.1.4 命令行工具安装
1.2 命令行工具kubectl
Kubernetes 提供 kubectl 是使用 Kubernetes API 与 Kubernetes 集群的控制面进行通信的命令行工具。
这个工具叫做 kubectl。
1.2.1 命令自动补全
1 | apt install bash-completion |
1.2.2 在任意节点使用kubectl
1 | # 1. 将 master 节点中 /etc/kubernetes/admin.conf 拷贝到需要运行的服务器的 /etc/kubernetes 目录中 |
1.2.3 资源操作
创建对象
1 | kubectl create -f ./my-manifest.yaml # 创建资源 |
显示和查找资源
1 | # Get commands with basic output |
更新资源
1 | $ kubectl rolling-update frontend-v1 -f frontend-v2.json # 滚动更新 pod frontend-v1 |
修补资源
1 | $ kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' # 部分更新节点 |
编辑资源
1 | $ kubectl edit svc/docker-registry # 编辑名为 docker-registry 的 service |
scale资源
1 | $ kubectl scale --replicas=3 rs/foo # Scale a replicaset named 'foo' to 3 |
删除资源
1 | $ kubectl scale --replicas=3 rs/foo # Scale a replicaset named 'foo' to 3 |
1.2.4 Pod与集群
与运行的Pod交互
1 | $ kubectl logs my-pod # dump 输出 pod 的日志(stdout) |
与节点和集群交互
1 | $ kubectl cordon my-node # 标记 my-node 不可调度 |
1.2.5 资源类型与别名
资源类型 | 缩写别名 |
---|---|
clusters |
|
componentstatuses |
cs |
configmaps |
cm |
daemonsets |
ds |
deployments |
deploy |
endpoints |
ep |
event |
ev |
horizontalpodautoscalers |
hpa |
ingresses |
ing |
jobs |
|
limitranges |
limits |
namespaces |
ns |
networkpolicies |
|
nodes |
no |
statefulsets |
|
persistentvolumeclaims |
pvc |
persistentvolumes |
pv |
pods |
po |
podsecuritypolicies |
psp |
podtemplates |
|
replicasets |
rs |
replicationcontrollers |
rc |
resourcequotas |
quota |
cronjob |
|
secrets |
|
serviceaccount |
sa |
services |
svc |
storageclasses |
|
thirdpartyresources |
1.2.6 格式化输出
- 输出json格式:-o json
- 仅打印资源名称:-o name
- 以纯文本格式输出所有信息:-o wide
- 输出yaml格式:-o yaml
1.3 API概述
官网文档:https://kubernetes.io/zh-cn/docs/reference/using-api/
REST API 是 Kubernetes 系统的重要部分,组件之间的所有操作和通信均由 API Server 处理的 REST AP I调用,大多数情况下, API 定义和实现都符合标准的 HTTP REST 格式,可以通过 kubectl 命令管理工具或其他命令行工具来执行。
1.3.1 类型
Alpha
- 包含 alpha 名称的版本(例如v1alpha1)。
- 该软件可能包含错误。启用一个功能可能会导致 bug。默认情况下,功能可能会被禁用。
- 随时可能会丢弃对该功能的支持,恕不另行通知。
- API 可能在以后的软件版本中以不兼容的方式更改,恕不另行通知。
- 该软件建议仅在短期测试集群中使用,因为错误的风险增加和缺乏长期支持。
Beta
- 包含 beta 名称的版本(例如 v2beta3 )。
- 该软件经过很好的测试。启用功能被认为是安全的。默认情况下功能是开启的。
- 细节可能会改变,但功能在后续版本不会被删除
- 对象的模式或语义在随后的 beta 版本或 Stable 版本中可能以不兼容的方式发生变化。如果这种情况发生时,官方会提供迁移操作指南。这可能需要删除、编辑和重新创建API对象。
- 该版本在后续可能会更改一些不兼容地方,所以建议用于非关键业务,如果你有多个可以独立升级的集群,你也可以放宽此限制。
- 大家使用过的 Beta 版本后,可以多给社区反馈,如果此版本在后续更新后将不会有太大变化。
Stable
- 该版本名称命名方式:vX 这里 X 是一个整数。
- Stable 版本的功能特性,将出现在后续发布的软件版本中。
1.3.2 访问控制
认证
授权
1.3.3 废弃api说明
https://kubernetes.io/zh-cn/docs/reference/using-api/deprecation-guide/
二、深入pod
2.1 Pod配置文件
1 | apiVersion: v1 # api 文档版本 |
2.2 探针
2.2.1 类型
StartupProbe
k8s 1.16 版本新增的探针,用于判断应用程序是否已经启动了。
当配置了 startupProbe 后,会先禁用其他探针,直到 startupProbe 成功后,其他探针才会继续。
作用:由于有时候不能准确预估应用一定是多长时间启动成功,因此配置另外两种方式不方便配置初始化时长来检测,而配置了 statupProbe 后,只有在应用启动成功了,才会执行另外两种探针,可以更加方便的结合使用另外两种探针使用。
1 | startupProbe: # 应用启动探针配置 |
LivenessProbe
用于探测容器中的应用是否运行,如果探测失败,kubelet 会根据配置的重启策略进行重启,若没有配置,默认就认为容器启动成功,不会执行重启策略。
1 | livenessProbe: |
ReadinessProbe
用于探测容器内的程序是否健康,它的返回值如果返回 success,那么就认为该容器已经完全启动,并且该容器是可以接收外部流量的。
1 | readinessProbe: |
2.2.2探测方式
ExecAction
在容器内部执行一个命令,如果返回值为 0,则任务容器时健康的。
1 | livenessProbe: |
TCPSocketAction
通过 tcp 连接监测容器内端口是否开放,如果开放则证明该容器健康
1 | livenessProbe: |
HTTPGetAction
生产环境用的较多的方式,发送 HTTP 请求到容器内的应用程序,如果接口返回的状态码在 200~400 之间,则认为容器健康。
1 | livenessProbe: |
2.2.3 参数配置
1 | initialDelaySeconds: 60 # 初始化时间 |
2.3 生命周期
1 | lifecycle: |
2.3.1 Pod退出流程
删除操作
Endpoint删除pod的ip地址
Pod变成Terminating状态
变为删除中的状态后,会给 pod 一个宽限期,让 pod 去执行一些清理或销毁操作。
1
2
3
4
5
6配置参数:
# 作用与 pod 中的所有容器
terminationGracePeriodSeconds: 30
containers:
- xxx执行perStop的指令
2.3.2 PreStop的应用
如果应用销毁操作耗时需要比较长,可以在 preStop 按照如下方式进行配置
1 | preStop: |
但是需要注意,由于 k8s 默认给 pod 的停止宽限时间为 30s,如果我们停止操作会超过 30s 时,不要光设置 sleep 50,还要将 terminationGracePeriodSeconds: 30 也更新成更长的时间,否则 k8s 最多只会在这个时间的基础上再宽限几秒,不会真正等待 50s
注册中心下线
数据清理
数据销毁
三、资源调度
3.1 Label和Slelctor
3.1.1 标签(Label)
配置文件
在各类资源的 metadata.labels
中进行配置
kubectl
临时创建label:
1 | kubectl label po <资源名称> app=hello -n namespace |
修改已经存在的标签:
1 | kubectl label po <资源名称> app=hello2 --overwrite |
查看label:
1 | # selector 按照 label 单值查找节点 |
3.1.2 选择器(Selector)
配置文件
在各对象的配置 spec.selector
或其他可以写 selector
的属性中编写
kubectl
1 | # 匹配单个值,查找 app=hello 的 pod |
3.2 Deployment
3.2.1 功能
创建
1 | 创建一个 deployment |
滚动更新
只有修改了 deployment 配置文件中的 template 中的属性后,才会触发更新操作
1 | 修改 nginx 版本号 |
多个滚动更新并行
假设当前有 5 个 nginx:1.7.9 版本,你想将版本更新为 1.9.1,当更新成功第三个以后,你马上又将期望更新的版本改为 1.9.2,那么此时会立马删除之前的三个,并且立马开启更新 1.9.2 的任务
回滚
有时候你可能想回退一个Deployment,例如,当Deployment不稳定时,比如一直crash looping。
默认情况下,kubernetes会在系统中保存前两次的Deployment的rollout历史记录,以便你可以随时会退(你可以修改revision history limit来更改保存的revision数)。
1 | 案例: |
扩容缩容
通过 kube scale 命令可以进行自动扩容/缩容,以及通过 kube edit 编辑 replcas 也可以实现扩容/缩容
扩容与缩容只是直接创建副本数,没有更新 pod template 因此不会创建新的 rs
暂停与恢复
1 | 由于每次对 pod template 中的信息发生修改后,都会触发更新 deployment 操作,那么此时如果频繁修改信息,就会产生多次更新,而实际上只需要执行最后一次更新即可,当出现此类情况时我们就可以暂停 deployment 的 rollout |
3.2.2 配置文件
1 | apiVersion: apps/v1 # deployment api 版本 |
3.3 StatefulSet
3.3.1 功能
创建
1 | kubectl create -f web.yaml |
扩容缩容
1 | # 扩容缩容 |
镜像更新
1 | # 镜像更新(目前还不支持直接更新 image,需要 patch 来间接实现) |
RollingUpdate
StatefulSet 也可以采用滚动更新策略,同样是修改 pod template 属性后会触发更新,但是由于 pod 是有序的,在 StatefulSet 中更新时是基于 pod 的顺序倒序更新的
灰度发布
利用滚动更新中的 partition 属性,可以实现简易的灰度发布的效果
例如我们有 5 个 pod,如果当前 partition 设置为 3,那么此时滚动更新时,只会更新那些 序号 >= 3 的 pod
利用该机制,我们可以通过控制 partition 的值,来决定只更新其中一部分 pod,确认没有问题后再主键增大更新的 pod 数量,最终实现全部 pod 更新
OnDelete
只有在 pod 被删除时会进行更新操作
删除
1 | # 删除 StatefulSet 和 Headless Service |
删除pvc
1 | # StatefulSet删除后PVC还会保留着,数据不再使用的话也需要删除 |
3.3.2 配置文件
1 |
|
3.4 DaemonSet
3.4.1 配置文件
1 | apiVersion: apps/v1 |
3.4.2 指定Node节点
DaemonSet 会忽略 Node 的 unschedulable 状态,有两种方式来指定 Pod 只运行在指定的 Node 节点上:
nodeSelector:只调度到匹配指定 label 的 Node 上
1
2
3
4
5
6
7
8
9先为 Node 打上标签
kubectl label nodes k8s-node1 svc_type=microsvc
然后再 daemonset 配置中设置 nodeSelector
spec:
template:
spec:
nodeSelector:
svc_type: microsvcnodeAffinity:功能更丰富的 Node 选择器,比如支持集合操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31nodeAffinity 目前支持两种:requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。
比如下面的例子代表调度到包含标签 wolfcode.cn/framework-name 并且值为 spring 或 springboot 的 Node 上,并且优选还带有标签 another-node-label-key=another-node-label-value 的Node。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: wolfcode.cn/framework-name
operator: In
values:
- spring
- springboot
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: pauseyyf/pausepodAffinity:调度到满足条件的 Pod 所在的 Node 上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33podAffinity 基于 Pod 的标签来选择 Node,仅调度到满足条件Pod 所在的 Node 上,支持 podAffinity 和 podAntiAffinity。这个功能比较绕,以下面的例子为例:
如果一个 “Node 所在空间中包含至少一个带有 auth=oauth2 标签且运行中的 Pod”,那么可以调度到该 Node
不调度到 “包含至少一个带有 auth=jwt 标签且运行中 Pod”的 Node 上
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: auth
operator: In
values:
- oauth2
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: auth
operator: In
values:
- jwt
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: pauseyyf/pause
3.4.3 滚动更新
不建议使用 RollingUpdate,建议使用 OnDelete 模式,这样避免频繁更新 ds
3.5 HPA自动扩/缩容
通过观察 pod 的 cpu、内存使用率或自定义 metrics 指标进行自动的扩容或缩容 pod 的数量。
通常用于 Deployment,不适用于无法扩/缩容的对象,如 DaemonSet
控制管理器每隔30s(可以通过–horizontal-pod-autoscaler-sync-period修改)查询metrics的资源使用情况
3.5.1 开启指标服务
1 | # 下载 metrics-server 组件配置文件 |
3.5.2 cpu、内存指标监控
1 | 实现 cpu 或内存的监控,首先有个前提条件是该对象必须配置了 resources.requests.cpu 或 resources.requests.memory 才可以,可以配置当 cpu/memory 达到上述配置的百分比后进行扩容或缩容 |
3.5.3 自定义metrics
- 控制管理器开启–horizontal-pod-autoscaler-use-rest-clients
- 控制管理器的–apiserver指向API Server Aggregator
- 在API Server Aggregator中注册自定义的metrics API
四、服务发布
4.1 Service
负责东西流量(同层级/内部服务网络通信)的通信
4.1.1 Service的定义
1 | apiVersion: v1 |
命令操作
1 | # 创建 service |
Endpoint
4.1.2代理k8s外部服务
1 | 实现方式: |
- 各环境访问名称统一
- 访问k8s集群外的其他服务
- 项目迁移
4.1.3反向代理外部域名
1 | apiVersion: v1 |
4.1.4 常用类型
ClusterIP
只能在集群内部使用,不配置类型的话默认就是 ClusterIP
ExternalName
返回定义的 CNAME 别名,可以配置为域名
NodePort
会在所有安装了 kube-proxy 的节点都绑定一个端口,此端口可以代理至对应的 Pod,集群外部可以使用任意节点 ip + NodePort 的端口号访问到集群中对应 Pod 中的服务。
当类型设置为 NodePort 后,可以在 ports 配置中增加 nodePort 配置指定端口,需要在下方的端口范围内,如果不指定会随机指定端口
端口范围:30000~32767
端口范围配置在 /usr/lib/systemd/system/kube-apiserver.service 文件中
LoadBalancer
使用云服务商(阿里云、腾讯云等)提供的负载均衡器服务
4.2 Ingress
Ingress 大家可以理解为也是一种 LB 的抽象,它的实现也是支持 nginx、haproxy 等负载均衡服务的
4.2.1 安装ingress-nginx
https://kubernetes.github.io/ingress-nginx/deploy/#using-helm
1 | 1.添加ingress-nginx官方helm仓库 |
1 | 进入ingress-nginx目录修改values.yaml参数配置 |
1 | 给node打标签 |
4.2.2 基本使用
创建一个ingress
文档:https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/
1 | apiVersion: networking.k8s.io/v1 |
多域名配置
1 | apiVersion: networking.k8s.io/v1 |
五、配置与存储
5.1 配置管理
5.1.1 ConfigMap
创建
使用 kubectl create configmap -h
查看示例,构建 configmap 对象
使用ConfigMap
1 | 创建configmap: |
5.1.2 加密数据配置Secret
与 ConfigMap 类似,用于存储配置信息,但是主要用于存储敏感信息、需要加密的信息,Secret 可以提供数据加密、解密功能。
在创建 Secret 时,要注意如果要加密的字符中,包含了有特殊字符,需要使用转义符转移,例如 $ 转移后为 $,也可以对特殊字符使用单引号描述,这样就不需要转移例如 1$289*-! 转换为 ‘1$289*-!’
1 | kubectl create secret -h |
5.1.3 SubPath的使用
使用 ConfigMap 或 Secret 挂载到目录的时候,会将容器中源目录给覆盖掉,此时我们可能只想覆盖目录中的某一个文件,但是这样的操作会覆盖整个文件,因此需要使用到 SubPath
配置方式:
- 定义 volumes 时需要增加 items 属性,配置 key 和 path,且 path 的值不能从 / 开始
- 在容器内的 volumeMounts 中增加 subPath 属性,该值与 volumes 中 items.path 的值相同
1 | kubectl create configmap nginx-conf-cm --from-file=./nginx.conf |
5.1.4 配置的热更新
我们通常会将项目的配置文件作为 configmap 然后挂载到 pod,那么如果更新 configmap 中的配置,会不会更新到 pod 中呢?
这得分成几种情况:
默认方式:会更新,更新周期是更新时间 + 缓存时间
subPath:不会更新
变量形式:如果 pod 中的一个变量是从 configmap 或 secret 中得到,同样也是不会更新的
对于 subPath 的方式,我们可以取消 subPath 的使用,将配置文件挂载到一个不存在的目录,避免目录的覆盖,然后再利用软连接的形式,将该文件链接到目标位置
但是如果目标位置原本就有文件,可能无法创建软链接,此时可以基于前面讲过的 postStart 操作执行删除命令,将默认的吻技安删除即可
5.1.4.1 通过edit命令直接修改configmap
1 | kubectl edit cm test-dir-config |
5.1.4.2 通过replace替换
由于 configmap 我们创建通常都是基于文件创建,并不会编写 yaml 配置文件,因此修改时我们也是直接修改配置文件,而 replace 是没有 --from-file
参数的,因此无法实现基于源配置文件的替换,此时我们可以利用下方的命令实现
# 该命令的重点在于 --dry-run
参数,该参数的意思打印 yaml 文件,但不会将该文件发送给 apiserver,再结合 -oyaml 输出 yaml 文件就可以得到一个配置好但是没有发给 apiserver 的文件,然后再结合 replace 监听控制台输出得到 yaml 数据即可实现替换
1 | kubectl create cm --from-file=nginx.conf --dry-run -oaml | kubectl replace -f- |
5.1.5 不可变的Secret和ConfigMap
对于一些敏感服务的配置文件,在线上有时是不允许修改的,此时在配置 configmap 时可以设置 immutable: true
来禁止修改
1 | apiVersion: v1 |
5.2 持久化存储
5.2.1 Volumes
Hostpath
将节点上的文件或目录挂载到 Pod 上,此时该目录会变成持久化存储目录,即使 Pod 被删除后重启,也可以重新加载到该目录,该目录下的文件不会丢失
配置文件
1 | apiVersion: v1 |
EmptyDir
EmptyDir 主要用于一个 Pod 中不同的 Container 共享数据使用的,由于只是在 Pod 内部使用,因此与其他 volume 比较大的区别是,当 Pod 如果被删除了,那么 emptyDir 也会被删除。
存储介质可以是任意类型,如 SSD、磁盘或网络存储。可以将 emptyDir.medium 设置为 Memory 让 k8s 使用 tmpfs(内存支持文件系统),速度比较快,但是重启 tmpfs 节点时,数据会被清除,且设置的大小会计入到 Container 的内存限制中。
配置文件
1 | apiVersion: v1 |
5.2.2 NFS挂载
nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
安装nfs
1 | # 安装 nfs |
配置文件
1 | apiVersion: v1 |
测试在其他node节点创建文件并在pod中查看
5.2.3 PV与PVC
持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。
5.2.3.1 生命周期
构建
静态构建
集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。
动态构建
如果集群中已经有的 PV 无法满足 PVC 的需求,那么集群会根据 PVC 自动构建一个 PV,该操作是通过 StorageClass 实现的。
想要实现这个操作,前提是 PVC 必须设置 StorageClass,否则会无法动态构建该 PV,可以通过启用 DefaultStorageClass 来实现 PV 的构建。
绑定
当用户创建一个 PVC 对象后,主节点会监测新的 PVC 对象,并且寻找与之匹配的 PV 卷,找到 PV 卷后将二者绑定在一起。
如果找不到对应的 PV,则需要看 PVC 是否设置 StorageClass 来决定是否动态创建 PV,若没有配置,PVC 就会一致处于未绑定状态,直到有与之匹配的 PV 后才会申领绑定关系。
使用
Pod 将 PVC 当作存储卷来使用,集群会通过 PVC 找到绑定的 PV,并为 Pod 挂载该卷。
Pod 一旦使用 PVC 绑定 PV 后,为了保护数据,避免数据丢失问题,PV 对象会受到保护,在系统中无法被删除。
回收策略
当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。 目前,数据卷可以被 Retained(保留)、Recycled(回收)或 Deleted(删除)。
保留(Retain)
回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为”已释放(released)”。 由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他申领。 管理员可以通过下面的步骤来手动回收该卷:
- 删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产 (例如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)在 PV 删除之后仍然存在。
- 根据情况,手动清除所关联的存储资产上的数据。
- 手动删除所关联的存储资产。
如果你希望重用该存储资产,可以基于存储资产的定义创建新的 PersistentVolume 卷对象。
删除(Delete)
对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)中移除所关联的存储资产。 动态制备的卷会继承其 StorageClass 中设置的回收策略, 该策略默认为 Delete。管理员需要根据用户的期望来配置 StorageClass; 否则 PV 卷被创建之后必须要被编辑或者修补。
回收(Recycle)
警告: 回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。
如果下层的卷插件支持,回收策略 Recycle 会在卷上执行一些基本的擦除 (rm -rf /thevolume/*)操作,之后允许该卷用于新的 PVC 申领。
5.2.3.2 PV
状态
Available: 空闲,未被绑定
Bound: 已经被PVC绑定
Released: PVC被删除,资源已回收,但是PV未被重新使用
Failed: 自动回收失败
配置文件
1 | apiVersion: v1 |
5.2.3.3 PVC
Pod绑定PVC
1 | 在 pod 的挂载容器配置中,增加 pvc 挂载 |
配置文件
1 | apiVersion: v1 |
5.2.3.4 StorageClass
制备器(Provisioner)
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。
NFS动态制备案例
参考文档1:https://kubernetes.io/docs/concepts/storage/storage-classes/
参考文档2:https://github.com/kubernetes-csi/csi-driver-nfs#readme
使用helm安装特定版本csi-driver-nfs
1 | helm repo add csi-driver-nfs https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts |
创建storage class
1 | apiVersion: storage.k8s.io/v1 |
创建PVC
1 | kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/pvc-nfs-csi-dynamic.yaml |
动态PVC测试
1 | apiVersion: v1 |
查看结果
1 | kubectl get pvc |
注:以下部分由于quay.io/external_storage/nfs-client-provisioner:latest镜像截至2024/5/8已有6年未更新,新版docker已不支持该版镜像;其他镜像源经过测试也无法使用故删除
NFS动态制备案例
nfs-provisioner
1 | apiVersion: apps/v1 |
StorageClass配置
1 | apiVersion: storage.k8s.io/v1 |
RBAC配置
1 | apiVersion: v1 |
1 | 创建rbac |
PVC处于Pending状态
在 k8s 1.20 之后,出于对性能和统一 apiserver 调用方式的初衷,移除了对 SelfLink 的支持,而默认上面指定的 provisioner 版本需要 SelfLink 功能,因此 PVC 无法进行自动制备。
配置SelfLink1
2
3
4
5
6
7
8
9
10
11
12修改 apiserver 配置文件
vim /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --feature-gates=RemoveSelfLink=false # 新增该行
......
修改后重新应用该配置
kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml不需要SelfLink的provisioner将 provisioner 修改为如下镜像之一即可gcr.io/k8s-staging-sig-storage/nfs-subdir-external-provisioner:v4.0.0registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0
PVC测试配置
1 | apiVersion: v1 |
六、高级调度
6.1 CronJob计划任务
cron表达式
1 | # ┌───────────── 分钟 (0 - 59) |
配置文件
1 | apiVersion: batch/v1 |
6.2 初始化容器InitContainer
在真正的容器启动之前,先启动 InitContainer,在初始化容器中完成真实容器所需的初始化操作,完成后再启动真实的容器。
相对于 postStart 来说,首先 InitController 能够保证一定在 EntryPoint 之前执行,而 postStart 不能,其次 postStart 更适合去执行一些命令操作,而 InitController 实际就是一个容器,可以在其他基础容器环境下执行更复杂的初始化功能。
1 | 在 pod 创建的模板中配置 initContainers 参数: |
6.3 污点与容忍
k8s 集群中可能管理着非常庞大的服务器,这些服务器可能是各种各样不同类型的,比如机房、地理位置、配置等,有些是计算型节点,有些是存储型节点,此时我们希望能更好的将 pod 调度到与之需求更匹配的节点上。
此时就需要用到污点(Taint)和容忍(Toleration),这些配置都是 key: value 类型的。
6.3.1 污点(Taint)
污点:是标注在节点上的,当我们在一个节点上打上污点以后,k8s 会认为尽量不要将 pod 调度到该节点上,除非该 pod 上面表示可以容忍该污点,且一个节点可以打多个污点,此时则需要 pod 容忍所有污点才会被调度该节点。
1 | # 为节点打上污点 |
污点的影响:
NoSchedule:不能容忍的 pod 不能被调度到该节点,但是已经存在的节点不会被驱逐
NoExecute:不能容忍的节点会被立即清除,能容忍且没有配置 tolerationSeconds 属性,则可以一直运行,设置了 tolerationSeconds: 3600 属性,则该 pod 还能继续在该节点运行 3600 秒
NoSchedule
如果不能容忍该污点,那么 Pod 就无法调度到该节点上
NoExecute
- 如果 Pod 不能忍受这类污点,Pod 会马上被驱逐。
- 如果 Pod 能够忍受这类污点,但是在容忍度定义中没有指定 tolerationSeconds, 则 Pod 还会一直在这个节点上运行。
- 如果 Pod 能够忍受这类污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。
6.3.2 容忍(Toleration)
容忍:是标注在 pod 上的,当 pod 被调度时,如果没有配置容忍,则该 pod 不会被调度到有污点的节点上,只有该 pod 上标注了满足某个节点的所有污点,则会被调度到这些节点
1 | # pod 的 spec 下面配置容忍 |
Equal
比较操作类型为 Equal,则意味着必须与污点值做匹配,key/value都必须相同,才表示能够容忍该污点
Exists
容忍与污点的比较只比较 key,不比较 value,不关心 value 是什么东西,只要 key 存在,就表示可以容忍。
6.4 亲和力
6.4.1 NodeAffinity
节点亲和力:进行 pod 调度时,优先调度到符合条件的亲和力节点上
RequiredDuringSchedulingIgnoredDuringExecution
硬亲和力,即支持必须部署在指定的节点上,也支持必须不部署在指定的节点上
PreferredDuringSchedulingIgnoredDuringExecution
软亲和力:尽量部署在满足条件的节点上,或尽量不要部署在被匹配的节点上
应用
匹配类型
- In:部署在满足条件的节点上
- Noth:匹配不在条件中的节点,实现节点反亲和性
- Exists:只要存在 key 名字就可以,不关心值是什么
- DoesNotExist:匹配指定 key 名不存在的节点,实现节点反亲和性
- Gt:value 为数值,且节点上的值小于指定的条件
- Lt:value 为数值,且节点上的值大于指定条件
配置模板
1 | apiVersion: v1 |
1 | 为node打标签 |
6.4.2 PodAffinity
Pod 亲和力:将与指定 pod 亲和力相匹配的 pod 部署在同一节点。
RequiredDuringSchedulingIgnoredDuringExecution
必须将应用部署在一块
PreferredDuringSchedulingIgnoredDuringExecution
尽量将应用部署在一块
配置模板
1 | apiVersion: v1 |
6.4.3 PodAntiAffinity
Pod 反亲和力:根据策略尽量部署或不部署到一块
RequiredDuringSchedulingIgnoredDuringExecution
不要将应用与之匹配的部署到一块
1 | podAffinity: |
PreferredDuringSchedulingIgnoredDuringExecution
尽量不要将应用部署到一块
七、身份认证与权限
Kubernetes 中提供了良好的多租户认证管理机制,如 RBAC、ServiceAccount 还有各种策略等。
通过该文件可以看到已经配置了 RBAC 访问控制
/usr/lib/systemd/system/kube-apiserver.service
7.1 认证
所有 Kubernetes 集群有两类用户:由 Kubernetes 管理的Service Accounts (服务账户)和(Users Accounts) 普通账户。
普通账户是假定被外部或独立服务管理的,由管理员分配 keys,用户像使用 Keystone 或 google 账号一样,被存储在包含 usernames 和 passwords 的 list 的文件里。
需要注意:在 Kubernetes 中不能通过 API 调用将普通用户添加到集群中。
- 普通帐户是针对(人)用户的,服务账户针对 Pod 进程。
- 普通帐户是全局性。在集群所有namespaces中,名称具有惟一性。
- 通常,群集的普通帐户可以与企业数据库同步,新的普通帐户创建需要特殊权限。服务账户创建目的是更轻量化,允许集群用户为特定任务创建服务账户。
- 普通帐户和服务账户的审核注意事项不同。
- 对于复杂系统的配置包,可以包括对该系统的各种组件的服务账户的定义。
7.1.1 User Accounts
7.1.2 Service Accounts
Service Account 自动化
Service Account Admission Controller
通过 Admission Controller 插件来实现对 pod 修改,它是 apiserver 的一部分。创建或更新 pod 时会同步进行修改 pod。当插件处于激活状态(在大多数发行版中都默认情况)创建或修改 pod 时,会按以下操作执行:
- 如果 pod 没有设置 ServiceAccount,则将 ServiceAccount 设置为 default。
- 确保 pod 引用的 ServiceAccount 存在,否则将会拒绝请求。
- 如果 pod 不包含任何 ImagePullSecrets,则将ServiceAccount 的 ImagePullSecrets 会添加到 pod 中。
- 为包含 API 访问的 Token 的 pod 添加了一个 volume。
- 把 volumeSource 添加到安装在 pod 的每个容器中,挂载在 /var/run/secrets/kubernetes.io/serviceaccount。
Token Countroller
TokenController 作为 controller-manager 的一部分运行。异步行为:
- 观察 serviceAccount 的创建,并创建一个相应的 Secret 来允许 API 访问。
- 观察 serviceAccount 的删除,并删除所有相应的ServiceAccountToken Secret
- 观察 secret 添加,并确保关联的 ServiceAccount 存在,并在需要时向 secret 中添加一个 Token。
- 观察 secret 删除,并在需要时对应 ServiceAccount 的关联
Service Account Controller
Service Account Controller 在 namespaces 里管理ServiceAccount,并确保每个有效的 namespaces 中都存在一个名为 “default” 的 ServiceAccount。
7.2 授权(RBAC)
7.2.1 Role
代表一个角色,会包含一组权限,没有拒绝规则,只是附加允许。它是 Namespace 级别的资源,只能作用与 Namespace 之内。
1 | # 查看已有的角色信息 |
配置文件
1 | apiVersion: rbac.authorization.k8s.io/v1 |
7.2.2 ClusterRole
功能与 Role 一样,区别是资源类型为集群类型,而 Role 只在 Namespace
1 | # 查看某个集群角色的信息 |
7.2.3 RoleBinding
Role 或 ClusterRole 只是用于制定权限集合,具体作用与什么对象上,需要使用 RoleBinding 来进行绑定。
作用于 Namespace 内,可以将 Role 或 ClusterRole 绑定到 User、Group、Service Account 上。
1 | # 查看 rolebinding 信息 |
配置文件
1 | apiVersion: rbac.authorization.k8s.io/v1 |
7.2.4 ClusterRoleBinding
与 RoleBinding 相同,但是作用于集群之上,可以绑定到该集群下的任意 User、Group 或 Service Account
八、附录
8.1 Redis Chart参数说明
参数 | 描述 | Default |
---|---|---|
image |
Redis image | bitnami/redis:{VERSION} |
imagePullPolicy |
Image pull policy | IfNotPresent |
serviceType |
Kubernetes Service type | ClusterIP |
usePassword |
Use password | true |
redisPassword |
Redis password | Randomly generated |
args |
Redis command-line args | [] |
redisExtraFlags |
Redis additional command line flags | [] |
persistence.enabled |
Use a PVC to persist data | true |
persistence.path |
Path to mount the volume at, to use other images | /bitnami |
persistence.subPath |
Subdirectory of the volume to mount at | "" |
persistence.existingClaim |
Use an existing PVC to persist data | nil |
persistence.storageClass |
Storage class of backing PVC | generic |
persistence.accessMode |
Use volume as ReadOnly or ReadWrite | ReadWriteOnce |
persistence.size |
Size of data volume | 8Gi |
resources |
CPU/Memory resource requests/limits | Memory: 256Mi , CPU: 100m |
metrics.enabled |
Start a side-car prometheus exporter | false |
metrics.image |
Exporter image | oliver006/redis_exporter |
metrics.imageTag |
Exporter image | v0.11 |
metrics.imagePullPolicy |
Exporter image pull policy | IfNotPresent |
metrics.resources |
Exporter resource requests/limit | Memory: 256Mi , CPU: 100m |
nodeSelector |
Node labels for pod assignment | {} |
tolerations |
Toleration labels for pod assignment | [] |
networkPolicy.enabled |
Enable NetworkPolicy | false |
networkPolicy.allowExternal |
Don’t require client label for connections | true |
service.annotations |
annotations for redis service | {} |
service.loadBalancerIP |
loadBalancerIP if service type is LoadBalancer |
`` |
securityContext.enabled |
Enable security context | true |
8.2 StorageClass 制备器
卷插件 | 内置制备器 | 配置例子 |
---|---|---|
AWSElasticBlockStore | ✓ | AWS EBS |
AzureFile | ✓ | Azure File |
AzureDisk | ✓ | Azure Disk |
CephFS | - | - |
Cinder | ✓ | OpenStack Cinder |
FC | - | - |
FlexVolume | - | - |
Flocker | ✓ | - |
GCEPersistentDisk | ✓ | GCE PD |
Glusterfs | ✓ | Glusterfs |
iSCSI | - | - |
Quobyte | ✓ | Quobyte |
NFS | - | - |
RBD | ✓ | Ceph RBD |
VsphereVolume | ✓ | vSphere |
PortworxVolume | ✓ | Portworx Volume |
ScaleIO | ✓ | ScaleIO |
StorageOS | ✓ | StorageOS |
Local | - | Local |