1.6. 第一个应用
这一章不追求覆盖所有字段,而是让你完整走一遍「部署 → 暴露 → 验证 → 变更 → 回滚」的闭环。走完之后,你会对声明式 API 有一次真实的体感。
- 层级:
L1 · 入门
- 难度:
入门
- 预计:
35 分钟
- 前置:
集群部署
1.6.1. 你将学到
用 Namespace 隔离应用
用 Deployment 管理 Pod,用 Service 暴露服务
用 Ingress 把服务发布到集群外
扩缩容、滚动更新、回滚的标准命令
用
get / describe / logs / exec建立排错证据链
1.6.2. 应用长什么样
我们使用仓库中的 ikubernetes/demoapp:v1.0 镜像。它启动一个 HTTP 服务,监听 80 端口,提供:
路径 |
用途 |
|---|---|
|
首页,显示 Pod 主机名 |
|
存活探针 |
|
就绪探针 |
|
版本信息 |
最终拓扑:
图:外部请求经 Ingress → Service → Pod 的路径。
1.6.3. 步骤 1 · 创建命名空间
kubectl create namespace demo
kubectl config set-context --current --namespace=demo
kubectl get ns demo
后续命令都默认在 demo 命名空间执行。切回默认:
kubectl config set-context --current --namespace=default
1.6.4. 步骤 2 · 创建 Deployment 和 Service
apiVersion: apps/v1
kind: Deployment
metadata:
name: demoapp
namespace: demo
labels:
app: demoapp
spec:
replicas: 2
selector:
matchLabels:
app: demoapp
template:
metadata:
labels:
app: demoapp
version: v1.0
spec:
containers:
- name: demoapp
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
path: /readyz
port: http
initialDelaySeconds: 3
periodSeconds: 5
livenessProbe:
httpGet:
path: /livez
port: http
initialDelaySeconds: 10
periodSeconds: 10
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: demoapp
namespace: demo
spec:
selector:
app: demoapp
ports:
- name: http
port: 80
targetPort: http
kubectl apply -f demoapp.yaml
kubectl get deploy,rs,pod,svc -n demo -o wide
预期:
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/demoapp 2/2 2 2 20s
NAME READY STATUS RESTARTS AGE
pod/demoapp-6c9f7d8b5c-2xk9p 1/1 Running 0 20s
pod/demoapp-6c9f7d8b5c-9m4qz 1/1 Running 0 20s
NAME TYPE CLUSTER-IP PORT(S) AGE
service/demoapp ClusterIP 10.96.123.45 80/TCP 20s
备注
selector 必须匹配
Deployment 的 spec.selector.matchLabels 必须与 template.metadata.labels 匹配,否则 API Server 直接拒绝。Service 的 selector 则匹配 Pod 标签,不匹配时 Endpoints 为空,这是「Service 访问不通」的第一大原因。
1.6.5. 步骤 3 · 在集群内验证
先看 Endpoints 是否就绪:
kubectl get endpoints demoapp -n demo
kubectl get endpointslice -n demo
预期 ENDPOINTS 至少有两个 Pod IP。然后用临时 Pod 访问:
kubectl run curl --image=curlimages/curl:8.10.1 --rm -it --restart=Never -n demo -- \
curl -s http://demoapp/
或者用 port-forward 在本地验证:
kubectl port-forward -n demo svc/demoapp 8080:80
# 另一个终端
curl -s http://127.0.0.1:8080/
预期返回包含 Pod 主机名的 HTML 或 JSON。
1.6.6. 步骤 4 · 用 Ingress 暴露
前提:已安装 ingress-nginx,并有一个可解析的域名。实验环境可以在本机 /etc/hosts 中把 demo.lab 指向 Ingress 入口 IP。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demoapp
namespace: demo
spec:
ingressClassName: nginx
rules:
- host: demo.lab
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demoapp
port:
number: 80
kubectl apply -f demoapp-ingress.yaml
kubectl get ingress -n demo
获取入口地址:
kubectl get svc -n ingress-nginx ingress-nginx-controller
如果类型是 LoadBalancer,用 EXTERNAL-IP;如果是 NodePort,用 节点IP:NodePort。然后:
curl -H 'Host: demo.lab' http://<INGRESS_IP>/
1.6.7. 步骤 5 · 扩缩容
kubectl scale deploy demoapp -n demo --replicas=5
kubectl rollout status deploy/demoapp -n demo
kubectl get pods -n demo -l app=demoapp
缩回 2 个:
kubectl scale deploy demoapp -n demo --replicas=2
观察 Pod 被终止时,Service 的 Endpoints 会先摘除再删除,避免流量打到正在退出的 Pod。
1.6.8. 步骤 6 · 滚动更新与回滚
更新镜像版本:
kubectl set image deploy/demoapp demoapp=ikubernetes/demoapp:v1.1 -n demo
kubectl rollout status deploy/demoapp -n demo
kubectl rollout history deploy/demoapp -n demo
验证新版本:
kubectl get pods -n demo -l app=demoapp -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'
回滚到上一版:
kubectl rollout undo deploy/demoapp -n demo
kubectl rollout status deploy/demoapp -n demo
回滚到指定版本:
kubectl rollout history deploy/demoapp -n demo
kubectl rollout undo deploy/demoapp -n demo --to-revision=1
小技巧
把变更原因写进 annotation
kubectl annotate deploy/demoapp -n demo \
kubernetes.io/change-cause="upgrade demoapp to v1.1" --overwrite
这样 rollout history 会显示可读的原因,而不是一串镜像名。
1.6.9. 步骤 7 · 删除
kubectl delete -f demoapp-ingress.yaml
kubectl delete -f demoapp.yaml
kubectl delete namespace demo
删除 namespace 会级联删除其中所有对象。实验环境可以这样做,生产环境要非常谨慎。
1.6.10. 排错证据链
当应用访问不通时,按下面顺序收集证据,不要跳步:
顺序 |
命令 |
你要确认什么 |
|---|---|---|
1 |
|
Pod 是否 Running、Ready |
2 |
|
Events 里有没有拉镜像、探针、调度失败 |
3 |
|
应用是否启动、是否报错 |
4 |
|
Service 是否选中了 Pod |
5 |
|
Ingress 是否拿到地址、后端是否正确 |
6 |
|
容器内应用本身是否正常 |
1.6.10.1. 常见现象
现象 |
原因 |
处理 |
|---|---|---|
Pod |
镜像名错 / 仓库不可达 |
|
Pod |
应用启动失败 / 配置错 |
|
Pod |
readiness 探针失败 |
|
Service 访问不通 |
selector 不匹配 / 端口错 |
|
Ingress 404 |
host / path 不匹配 |
检查 |
Ingress 502 |
后端未 Ready / 端口错 |
|
滚动更新卡住 |
新 Pod 起不来 / 资源不足 |
|
1.6.11. 进阶挑战
给 Deployment 加上
strategy.rollingUpdate.maxSurge/maxUnavailable,观察更新过程中 Pod 数量的变化。故意把 readiness 探针路径改成
/not-exist,观察 Endpoints 如何变化,再修复。用
kubectl diff -f demoapp.yaml预览一次变更,再 apply。把 Service 改成
NodePort,记录端口范围与访问方式。用
kubectl scale缩到 0,再访问 Service,观察返回什么。
1.6.12. 小结
一个最小应用需要:Deployment 管副本,Service 做稳定入口,Ingress 做外部暴露。
kubectl get看状态,describe看事件,logs看应用,三者缺一不可。滚动更新与回滚是 Deployment 的核心能力,务必亲手做一次失败回滚。