Kubernetes Job 与 Service 实战笔记(Job · CronJob · Service · NodePort)
Kubernetes Job 与 Service 实战笔记(Job · CronJob · Service · NodePort)
本手册由
master30_2026-08-10_14_17_43.log(8/10 全天实验)整理而成,在 kubeadm v1.30.2 集群(master30 + worker31/32,运行第 5 天)上完成。
两大主题:① 批处理(Job / CronJob,含失败重试、并行、超时、TTL)→ ② 服务发现(hostPort / ClusterIP / selector / NodePort)。命令提示符保留真实时间戳,报错与修复全部来自实际操作。
📑 目录
| # | 章节 | 核心内容 |
|---|---|---|
| 一 | 整体框架与实验总览 | 实验流程图、两大主题、节点环境 |
| 二 | 实验准备:命名空间与上下文 | controller / services 命名空间、set-context 切换 |
| 三 | Job 基础:一次性任务 | 命令行创建、job.yaml、查看日志、同名冲突 |
| 四 | Job 失败重试:restartPolicy 与 backoffLimit | 故意写错命令、两种重启策略、最多重建次数 |
| 五 | Job 高级参数 | completions / parallelism / activeDeadlineSeconds / TTL |
| 六 | CronJob 定时任务 | 每 2 分钟执行、自动生成 Job、历史保留 |
| 七 | hostPort 的局限 | Pod IP 直连、宿主机端口、端口冲突 |
| 八 | ClusterIP Service:负载均衡 | 创建 Service、Endpoints、60 次轮询验证 |
| 九 | Service 进阶:selector 与 expose | 相同标签自动接入、错误 selector、rollout restart |
| 十 | 实战:MySQL + WordPress NodePort | expose mysql、ClientIP 连接、NodePort 对外访问 |
| 十一 | 常见错误与排查(重点) | 本次实验全部报错:原因 + 修复 |
| 十二 | 命令速查表 | Job / CronJob / Service 常用命令 |
一、整体框架与实验总览
1.1 实验流程图
1.2 节点环境
| 节点 | 主机名 | IP | 角色 |
|---|---|---|---|
| master30 | master30.ningcode.cn | 10.1.8.30 | 控制平面 |
| worker31 | worker31.ningcode.cn | 10.1.8.31 | 工作节点 |
| worker32 | worker32.ningcode.cn | 10.1.8.32 | 工作节点 |
- Pod 网段
10.224.0.0/16;Service 网段10.96.0.0/12(本次 Service IP 如 10.100.254.252、10.108.123.75) - 私有镜像仓库
hub.laoma.cloud(实验期间 192.168.46.201:80 曾连接拒绝,遇到拉镜像失败改用官方镜像或 nerdctl 预热)
1.3 主题速览
| 主题 | 核心知识点 | 日志时间 |
|---|---|---|
| Job | 一次性任务、失败重试、completions/parallelism/超时/TTL | 14:17–15:28 |
| CronJob | 定时创建 Job、历史保留 3 个 | 15:28–15:32 |
| Service | hostPort 局限、ClusterIP 负载均衡、selector | 16:20–16:40 |
| 实战 | MySQL expose + wordpress NodePort(31055) | 17:30–17:35 |
二、实验准备:命名空间与上下文
2.1 检查集群状态
root@master30 ~ 14:17:58# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master30.ningcode.cn Ready control-plane 5d v1.30.2
worker31.ningcode.cn Ready <none> 4d23h v1.30.2
worker32.ningcode.cn Ready <none> 4d23h v1.30.2
2.2 建立 controller / services 命名空间
批处理实验用 controller 命名空间,Service 实验用 services 命名空间(本次日志中 controller 命名空间沿用之前实验创建,services 为本次新建):
root@master30 ~ 16:20:18# kubectl create ns services
namespace/services created
root@master30 ~ 16:20:34# kubectl config set-context --current --namespace services
Context "kubernetes-admin@kubernetes" modified.
提示:切换命名空间后所有 kubectl 操作默认在该命名空间内;跨主题实验建议一个主题一个命名空间,互不干扰。
三、Job 基础:一次性任务
3.1 概念
- Job 负责运行"跑完就退出"的任务(批处理、计算、初始化数据等),Pod 执行成功后 Job 标记为 Complete
- 与 Deployment/RS 不同:Deployment 要求 Pod 一直 Running,Job 要求 Pod 正常退出(exit 0)
3.2 命令行创建 Job
# 注意:命令要放在 -- 分隔符之后
root@master30 ~ 14:31:54# kubectl create job myjob --image=busybox -- echo hello k8s job!
job.batch/myjob created
root@master30 ~ 14:31:59# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-87cjz 0/1 Completed 0 4s
NAME STATUS COMPLETIONS DURATION AGE
job.batch/myjob Complete 1/1 3s 4s
# 查看任务输出
root@master30 ~ 14:32:03# kubectl logs myjob-87cjz
hello k8s job!
坑:
kubectl create job myjob --image=... --echo hello会报unknown flag: --echo——必须用--把命令与 kubectl 参数分开。
3.3 用 YAML 创建 Job(job.yaml)
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ["echo", "hello k8s job! "]
restartPolicy: Never # Job 的 restartPolicy 只支持 Never / OnFailure
root@master30 ~ 14:32:45# kubectl apply -f job.yaml
job.batch/myjob created
root@master30 ~ 14:32:49# kubectl get all
pod/myjob-g9zwt 0/1 Completed 0 11s
job.batch/myjob Complete 1/1 3s 11s
3.4 查看 Job 状态
root@master30 ~ 14:33:00# kubectl get jobs.batch myjob
NAME STATUS COMPLETIONS DURATION AGE
myjob Complete 1/1 3s 18s
COMPLETIONS 显示
1/1表示 1 个 Pod 成功完成;0/1表示还没成功。
3.5 同名 Job 不能重复创建
root@master30 ~ 14:31:03# kubectl create job myjob --image=busybox -- echo hello k8s job!
error: failed to create job: jobs.batch "myjob" already exists
# 先删再建
root@master30 ~ 14:31:41# kubectl delete jobs.batch myjob
job.batch "myjob" deleted
四、Job 失败重试:restartPolicy 与 backoffLimit
4.1 故意写错命令,观察失败现象
把 command 故意写成不存在的 echoxxx:
# job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
command: ["echoxxx", "hello k8s job! "]
restartPolicy: Never
root@master30 ~ 14:57:34# kubectl apply -f job.yaml
job.batch/myjob created
root@master30 ~ 14:57:37# kubectl get all
pod/myjob-c6str 0/1 StartError 0 4s
job.batch/myjob Running 0/1 4s 4s
describe pod 看到核心报错:
State: Terminated
Reason: StartError
Message: exec: "echoxxx": executable file not found in $PATH: unknown
Exit Code: 128
4.2 restartPolicy: Never —— 失败后 Job 重建新 Pod
restartPolicy: Never 时容器失败不会原地重启,Job 控制器会创建新的 Pod 重试(日志中看到 c6str、5d4qz 等一串 Pod):
root@master30 ~ 14:57:54# kubectl get all
pod/myjob-5d4qz 0/1 StartError 0 8s
pod/myjob-c6str 0/1 StartError 0 18s
4.3 restartPolicy: OnFailure —— 同一个 Pod 内重启
# 只把 restartPolicy 改成 OnFailure
restartPolicy: OnFailure
root@master30 ~ 14:59:39# kubectl apply -f job.yaml
root@master30 ~ 14:59:49# kubectl get all
pod/myjob-pqnqf 0/1 CrashLoopBackOff 1 (3s ago) 3s
root@master30 ~ 15:00:01# kubectl get all
pod/myjob-pqnqf 0/1 RunContainerError 2 (5s ago) 18s
现象:Pod 数量始终是 1,RESTARTS 数值不断增加——容器在同一个 Pod 内反复重启(CrashLoopBackOff)。
4.4 backoffLimit:最多重建几次
spec:
backoffLimit: 2 # 最多重建 2 次(加上首次,最多 3 个 Pod)
template:
spec:
...
restartPolicy: OnFailure
root@master30 ~ 15:01:10# kubectl apply -f job.yaml
# 依次出现 qjccx → 7vfl2 → qz46d 三个 Pod 都失败后
root@master30 ~ 15:02:07# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-7vfl2 0/1 StartError 0 24s
pod/myjob-qjccx 0/1 StartError 0 35s
pod/myjob-qz46d 0/1 StartError 0 4s
NAME STATUS COMPLETIONS DURATION AGE
job.batch/myjob Failed 0/1 35s 35s
结论:backoffLimit: 2 = 最多重建 2 次(共 3 个 Pod),全部失败后 Job 状态变为 Failed。
五、Job 高级参数
5.1 completions:需要成功多少次
默认 Job 成功 1 个 Pod 就 Complete;completions: 2 表示要有 2 个 Pod 成功执行:
spec:
completions: 2
template:
spec:
containers:
- name: hello
image: hub.laoma.cloud/library/busybox
command: ["echo", "hello k8s job! "]
restartPolicy: Never
root@master30 ~ 15:04:14# kubectl apply -f job.yaml
root@master30 ~ 15:04:33# kubectl get all
pod/myjob-5th5d 0/1 Completed 0 4s
pod/myjob-xlvpj 0/1 Completed 0 7s
NAME STATUS COMPLETIONS DURATION AGE
job.batch/myjob Complete 2/2 6s 7s
现象:串行创建 2 个 Pod,都成功后 Job 才 Complete(COMPLETIONS 0/2 → 1/2 → 2/2)。
5.2 parallelism:并行执行
spec:
completions: 6 # 总共需要 6 个成功
parallelism: 2 # 每次同时跑 2 个
template: ...
root@master30 ~ 15:06:22# kubectl apply -f job.yaml
root@master30 ~ 15:06:33# kubectl get all
pod/myjob-2m4gc 0/1 Completed 0 10s
pod/myjob-89mrr 0/1 Completed 0 14s
pod/myjob-8vsrc 0/1 Completed 0 13s
pod/myjob-f4zwp 0/1 Completed 0 16s
pod/myjob-m9fm6 0/1 Completed 0 11s
pod/myjob-qrfsz 0/1 Completed 0 16s
NAME STATUS COMPLETIONS DURATION AGE
job.batch/myjob Complete 6/6 8s 16s
效果:每次运行 2 个 Pod,直到总共 6 个 Pod 成功完成。不指定 parallelism 时默认为 1(串行)。
5.3 activeDeadlineSeconds:任务超时
apiVersion: batch/v1
kind: Job
metadata:
name: pi-with-timeout
spec:
backoffLimit: 5
activeDeadlineSeconds: 10 # 整个 Job 最长运行 10 秒
template:
spec:
containers:
- name: pi
image: hub.laoma.cloud/library/perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
root@master30 ~ 15:07:42# kubectl apply -f job.yaml
job.batch/pi-with-timeout created
root@master30 ~ 15:08:02# kubectl get all
pod/pi-with-timeout-rjpjx 0/1 Terminating 0 24s
NAME STATUS COMPLETIONS DURATION AGE
job.batch/pi-with-timeout Failed 0/1 24s 24s
要点:到达
activeDeadlineSeconds后,所有运行中 Pod 被终止,Job 状态更新为Failed、reason 为DeadlineExceeded;且它的优先级高于 backoffLimit——超时后即使重试次数没到也不再部署新 Pod。
5.4 ttlSecondsAfterFinished:自动清理
spec:
ttlSecondsAfterFinished: 100 # Job 结束后 100 秒自动删除(含 Pod)
template:
spec:
containers:
- name: pi
image: hub.laoma.cloud/library/perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
root@master30 ~ 15:09:45# kubectl apply -f job.yaml
job.batch/pi-with-ttl created
说明:Job 结束后 100 秒可被 TTL 控制器级联删除(Pod + Job 一起清掉);设为 0 立即清理,不设置则永不自动清除。该机制是 Alpha 功能,需开启
TTLAfterFinished特性门控。
5.5 计算圆周率的 pi Job(成功案例)
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl
imagePullPolicy: IfNotPresent
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(200)"]
restartPolicy: Never
backoffLimit: 4
root@master30 ~ 14:35:13# kubectl apply -f job-pi.yaml
job.batch/pi created
# 镜像能拉到的话,Pod 完成后 kubectl logs pi-xxxxx 会打印 200 位圆周率
本次实验 pi Job 因内网仓库 192.168.46.201 连接拒绝(perl 镜像拉不到)一直重试,
describe pod可见ErrImagePull / ImagePullBackOff,删掉 Pod 后 Job 会再建新 Pod 继续重试,直到 backoffLimit 耗尽。
六、CronJob 定时任务
6.1 概念
CronJob 按 Cron 表达式定时创建 Job,适合定时备份、定时清理、定时采集等场景。
6.2 创建每 2 分钟执行一次的 CronJob
root@master30 ~ 15:28:31# kubectl create cronjob mycronjob \
--image=hub.laoma.cloud/library/busybox \
--schedule='*/2 * * * *' \
-- echo hello k8s job!
cronjob.batch/mycronjob created
6.3 观察自动生成的 Job
root@master30 ~ 15:31:32# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/mycronjob-29772450-5svhb 0/1 ContainerCreating 0 2m13s
pod/mycronjob-29772452-64pl8 0/1 Completed 0 13s
NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob.batch/mycronjob */2 * * * * <none> False 1 13s 2m42s
NAME STATUS COMPLETIONS DURATION AGE
job.batch/mycronjob-29772450 Running 0/1 2m13s 2m13s
job.batch/mycronjob-29772452 Complete 1/1 3s 13s
观察点:
- CronJob 名字带时间戳后缀(
mycronjob-29772452),每个调度周期生成一个新的 Job*/2 * * * *表示每 2 分钟执行一次- 默认最多保留 3 个已完成的历史 Job(本实验"等待一段时间,最多创建 3 个 job")
七、hostPort 的局限
7.1 先看:直接访问 Pod IP
# 创建单个 Pod(httpd),加 hostPort 前先验证 Pod IP 可访问
root@master30 ~ 16:23:43# kubectl apply -f pod-web.yaml
pod/web created
root@master30 ~ 16:24:03# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
web 1/1 Running 0 16s 10.224.83.166 worker32.ningcode.cn
root@master30 ~ 16:24:09# curl http://10.224.83.166
<html><body><h1>It works!</h1></body></html>
7.2 hostPort:把容器端口映射到宿主机
在 pod-web.yaml 中加 ports(hostPort: 8080):
spec:
containers:
- image: hub.laoma.cloud/library/httpd
name: web
ports:
- containerPort: 80
hostPort: 8080
root@master30 ~ 16:25:24# kubectl apply -f pod-web.yaml
pod/web created
root@master30 ~ 16:25:32# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
web 1/1 Running 0 6s 10.224.83.167 worker32.ningcode.cn
7.3 hostPort 的两个致命问题
问题一:只能通过 Pod 所在节点的 IP 访问。 本次 Pod 调度在 worker32,尝试访问 worker31 的 8080 失败:
root@master30 ~ 16:25:55# curl http://worker31.ningcoe.cn:8080
curl: (6) Could not resolve host: worker31.ningcoe.cn
结论:Pod 只能通过所在主机的 IP 访问,集群外部需要知道 Pod 运行在哪台主机(且 /etc/hosts 拼写也要对:ningcode.cn)。
问题二:同一节点上相同 hostPort 的 Pod 会端口冲突。 Deployment 开 4 个副本(都带 hostPort: 8080)时 2 个 Pending:
root@master30 ~ 16:30:22# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-6f85d979d4-2lb7z 0/1 Pending 0 6s
web-6f85d979d4-475pw 1/1 Running 0 6s
web-6f85d979d4-cznr7 1/1 Running 0 6s
web-6f85d979d4-n9g2c 0/1 Pending 0 6s
root@master30 ~ 16:30:43# kubectl describe pod web-6f85d979d4-2lb7z
Events:
Warning FailedScheduling 0/3 nodes are available: 1 node(s) had untolerated taint
{node-role.kubernetes.io/control-plane: }, 2 node(s) didn't have free ports for the
requested pod ports.
结论:控制器管理多个相同 hostPort 的 Pod 必然端口冲突(一台机器 8080 只有一个)。所以生产基本不用 hostPort,而是用 Service。
7.4 清理并重新部署(不带 hostPort)
root@master30 ~ 16:31:55# kubectl delete deployments.apps web
deployment.apps "web" deleted
root@master30 ~ 16:32:10# kubectl create deployment web --image=hub.laoma.cloud/library/httpd:2.4.58 --replicas=3
deployment.apps/web created
root@master30 ~ 16:32:23# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
web-d455dd6b-g2l5j 1/1 Running 0 4s app=web,pod-template-hash=d455dd6b
web-d455dd6b-rpwzl 1/1 Running 0 4s app=web,pod-template-hash=d455dd6b
web-d455dd6b-z5xzd 1/1 Running 0 4s app=web,pod-template-hash=d455dd6b
三个 Pod 全部 Running,标签都是
app=web——这就是接下来 Service 选后端 Pod 的依据。
八、ClusterIP Service:负载均衡
8.1 创建 ClusterIP Service
# --tcp=8080:80 表示 Service 端口 8080 → 容器端口 80
root@master30 ~ 16:32:27# kubectl create service clusterip web --tcp=8080:80
service/web created
root@master30 ~ 16:32:33# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.100.254.252 <none> 8080/TCP 17s
8.2 查看 Service 与 Endpoints
root@master30 ~ 16:33:08# kubectl describe service web | grep -e Endpoints -e IP:
IP: 10.100.254.252
Endpoints: 10.224.195.158:80,10.224.83.169:80,10.224.83.170:80
Endpoints 自动收集了 3 个匹配
selector: app=web的 Pod IP——Service 不直接连 Pod 名,而是通过标签选择后端。
8.3 通过 Service 访问(注意端口)
# 端口是 8080,不是 80!不带端口会一直挂起
root@master30 ~ 16:34:12# curl 10.100.254.252:8080
<html><body><h1>It works!</h1></body></html>
8.4 验证负载均衡:三个 Pod 轮流接流量
给每个 Pod 写入不同的 index.html,然后连续请求 60 次:
root@master30 ~ 16:36:52# for pod in $(kubectl get pods -o name | awk -F/ '{print $2}')
> do
> kubectl exec -it $pod -- bash -c "echo $pod > htdocs/index.html"
> done
root@master30 ~ 16:37:15# for i in {1..60};do curl -s 10.100.254.252:8080;done | sort | uniq -c
16 web-d455dd6b-g2l5j
16 web-d455dd6b-rpwzl
28 web-d455dd6b-z5xzd
60 次请求被分发到 3 个 Pod(16/16/28)——ClusterIP Service 自动做负载均衡。
九、Service 进阶:selector 与 expose
9.1 相同标签的 Pod 自动接入 Service
# 手动创建一个带 app=web 标签的独立 Pod
root@master30 ~ 16:37:59# kubectl run web --image=hub.laoma.cloud/library/httpd --labels=app=web
pod/web created
# Service 立刻把请求转发给它(返回默认页 It works!,而非三个 Pod 的名字)
root@master30 ~ 16:38:08# for i in {1..60};do curl -s 10.100.254.252:8080;done | sort | uniq -c
15 <html><body><h1>It works!</h1></body></html>
14 web-d455dd6b-g2l5j
17 web-d455dd6b-rpwzl
14 web-d455dd6b-z5xzd
Service 只认标签不认来源:任何带
app=web标签的 Pod 都会自动成为后端。
9.2 rollout restart:重建后标签没变,流量照常
root@master30 ~ 16:38:18# kubectl rollout restart deployment web
deployment.apps/web restarted
root@master30 ~ 16:38:35# for i in {1..60};do curl -s 10.100.254.252:8080;done | sort | uniq -c
60 <html><body><h1>It works!</h1></body></html>
新 Pod 重建后(之前的 index.html 内容丢失),Service 依然正常转发——用户访问无感知,这就是 Service 的价值。
9.3 expose:一条命令创建 Service
# 错误示范:selector 写成 app2=web2,与实际标签 app=web 不匹配
root@master30 ~ 16:39:27# kubectl expose deployment web --port=8080 --target-port=80 --selector=app2=web2
service/web exposed
root@master30 ~ 16:39:37# kubectl describe svc web | grep -e IP: -e Endpoints
IP: 10.102.92.153
Endpoints: 10.224.195.159:80,10.224.83.173:80
现象:只匹配到 2 个 Endpoints(且都是不相关 Pod)。selector 必须与 Pod 标签一致,否则 Service 找不到正确的后端。
# 正确用法:省略 --selector,自动取 Deployment 的标签
kubectl expose deployment web --port=8080 --target-port=80
# 也可以先 dry-run 导出再改
root@master30 ~ 16:40:16# kubectl create service clusterip web --tcp=8080:80 -o yaml --dry-run=client > svc-web.yml
9.4 svc-web.yml 参考
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 8080
targetPort: 80
十、实战:MySQL + WordPress NodePort
10.1 创建 MySQL Pod 并暴露为 ClusterIP Service
root@master30 ~ 17:31:01# kubectl run mysql \
--image=hub.laoma.cloud/library/mysql \
--image-pull-policy=IfNotPresent \
--env=MYSQL_ROOT_PASSWORD=redhat \
--env=MYSQL_USER=tom \
--env=MYSQL_PASSWORD=redhat \
--env=MYSQL_DATABASE=blog \
--dry-run=client -o yaml > pod-mysql.yaml
root@master30 ~ 17:31:11# kubectl apply -f pod-mysql.yaml
pod/mysql created
# expose 给集群内其他 Pod 访问(自动创建 ClusterIP Service)
root@master30 ~ 17:31:20# kubectl expose pod mysql --port=3306 --target-port=3306
service/mysql exposed
root@master30 ~ 17:31:32# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql ClusterIP 10.108.123.75 <none> 3306/TCP 8s
10.2 通过 ClusterIP 连接 MySQL
# 宿主机装 mysql-client
root@master30 ~ 17:31:40# apt install -y mysql-client
# 坑1:连错 IP(10.111.69.45 不是 mysql Service 的 IP)
root@master30 ~ 17:31:50# mysql -u tom -predhat -h 10.111.69.45 -e 'show databases;'
ERROR 2003 (HY000): Can't connect to MySQL server on '10.111.69.45:3306' (111)
# 坑2:MySQL 8 的 caching_sha2_password 要求安全连接,--ssl-mode=DISABLED 会报错
root@master30 ~ 17:32:16# mysql -u tom -predhat -h 10.108.123.75 --ssl-mode=DISABLED -e 'show databases;'
ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password' reported error:
Authentication requires secure connection.
# 正确:直接连 ClusterIP,不强制关闭 SSL
root@master30 ~ 17:33:42# mysql -u tom -predhat -h 10.108.123.75 -e 'show databases;'
+--------------------+
| Database |
+--------------------+
| blog |
| information_schema |
| performance_schema |
+--------------------+
10.3 创建 WordPress 并通过 NodePort 对外提供服务
root@master30 ~ 17:33:43# kubectl run wordpress \
--image=hub.laoma.cloud/library/wordpress \
--image-pull-policy=IfNotPresent \
--env=WORDPRESS_DB_USER=tom \
--env=WORDPRESS_DB_PASSWORD=redhat \
--env=WORDPRESS_DB_NAME=blog \
--env=WORDPRESS_DB_HOST=10.108.123.75
pod/wordpress created
# NodePort 类型:集群外可通过 节点IP:31055 访问
root@master30 ~ 17:34:10# kubectl expose pod wordpress --port=80 --target-port=80 --type NodePort
service/wordpress exposed
root@master30 ~ 17:34:15# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql ClusterIP 10.108.123.75 <none> 3306/TCP 2m47s
wordpress NodePort 10.96.250.25 <none> 80:31055/TCP 4s
访问方式:浏览器打开
http://<任一节点IP>:31055即可进入 wordpress 安装页。
NodePort 范围:默认 30000–32767;80:31055表示容器 80 端口映射到节点 31055 端口。
十一、常见错误与排查(重点)
以下全部来自本次实验真实报错,按本文档操作即可避开。
11.1 命令语法类
| 错误示范 | 报错 | 正确写法 |
|---|---|---|
kubectl create job myjob --image=... --echo hello |
unknown flag: --echo |
命令放 -- 后:kubectl create job myjob --image=... -- echo hello |
kubectl create job myjob --image=busybox -- echo ...(重复创建) |
jobs.batch "myjob" already exists |
先 kubectl delete jobs.batch myjob 再创建 |
kubectl apply -y job.yaml |
unknown shorthand flag: 'y' in -y |
kubectl apply -f job.yaml |
kubectl get al |
the server doesn't have a resource type "al" |
kubectl get all |
for pod in $(kubectlget pods ...) |
kubectlget: command not found |
kubectl get pods(命令与子命令间要有空格) |
curl http://worker31.ningcoe.cn:8080 |
Could not resolve host |
拼写 ningcode.cn;且 hostPort 只监听 Pod 所在节点 |
curl 10.100.254.252 |
一直挂起无响应 | Service 端口是 8080:curl 10.100.254.252:8080 |
11.2 镜像与状态类
| 现象 | 原因 | 处理 |
|---|---|---|
Pod ErrImagePull / ImagePullBackOff |
私有仓库 192.168.46.201:80 连接拒绝 |
describe pod 看 Events;换官方镜像或用 nerdctl pull 预热 |
kubectl logs pi-xhzgl 报 container ... is waiting to start: ContainerCreating |
容器还没启动完 | 等 Pod Running 后再看日志 |
Pod StartError,Message 含 exec: "echoxxx": executable file not found |
命令写错/镜像里没有该命令 | 检查 command 拼写,用 kubectl run ... -- sleep 3600 进容器验证 |
CrashLoopBackOff / RunContainerError |
restartPolicy: OnFailure 原地重启 | 确认是逻辑错误还是环境问题;配合 backoffLimit 限制重试 |
Job Failed 且出现多个 Pod |
backoffLimit 达到上限(2 = 最多重建 2 次) | 调大 backoffLimit 或修复命令 |
Job Failed reason DeadlineExceeded |
运行超过 activeDeadlineSeconds |
任务本身太久,调大超时或优化任务 |
11.3 Service 类
| 现象 | 原因 | 修复 |
|---|---|---|
Deployment 多副本 Pending,Events 报 didn't have free ports |
都配了相同的 hostPort,同节点端口冲突 | 去掉 hostPort,改用 Service |
| Service 的 Endpoints 为空或不对 | selector 与 Pod 标签不匹配(如 app2=web2) | kubectl get pods --show-labels 对比,selector 用真实标签 |
| 手动创建的 Pod 也被 Service 转发 | 该 Pod 带相同标签 | Service 只认标签;不想接入就换标签或删 Pod |
mysql 连接 ERROR 2003 |
IP 写错(不是 Service 的 ClusterIP) | kubectl get svc mysql 查 ClusterIP |
mysql 连接 ERROR 2061 ... caching_sha2_password ... secure connection |
客户端 --ssl-mode=DISABLED 强制关闭 SSL |
去掉该参数,让 MySQL 8 走默认 SSL 认证 |
| NodePort 访问不了 | 端口不在 30000–32767 或防火墙未放行 | 用 kubectl get svc 里的 80:31055 端口;放行节点防火墙 |
11.4 概念要点
- Job 的
restartPolicy只支持Never/OnFailure:Never 失败重建新 Pod,OnFailure 原地重启 backoffLimit控制最大重建次数;activeDeadlineSeconds控制总时长且优先于 backoffLimitcompletions需要成功次数,parallelism并行数量,两者组合实现"每次跑 N 个直到 M 个成功"- Service 通过 selector 选后端,Pod IP 变化不影响访问;hostPort 只适合单副本、要明确知道调度节点
- ClusterIP 集群内访问;NodePort 通过
节点IP:30000-32767对外访问
十二、命令速查表
12.1 Job / CronJob
| 命令 | 用途 |
|---|---|
kubectl create job myjob --image=busybox -- echo hi |
命令行创建 Job |
kubectl apply -f job.yaml / kubectl delete jobs.batch myjob |
文件创建 / 删除 |
kubectl get jobs.batch / kubectl get all |
查看 Job 状态 |
kubectl logs <pod名> |
查看任务输出 |
kubectl describe pod <pod名> |
查看失败原因(Events / Message) |
kubectl create cronjob mycronjob --image=busybox --schedule='*/2 * * * *' -- echo hi |
创建 CronJob |
12.2 Job 常用 spec 字段
| 字段 | 作用 |
|---|---|
restartPolicy: Never / OnFailure |
Pod 失败策略 |
backoffLimit: 2 |
最大重建次数 |
completions: 6 |
需要成功多少次 |
parallelism: 2 |
同时运行几个 |
activeDeadlineSeconds: 10 |
总超时时间 |
ttlSecondsAfterFinished: 100 |
结束后自动清理 |
12.3 Service
| 命令 | 用途 |
|---|---|
kubectl create service clusterip web --tcp=8080:80 |
创建 ClusterIP Service |
kubectl expose pod mysql --port=3306 --target-port=3306 |
expose Pod |
kubectl expose pod wordpress --port=80 --target-port=80 --type NodePort |
创建 NodePort |
kubectl get svc / kubectl describe svc web |
查看 Service 与 Endpoints |
kubectl get pods --show-labels |
查看 Pod 标签(对照 selector) |
kubectl delete svc web |
删除 Service |
小结
- Job 是"跑完即走"的控制器:成功看 COMPLETIONS,失败看 restartPolicy + backoffLimit;生产任务记得配
activeDeadlineSeconds防止卡死,配 TTL 自动清理。 - 失败排查三步:
kubectl get all看状态 →describe pod看 Events/Message → 修复后先删同名 Job 再重建。 - hostPort 能不用就不用:端口冲突 + 绑定调度节点两个致命缺陷;对外服务首选 Service。
- Service 三件套:ClusterIP(集群内)、NodePort(集群外)、selector(选后端);标签写错 = Endpoints 为空。
- NodePort 范围 30000–32767,云服务器别忘了放行防火墙端口。
- MySQL 8 认证坑:
caching_sha2_password别用--ssl-mode=DISABLED,直接连 ClusterIP 即可。
本文档由 master30_2026-08-10_14_17_43.log 整理而来,命令提示符保留真实时间戳,全部 YAML 与报错均来自实际操作。
更多推荐
所有评论(0)