目录
一、初始化容器 InitContainer
首先来看初始化容器,顾名思义,初始化容器是用来进行初始化操作的。很多情况下,程序的启动需要依赖各类配置、资源。但是又不能继承在原有的启动命令或者镜像当中,因为程序的镜像可能并没有加载配置命令,此时 InitContainer 就起了很大的作用。
1. InitContainer 的基本概念
InitContainer 是 Kubernetes 的初始化容器(也可称之为 Init 容器),它是一种特殊的容器,在 Pod 内的应用容器启动之前运行,可以包括一些应用镜像中不存在的使用工具和安装脚本,用以在程序启动时进行初始化,比如创建文件、修改内核参数、等待依赖程序启动等。
每个 Pod 中可以包含多个容器,同时 Pod 也可以有一个或多个先于应用程序启动的 Init 容器,在 Pod 定义中和 container 同级,按顺序逐个执行,当所有的 Init 容器运行完成时,Kubernetes 才会启动 Pod 内的普通容器。
Init 容器与普通的容器非常像,除了如下几点:
- 他们总是运行到完成。
- 上一个运行完成才会运行下一个。
- 如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,但是如果 Pod 对应的 restartPolicy 值为 Never,Kubernetes 则不会重新启动 Pod。
为 Pod 设置 Init 容器需要在 Pod 的 spec 中添加 initContainer 字段,该字段和应用的 containers 属组同级相邻,配置方式和 containers 中的普通容器差不多,Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。但是 Init 容器对资源请求和限制的处理稍有不同,Init 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe,因为他们必须在 Pod 就绪之前运行完成。
备注:
lifecycle 主要指的是容器在其运行环境中经历的不同阶段,以及 Kubernetes 如何管理和响应这些阶段的能力。Kubernetes 提供了容器生命周期钩子(Container Lifecycle Hooks),这是一种允许用户指定在容器生命周期的特定点上运行代码片段或命令的功能。
容器生命周期钩子主要包括以下两个部分:
- PostStart:这个钩子在容器被创建之后立即调用,但需要注意的是,在钩子执行的时候容器内的主进程尚未启动。这可以用于执行一些初始化任务,比如建立文件系统的缓存目录或其他类型的准备动作。
- PreStop:这个钩子在发送信号给容器内的主进程之前调用,这意味着这是在容器被终止之前可以执行一些清理工作的最后机会。它常被用来做一些资源释放的工作,比如关闭已经打开的数据库连接或网络连接等。
在生产环境中,为了应用的安全和优化镜像的体积,业务镜像一般不会安装高危工具和并不常用的运维工具,比如 cur1、sed、awk、python 或 dig 等,同时建议使用非 root 用户去启动容器。但是某些应用启动之前可能需要检查依赖的服务有没有成功运行,或者需要更高的权限去修改一些系统级的配置,而这些检测或配置更改都是一次性的,所以在制作业务镜像时没有必要为了一次配置的变更去安装一个配置工具,更没有必要因为使用一次高权限而把整个镜像改成以 root 身份运行。
考虑到上述问题和需求,Kubernetes 引入了初始化容器的概念,Init 容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:
- Init 容器可以包含安装过程中应用容器中不存在的实用工具或个性化代码。
- Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
- Init 容器可以以 root 身份运行,执行一些高权限命令。
- Init 容器相关操作执行完成后就会退出,不会给业务容器带来安全隐患。
由于 Init 容器必须在应用容器启动之前运行完成,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足一组先决条件,Pod 内的所有应用容器才会并行启动。
2. 示例 1-- 延迟指定时间后启动
创建一个 pod,initContainers 指定初始化容器,command:["sh","-c","sleep15"] 表示初始化容器需要休眠 15 秒。
[root@k8s-master~]#cat init01.ym1
apiVersion:v1
kind:Pod
metadata:
creationTimestamp:nul1
labels:
run:initc01
name:initc01
spec:
terminationGracePeriodSeconds:0
containers:
image:nginx:1.7.9
imagePul1Policy:IfNotPresent
name:n1
resources:{}
initContainers:
- name:initc01
image:nginx:1.7.9
imagePul1Policy:IfNotPresent
command:["sh","-","sleep 15"]
dnsPolicy:ClusterFirst
restartPolicy:Never
status:{}
[root@k8s-master ~]#kubectl create -f init01.ym1
[root@k8s-master ~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 Init:0/1 0 2s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 Init:0/1 0 8s
[root@k8s-master ~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 Init:0/1 0 9s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
initc01 0/1 PodInitializing 0 17s
[root@k8s-master ~]#kubectl get pod NAME READY STATUS RESTARTS AGE initc01 0/1 PodInitializing 0 18s
[root@k8s-master~]#kubectl get pod NAME READY STATUS RESTARTS AGE initc011/1 Running 0 18s
3. 示例 2-- 使用初始化容器修改内核参数
在容器里修改内核参数,实际上修改的就是物理机的内核参数,为了安全性,一般不允许在容器里修改内核参数,Seccomp 代表安全计算(Secure Computing)模式,seccomp 控制了容器能做哪些操作,添加 securityContext 参数之后就可以修改内核参数了。
创建一个 pod,initContainers 初始化容器里的 securityContext:privileged:true 表示该容器具有特权,可以执行命令 "sh","-","/sbin/sysct1-w vm.swappiness=e",swappiness 设置为 0 表示尽量少使用 swap 内存。
[root@k8s-master~]#cat init02.ym1
apiVersion: v1
kind:Pod
metadata:
creationTimestamp: nul1
labels:
run:initc02
6
name:initc02
spec:
terminationGracePeriodSeconds:0
州课
containers:
image:nginx:1.7.9
imagePul1Policy:IfNotPresent
name:n1
resources:{}
initContainers:
-name:initc02
image:alpine
imagePul1Policy:IfNotPresent
command:["sh","-","/sbin/sysct1-w vm.swappiness=0"]
securityContext:
privileged:true
dns Policy:ClusterFirst
restartPolicy: Never
status:{}
[root@k8s-master ~]# kubectl apply -f init02.ym1 [root@k8s-master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE initc 1/1 Running 0 7m7s
#查看 Pod 中的容器
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
NODE READINESS GATES
initc02 1/1 Running 0 5m18s 10.244.85.195 k8s-node01 <none>
<none>
查看 k8s-node01 机器上的 swappiness 值是否为 0
[root@k8s-node01~]#cat/proc/sys/vm/swappiness
0
4. 示例 3-- 等待依赖服务启动
有时某些服务需要依赖其他组件才能启动,比如后端应用需要数据库启动之后,应用才能正常启动,此时需要检测数据库实例是否正常,等待数据库可以正常使用时,在启动后端应用,此时可以使用初始化容器进行控制。
(1)创建第一个 Pod
该 Pod 内要依赖两个条件才能启动,一个是利用 busybox 容器检测 redis-service 服务是否生成,第二个是检测 mysq1-server 服务是否生成。
[root@k8s-master ~]#cat myapp. yam1
apiVersion: v1
kind:Pod
metadata:
name: nginx
1abe1s:
name:nginx
spec:
containers:
name:nginx
image:nginx:1.7.9
ports:
containerPort:86
initContainers:
8
-name:init-redis
image:busybox:1.28
command:['sh','-c','until nslookup redis-server; do echo waiting for redis; sleep 2;done;']
- name:init-mysq1
image:busybox:1.28
command:['sh','-c','until nslookup mysql-server; do echo waiting for mysql;sleep 2;done;']
[root@k8s-master~]# kubectl create -f myapp.yam1
[root@k8s-master~]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Init:0/20 8s
(2)创建第一个被依赖的 service
[root@k8s-master ~]#cat redis-deployment.yam1
apiVersion: apps/v1
kind:Deployment
metadata:
工场
labels:
app:redis
name:redis
spec:
replicas:1
selector:
matchLabels:
app: redis
template:
metadata:
1abels:
app:redis
88
spec:
89
containers:
90
image:redis:5.0
91
imagePul1Policy:IfNotPresent
92
name:redis
93
apiVersion: v1
94
kind:Service
95
metadata:
96
labels:
97
app:redis
98
name:redis-service
99
spec:
100
ports:
101
-port:6379
102
protocol:TCP
103
targetPort:6379
104
selector:
105
app: redis
106
type: NodePort
[root@k8s-master ~]#kubect1 create-f redis-deployment.yam1
deployment.apps/redis created
service/redis-service created
[root@k8s-master ~]#kubectl get svc
[root@k8s-master ~]# kubect1 get pod
NAME READY STATUS RESTARTS AGE
nginx 0/1 Init:1/2 0 2m20s
redis-56bcf55554-mtrqx1/1 Running 0 8s
(3)创建第二个被依赖的 service
[root@k8s-master ~]#cat mysql-deployment.yam1
apiVersion:apps/v1
kind:Deployment
metadata:
labels:
app:mysql
name: mysq1
spec:
replicas: 1
selector:
matchLabels:
工场
app:mysq1
template :
metadata:
labels:
app:mysql
spec:
containers:
env:
- name: MYSQL_ROOT_PASSWORD
value:'moonfdd'
image:'mysq1:8.0'
imagePu11Policy:IfNotPresent
11
name:mysql
volumeMounts:
-mountPath:/var/1ib/mysq1
州课
name:volv
volumes:
hostPath:
path:/root/k8s/moonfdd/mysq1/var/1ib/mysq1
type:DirectoryOrCreate
name:volv
apiVersion:v1
kind:Service
metadata:
labels:
app:mysql
name:mysql-service
spec:
ports:
-port:3306
protocol:TCP
targetPort:3306
selector:
app:mysql
type:NodePort
[root@k8s-master ~]#kubectl create-f mysql-deployment .yam1
deployment.apps/mysq1 created
service/mysq1-service created
[root@k8s-master ~]# kubect1 get svc
NAME TYPE CLUSTER-IP
EXTERNAL−IP
PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP
6 d20 h
mysql-service NodePort 10.101.159.200 <none> 3306:31452/TCP
6s
redis-service NodePort
10.104⋅3⋅33
<none> 6379:32077/TCP
6 m12 s
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
mysql-dc77b5fb8-zczrz 0/1 Running 2 (23s ago) 39s
4:pause 容器
在 Kubernetes 中,pause 容器并不是指暂停容器的执行,而是指一个特殊的辅助容器,它被用于每个 Pod 以帮助实现网络命名空间。pause 容器是 Kubernetes 中每个 Pod 的第一个容器,它的主要作用是作为 Pod 内所有其他容器共享网络命名空间的基础。
每个 Kubernetes Pod 都有一个 pause 容器,它是 Pod 的第一个容器,也是唯一必须运行的容器。即使 Pod 中的其他容器都停止了,只要 pause 容器还在运行,Pod 就不会被 Kubernetes 认为是完全停止的。
(1)pause 容器的作用
- 命名空间共享:为Pod内的所有容器提供网络、PID以及其他Linux命名空间的共享环境。
- 网络初始化:负责创建Pod的网络接口,并配置网络策略,使得Pod内的所有容器可以共享同一个IP地址和端口空间。
- PID Namespace管理:作为Pod内所有容器的Init进程,确保当Pod中最后一个应用容器终止时,整个Pod也能被正确清理。
(2)使用pause容器的好处
- 简化了Pod内的网络配置。
- 提高了Pod网络的一致性。
- 减少了管理Pod网络复杂性的开销。
二、临时容器Ephemeral Containers
在生产环境中,为优化镜像体积和提高安全性,容器通常不会安装过多高危工具(如 curl、wget、dig、net-tools 等)。这虽增强了安全性,却给容器调试带来不便,例如难以查看进程、链接情况或排查服务问题。由于这些工具并非高频使用,预先安装并不合理,而临时需要时再安装又较为繁琐。
为解决此类问题,Kubernetes 在 1.16 版本后引入了 Ephemeral Containers(临时容器)概念,可实现在线调试操作而无需安装第三方工具。
1:临时容器的概念
临时容器是临时添加到 Pod 中用于在线调试应用的容器,其特点如下:
- 与其他容器不同,临时容器不会自动重启,因此不适用于构建应用程序。
- 声明方式与普通容器类似,但无端口配置,不支持 ports、livenessProbe、readnessProbe、resources 等字段(因无需这些配置即可完成调试功能,且其状态不影响其他正常容器)。
- 通过 API 中特殊的 EphemeralContainers 处理器创建,而非直接添加到 pod.spec 字段,故不能用 kubectl edit 添加。
- 添加到 Pod 后无法更改或删除,且当 Pod 重启后,临时容器会被销毁。
- 为便于调试,添加时建议使用包含常用工具的镜像。在业务容器崩溃、容器镜像不含调试工具导致 kubectl exec 不可用,尤其是使用 destroless 镜像(虽减少攻击面和漏洞,但排查问题困难)时,临时容器能发挥重要作用,方便交互故障排查和在线 Debug。
2:临时容器的使用
(1)创建 tomcat 的资源清单:
[root@k8s-master ~]#cat pod-tomcat.yam
apiVersion: v1
kind:Pod
metadata:
name:tomcat-test
namespace:default
1abels:
app: tomcat
spec:
containers:
-name: tomcat-java
ports:
containerPort:8080
image:kubeguide/tomcat-app:v1
imagePul1Policy:IfNotPresent
(2)创建 Pod:
[root@k8s-master ~]#kubect1 apply-f pod-tomcat.yam1
[root@k8s-master ~]# kubect1 get pod
NAME READY STATUS RESTARTS AGE
tomcat-test 1/1 Running 0 64s
(3)为 tomcat 的 pod 创建临时容器:
[root@k8s-master~]#kubect1 debug-it tomcat-test--image=busybox:1.28--target=tomcat-java
/#ps-ef| grep tomcat
1 root
-Djava.uti1.1ogging.config.file=/usr/local/tomcat/conf/logging.properties
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djdk.t1s.ephemeralDHKeySize=2048-Djava.endorsed.dirs/usr/1ocal/tomcat/endorsed -classpath
/usr/1ocal/tomcat/bin/bootstrap.jar:/usr/1ocal/tomcat/bin/tomcat-juli.jar
-Dcatalina.base=/usr/1ocal/tomcat -Dcatalina.home=/usr/local/tomcat
-Djava.io.tmpdi=/usr/1ocal/tomcat/temp org.apache.catalina.startup.Bootstrap start
41 root 0:00 grep tomcat
(4)查看 tomcat-test 这个 pod 是否已有临时容器:
[root@k8s-master ~]#kubectl describe pods tomcat-test
Containers:
tomcat-java:
ContainerID:docker://d37b1f054d9c206df7847d3c51b51cb208e7b142a492597851888f8c83eda1fa
Image: kubeguide/tomcat-app:v1
Image ID:
docker-pullable://kubeguide/tomcat-app@sha256:7a9193c2e5c6c74b4ad49a8abbf75373d4ab76c8f8db87672dc
526b96ac69ac4
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Sun,09Jul202317:37:57+0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4ggbn(ro)
Ephemeral Containers:
debugger-czh68:
Container ID: docker://eaf268329bb4f26545b4ddbdfdc4361bcfb2906ec7a86d3a466c104ba8a837d5
Image: busybox:1.28
Image
docker-pullable://busyboxasha256:141c253bc4c3fdoa201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
Port: <none>
Host Port: <none>
State: Terminated
Reason : Completed
Exit Code: 0
Started: Sun,09Jul 202317:38:49+0800
Finished: Sun,09Jul 2023 17:41:09+0800
三、自动扩缩容HPA
在集群安装过程中,可安装 Metrics Server 组件,该组件负责采集 Pod 和 Node 的度量值指标(如 CPU、内存使用率等),集群中的 Dashboard 能展示 CPU、内存信息也依赖于此。此外,结合 Metrics Server 提供的数据与 Kubernetes 的 HPA 功能,可实现 Pod 的自动扩缩容。
1:什么是 HPA
HPA(Horizontal Pod Autoscaler,水平 Pod 自动伸缩器)能根据观察到的 CPU、内存使用率或自定义度量标准,自动扩展或缩容 Pod 的数量。需要注意的是,HPA 不适用于无法缩放的对象(如 DaemonSet)。HPA 控制器会定期调整 RC 或 Deployment 的副本数,使观察到的平均 CPU 利用率与用户指定的目标相匹配。
2:HPA 的工作原理
- HPA 依据观察到的 CPU、内存使用率或自定义度量标准,自动调整 Pod 数量。
- HPA 控制器定期调整 RC(Replication Controller)或 Deployment 的副本数,以让观察到的平均 CPU 利用率(或其他度量标准)与用户指定目标匹配。
- HPA 需通过 Metrics Server 获取度量指标,Metrics Server 负责采集 Pod 和 Node 的 CPU、内存使用率等度量值指标。
3:HPA 的工作流程
- 配置 HPA:使用
kubectl autoscale
命令为 Deployment、ReplicaSet 或 StatefulSet 等资源创建 HPA 对象,并指定目标 CPU 利用率、最小副本数和最大副本数。- 度量指标采集:Metrics Server 定期采集 Pod 和 Node 的度量值指标,并将数据提供给 HPA 控制器。
- 伸缩决策:HPA 控制器对比当前 Pod 的度量指标与目标利用率,若当前利用率高于目标,增加 Pod 副本数;若低于目标,则减少副本数(在最小和最大副本数之间调整)。
- Pod 伸缩:Kubernetes 根据 HPA 控制器的决策,自动调整 Pod 的副本数,实现自动扩缩容。
4:HPA 的应用场景
- 应对流量波动:在 web 服务中,流量可能随时间波动,HPA 可根据流量变化自动调整 Pod 副本数,保障服务的稳定性和响应速度。
- 资源优化:通过自动扩缩容,HPA 能在保证服务质量的同时,最大化资源利用率,降低运营成本。
5:HPA 实践 —— 实现 web 服务器的自动伸缩特性
具体步骤如下:
1:用 Deployment 启动 Nginx 服务(需配置 requests 参数),创建nginx-deployment.yaml
文件并执行kubectl create -f nginx-deployment.yaml
命令。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-server
labels:
name: nginx-server
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
resources:
requests:
cpu: 10m
image: nginx:1.7.9
ports:
- name: nginx
containerPort: 80
2:配置 nginx-server 的 Service,执行kubectl expose deployment nginx-server --port=80
命令。
3:使用kubectl autoscale
命令创建 HPA,如kubectl autoscale deployment nginx-server --cpu-percent=10 --min=1 --max=10
,该设置表示当 CPU 使用率超过 10% 时增加 Pod 数量,保持所有 Pod 的平均 CPU 利用率为 10%,允许的最大 Pod 数量为 10,最少为 1。
4:查看当前 HPA 状态,初始未发送请求时 CPU 使用率为 0%。
kubectl get hpa
5:查看 nginx 的 Service 地址,获取集群 IP。
kubectl get service -n default
6:进行压力测试,通过新终端的死循环模拟访问 Service 以增加 Pod 负载,如while true;do wget -q -O- http://10.107.217.171 >/dev/null;done
。
7:压力测试后查看 HPA 状态,可见 CPU 使用率升高,一段时间后 Pod 副本数增加到 10 个。
kubectl get hpa
8:停止压力测试,等待一段时间后,HPA 状态显示 CPU 使用率下降,Pod 副本数也会逐渐减少。
kubectl get pod
kubectl get hpa
kubectl get pod ##查看副本数
9:最后可执行相关命令清除资源。
kubectl delete -f nginx-deployment.yaml
kubectl delete -f mysql-deployment.yaml
kubectl delete -f redis-deployment.yaml
kubectl delete -f init01.yaml
kubectl delete -f init02.yaml