前言
本文面向 Kubernetes 初学者,以两台 CentOS 7 虚拟机(Master 与 Node1)为基础环境,围绕 Pod 的创建、管理、生命周期与常见 kubectl 命令,提供一套可直接照做的完整实验文档。全文按「命令式对象管理」「kubectl 命令实操」「Pod 的生命周期」「按当前虚拟机地址执行的完整实验步骤」四章组织,每一条命令都给出具体执行节点、完整命令与预期输出,方便读者在真实环境中逐步复现。
阅读本文前,建议先掌握以下基础概念:
- Pod:Kubernetes 中最小的调度与运行单元,一个 Pod 内可包含一个或多个容器,共享网络命名空间与存储卷。
- 命名空间(Namespace) :用于在同一集群内对资源进行逻辑隔离,默认命名空间为
default。 - 控制器(Controller):如 Deployment、ReplicaSet,负责维护 Pod 的期望副本数并处理滚动更新与故障恢复。
- 探针(Probe):包括存活探针(livenessProbe)与就绪探针(readinessProbe),用于判断容器是否存活、是否具备对外提供服务的能力。
一、命令式对象管理
1. 命名空间管理
以下命令在 Master 节点(k8s-master,172.25.254.100)上执行。
查看命名空间
bash
[root@k8s-master ~]# kubectl get namespaces
NAME STATUS AGE
default Active 4h20m
kube-flannel Active 3h31m
kube-node-lease Active 4h20m
kube-public Active 4h20m
kube-system Active 4h20m
创建命名空间
bash
[root@k8s-master ~]# kubectl create namespace timinglee
namespace/timinglee created
[root@k8s-master ~]# kubectl get namespaces
NAME STATUS AGE
default Active 4h22m
kube-flannel Active 3h32m
kube-node-lease Active 4h22m
kube-public Active 4h22m
kube-system Active 4h22m
timinglee Active 5s
删除命名空间
bash
[root@k8s-master ~]# kubectl delete namespaces timinglee
namespace "timinglee" deleted
2. Pod 管理
查看 Pod 运行情况
bash
[root@k8s-master ~]# kubectl get pods -o wide
No resources found in default namespace.
创建 Pod
bash
[root@k8s-master ~]# kubectl run lee --image nginx:latest
pod/lee created
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
lee 1/1 Running 0 25s 10.244.1.10 k8s-node1 <none> <none>
当 Pod 创建出现问题(镜像拉取失败)
bash
[root@k8s-master ~]# kubectl run error --image lee:v1
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
error 0/1 ImagePullBackOff 0 38s 10.244.2.3 k8s-node2 <none> <none>
lee 1/1 Running 0 91s 10.244.1.10 k8s-node1 <none> <none>
查看 Pod 详细信息
bash
[root@k8s-master ~]# kubectl describe pods error
Name: error
Namespace: default
Priority: 0
Service Account: default
Node: k8s-node2/172.25.254.20
Start Time: Thu, 20 Aug 2026 15:08:30 +0800
Labels: run=error
Annotations: <none>
Status: Pending
IP: 10.244.2.3
IPs:
IP: 10.244.2.3
Containers:
error:
Container ID:
Image: lee:v1
Image ID:
Port: <none>
Host Port: <none>
State: Waiting
Reason: ImagePullBackOff
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-kfcjl (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-kfcjl:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 87s default-scheduler Successfully assigned default/error to k8s-node2
Warning Failed 28s (x2 over 65s) kubelet spec.containers{error}: Failed to pull image "lee:v1": Error response from daemon: failed to resolve reference "docker.io/library/lee:v1": docker.io/library/lee:v1: not found
Warning Failed 28s (x2 over 65s) kubelet spec.containers{error}: Error: ErrImagePull
Normal BackOff 16s (x2 over 64s) kubelet spec.containers{error}: Back-off pulling image "lee:v1"
Warning Failed 16s (x2 over 64s) kubelet spec.containers{error}: Error: ImagePullBackOff
Normal Pulling 2s (x3 over 86s) kubelet spec.containers{error}: Pulling image "lee:v1"
删除 Pod
bash
[root@k8s-master ~]# kubectl delete pods error
[root@k8s-master ~]# kubectl delete pods --all
pod "error" deleted from default namespace
pod "lee" deleted from default namespace
二、kubectl 命令实操
本章通过实际操作演示 kubectl 的常用命令。实验前请先完成第四章的环境准备,并确保 myapp:v1、myapp:v2 镜像已上传到私有仓库 reg.timinglee.org/library。以下命令默认在 Master 节点(k8s-master)上执行。
1. 上传实验镜像到仓库 library 中
bash
[root@k8s-master ~]# docker load -i myapp.tar.gz
[root@k8s-master ~]# docker tag timinglee/myapp:v1 reg.timinglee.org/library/myapp:v1
[root@k8s-master ~]# docker push reg.timinglee.org/library/myapp:v1
[root@k8s-master ~]# docker tag timinglee/myapp:v2 reg.timinglee.org/library/myapp:v2
[root@k8s-master ~]# docker push reg.timinglee.org/library/myapp:v2
2. 生成实验所需 yml 文件
bash
[root@k8s-master ~]# vim replica.yml
yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
labels:
app: replica # 设定控制器标签
name: replica
spec:
replicas: 2 # 启动 pod 数量
selector:
matchLabels:
app: replica # 控制器标签选择器
template:
metadata:
labels:
app: replica # 开启 pod 的属性模板
spec:
containers:
- image: myapp:v1
name: myapp
3. kubectl 命令使用方法
create
bash
[root@k8s-master ~]# kubectl create deployment webcluster --replicas 2 --image myapp:v1
deployment.apps/webcluster created
[root@k8s-master ~]# kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
webcluster 2/2 2 2 15s
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-77c87d9946-28thm 1/1 Running 0 24s
webcluster-77c87d9946-vwrsq 1/1 Running 0 24s
[root@k8s-master ~]# kubectl delete deployments.apps webcluster
deployment.apps "webcluster" deleted from default namespace
[root@k8s-master ~]# kubectl get pods
No resources found in default namespace.
edit
bash
[root@k8s-master ~]# kubectl create deployment webcluster --image myapp:v1
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-77c87d9946-2cgr7 1/1 Running 0 36s
[root@k8s-master ~]# kubectl edit deployments.apps webcluster
replicas: 2
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-77c87d9946-2cgr7 1/1 Running 0 36s
webcluster-77c87d9946-2wqn7 1/1 Running 0 103s
patch
bash
# 创建 deployment
[root@k8s-master ~]# kubectl create deployment webcluster --image myapp:v1 --replicas 2
deployment.apps/webcluster created
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-77c87d9946-5vsrl 1/1 Running 0 5s
webcluster-77c87d9946-r7q8c 1/1 Running 0 5s
# 使用 patch 动态修改副本数为 1
[root@k8s-master ~]# kubectl patch deployments.apps webcluster -p '{"spec":{"replicas":1}}'
deployment.apps/webcluster patched
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-77c87d9946-r7q8c 1/1 Running 0 21s
expose
bash
# 扩容到 4 个副本用于演示负载均衡
[root@k8s-master ~]# kubectl scale deployment webcluster --replicas 4
deployment.apps/webcluster scaled
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-77c87d9946-8wb5c 1/1 Running 0 10s
webcluster-77c87d9946-kp265 1/1 Running 0 10s
webcluster-77c87d9946-m49sc 1/1 Running 0 10s
webcluster-77c87d9946-r7q8c 1/1 Running 0 42s
# 暴露 Service
[root@k8s-master ~]# kubectl expose deployment webcluster --port 80 --target-port 80
service/webcluster exposed
[root@k8s-master ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d1h
webcluster ClusterIP 10.111.5.166 <none> 80/TCP 0s
# 查看 Service 详情
[root@k8s-master ~]# kubectl describe svc webcluster
Name: webcluster
Namespace: default
Labels: app=webcluster
Annotations: <none>
Selector: app=webcluster
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.111.5.166
IPs: 10.111.5.166
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.6:80,10.244.2.13:80,10.244.2.14:80 + 1 more...
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
# 测试负载均衡(多次访问会轮询到不同的 Pod)
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-kp265
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-r7q8c
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-8wb5c
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-8wb5c
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-m49sc
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-r7q8c
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-r7q8c
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-8wb5c
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-r7q8c
[root@k8s-master ~]# curl 10.111.5.166/hostname.html
webcluster-77c87d9946-kp265
logs
bash
[root@k8s-master ~]# kubectl logs pods/webcluster-77c87d9946-gh9v7
10.244.0.0 - - [20/Aug/2026:08:43:52 +0000] "GET /hostname.html HTTP/1.1" 200 28 "-" "curl/7.76.1" "-"
10.244.0.0 - - [20/Aug/2026:08:43:52 +0000] "GET /hostname.html HTTP/1.1" 200 28 "-" "curl/7.76.1" "-"
attach
bash
[root@k8s-master ~]# docker load -i busybox-latest.tar.gz
Loaded image: busybox:latest
[root@k8s-master ~]# docker tag busybox:latest reg.timinglee.org/library/busybox:latest
[root@k8s-master ~]# kubectl run -it testpod --image busybox:latest
All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.
If you don't see a command prompt, try pressing enter.
/ #
/ #
/ # <ctrl+pq>
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testpod 1/1 Running 0 55s
[root@k8s-master ~]# kubectl attach pods/testpod -it
All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.
If you don't see a command prompt, try pressing enter.
/ #
/ #
/ #
[root@k8s-master ~]# kubectl attach pods/testpod -it
All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.
If you don't see a command prompt, try pressing enter.
/ #
exec
bash
[root@k8s-master ~]# kubectl run testpod --image nginx:latest
pod/testpod created
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testpod 1/1 Running 0 4s
[root@k8s-master ~]# kubectl exec -it pods/testpod -c testpod -- /bin/bash
root@testpod:/#
cp
bash
[root@k8s-master ~]# kubectl cp testpod:/usr/share/nginx/html/index.html /mnt/test
tar: Removing leading `/' from member names
[root@k8s-master ~]# kubectl cp testpod:/usr/share/nginx/html /mnt/
tar: Removing leading `/' from member names
[root@k8s-master ~]# ls /mnt/
50x.html docker.service file1 hgfs index.html test
[root@k8s-master ~]# echo timinglee > /mnt/index.html
[root@k8s-master ~]# kubectl cp /mnt/index.html testpod:/usr/share/nginx/html/index.html
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 6m3s 10.244.1.12 k8s-node1 <none> <none>
[root@k8s-master ~]# curl 10.244.1.12
timinglee
rollout
bash
[root@k8s-master pod]# kubectl create deployment webcluster --image myapp:v1 --replicas 2 --dry-run=client -o yaml > webcluster.yml
[root@k8s-master pod]# vim webcluster.yml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: webcluster
name: webcluster
spec:
replicas: 2
selector:
matchLabels:
app: webcluster
template:
metadata:
labels:
app: webcluster
spec:
containers:
- image: myapp:v1
name: myapp
bash
[root@k8s-master pod]# kubectl apply -f webcluster.yml
deployment.apps/webcluster created
[root@k8s-master pod]# kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
webcluster 2/2 2 2 7s
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-77c87d9946-5qbxt 1/1 Running 0 16s
webcluster-77c87d9946-95b2g 1/1 Running 0 16s
[root@k8s-master pod]# kubectl rollout status deployment webcluster
deployment "webcluster" successfully rolled out
deployment.apps/webcluster resumed
[root@k8s-master pod]# kubectl rollout restart deployment webcluster
deployment.apps/webcluster restarted
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-7bfd865747-jmhwl 1/1 Running 0 6s
webcluster-7bfd865747-qv2xt 1/1 Running 0 8s
[root@k8s-master pod]# kubectl rollout restart deployment webcluster
deployment.apps/webcluster restarted
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-7bfd865747-jmhwl 1/1 Running 0 19s
webcluster-7bfd865747-qv2xt 0/1 Completed 0 21s
webcluster-9787d97f6-z7xv5 1/1 Running 0 1s
webcluster-9787d97f6-zl6q2 0/1 ContainerCreating 0 0s
scale
bash
[root@k8s-master pod]# kubectl scale deployment webcluster --replicas 4
deployment.apps/webcluster scaled
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-9787d97f6-bh796 1/1 Running 0 2s
webcluster-9787d97f6-bh8jd 1/1 Running 0 2s
webcluster-9787d97f6-z7xv5 1/1 Running 0 89s
webcluster-9787d97f6-zl6q2 1/1 Running 0 88s
[root@k8s-master pod]# kubectl scale deployment webcluster --replicas 1
deployment.apps/webcluster scaled
[root@k8s-master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
webcluster-9787d97f6-zl6q2 1/1 Running 0 93s
label
bash
[root@k8s-master pod]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
webcluster-9787d97f6-zl6q2 1/1 Running 0 6m57s app=webcluster,p
三、Pod 的生命周期
Pod 从创建到销毁会经历多个阶段,本章通过 init 容器、存活探针与就绪探针三个实验,演示如何控制 Pod 的启动顺序、健康检查与流量接入。实验均在 Master 节点(k8s-master)的 pod 目录下进行。
1. init 容器
bash
[root@k8s-master pod]# kubectl run webserver --image myapp:v1 --dry-run=client -o yaml > init-example.yml
[root@k8s-master pod]# vim init-example.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: webserver
name: webserver
spec:
initContainers:
- name: busybox
image: busybox:latest
command:
- /bin/sh
- -c
- "until test -e /testfile;do echo wating for myservice; sleep 2;done"
containers:
- image: myapp:v1
name: webserver
restartPolicy: Always
bash
[root@k8s-master pod]# watch -n 1 kubectl get pods -o wide
[root@k8s-master pod]# kubectl apply -f init-example.yml
pod/webserver created
[root@k8s-master pod]# kubectl exec -it pods/webserver -c busybox -- /bin/sh
/ #
/ # touch /testfile
2. 存活探针 liveness
没有存活探针时
bash
[root@k8s-master pod]# kubectl run testpod --image myapp:v1 --dry-run=client -o yaml > livness-example.yaml
[root@k8s-master pod]# vim livness-example.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: webserver
name: webserver
spec:
containers:
- image: myapp:v1
name: webserver
command: ["/bin/sh", "-c"]
args:
- |
nginx -g "daemon off;"
sleep 10000
restartPolicy: Always
bash
[root@k8s-master pod]# kubectl apply -f livness-example.yaml
# 监控程序
[root@k8s-master pod]# watch -n 1 kubectl get pods -o wide
# 测试操作:停止 nginx
[root@k8s-master pod]# kubectl exec -it pods/webserver -c webserver -- /bin/sh
# nginx -s stop
# 查看 pod 的状态仍然是 Running,但是访问此 pod 时访问失败
[root@k8s-node2 ~]# curl 10.244.5.47
curl: (7) Failed to connect to 10.244.5.47 port 80: 拒绝连接
配置存活探针后
bash
[root@k8s-master pod]# kubectl delete -f livness-example.yaml --force
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: webserver
name: webserver
spec:
containers:
- image: myapp:v1
name: testpod
command: ["/bin/sh", "-c"]
args:
- |
nginx -g "daemon off;"
sleep 10000
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 3
periodSeconds: 1
timeoutSeconds: 1
restartPolicy: Always
bash
[root@k8s-master pod]# kubectl get pods -o wide -w
[root@k8s-master pod]# kubectl exec -it pods/webserver -c webserver -- /bin/sh
/ # nginx -s stop
2026/08/23 03:59:45 [notice] 15#15: signal process started
/ # exit
[root@k8s-master pod]# curl 10.244.5.66
curl: (7) Failed to connect to 10.244.5.66 port 80: 拒绝连接
[root@k8s-master pod]# curl 10.244.5.66
curl: (7) Failed to connect to 10.244.5.66 port 80: 拒绝连接
[root@k8s-master pod]# curl 10.244.5.66
curl: (7) Failed to connect to 10.244.5.66 port 80: 拒绝连接
[root@k8s-master pod]# curl 10.244.5.66
curl: (7) Failed to connect to 10.244.5.66 port 80: 拒绝连接
[root@k8s-master pod]# curl 10.244.5.66
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
3. 就绪探针 readiness
没有就绪探针的情况
bash
[root@k8s-master pod]# vim readness-example.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: webserver
name: webserver
spec:
containers:
- image: myapp:v1
name: webserver
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
labels:
run: webserver
name: webserver
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: webserver
bash
[root@k8s-master pod]# kubectl apply -f readness-example.yml
pod/webserver unchanged
service/webserver created
[root@k8s-master pod]# kubectl describe svc webserver
Name: webserver
Namespace: default
Labels: run=webserver
Annotations: <none>
Selector: run=webserver
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.102.162.217
IPs: 10.102.162.217
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.5.67:80
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
# 删除默认发布文件
[root@k8s-master ~]# kubectl exec -it pods/webserver -c webserver -- /bin/sh
/ # cd /usr/share/nginx/
/usr/share/nginx # ls
html
/usr/share/nginx # cd html/
/usr/share/nginx/html # ls
50x.html index.html
/usr/share/nginx/html # rm -fr index.html
/usr/share/nginx/html # ls
50x.html
/usr/share/nginx/html #
# 验证是否在访问 service 时 endpoints 中被下架
[root@k8s-master pod]# kubectl describe svc webserver
Name: webserver
Namespace: default
Labels: run=webserver
Annotations: <none>
Selector: run=webserver
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.102.162.217
IPs: 10.102.162.217
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.5.67:80 # 还在
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
# 业务问题
[root@k8s-master pod]# curl 10.102.162.217
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.12.2</center>
</body>
</html>
有就绪探针的情况
bash
[root@k8s-master pod]# vim readness-example.yml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: webserver
name: webserver
spec:
containers:
- image: myapp:v1
name: webserver
readinessProbe:
httpGet:
path: /index.html
port: 80
initialDelaySeconds: 3
periodSeconds: 2
timeoutSeconds: 1
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
labels:
run: webserver
name: webserver
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: webserver
bash
[root@k8s-master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webserver 1/1 Running 0 7s 10.244.5.69 k8s-node2 <none> <none>
[root@k8s-master pod]# kubectl describe svc webserver
Name: webserver
Namespace: default
Labels: run=webserver
Annotations: <none>
Selector: run=webserver
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.110.248.237
IPs: 10.110.248.237
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.5.69:80
Session Affinity: None
Internal Traffic Policy: Cluster
Events: <none>
# 复现问题
/usr/share/nginx/html # command terminated with exit code 137
[root@k8s-master ~]#
/ # rm -fr /usr/share/nginx/html/index.html
/ # echo timinglee > /usr/share/nginx/html/index.html
/ #
四、按当前虚拟机地址执行的完整实验步骤
本章按当前实验环境(Master:172.25.254.100,Node1:172.25.254.10)给出从零开始搭建 Kubernetes 集群并完成 Pod 管理实验的完整步骤。请严格按照 4.1 至 4.6 的顺序执行,每完成一小节即可通过验证命令确认结果。
本节按当前环境修正:
- Kubernetes Master:
172.25.254.100,主机名k8s-master - Kubernetes Node:
172.25.254.10,主机名k8s-node1 - Pod 网络:假设使用 Flannel,CIDR 为
10.244.0.0/16 - 以下命令中的
[master]表示在 Master 执行,[node1]表示在 Node1 执行
4.1 两台虚拟机配置主机名和 hosts
master 执行
bash
hostnamectl set-hostname k8s-master
node1 执行
bash
hostnamectl set-hostname k8s-node1
master 和 node1 都执行
bash
cat >> /etc/hosts <<EOF
172.25.254.100 k8s-master
172.25.254.10 k8s-node1
EOF
验证主机名和 hosts 配置
bash
[root@k8s-master ~]# hostname
k8s-master
[root@k8s-master ~]# ping -c 2 k8s-node1
PING k8s-node1 (172.25.254.10) 56(84) bytes of data.
64 bytes from k8s-node1 (172.25.254.10): icmp_seq=1 ttl=64 time=0.3 ms
64 bytes from k8s-node1 (172.25.254.10): icmp_seq=2 ttl=64 time=0.3 ms
4.2 初始化集群(Master 节点执行)
master 执行 kubeadm init
bash
[root@k8s-master ~]# kubeadm init \
--apiserver-advertise-address=172.25.254.100 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.28.2 \
--pod-network-cidr=10.244.0.0/16
配置 kubectl 访问集群
bash
[root@k8s-master ~]# mkdir -p $HOME/.kube
[root@k8s-master ~]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@k8s-master ~]# chown $(id -u):$(id -g) $HOME/.kube/config
[root@k8s-master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master NotReady control-plane 2m v1.28.2
4.3 安装 Flannel 网络插件(Master 节点执行)
bash
[root@k8s-master ~]# kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
[root@k8s-master ~]# kubectl get pods -n kube-flannel -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kube-flannel-ds-abc12 1/1 Running 0 30s 172.25.254.100 k8s-master
4.4 加入 Node 节点(node1 执行)
node1 执行 kubeadm join
bash
[root@k8s-node1 ~]# kubeadm join 172.25.254.100:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
master 验证节点状态
bash
[root@k8s-master ~]# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE
k8s-master Ready control-plane 5m v1.28.2 172.25.254.100 <none> CentOS Linux 7
k8s-node1 Ready <none> 1m v1.28.2 172.25.254.10 <none> CentOS Linux 7
4.5 上传实验镜像到私有仓库
bash
[root@k8s-master ~]# docker load -i myapp.tar.gz
[root@k8s-master ~]# docker tag timinglee/myapp:v1 reg.timinglee.org/library/myapp:v1
[root@k8s-master ~]# docker push reg.timinglee.org/library/myapp:v1
[root@k8s-master ~]# docker tag timinglee/myapp:v2 reg.timinglee.org/library/myapp:v2
[root@k8s-master ~]# docker push reg.timinglee.org/library/myapp:v2
**说明:**若私有仓库未配置 HTTPS 证书,需先在每台节点上配置 Docker 的 insecure-registries 信任,否则后续创建 Pod 拉取镜像时会报 ErrImagePull 错误。
4.6 按章节顺序执行 Pod 管理实验
**说明:**执行实验前,请确认 myapp:v1 和 myapp:v2 镜像已成功上传到私有仓库,且各节点已配置好镜像仓库信任;否则后续章节中的 kubectl run、create deployment 等命令会因镜像拉取失败而无法正常演示。
完成上述环境准备后,即可按本文「一、命令式对象管理」「二、kubectl 命令实操」「三、Pod 的生命周期」三章中的命令和步骤,在 Master 节点(k8s-master)上依次执行全部实验,验证命名空间、Pod 创建删除、kubectl 常用命令以及 init 容器、存活探针、就绪探针等生命周期特性。
建议按以下顺序依次完成实验:
- 在「一、命令式对象管理」中,先练习命名空间的查看、创建与删除,再通过
kubectl run创建 Pod,并观察镜像拉取失败时的排错流程。 - 在「二、kubectl 命令实操」中,依次练习 create、edit、patch、expose、logs、attach、exec、cp、rollout、scale、label 等命令,重点理解 Deployment 的滚动更新与副本扩缩容。
- 在「三、Pod 的生命周期」中,分别验证 init 容器、存活探针与就绪探针的行为差异,理解探针对 Pod 状态和 Service 后端摘除的影响。
全部实验完成后,建议清理实验资源,避免占用集群资源:
bash
[root@k8s-master pod]# kubectl delete deployment webcluster
[root@k8s-master pod]# kubectl delete svc webserver
[root@k8s-master pod]# kubectl delete pods --all