实验简介
实验核心目标
- 掌握 Pod 两种核心创建方式(命令式、YAML 声明式)的操作流程与差异;
- 理解 K8s 核心资源类型(Node、Namespace)与 Pod 的关联关系;
- 熟练使用 kubectl 命令完成 Pod/Deployment 的增删改查、扩缩容、版本回滚等运维操作;
- 掌握 Pod 多容器部署、网络配置、节点调度等高级配置;
- 理解 Pod 生命周期管控机制(Init 容器、存活探针、就绪探针)的作用与配置方式。
实验主要内容模块
1. Pod 资源创建方式
- 命令式创建 :直接通过
kubectl run命令快速创建、查看、删除 Pod,熟悉 Pod 基础状态(Running/ErrImagePull 等)的识别; - YAML 声明式创建 :通过
kubectl create deployment --dry-run生成模板 YAML,对比kubectl create(仅创建)与kubectl apply(创建 / 更新)的差异,掌握 Deployment 控制器管理 Pod 的方式。
2. K8s 核心资源与 kubectl 命令实操
- 查看 Node 节点状态、生成节点加入命令,理解集群节点架构;
- 管理 Namespace:创建命名空间、在指定命名空间部署 Pod,实现资源隔离;
- 核心 kubectl 命令:
edit/patch修改资源、expose暴露服务、logs查看日志、exec/attach进入容器、scale扩缩容、label管理标签等。
3. Pod 高级配置与应用部署
- 单 / 多容器 Pod 部署:验证 Pod 内容器网络互通(共享网络命名空间);
- 网络配置:容器端口映射(containerPort/hostPort)、共享宿主机网络(hostNetwork);
- 节点调度:通过
nodeSelector指定 Pod 运行节点; - 版本管理:通过
kubectl set image更新容器镜像,rollout history/undo实现版本回滚。
4. Pod 生命周期管控
- Init 容器:配置初始化容器,验证 Init 容器完成后业务容器才启动的逻辑;
- 存活探针(LivenessProbe):监控容器健康状态,容器异常时自动重启;
- 就绪探针(ReadinessProbe):验证容器服务就绪状态,未就绪时剔除 Service 端点,避免流量分发至未就绪 Pod。
实验价值
通过本实验可掌握 K8s 中 Pod 从创建到销毁的全生命周期管理,理解声明式 API 的核心优势,掌握生产环境中 Pod 运维的核心命令与配置技巧,为后续 K8s 服务编排、高可用部署打下基础。
1. 基础 Pod 与 Deployment 操作 (建立、扩缩容、暴露服务)
# 工作区准备
[root@master ~]# mkdir -p ~/pod-labs && cd ~/pod-labs
# 命令式:直接运行并查看
[root@master pod-labs]# kubectl run webpod --image nginx:latest --port 80
pod/webpod created
[root@master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webpod 1/1 Running 0 6s 10.244.2.3 node2 <none> <none>
[root@master ~]# kubectl delete pod webpod
pod "webpod" deleted from default namespace
# 声明式:生成 Deployment 的 YAML 并应用
[root@master ~]# kubectl create deployment test --image nginx --replicas 1 --dry-run=client -o yaml > test.yml
[root@master ~]# kubectl apply -f test.yml
deployment.apps/test created
# 扩缩容测试
[root@master ~]# kubectl scale deployment test --replicas 3
deployment.apps/test scaled
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-56848fd9dc-bgx58 1/1 Running 0 4s
test-56848fd9dc-gcq8m 1/1 Running 0 18s
test-56848fd9dc-q9b4h 1/1 Running 0 4s
[root@master ~]# kubectl scale deployment test --replicas 1
deployment.apps/test scaled
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-56848fd9dc-bgx58 1/1 Running 0 12s
# 暴露服务并通过动态提取 ClusterIP 进行访问测试
[root@master ~]# kubectl expose deployment test --port 80 --target-port 80
service/test exposed
[root@master ~]# SVC_IP=$(kubectl get svc test -o jsonpath='{.spec.clusterIP}')
[root@master ~]# curl $SVC_IP
<!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>
# 清理当前实验环境
[root@master ~]# kubectl delete -f test.yml
deployment.apps "test" deleted from default namespace
[root@master ~]# kubectl delete svc test
service "test" deleted from default namespace
2. 命名空间与标签管理
# 创建并使用独立的 namespace
[root@master ~]# kubectl create namespace timinglee
namespace/timinglee created
[root@master ~]# kubectl -n timinglee run testpod --image nginx:latest
pod/testpod created
# 验证
[root@master ~]# kubectl -n timinglee get pods
NAME READY STATUS RESTARTS AGE
testpod 1/1 Running 0 11s
# 为 Pod 打标签与验证
[root@master ~]# kubectl label pods testpod -n timinglee name=lee
pod/testpod labeled
[root@master ~]# kubectl get pods -n timinglee --show-labels
NAME READY STATUS RESTARTS AGE LABELS
testpod 1/1 Running 0 26s name=lee,run=testpod
# 删除指定标签 (注意最后的减号)
[root@master ~]# kubectl label pods testpod -n timinglee name-
pod/testpod unlabeled
# 统一清理
[root@master ~]# kubectl delete namespace timinglee
namespace "timinglee" deleted
3. 应用版本滚动更新与回滚
# 部署 v2 版本并暴露服务
[root@master ~]# kubectl create deployment webcluster --image myapp:v2 --replicas 2
deployment.apps/webcluster created
[root@master ~]# kubectl expose deployment webcluster --port 80 --target-port 80
service/webcluster exposed
# 获取 IP 并验证当前版本
[root@master ~]# docker load -i myapp.tar.gz
d39d92664027: Loading layer 4.232MB/4.232MB
8460a579ab63: Loading layer 11.61MB/11.61MB
c1dc81a64903: Loading layer 3.584kB/3.584kB
68695a6cfd7d: Loading layer 4.608kB/4.608kB
05a9e65e2d53: Loading layer 16.38kB/16.38kB
a0d2c4392b06: Loading layer 7.68kB/7.68kB
Loaded image: timinglee/myapp:v1
Loaded image: timinglee/myapp:v2
[root@master ~]# docker tag timinglee/myapp:v1 reg.timinglee.org/library/myapp:v1
[root@master ~]# docker tag timinglee/myapp:v2 reg.timinglee.org/library/myapp:v2
[root@master ~]# docker push reg.timinglee.org/library/myapp:v1
The push refers to repository [reg.timinglee.org/library/myapp]
a0d2c4392b06: Pushed
05a9e65e2d53: Pushed
68695a6cfd7d: Pushed
c1dc81a64903: Pushed
8460a579ab63: Pushed
d39d92664027: Pushed
v1: digest: sha256:9eeca44ba2d410e54fccc54cbe9c021802aa8b9836a0bcf3d3229354e4c8870e size: 1569
[root@master ~]# docker push reg.timinglee.org/library/myapp:v2
The push refers to repository [reg.timinglee.org/library/myapp]
05a9e65e2d53: Layer already exists
68695a6cfd7d: Layer already exists
c1dc81a64903: Layer already exists
8460a579ab63: Layer already exists
d39d92664027: Layer already exists
v2: digest: sha256:5f4afc8302ade316fc47c99ee1d41f8ba94dbe7e3e7747dd87215a15429b9102 size: 1362
[root@master ~]# kubectl set image deployments webcluster myapp=reg.timinglee.org/library/myapp:v2
deployment.apps/webcluster image updated
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-6fbfd55778-qf96n 1/1 Running 0 4s
webcluster-6fbfd55778-v84t9 1/1 Running 0 6s
[root@master ~]# SVC_IP=$(kubectl get svc webcluster -o jsonpath='{.spec.clusterIP}')
[root@master ~]# curl $SVC_IP
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
# 更新版本至 v1
[root@master ~]# kubectl set image deployments webcluster myapp=myapp:v1
deployment.apps/webcluster image updated
[root@master ~]# kubectl rollout status deployment webcluster
deployment "webcluster" successfully rolled out
[root@master ~]# curl $SVC_IP
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
# 一键回滚至上一个版本 (v2)
[root@master ~]# kubectl rollout undo deployment webcluster --to-revision 1
deployment.apps/webcluster rolled back
[root@master ~]# kubectl rollout status deployment webcluster
deployment "webcluster" successfully rolled out
[root@master ~]# curl $SVC_IP
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
# 清理
[root@master ~]# kubectl delete deployment webcluster
deployment.apps "webcluster" deleted from default namespace
[root@master ~]# kubectl delete svc webcluster
service "webcluster" deleted from default namespace
4. Pod 进阶配置 (节点选择、端口映射、共享网络)
# 场景 A: 指定部署在 Node1 且映射宿主机端口
[root@master ~]# cat <<EOF > 5test.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: lee1
name: lee1
spec:
nodeSelector:
kubernetes.io/hostname: node1
containers:
- image: myapp:v1
name: myappv1
ports:
- name: webport
containerPort: 80
hostPort: 80
protocol: TCP
EOF
[root@master ~]# kubectl apply -f 5test.yml
pod/lee1 created
[root@master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
lee1 1/1 Running 0 3s 10.244.1.10 node1 <none> <none>
# 清理
[root@master ~]# kubectl delete -f 5test.yml
pod "lee1" deleted from default namespace
# 场景 B: 共享 Node1 宿主机网络空间
[root@master ~]# cat <<EOF > 6test.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: lee1
name: lee1
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/hostname: node1
containers:
- image: busybox:latest
name: busybox
command:
- /bin/sh
- -c
- sleep 1000
EOF
[root@master ~]# kubectl apply -f 6test.yml
pod/lee1 created
[root@master ~]# sleep 3
# 此时查看到的网卡信息为 node1 的真实物理网卡信息
[root@master ~]# kubectl exec -it lee1 -c busybox -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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 qlen 1000
link/ether 00:0c:29:f2:ef:6c brd ff:ff:ff:ff:ff:ff
inet 172.25.254.10/24 brd 172.25.254.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::fc4:f2e3:a2e:9385/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether e6:4b:a0:cb:46:96 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue
link/ether 06:8a:d6:36:45:2d brd ff:ff:ff:ff:ff:ff
inet 10.244.1.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::48a:d6ff:fe36:452d/64 scope link
valid_lft forever preferred_lft forever
5: cni0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue qlen 1000
link/ether 62:c8:67:ba:53:c4 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.1/24 brd 10.244.1.255 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::60c8:67ff:feba:53c4/64 scope link
valid_lft forever preferred_lft forever
# 清理
kubectl delete -f 6test.yml
5. 生命周期管理 (Init 容器与就绪探针)
# 场景 A: Init 容器控制主容器启动
[root@master ~]# cat <<EOF > init.yml
apiVersion: v1
kind: Pod
metadata:
name: lee-init
spec:
initContainers:
- name: init-myservice
image: busybox
command: ["sh","-c","until test -e /testfile;do echo wating; sleep 2;done"]
containers:
- image: myapp:v1
name: myappv1
EOF
[root@master ~]# kubectl apply -f init.yml
pod/lee-init created
# 此时 Pod 状态会卡在 Init:0/1
[root@master ~]# kubectl get pods lee-init
NAME READY STATUS RESTARTS AGE
lee-init 0/1 Init:0/1 0 13s
# 通过命令强行给 Init 容器传入启动信号 (/testfile)
[root@master ~]# kubectl exec -it lee-init -c init-myservice -- touch /testfile
[root@master ~]# sleep 3
# 此时主容器顺利启动,状态变为 Running
[root@master ~]# kubectl get pods lee-init
NAME READY STATUS RESTARTS AGE
lee-init 1/1 Running 0 36s
[root@master ~]# kubectl delete -f init.yml
pod "lee-init" deleted from default namespace
# 场景 B: 就绪探针 (Readiness Probe)
[root@master ~]# cat <<EOF > probe.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webcluster-probe
spec:
replicas: 1
selector:
matchLabels:
app: webcluster-probe
template:
metadata:
labels:
app: webcluster-probe
spec:
containers:
- image: myapp:v1
name: myapp
readinessProbe:
httpGet:
path: /test.html
port: 80
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 1
---
apiVersion: v1
kind: Service
metadata:
name: webcluster-probe
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: webcluster-probe
EOF
[root@master ~]# kubectl apply -f probe.yml
deployment.apps/webcluster-probe created
service/webcluster-probe created
[root@master ~]# sleep 3
# 因为没有 test.html,容器状态为 Running,但 READY 为 0/1 (流量不接入)
[root@master ~]# kubectl get pods -l app=webcluster-probe
NAME READY STATUS RESTARTS AGE
webcluster-probe-7ccc496879-vtc5k 0/1 Running 0 19s
# 动态获取 Pod 名称,并在容器内创建所需的探针验证文件
[root@master ~]# POD_NAME=$(kubectl get pods -l app=webcluster-probe -o jsonpath="{.items[0].metadata.name}")
[root@master ~]# kubectl exec -it $POD_NAME -c myapp -- sh -c "echo timinglee > /usr/share/nginx/html/test.html"
# 等待3秒探针重试后,READY 会变为 1/1
[root@master ~]# sleep 3
[root@master ~]# kubectl get pods -l app=webcluster-probe
NAME READY STATUS RESTARTS AGE
webcluster-probe-7ccc496879-vtc5k 1/1 Running 0 52s
# 测试销毁探针文件,READY 会重新掉回 0/1
[root@master ~]# kubectl exec -it $POD_NAME -c myapp -- rm -fr /usr/share/nginx/html/test.html
[root@master ~]# sleep 3
[root@master ~]# kubectl get pods -l app=webcluster-probe
NAME READY STATUS RESTARTS AGE
webcluster-probe-7ccc496879-vtc5k 0/1 Running 0 81s
# 最终清理
[root@master ~]# kubectl delete -f probe.yml
deployment.apps "webcluster-probe" deleted from default namespace
service "webcluster-probe" deleted from default namespace