跳转至

第一个应用

这一章不追求覆盖所有字段,而是让你完整走一遍「部署 → 暴露 → 验证 → 变更 → 回滚」的闭环。走完之后,你会对声明式 API 有一次真实的体感。

难度 · 入门 预计 35 分钟 前置:集群部署

你将学到

  • 用 Namespace 隔离应用
  • 用 Deployment 管理 Pod,用 Service 暴露服务
  • 用 Ingress 把服务发布到集群外
  • 扩缩容、滚动更新、回滚的标准命令
  • 用 get / describe / logs / exec 建立排错证据链

应用长什么样

我们使用仓库中的 ikubernetes/demoapp:v1.0 镜像。它启动一个 HTTP 服务,监听 80 端口,提供:

路径 用途
/ 首页,显示 Pod 主机名
/livez 存活探针
/readyz 就绪探针
/version 版本信息

最终拓扑:

flowchart LR
  U[客户端] --> I[Ingress<br/>demo.lab]
  I --> S[Service<br/>demoapp]
  S --> P1[Pod v1]
  S --> P2[Pod v1]
  P1 --> D[Deployment]
  P2 --> D

图:外部请求经 Ingress → Service → Pod 的路径。

步骤 1 · 创建命名空间

kubectl create namespace demo
kubectl config set-context --current --namespace=demo
kubectl get ns demo

后续命令都默认在 demo 命名空间执行。切回默认:

kubectl config set-context --current --namespace=default

步骤 2 · 创建 Deployment 和 Service

demoapp.yaml
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 访问不通」的第一大原因。

步骤 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。

步骤 4 · 用 Ingress 暴露

前提:已安装 ingress-nginx,并有一个可解析的域名。实验环境可以在本机 /etc/hosts 中把 demo.lab 指向 Ingress 入口 IP。

demoapp-ingress.yaml
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>/

步骤 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。

步骤 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 会显示可读的原因,而不是一串镜像名。

步骤 7 · 删除

kubectl delete -f demoapp-ingress.yaml
kubectl delete -f demoapp.yaml
kubectl delete namespace demo

删除 namespace 会级联删除其中所有对象。实验环境可以这样做,生产环境要非常谨慎。

排错证据链

当应用访问不通时,按下面顺序收集证据,不要跳步:

顺序 命令 你要确认什么
1 kubectl get pods -n demo Pod 是否 Running、Ready
2 kubectl describe pod <pod> -n demo Events 里有没有拉镜像、探针、调度失败
3 kubectl logs <pod> -n demo 应用是否启动、是否报错
4 kubectl get endpoints demoapp -n demo Service 是否选中了 Pod
5 kubectl get ingress -n demo Ingress 是否拿到地址、后端是否正确
6 kubectl exec -n demo <pod> -- wget -qO- localhost:80 容器内应用本身是否正常

常见现象

现象 原因 处理
Pod ImagePullBackOff 镜像名错 / 仓库不可达 describe pod 看 Events,crictl pull 测试
Pod CrashLoopBackOff 应用启动失败 / 配置错 logs --previous 看上一次崩溃日志
Pod Running 但 0/1 readiness 探针失败 describe 看 probe 失败原因,进容器 curl
Service 访问不通 selector 不匹配 / 端口错 get endpoints,对照 Service 与 Pod 标签
Ingress 404 host / path 不匹配 检查 Host 头、pathType、IngressClass
Ingress 502 后端未 Ready / 端口错 get endpoints,检查 targetPort
滚动更新卡住 新 Pod 起不来 / 资源不足 rollout status、describe、get events

进阶挑战

  1. 给 Deployment 加上 strategy.rollingUpdate.maxSurge / maxUnavailable,观察更新过程中 Pod 数量的变化。
  2. 故意把 readiness 探针路径改成 /not-exist,观察 Endpoints 如何变化,再修复。
  3. 用 kubectl diff -f demoapp.yaml 预览一次变更,再 apply。
  4. 把 Service 改成 NodePort,记录端口范围与访问方式。
  5. 用 kubectl scale 缩到 0,再访问 Service,观察返回什么。

小结

  • 一个最小应用需要:Deployment 管副本,Service 做稳定入口,Ingress 做外部暴露。
  • kubectl get 看状态,describe 看事件,logs 看应用,三者缺一不可。
  • 滚动更新与回滚是 Deployment 的核心能力,务必亲手做一次失败回滚。

延伸阅读