目录
- 一、命令式对象管理
- 1.1 命名空间管理
- 1.2 Pod 的创建与查看
- 1.3 Pod 故障排查
- 1.4 Pod 的删除
- 二、kubectl 命令实操
- 2.1 实验镜像准备
- 2.2 用 dry-run 生成 yaml 文件
- 2.3 常用管理命令
- 2.4 滚动更新命令
- 2.5 弹性伸缩命令
- 三、利用 yaml 文件声明资源
- 3.1 单 Pod 运行多容器
- 3.2 暴露端口
- 3.3 指定环境变量
- 3.4 选择运行节点
- 3.5 共享宿主机网络
- 3.6 资源优先级 QoS
- 3.7 容器重启规则
- 四、Pod 的生命周期
- 4.1 init 容器
- 4.2 存活探针 livenessProbe
- 4.3 就绪探针 readinessProbe
一、命令式对象管理
命令式对象管理是指直接通过 kubectl 命令操作集群中的资源对象,不依赖 yaml 文件。
命令分类
| 查看 | kubectl get <资源> | 查看资源列表,-o wide 显示 IP 和所在节点 |
| 创建 | kubectl run/create <资源> | 命令式创建 Pod、Deployment 等 |
| 描述 | kubectl describe <资源> | 查看资源详细信息,包括事件 |
| 删除 | kubectl delete <资源> | 删除资源 |
1.1 命名空间管理
命名空间(Namespace)用来对集群资源做逻辑隔离。集群初始化后会自带 5 个命名空间:
[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
| default | 默认命名空间,用户资源默认放这里 |
| kube-system | 集群核心组件(apiserver、controller-manager 等) |
| kube-public | 公共资源,所有用户可读 |
| kube-node-lease | 节点心跳租约 |
| kube-flannel | 网络插件 flannel 的资源 |
创建命名空间后,kubectl get namespaces 中会出现新项:
[root@k8s-master ~]# kubectl create namespace timinglee
[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
删除命名空间:
[root@k8s-master ~]# kubectl delete namespaces timinglee
namespace "timinglee" deleted
删除命名空间会连同其中的所有资源一起删除,操作前要确认。
1.2 Pod 的创建与查看
Pod 是 Kubernetes 的最小调度单位。用 kubectl run 直接创建:
# 查看当前命名空间中的 pod
[root@k8s-master ~]# kubectl get pods -o wide
No resources found in default namespace.
# 创建 pod
[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>
-o wide 会额外显示 pod 的 IP 和所在节点。本例中 pod 被调度到 k8s-node1,分配的集群 IP 是 10.244.1.10。
1.3 Pod 故障排查
镜像不存在时,pod 会停在 ImagePullBackOff 状态:
[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>
用 kubectl describe 查看详细信息定位原因:
[root@k8s-master ~]# kubectl describe pods error
Name: error
Namespace: default
Node: k8s-node2/172.25.254.20
Status: Pending
IP: 10.244.2.3
Containers:
error:
Image: lee:v1
State: Waiting
Reason: ImagePullBackOff
Ready: False
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready False
ContainersReady False
PodScheduled True
QoS Class: BestEffort
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 Failed to pull image "lee:v1": ... not found
Warning Failed 28s (x2 over 65s) kubelet Error: ErrImagePull
Normal BackOff 16s (x2 over 64s) kubelet Back-off pulling image "lee:v1"
Warning Failed 16s (x2 over 64s) kubelet Error: ImagePullBackOff
Normal Pulling 2s (x3 over 86s) kubelet Pulling image "lee:v1"
Pod 从创建到运行,以及拉镜像失败的路径如下:
#mermaid-svg-idCeFR1RsOJl3kCx{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-idCeFR1RsOJl3kCx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-idCeFR1RsOJl3kCx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-idCeFR1RsOJl3kCx .error-icon{fill:#552222;}#mermaid-svg-idCeFR1RsOJl3kCx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-idCeFR1RsOJl3kCx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-idCeFR1RsOJl3kCx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-idCeFR1RsOJl3kCx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-idCeFR1RsOJl3kCx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-idCeFR1RsOJl3kCx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-idCeFR1RsOJl3kCx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-idCeFR1RsOJl3kCx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-idCeFR1RsOJl3kCx .marker.cross{stroke:#333333;}#mermaid-svg-idCeFR1RsOJl3kCx svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-idCeFR1RsOJl3kCx p{margin:0;}#mermaid-svg-idCeFR1RsOJl3kCx .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-idCeFR1RsOJl3kCx .cluster-label text{fill:#333;}#mermaid-svg-idCeFR1RsOJl3kCx .cluster-label span{color:#333;}#mermaid-svg-idCeFR1RsOJl3kCx .cluster-label span p{background-color:transparent;}#mermaid-svg-idCeFR1RsOJl3kCx .label text,#mermaid-svg-idCeFR1RsOJl3kCx span{fill:#333;color:#333;}#mermaid-svg-idCeFR1RsOJl3kCx .node rect,#mermaid-svg-idCeFR1RsOJl3kCx .node circle,#mermaid-svg-idCeFR1RsOJl3kCx .node ellipse,#mermaid-svg-idCeFR1RsOJl3kCx .node polygon,#mermaid-svg-idCeFR1RsOJl3kCx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-idCeFR1RsOJl3kCx .rough-node .label text,#mermaid-svg-idCeFR1RsOJl3kCx .node .label text,#mermaid-svg-idCeFR1RsOJl3kCx .image-shape .label,#mermaid-svg-idCeFR1RsOJl3kCx .icon-shape .label{text-anchor:middle;}#mermaid-svg-idCeFR1RsOJl3kCx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-idCeFR1RsOJl3kCx .rough-node .label,#mermaid-svg-idCeFR1RsOJl3kCx .node .label,#mermaid-svg-idCeFR1RsOJl3kCx .image-shape .label,#mermaid-svg-idCeFR1RsOJl3kCx .icon-shape .label{text-align:center;}#mermaid-svg-idCeFR1RsOJl3kCx .node.clickable{cursor:pointer;}#mermaid-svg-idCeFR1RsOJl3kCx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-idCeFR1RsOJl3kCx .arrowheadPath{fill:#333333;}#mermaid-svg-idCeFR1RsOJl3kCx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-idCeFR1RsOJl3kCx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-idCeFR1RsOJl3kCx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-idCeFR1RsOJl3kCx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-idCeFR1RsOJl3kCx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-idCeFR1RsOJl3kCx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-idCeFR1RsOJl3kCx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-idCeFR1RsOJl3kCx .cluster text{fill:#333;}#mermaid-svg-idCeFR1RsOJl3kCx .cluster span{color:#333;}#mermaid-svg-idCeFR1RsOJl3kCx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-idCeFR1RsOJl3kCx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-idCeFR1RsOJl3kCx rect.text{fill:none;stroke-width:0;}#mermaid-svg-idCeFR1RsOJl3kCx .icon-shape,#mermaid-svg-idCeFR1RsOJl3kCx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-idCeFR1RsOJl3kCx .icon-shape p,#mermaid-svg-idCeFR1RsOJl3kCx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-idCeFR1RsOJl3kCx .icon-shape .label rect,#mermaid-svg-idCeFR1RsOJl3kCx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-idCeFR1RsOJl3kCx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-idCeFR1RsOJl3kCx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-idCeFR1RsOJl3kCx :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
kubectl run lee–image nginx:latest
Scheduler 调度Scheduled → k8s-node1
kubelet 拉取镜像Pulling → 成功
容器启动Running 1/1
kubectl run error–image lee:v1
Scheduler 调度Scheduled → k8s-node2
镜像不存在ErrImagePull
ImagePullBackOff状态 Waiting, Ready False
describe 中的 Events 是排查问题的关键:Scheduled 表示调度成功,ErrImagePull / ImagePullBackOff 说明镜像拉取失败,通常是镜像名写错或镜像不存在。
1.4 Pod 的删除
# 删除单个 pod
[root@k8s-master ~]# kubectl delete pods error
# 删除当前命名空间下所有 pod
[root@k8s-master ~]# kubectl delete pods –all
pod "error" deleted from default namespace
pod "lee" deleted from default namespace
二、kubectl 命令实操
2.1 实验镜像准备
加载到本地并推送进 Harbor 仓库的 library 项目:
[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.2 用 dry-run 生成 yaml 文件
–dry-run=client -o yaml 可以在不真正创建资源的情况下,把资源的 yaml 定义输出到文件,再按需修改后 apply:
[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
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
[root@k8s-master pod]# kubectl apply -f webcluster.yml
deployment.apps/webcluster created
yaml 中四个字段的含义:
| metadata.labels | 资源的标签 |
| spec.replicas | 期望的 pod 数量 |
| spec.selector.matchLabels | 控制器通过该标签选择它管理的 pod |
| spec.template | 创建 pod 的属性模板 |
2.3 常用管理命令
create:创建资源
[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.
由 Deployment 创建的 pod 命名带 pod-template-hash 后缀(如 77c87d9946),同一个 Deployment 下的 pod 使用相同的模板哈希。
edit:编辑资源
[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:打补丁修改单个字段
[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-2wqn7 1/1 Running 0 6m20s
expose:暴露服务
[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 6h
webcluster ClusterIP 10.97.61.108 <none> 80/TCP 18s
[root@k8s-master ~]# kubectl describe svc webcluster
Name: webcluster
Selector: app=webcluster
Type: ClusterIP
IP: 10.97.61.108
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.11:80,10.244.5.10:80,10.244.5.9:80 + 1 more...
Service 通过 Selector(标签选择器)找到后端的 pod,访问 ClusterIP 时会轮询到不同的 pod:
[root@k8s-master ~]# curl 10.97.61.108/hostname.html
webcluster-77c87d9946-gh9v7
[root@k8s-master ~]# curl 10.97.61.108/hostname.html
webcluster-77c87d9946-m69wl
[root@k8s-master ~]# curl 10.97.61.108/hostname.html
webcluster-77c87d9946-4p4mz
多次 curl 返回不同的 pod 名,说明流量被分发到多个副本上。
logs:查看日志
[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" "-"
attach:挂接容器
# 交互式创建 pod
[root@k8s-master ~]# kubectl run -it testpod –image busybox:latest
/ # # ctrl+pq 退出
# 重新挂接到容器终端
[root@k8s-master ~]# kubectl attach pods/testpod -it
/ #
exec:在容器中执行命令
[root@k8s-master ~]# kubectl run testpod –image nginx:latest
[root@k8s-master ~]# kubectl exec -it pods/testpod -c testpod — /bin/bash
root@testpod:/#
exec 适合进容器做诊断,attach 是挂接到容器的主进程终端,两者场景不同。
cp:容器与主机间拷贝文件
# 容器 → 主机
[root@k8s-master ~]# kubectl cp testpod:/usr/share/nginx/html/index.html /mnt/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 ~]# curl 10.244.1.12
timinglee
label:管理标签
[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,pod-template-hash=9787d97f6
# 删除标签(app- 表示删除 app 标签)
[root@k8s-master pod]# kubectl label pods webcluster-9787d97f6-zl6q2 app-
pod/webcluster-9787d97f6-zl6q2 unlabeled
# 添加标签
[root@k8s-master pod]# kubectl label pods webcluster-9787d97f6-zl6q2 app=webcluster
pod/webcluster-9787d97f6-zl6q2 labeled
标签是 Kubernetes 资源关联的基础:Service 靠标签找 Pod,控制器靠标签管理 Pod。删除标签后 pod 会脱离控制器的管理范围。
2.4 滚动更新命令
rollout status:查看更新状态
[root@k8s-master pod]# kubectl rollout status deployment webcluster
deployment "webcluster" successfully rolled out
rollout restart:重启应用
[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
restart 不是重启已有容器,而是用新的 pod 模板重新创建一批 pod。pod 名中的模板哈希从 77c87d9946 变成了 7bfd865747、9787d97f6,旧 pod 状态变为 Completed。
2.5 弹性伸缩命令
# 扩容到 4 个副本
[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
# 缩容到 1 个副本
[root@k8s-master pod]# kubectl scale deployment webcluster –replicas 1
deployment.apps/webcluster scaled
三、利用 yaml 文件声明资源
命令式操作适合临时实验,正式的资源配置用 yaml 文件声明,便于版本管理。
3.1 单 Pod 运行多容器
一个 Pod 可以放多个容器,它们共享网络和存储。下面在 testpod 中同时运行 myapp 和 busybox 两个容器:
[root@k8s-master ~]# kubectl run testpod –image myapp:v1 –dry-run=client -o yaml > testpod.yaml
[root@k8s-master ~]# vim testpod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
containers:
– image: myapp:v1
name: myapp1
– image: busyboxplus:latest
name: busybox
command:
– /bin/sh
– -c
– sleep 10000
[root@k8s-master ~]# kubectl apply -f testpod.yaml
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testpod 2/2 Running 0 2m41s
READY 2/2 表示两个容器都正常运行。进入 busybox 容器,用 127.0.0.1 访问同 Pod 内 myapp 的服务:
[root@k8s-master ~]# kubectl exec -it pods/testpod -c busybox — /bin/sh
/ # curl 127.0.0.1
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
多容器共享网络栈,所以容器间用 localhost 就能互通。exec 时要通过 -c 指定进哪个容器。
3.2 暴露端口
在容器配置中声明端口映射,可以把 pod 的端口映射到宿主机上:
[root@k8s-master pod]# vim testpod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
containers:
– image: myapp:v1
name: myapp1
ports:
– name: http
containerPort: 80 # pod 内部容器端口
hostPort: 80 # pod 所在节点端口
protocol: TCP # 端口所用协议
[root@k8s-master pod]# kubectl apply -f testpod.yaml
[root@k8s-master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 3m33s 10.244.5.43 k8s-node2 <none> <none>
[root@k8s-master pod]# curl k8s-node2
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
| name | 端口名称 |
| containerPort | 容器内服务的端口 |
| hostPort | 映射到宿主机的端口 |
| protocol | 协议类型,一般 TCP |
pod 运行在 k8s-node2 上,所以直接访问 k8s-node2 的 80 端口就能拿到业务页面。
3.3 指定环境变量
用 env 给容器传环境变量。下面用 mysql 和 phpmyadmin 两个容器演示:
[root@k8s-master pod]# vim mysql.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: mysql
name: mysql
spec:
containers:
– image: mysql:8.0
name: mysql8
env:
– name: MYSQL_ROOT_PASSWORD
value: lee
– image: phpmyadmin:latest
name: mysqladmin
env:
– name: PMA_ARBITRARY
value: "1"
ports:
– name: phpadminport
containerPort: 80
hostPort: 80
protocol: TCP
[root@k8s-master pod]# kubectl apply -f mysql.yml
[root@k8s-master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 2/2 Running 0 36s 10.244.1.44 k8s-node1 <none> <none>
MYSQL_ROOT_PASSWORD 是 mysql 镜像要求的 root 密码变量,PMA_ARBITRARY 让 phpmyadmin 可以连接任意 mysql 服务器。
3.4 选择运行节点
默认由调度器决定 pod 跑在哪台节点。想指定节点,用 nodeSelector 按节点的标签匹配:
[root@k8s-master ~]# kubectl get nodes –show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready control-plane 29h v1.35.7 ...,kubernetes.io/hostname=k8s-master,...
k8s-node1 Ready <none> 29h v1.35.7 ...,kubernetes.io/hostname=k8s-node1,...
k8s-node2 Ready <none> 24h v1.35.7 ...,kubernetes.io/hostname=k8s-node2,...
[root@k8s-master pod]# vim mysql.yml
spec:
nodeSelector:
kubernetes.io/hostname: k8s-node2 # 指定调度到 k8s-node2
containers:
...
[root@k8s-master pod]# kubectl apply -f mysql.yml
pod/mysql created
[root@k8s-master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 0/2 ContainerCreating 0 11s <none> k8s-node2 <none> <none>
pod 被调度到了 k8s-node2,节点选择生效。nodeSelector 的值取自节点的标签,比如 kubernetes.io/hostname。
3.5 共享宿主机网络
hostNetwork: true 让 pod 直接使用宿主机网络栈,不再分配独立的 Pod IP:
[root@k8s-master pod]# vim testpod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
hostNetwork: true
containers:
– image: busybox:latest
name: busybox
command:
– /bin/sh
– -c
– sleep 10000
[root@k8s-master pod]# kubectl apply -f testpod.yaml
[root@k8s-master pod]# kubectl exec -it pods/testpod -c busybox — /bin/sh
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 00:0C:29:E9:E3:90
inet addr:172.25.254.20 Bcast:172.25.254.255 Mask:255.255.255.0
...
flannel.1 Link encap:Ethernet HWaddr DE:5A:26:1C:E5:A0
inet addr:10.244.5.0 Bcast:0.0.0.0 Mask:255.255.255.255
...
容器里看到的 eth0 地址是宿主机 172.25.254.20,说明 pod 直接用了宿主机的网卡。
3.6 资源优先级 QoS
Kubernetes 根据容器是否设置 requests(期望值)和 limits(上限值),把 pod 划分为三个 QoS 等级。资源紧张时按优先级回收:
BestEffort:没有任何资源限制,优先级最低
[root@k8s-master pod]# kubectl describe pods testpod | grep "QoS Class:"
QoS Class: BestEffort
Burstable:设定了资源限制,但期望值和限制值不同,优先级次之
spec:
containers:
– image: busybox:latest
name: busybox
command:
– /bin/sh
– -c
– sleep 10000
resources:
limits:
cpu: 700m
memory: 200M
requests:
cpu: 500m
memory: 100M
[root@k8s-master pod]# kubectl describe pods testpod | grep "QoS Class:"
QoS Class: Burstable
Guaranteed:期望值和最大使用限制相同,优先级最高
spec:
containers:
– image: busybox:latest
name: busybox
command:
– /bin/sh
– -c
– sleep 10000
resources:
limits:
cpu: 500m
memory: 100M
requests:
cpu: 500m
memory: 100M
[root@k8s-master pod]# kubectl apply -f testpod.yaml
[root@k8s-master pod]# kubectl describe pods testpod | grep "QoS Class:"
QoS Class: Guaranteed
| BestEffort | 未设置任何 requests / limits | 最低 |
| Burstable | requests 与 limits 不同 | 次之 |
| Guaranteed | requests 与 limits 相同 | 最高 |
优先级越高,节点资源不足时越不容易被驱逐。
3.7 容器重启规则
restartPolicy 决定容器退出后是否重启,有三种取值:
| Always | 无论什么原因退出都会重新运行 |
| OnFailure | 非正常关闭(报错退出)才重启 |
| Never | 容器关闭后不重启 |
实验验证:在节点上用 docker rm -f 强制删除容器,观察 pod 的反应。
# restartPolicy: Always,容器被删除后会自动拉起
[root@k8s-master pod]# kubectl get pods -o wide -w
[root@k8s-node2 ~]# docker rm -f 3051d9de4c36
# restartPolicy: OnFailure,正常退出(sleep 命令跑完)后不重启
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
hostNetwork: true
restartPolicy: OnFailure
containers:
– image: busybox:latest
name: busybox
command:
– /bin/sh
– -c
– sleep 30
# 等 30 秒让容器中的命令运行完成后再次观察
# restartPolicy: Never,容器关闭后不重启
apiVersion: v1
kind: Pod
metadata:
labels:
run: testpod
name: testpod
spec:
hostNetwork: true
restartPolicy: Never
containers:
– image: busybox:latest
name: busybox
command:
– /bin/sh
– -c
– sleep 30
[root@k8s-master pod]# kubectl get pods -o wide -w
[root@k8s-node2 ~]# docker rm -f 3051d9de4c36
容器退出后是否重启由 restartPolicy 决定:Always 无论什么原因都重新运行,OnFailure 只在非正常关闭时重启,Never 关闭后不再拉起。
四、Pod 的生命周期
Pod 从创建到销毁要经历初始化、运行、探针检查等阶段:
#mermaid-svg-h0C2ViuGnpaOXx1V{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h0C2ViuGnpaOXx1V .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h0C2ViuGnpaOXx1V .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h0C2ViuGnpaOXx1V .error-icon{fill:#552222;}#mermaid-svg-h0C2ViuGnpaOXx1V .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h0C2ViuGnpaOXx1V .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h0C2ViuGnpaOXx1V .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h0C2ViuGnpaOXx1V .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h0C2ViuGnpaOXx1V .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h0C2ViuGnpaOXx1V .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h0C2ViuGnpaOXx1V .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h0C2ViuGnpaOXx1V .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h0C2ViuGnpaOXx1V .marker.cross{stroke:#333333;}#mermaid-svg-h0C2ViuGnpaOXx1V svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h0C2ViuGnpaOXx1V p{margin:0;}#mermaid-svg-h0C2ViuGnpaOXx1V .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-h0C2ViuGnpaOXx1V .cluster-label text{fill:#333;}#mermaid-svg-h0C2ViuGnpaOXx1V .cluster-label span{color:#333;}#mermaid-svg-h0C2ViuGnpaOXx1V .cluster-label span p{background-color:transparent;}#mermaid-svg-h0C2ViuGnpaOXx1V .label text,#mermaid-svg-h0C2ViuGnpaOXx1V span{fill:#333;color:#333;}#mermaid-svg-h0C2ViuGnpaOXx1V .node rect,#mermaid-svg-h0C2ViuGnpaOXx1V .node circle,#mermaid-svg-h0C2ViuGnpaOXx1V .node ellipse,#mermaid-svg-h0C2ViuGnpaOXx1V .node polygon,#mermaid-svg-h0C2ViuGnpaOXx1V .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h0C2ViuGnpaOXx1V .rough-node .label text,#mermaid-svg-h0C2ViuGnpaOXx1V .node .label text,#mermaid-svg-h0C2ViuGnpaOXx1V .image-shape .label,#mermaid-svg-h0C2ViuGnpaOXx1V .icon-shape .label{text-anchor:middle;}#mermaid-svg-h0C2ViuGnpaOXx1V .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-h0C2ViuGnpaOXx1V .rough-node .label,#mermaid-svg-h0C2ViuGnpaOXx1V .node .label,#mermaid-svg-h0C2ViuGnpaOXx1V .image-shape .label,#mermaid-svg-h0C2ViuGnpaOXx1V .icon-shape .label{text-align:center;}#mermaid-svg-h0C2ViuGnpaOXx1V .node.clickable{cursor:pointer;}#mermaid-svg-h0C2ViuGnpaOXx1V .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-h0C2ViuGnpaOXx1V .arrowheadPath{fill:#333333;}#mermaid-svg-h0C2ViuGnpaOXx1V .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-h0C2ViuGnpaOXx1V .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-h0C2ViuGnpaOXx1V .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h0C2ViuGnpaOXx1V .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-h0C2ViuGnpaOXx1V .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h0C2ViuGnpaOXx1V .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-h0C2ViuGnpaOXx1V .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h0C2ViuGnpaOXx1V .cluster text{fill:#333;}#mermaid-svg-h0C2ViuGnpaOXx1V .cluster span{color:#333;}#mermaid-svg-h0C2ViuGnpaOXx1V div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-h0C2ViuGnpaOXx1V .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h0C2ViuGnpaOXx1V rect.text{fill:none;stroke-width:0;}#mermaid-svg-h0C2ViuGnpaOXx1V .icon-shape,#mermaid-svg-h0C2ViuGnpaOXx1V .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h0C2ViuGnpaOXx1V .icon-shape p,#mermaid-svg-h0C2ViuGnpaOXx1V .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-h0C2ViuGnpaOXx1V .icon-shape .label rect,#mermaid-svg-h0C2ViuGnpaOXx1V .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h0C2ViuGnpaOXx1V .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-h0C2ViuGnpaOXx1V .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-h0C2ViuGnpaOXx1V :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
检测失败
正常
就绪
未就绪
Pod 创建
init 容器按顺序执行,全部成功才启动主容器
主容器启动Running
存活探针livenessProbe
kubelet 重启容器RestartCount +1
就绪探针readinessProbe
加入 Service Endpoints接收流量
从 Endpoints 移除不接收流量
4.1 init 容器
initContainers 在业务容器之前运行,按顺序逐个执行,全部成功后才启动主容器。适合做前置检查、初始化数据等。
[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
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
[root@k8s-master pod]# kubectl apply -f init-example.yml
# 另一终端监控 pod 状态
[root@k8s-master pod]# watch -n 1 kubectl get pods -o wide
# init 容器会一直等待 /testfile 出现,此时主容器未启动
[root@k8s-master pod]# kubectl exec -it pods/webserver -c busybox — /bin/sh
/ # touch /testfile
touch /testfile 之后 init 容器的等待条件满足,主容器才开始创建。
4.2 存活探针 livenessProbe
存活探针用来判断容器是否还活着。没有存活探针时,进程死掉容器不会自动恢复:
[root@k8s-master pod]# kubectl apply -f livness-example.yaml
# 测试操作:停掉 nginx 进程
[root@k8s-master pod]# kubectl exec -it pods/webserver -c webserver — /bin/sh
# nginx -s stop
# pod 状态仍是 Running,但访问已经失败
[root@k8s-master pod]# curl 10.244.5.47
curl: (7) Failed to connect to 10.244.5.47 port 80: 拒绝连接
加了存活探针后,探针检测失败会触发容器重启:
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
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 3
periodSeconds: 1
timeoutSeconds: 1
restartPolicy: Always
[root@k8s-master pod]# kubectl get pods -o wide -w
# 停掉 nginx 后,探针连续检测失败,容器被 kubelet 重启
[root@k8s-master pod]# kubectl exec -it pods/webserver -c webserver — /bin/sh
/ # nginx -s stop
[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> # 恢复
| tcpSocket.port | 探测方式:TCP 连接指定端口 |
| initialDelaySeconds | 容器启动后延迟多久开始探测 |
| periodSeconds | 探测周期 |
| timeoutSeconds | 单次探测超时时间 |
nginx 停了之后,pod 先是连不上,过几秒探针发现异常自动重启容器,服务恢复。没有探针时同样的情况只能人工介入。
4.3 就绪探针 readinessProbe
就绪探针判断容器是否准备好接收流量。没有就绪探针时,只要容器 Running,Service 就会把流量转进来,即使业务实际不可用:
[root@k8s-master pod]# vim readness-example.yml
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
[root@k8s-master pod]# kubectl apply -f readness-example.yml
[root@k8s-master pod]# kubectl describe svc webserver
Endpoints: 10.244.5.67:80
# 删除默认发布文件,业务已经 403
[root@k8s-master ~]# kubectl exec -it pods/webserver -c webserver — /bin/sh
/usr/share/nginx/html # rm -fr index.html
# Service 的 Endpoints 里 pod 还在,但访问返回 403
[root@k8s-master pod]# kubectl describe svc webserver
Endpoints: 10.244.5.67:80 # 还在
[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>
加了就绪探针后,只有探针检查通过(这里是对 /index.html 做 HTTP GET)pod 才会被加入 Endpoints:
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
[root@k8s-master pod]# kubectl apply -f readness-example.yml
[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>
| livenessProbe | 判断容器是否存活 | 重启容器 |
| readinessProbe | 判断容器是否就绪 | 从 Endpoints 摘除,不接收流量 |
存活探针管"要不要重启",就绪探针管"能不能接流量",两者职责不同,一般同时配置。





