一、Kubernetes 的资源
1. Kubernetes 的资源是什么
Kubernetes(简称 K8s)把集群里所有的东西(计算、网络、存储等)都抽象成"资源"。你想让 K8s 干活,本质就是在创建、修改或删除这些资源。
- 集群系统:K8s 是一堆服务器连在一起组成的"团队"(集群)。你不用关心程序具体跑在哪台机器上,只管把服务部署到这个团队里。
- 部署服务就是跑容器:把你的程序打包塞进容器里,让 K8s 在集群中的某台机器上把这个容器运行起来。
- 最小管理单元是 Pod:K8s 不直接管理容器,它管理的最小单位叫 Pod。你得先把容器放进 Pod 这个"盒子"(或"豆荚")里,K8s 才认。一个 Pod 里通常可以放一个或多个关系紧密的容器。
- 通过 Pod 控制器管理 Pod:K8s 会派一个"Pod 控制器"来统一管理 Pod。比如你要求跑 3 个 Pod,控制器就保证永远有 3 个;哪个 Pod 挂了,控制器立马给你拉起一个新的。
- Service 实现服务访问 :Pod 随时可能被重建、IP 会变,K8s 提供 Service 这个组件,作为 Pod 的"固定前台/接线员",对外提供稳定访问入口,再把请求转发给后端 Pod。
- 存储系统实现数据持久化:Pod 里的程序产生的数据如果只存在 Pod 内,Pod 一重启就没了。K8s 提供各种存储系统(如挂载网络硬盘),把重要数据存到 Pod 之外的地方,实现持久化。
2. 资源的管理方式
K8s 操作资源有三种方式:
-
命令式对象管理:直接使用命令操作资源。
bashkubectl run nginx-pod --image=nginx:latest --port=80 -
命令式对象配置:通过命令配合配置文件操作资源。
bashkubectl create/patch -f nginx-pod.yaml -
声明式对象配置 :通过
apply命令配合配置文件操作资源。bashkubectl apply -f nginx-pod.yaml
2.1 命令式对象管理
kubectl 命令语法:
kubectl [command] [type] [name] [flags]
- command :指定要对资源执行的操作,例如
create、get、delete。 - type :指定资源类型,比如
deployment、pod、service。 - name:指定资源的名称,大小写敏感。
- flags:额外的可选参数。
2.2 资源类型
常见资源类型与命令参数详见 K8s 官方文档(kubectl api-resources 可列出当前集群支持的资源)。
二、什么是 Pod
Pod 是 K8s 最小的可部署计算单元,是一组一个或多个容器的集合,这些容器共享网络、存储和命名空间,总是被一起调度到同一台机器上。
核心概念类比:
- 容器:宿舍里的一个人,跑着一个具体的程序。
- Pod:一间宿舍,把关系紧密的几个容器塞进同一个房间。
- Node(节点):一栋楼,即宿舍所在的物理机/虚拟机。
- Pod 网络 :宿舍的门牌号+内线电话。Pod 有独立 IP,内部容器用
localhost就能互相通话。
2.1 创建自主式 Pod
优点
- 灵活性极高:可以像写代码一样精确控制 Pod 的每个细节(镜像版本、CPU 限制、环境变量、启动命令等),不需要复杂的控制器配置,适合特定调试需求。
- 学习与调试的"活教材":手动创建 Pod 是理解 K8s 底层概念(镜像拉取、容器运行时、网络配置)的最佳途径;排查故障时可快速起一个 Pod 验证网络连通性或环境配置,用完即删。
- 适用于特殊场景:如一次性任务(运行 Job 导出数据库备份)、快速验证(测试某个新镜像能否跑通)。
缺点
- 管理极其复杂(无法规模化):跑 100 个同样的 Nginx 得敲 100 遍命令;无法自动扩缩容,流量变化不会自动调整副本数。
- 缺乏自愈与高级功能:宿主机宕机或容器崩溃时,K8s 不会尝试重启或在其他机器重建,像断了线的风筝;也无法使用服务发现、滚动更新等核心功能。
- 可维护性极差:配置只存在脑子或历史命令里,时间久了或迁移集群时难以复现,因为没有留下"说明书"(YAML 文件)。
2.2 利用控制器管理 Pod
优点
- 自愈能力(高可用):容器崩溃、Pod 被删、节点宕机,控制器都会自动在其他可用节点重建 Pod,服务不中断。
- 弹性伸缩:流量高峰一键(或自动)扩容副本数,低谷时缩容节省资源。
- 滚动更新与版本回滚:更新镜像时逐个替换旧 Pod,用户无感知;新版本有 Bug 可一条命令回滚到上一稳定版本。
- 批量统一管理:一个控制器管几百上千个 Pod,统一调度策略和资源配置,增删改查都在控制器层面操作。
- 无缝对接 Service:控制器新建的 Pod 自动带指定标签,Service 靠标签自动发现新 Pod 并加入负载均衡,前端访问地址不变。
缺点(局限性)
- 学习成本较高:需要理解不同控制器类型(Deployment、StatefulSet、DaemonSet 等)及其 YAML 字段含义。
- 配置不当有风险:副本数过大可能耗尽集群资源;滚动更新策略配错可能导致服务短暂不可用;存储卷绑定错误可能丢数据。
- 资源开销微增:控制器本身占用少量 etcd 存储和 API Server 通信资源,但相比稳定性收益几乎可忽略。
- 杀鸡用牛刀:临时调试、跑一次性脚本验证时用控制器反而麻烦,直接起个自主式 Pod 更快。
2.3 利用 YAML 文件部署应用
命令行操作是"命令式"的,要一步步指挥 K8s;YAML 是"声明式"的,只需在文件里写清楚最终想要的状态。
YAML 文件部署的优点
可重复性与版本控制:
- 环境一致:测试、预发、生产环境用同一套 YAML,彻底解决"我这能跑、你那不能跑"的问题。
- 历史追溯:谁修改了配置、什么时候修改、修改了什么,全部有迹可循。
- 一键回滚:新版本出问题,直接回退到上一个正常的 YAML 版本,瞬间恢复应用。
无缝对接 CI/CD 与自动化工具:
- 自动化部署:在 CI/CD 流程(Jenkins、GitLab CI)中,代码提交后自动触发脚本读取 YAML 并推送应用到不同环境,实现无人值守持续交付。
- 工具链支持 :除
kubectl apply外,还可使用静态分析工具对 YAML 做语法检查、安全策略扫描(如检查是否暴露数据库密码),确保交付质量。
2.4 资源清单
Pod YAML 常见字段说明:
顶层字段
- apiVersion (String):K8s API 版本,目前基本是
v1,可用kubectl api-versions查询。 - kind (String):资源类型/角色,如
Pod。 - metadata (Object):元数据对象。
- metadata.name(String):资源名称,由用户编写(如 Pod 名字)。
- metadata.namespace(String):命名空间,由用户定义。
- spec(Object):详细定义对象。
容器相关(spec.containers\[\])
- spec.containers\[\](list):容器列表定义。
- spec.containers\[\].name(String):容器名称。
- spec.containers\[\].image(string):镜像名称。
- spec.containers\[\].imagePullPolicy (String):镜像拉取策略。
Always每次重新拉取;IfNotPresent本地有则用本地;Never仅用本地镜像。 - spec.containers\[\].command\[\](list):容器运行时启动命令,未指定则运行镜像打包时指定的命令。
- spec.containers\[\].args\[\](list):容器运行参数,可指定多个。
- spec.containers\[\].workingDir(String):容器工作目录。
- spec.containers\[\].volumeMounts\[\] (list):容器内部存储卷配置。
- spec.containers\[\].volumeMounts\[\].name(String):可被容器挂载的存储卷名称。
- spec.containers\[\].volumeMounts\[\].mountPath(String):存储卷挂载路径。
- spec.containers\[\].volumeMounts\[\].readOnly (String):读写模式,
true/false,默认读写。
- spec.containers\[\].ports\[\] (list):容器需要用到的端口列表。
- spec.containers\[\].ports\[\].name(String):端口名称。
- spec.containers\[\].ports\[\].containerPort(String):容器监听端口号。
- spec.containers\[\].ports\[\].hostPort(String):容器所在主机监听端口号,默认同 containerPort。注意:设置 hostPort 后同一台主机无法启动该容器的相同副本(主机端口冲突)。
- spec.containers\[\].ports\[\].protocol(String):端口协议,支持 TCP/UDP,默认 TCP。
- spec.containers\[\].env\[\] (list):容器运行前需设置的环境变量列表。
- spec.containers\[\].env\[\].name(String):环境变量名称。
- spec.containers\[\].env\[\].value(String):环境变量值。
- spec.containers\[\].resources (Object):资源限制与请求值(容器资源上限)。
- spec.containers\[\].resources.limits (Object):容器运行时资源上限。
- spec.containers\[\].resources.limits.cpu (String):CPU 限制,单位核心数,
1 = 1000m。 - spec.containers\[\].resources.limits.memory(String):内存限制,单位 MiB/GiB。
- spec.containers\[\].resources.limits.cpu (String):CPU 限制,单位核心数,
- spec.containers\[\].resources.requests (Object):容器启动和调度时的限制设置。
- spec.containers\[\].resources.requests.cpu(String):CPU 请求,单位 core,容器启动初始化可用数量。
- spec.containers\[\].resources.requests.memory(String):内存请求,单位 MiB/GiB,容器启动初始化可用数量。
- spec.containers\[\].resources.limits (Object):容器运行时资源上限。
Pod 级字段(spec)
- spec.restartPolicy (string):Pod 重启策略,默认
Always。Always:Pod 一旦终止,无论容器如何终止,kubelet 都重启它。OnFailure:只有 Pod 以非零退出码终止时 kubelet 才重启该容器;正常结束(退出码 0)不重启。Never:Pod 终止后 kubelet 将退出码报告给 Master,不重启。
- spec.nodeSelector (Object):Node 的 Label 过滤标签,以
key:value格式指定。 - spec.imagePullSecrets (Object):pull 镜像时使用的 secret 名称,以
name:secretkey格式指定。 - spec.hostNetwork (Boolean):是否使用主机网络模式,默认
false。设为true表示使用宿主机网络、不使用 docker 网桥,且同一台宿主机上无法启动第二个副本。
运行单个和多个容器
使用 YAML 模板创建单容器 Pod:
bash
[root@k8s-master pod]# kubectl run test --image myapp:v1 --dry-run=client -o yaml > pod.yml
[root@k8s-master pod]# vim pod.yml
yaml
apiVersion: v1 # API 版本号,v1 是 K8s 最核心的 API 组
kind: Pod # 资源类型:Pod(最小部署单元)
metadata: # 元数据,相当于 Pod 的"身份证信息"
labels: # 标签,便于分类、查找、被 Service 关联
run: test # 标签键值对:这个 Pod 用于跑测试任务
name: test # Pod 名字,同一命名空间内不能重名
spec: # 规格/蓝图,描述 Pod 最终长什么样
containers: # 容器列表(数组),Pod 里可跑一个或多个容器
- image: myapp:v1 # 容器镜像(myapp,版本 v1)
name: test # 容器名,Pod 内部必须唯一
bash
[root@k8s-master pod]# kubectl apply -f pod.yml
pod/test created
多容器 Pod(一个 Pod 跑两个容器):
bash
[root@k8s-master pod]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: test
name: test
spec:
containers:
- image: myapp:v1
name: test
- image: myapp:v2 # 添加的第二个镜像
name: test2 # 第二个容器的名字
bash
[root@k8s-master pod]# kubectl apply -f pod.yml
理解 Pod 间的网络整合
一个 Pod 内两个容器共享网络,可用 localhost 直接互通:
bash
[root@k8s-master ~]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: timinglee
name: test
spec:
containers:
- image: myapp:v1
name: myapp1
- image: busyboxplus:latest
name: busyboxplus
command: ["/bin/sh","-c","sleep 1000000"]
bash
[root@k8s-master ~]# kubectl apply -f pod.yml
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test 2/2 Running 0 8m1s
# 在 busyboxplus 容器里通过 localhost 访问同 Pod 的 myapp1 容器
[root@k8s-master pod]# kubectl exec test -c busyboxplus -- curl -s localhost
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
端口映射
bash
[root@k8s-master pod]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: test
name: test
spec:
containers:
- image: myapp:v1
name: test
ports:
- name: http
containerPort: 80 # 容器端口
hostPort: 80 # 主机端口
protocol: tcp # 协议
hostPort会把容器端口映射到宿主机网络,生产环境建议改用 Service 暴露服务。
如何设定环境变量
bash
[root@k8s-master ~]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: timinglee
name: test
spec:
containers:
- image: busybox:latest
name: busybox
command: ["/bin/sh","-c","echo $NAME;sleep 3000000"]
env:
- name: NAME
value: ttt
bash
[root@k8s-master pod]# kubectl apply -f pod.yml
pod/test created
[root@k8s-master pod]# kubectl logs pods/test busybox
ttt
资源限制
bash
[root@k8s-master pod]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: test
name: test
spec:
containers:
- image: myapp:v1
name: myapp
resources:
limits: # 最高资源限制
cpu: 500m
memory: 50M
requests: # 期望使用资源
cpu: 500m
memory: 50M
bash
[root@k8s-master pod]# kubectl describe pods test
Restart Count: 0
Limits:
cpu: 500m
memory: 50M
Requests:
cpu: 500m
memory: 50M
运行节点选择
查看各节点标签:
bash
[root@k8s-master pod]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready control-plane 5d7h v1.35.7 beta.kubernetes.io/arch=amd64,...,kubernetes.io/hostname=k8s-master,...
k8s-node1 Ready <none> 5d7h v1.35.7 ...,kubernetes.io/hostname=k8s-node1,...
k8s-node2 Ready <none> 5d7h v1.35.7 ...,kubernetes.io/hostname=k8s-node2,...
通过 nodeSelector 指定 Pod 调度到 k8s-node1:
bash
[root@k8s-master pod]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: test
name: test
spec:
nodeSelector:
kubernetes.io/hostname: k8s-node1
containers:
- image: myapp:v1
name: myapp
bash
[root@k8s-master pod]# kubectl apply -f pod.yml
pod/test created
[root@k8s-master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 1/1 Running 0 36s 10.244.2.4 k8s-node1 <none> <none>
共享宿主机网络
设置 hostNetwork: true 让 Pod 使用宿主机网络命名空间(此时 Pod 内的网络即宿主机网络):
bash
[root@k8s-master pod]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: test
name: test
spec:
hostNetwork: true # 使用宿主机网络,不使用 docker 网桥
containers:
- image: myapp:v1
name: myapp
bash
[root@k8s-master pod]# kubectl exec -it pods/test -c myapp -- /bin/sh
/ # ip a
# 此时看到的是宿主机的网络接口(eth0、docker0、flannel.1、cni0 等)
注意:开启
hostNetwork后,同一台宿主机上无法启动该 Pod 的第二个副本(端口冲突)。
三、Pod 的生命周期
Pod 启动时先按顺序执行 Init 容器 (完成初始化前置条件),Init 容器全部成功退出后,主容器才并行启动。主容器运行期间可通过 探针(Probe) 进行存活、就绪和启动检测。
Init 容器的 5 个特点:
- 自带"专业工具箱"(包含特定工具) :主容器追求精简常缺
curl等工具;Init 容器携带这些工具临时执行初始化任务,完成后即退出,避免工具残留主容器,保持镜像精简安全。 - 绝不"污染"主环境(安全性):Init 容器在独立环境执行下载证书等"脏活",与主容器隔离。主容器不装额外工具,保持镜像纯净安全。
- 各干各的,互不干扰(独立工作):开发只管把应用代码打包进镜像,运维用 Init 容器动态注入配置或证书,双方无需互相修改对方镜像。
- 拥有"特权视角"(文件系统隔离与 Secrets 权限):Init 容器有权访问 K8s Secrets 取出数据库密码等敏感信息,处理后传给主容器;主容器无直接访问权限,降低密码泄露风险。
- 严格的"前置检查"(阻塞启动机制):Init 容器持续检测依赖服务(如数据库)是否就绪,未就绪则一直等待,阻塞主容器启动,确保主应用运行时依赖一定可用,避免崩溃。
3.1 Init 容器示例
创建带 Init 容器的 Pod:Init 容器循环检测 /testfile 是否存在,不存在则持续等待;手动创建该文件后 Init 容器退出,主容器启动。
bash
[root@k8s-master pod]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
name: initpod
name: initpod
spec:
containers:
- image: myapp:v1
name: myapp
initContainers:
- name: init-myservice
image: busybox
command: ["sh","-c","until test -e /testfile;do echo wating for myservice; sleep 2;done"]
bash
[root@k8s-master ~]# kubectl apply -f pod.yml
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
initpod 0/1 Init:0/1 0 114s
# 查看 Init 容器日志,正在等待 /testfile
[root@k8s-master pod]# kubectl logs pods/initpod init-myservice
wating for myservice
wating for myservice
...
# 在 Init 容器内创建 /testfile,解除阻塞
[root@k8s-master pod]# kubectl exec pods/initpod -c init-myservice -- /bin/sh -c "touch /testfile"
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
initpod 1/1 Running 0 3m4s
3.2 探针
探针类型(诊断方式)
- ExecAction:在容器内执行自定义命令,退出码为 0 则判定健康,适合检查特定进程是否存在或执行自定义脚本。
- TCPSocketAction:尝试建立 TCP 连接,指定端口成功连通则判定健康,常用于数据库等需网络连通性的服务检查。
- HTTPGetAction:向容器发送 HTTP GET 请求,响应状态码在 200-400 之间则判定健康,专用于 Web 服务存活探测。
探针作用(故障处理)
- LivenessProbe(存活):检测容器是否存活。失败则杀掉容器并按重启策略重建;不配置则默认容器始终存活,可能导致死锁。
- ReadinessProbe(就绪):检测容器能否接收流量。失败则从 Service Endpoints 剔除,确保应用未就绪时不转发请求。
- StartupProbe(启动):检测应用是否启动完成。存在时暂时禁用其他探针直至成功,专用于慢启动应用,防止启动中被误判为宕机。
探针区别
- Readiness vs Liveness:Readiness 决定"是否接收流量"(影响路由),Liveness 决定"是否重启容器"(影响生命周期);前者防脏数据,后者保自愈。
- Startup vs 其他:Startup 专治"慢启动",容器刚启动时仅执行它,成功后才激活 Liveness/Readiness;其他探针是周期性持续进行的。
存活探针
对 myapp:v1 使用 TCP 存活探针检测 8080 端口(该镜像实际未开启 8080,因此探针失败,Pod 反复重启):
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
name: liveness
name: liveness
spec:
containers:
- image: myapp:v1
name: myapp
livenessProbe:
tcpSocket: # 检测端口
port: 8080
initialDelaySeconds: 3 # 容器启动后多少秒探针开始工作
periodSeconds: 1 # 探针探测周期
timeoutSeconds: 1 # 等待响应超时时间
bash
[root@k8s-master pod]# kubectl apply -f pod.yml
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness 0/1 CrashLoopBackOff 5 (58s ago) 2m50s
# 容器未启动,因为镜像未开启 8080 端口,探针持续失败
就绪探针
对 myapp:v1 使用 HTTP 就绪探针检测 /test.html(该文件默认不存在,Pod 处于未就绪状态;创建文件后变为 Ready):
bash
[root@k8s-master ~]# vim pod.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
name: readiness
name: readiness
spec:
containers:
- image: myapp:v1
name: myapp
readinessProbe:
httpGet:
path: /test.html
port: 80
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 1
bash
[root@k8s-master pod]# kubectl apply -f pod.yml
# 暴露 80 端口
[root@k8s-master ~]# kubectl expose pod readiness --port 80 --target-port 80
service/readiness exposed
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness 0/1 CrashLoopBackOff 7 (3m30s ago) 9m38s
readiness 0/1 Running 0 56s
# readiness 为 0/1:因为 /usr/share/nginx/html/test.html 不存在,未满足就绪条件
# 创建 test.html 文件
[root@k8s-master pod]# kubectl exec pods/readiness -c myapp -- /bin/sh -c "echo test > /usr/share/nginx/html/test.html"
# 再次查看,readiness 变为 1/1 Ready
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness 0/1 CrashLoopBackOff 9 (58s ago) 12m
readiness 1/1 Running 0 3m49s
# 查看 Service,Endpoints 已填入 Pod IP(满足条件后端口暴露)
[root@k8s-master pod]# kubectl describe svc readiness
Name: readiness
Namespace: default
Labels: name=readiness
Selector: name=readiness
Type: ClusterIP
IP: 10.104.48.94
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.2.7:80
Session Affinity: None
Events: <none>