0. 实验环境与准备
0.1 集群与镜像清单
本文用到的镜像(仓库里 library 项目需提前准备好):
| 镜像 | 用途 |
|---|---|
myapp:v1 / myapp:v2 |
基于 nginx 的演示镜像,访问返回 Hello MyApp | Version: vX |
busybox / busyboxplus |
轻量调试容器 |
nginx:latest |
通用 Web 容器 |
mysql:8.0 + phpmyadmin:latest |
多容器 Pod 演示 |
perl:5.34.0 |
Job 批处理演示 |
如果镜像在本地 tar 包里,先导入并推到私有仓库(以 myapp 为例):
bash
docker load -i myapp.tar.gz
docker tag timinglee/myapp:v1 reg.timinglee.org/library/myapp:v1
docker push reg.timinglee.org/library/myapp:v1
docker tag timinglee/myapp:v2 reg.timinglee.org/library/myapp:v2
docker push reg.timinglee.org/library/myapp:v2
0.2 两种操作风格:命令式 vs 声明式
| 风格 | 例子 | 特点 |
|---|---|---|
| 命令式 | kubectl run, kubectl create, kubectl expose |
一行搞定,快;但不留"底稿",难回滚、难版本管理 |
| 声明式 | kubectl apply -f xxx.yaml |
把期望状态写进 YAML,可纳入 Git,是生产环境的标准做法 |
1. 实验一:Pod 基础与 kubectl 命令
1.1 命名空间管理
命名空间(Namespace)用来给资源分组隔离,默认都在 default。
bash
kubectl get namespaces # 查看所有命名空间
kubectl create namespace timinglee # 创建
kubectl delete namespace timinglee # 删除(会一并删掉里面的资源,谨慎!)
预期输出(创建后):
bash
NAME STATUS AGE
default Active 4h20m
kube-flannel Active 3h31m
kube-node-lease Active 4h20m
kube-public Active 4h20m
kube-system Active 4h20m
timinglee Active 5s
1.2 创建第一个 Pod 与排错
bash
kubectl run lee --image nginx:latest
kubectl get pods -o wide
bash
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>
故意创建一个拉不到的镜像,看K8s 怎么报错:
bash
kubectl run error --image lee:v1
kubectl get pods -o wide
bash
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>
用 describe 看详细原因
bash
kubectl describe pods error
在 Events 里你会看到:
bash
Warning Failed kubelet Failed to pull image "lee:v1": docker.io/library/lee:v1: not found
Warning Failed kubelet Error: ErrImagePull
Normal BackOff kubelet Back-off pulling image "lee:v1"
Warning Failed kubelet Error: ImagePullBackOff
清理:
bash
kubectl delete pods error
kubectl delete pods --all # 删除 default 命名空间下所有 Pod
1.3 核心 kubectl 命令逐一练
create ------ 创建 Deployment 并管理副本
bash
kubectl create deployment webcluster --replicas 2 --image myapp:v1
kubectl get deployments.apps
kubectl delete deployments.apps webcluster
edit ------ 在线修改(会打开默认编辑器)
bash
kubectl create deployment webcluster --image myapp:v1
kubectl edit deployments.apps webcluster # 把 replicas 改成 2 保存
kubectl get pods # 会发现多了一个 Pod
patch ------ 不改文件,直接命令行打补丁
bash
kubectl patch deployments.apps webcluster -p '{"spec":{"replicas":1}}'
kubectl get pods # 又变回 1 个
expose ------ 把 Deployment 暴露成 Service(集群内可访问)
bash
kubectl expose deployment webcluster --port 80 --target-port 80
kubectl get service
curl 10.97.61.108/hostname.html # 多次访问会轮询到不同 Pod
logs / exec / cp ------ 查日志、进容器、拷文件
bash
kubectl logs pods/webcluster-xxxxx # 看日志
kubectl exec -it pods/testpod -c testpod -- /bin/bash # 进容器
kubectl cp testpod:/usr/share/nginx/html/index.html /mnt/test # 拷出来
echo timinglee > /mnt/index.html
kubectl cp /mnt/index.html testpod:/usr/share/nginx/html/index.html # 拷回去
curl <pod-ip> # 返回 timinglee
rollout ------ 发布状态与重启
bash
kubectl rollout status deployment webcluster
kubectl rollout restart deployment webcluster # 滚动重启(Pod 会一批批换新)
scale ------ 扩缩容
bash
kubectl scale deployment webcluster --replicas 4
kubectl scale deployment webcluster --replicas 1
label ------ 标签是 K8s 的"身份证"
bash
kubectl get pods --show-labels
kubectl label pods webcluster-xxxxx app- # 删除标签
kubectl label pods webcluster-xxxxx app=webcluster # 加回标签
2. 实验二:用 YAML 声明式玩转 Pod
2.1 一个 Pod 里跑多个容器
bash
apiVersion: v1
kind: Pod
metadata:
name: testpod
labels:
run: testpod
spec:
containers:
- image: myapp:v1
name: myapp1
- image: busyboxplus:latest
name: busybox
command: ["/bin/sh", "-c", "sleep 10000"]
bash
kubectl apply -f testpod.yaml
kubectl get pods # READY 列显示 2/2
kubectl exec -it pods/testpod -c busybox -- /bin/sh
/ # curl 127.0.0.1 # 从 busybox 访问同 Pod 里的 myapp
2.2 hostPort:把容器端口暴露到节点
bash
apiVersion: v1
kind: Pod
metadata:
name: testpod
spec:
containers:
- image: myapp:v1
name: myapp1
ports:
- name: http
containerPort: 80
hostPort: 80 # Pod 所在节点的 80 端口会转发进来
protocol: TCP
bash
kubectl apply -f testpod.yaml
kubectl get pods -o wide # 记下 NODE 列,比如 k8s-node2
curl k8s-node2 # 直接访问节点 IP:80
2.3 用环境变量给容器传配置
bash
apiVersion: v1
kind: Pod
metadata:
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
bash
kubectl apply -f mysql.yaml
kubectl get pods -o wide # 浏览器访问该节点的 80 端口即可进 phpMyAdmin
2.4 nodeSelector:指定 Pod 落在哪个节点
先看节点有哪些标签:
bash
kubectl get nodes --show-labels
bash
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
nodeSelector:
kubernetes.io/hostname: k8s-node2 # 只调度到 node2
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
bash
kubectl apply -f mysql.yaml
kubectl get pods -o wide # NODE 一定是 k8s-node2
2.5 hostNetwork:让 Pod 共享宿主机网络
bash
apiVersion: v1
kind: Pod
metadata:
name: testpod
spec:
hostNetwork: true
containers:
- image: busybox:latest
name: busybox
command: ["/bin/sh", "-c", "sleep 10000"]
kubectl apply -f testpod.yaml
kubectl exec -it pods/testpod -c busybox -- /bin/sh
/ # ifconfig # 看到的是宿主机的 eth0,而不是 Pod 自己的网络
2.6 QoS 三档:资源优先级
当节点资源紧张时,K8s 按 QoS 决定先杀谁。三档由 resources 的配置决定:
| 级别 | 条件 | 优先级 |
|---|---|---|
| BestEffort | 完全不设 requests/limits | 最低,最先被驱逐 |
| Burstable | 设了限制,但 requests ≠ limits | 中间 |
| Guaranteed | requests == limits(且都设了) | 最高,最后才被驱逐 |
BestEffort:
bash
apiVersion: v1
kind: Pod
metadata:
name: qos-besteffort
spec:
containers:
- image: busybox:latest
name: busybox
command: ["/bin/sh", "-c", "sleep 10000"]
Burstable(requests < limits):
bash
apiVersion: v1
kind: Pod
metadata:
name: qos-burstable
spec:
containers:
- image: busybox:latest
name: busybox
command: ["/bin/sh", "-c", "sleep 10000"]
resources:
requests:
cpu: 500m
memory: 100Mi
limits:
cpu: 700m
memory: 200Mi
Guaranteed(requests == limits):
bash
apiVersion: v1
kind: Pod
metadata:
name: qos-guaranteed
spec:
containers:
- image: busybox:latest
name: busybox
command: ["/bin/sh", "-c", "sleep 10000"]
resources:
requests:
cpu: 500m
memory: 100Mi
limits:
cpu: 500m
memory: 100Mi
验证:
bash
kubectl describe pods qos-besteffort | grep "QoS Class:" # BestEffort
kubectl describe pods qos-burstable | grep "QoS Class:" # Burstable
kubectl describe pods qos-guaranteed | grep "QoS Class:" # Guaranteed
2.7 重启策略:Always / OnFailure / Never
restartPolicy 决定容器退出后 K8s 怎么办:
| 策略 | 行为 | 典型场景 |
|---|---|---|
Always |
不管什么原因退出都重启 | 长期运行的服务(默认) |
OnFailure |
非正常退出才重启 | 允许正常结束的批处理 |
Never |
退出后绝不重启 | 任务只跑一次,结果靠查日志 |
bash
# Always 示例(容器命令 sleep 60,结束后会被立刻拉起)
apiVersion: v1
kind: Pod
metadata:
name: testpod
spec:
restartPolicy: Always
containers:
- image: busybox:latest
name: busybox
command: ["/bin/sh", "-c", "sleep 60"]
# Never 示例(sleep 30 结束后 Pod 变 Completed,不再重启)
apiVersion: v1
kind: Pod
metadata:
name: testpod
spec:
restartPolicy: Never
containers:
- image: busybox:latest
name: busybox
command: ["/bin/sh", "-c", "sleep 30"]