3Kubernetes 实战

在 Kubernetes 中运行应用

部署第一个应用

DeploymentServicePod

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第3章:在Kubernetes中运行应用程序(Running Applications in Kubernetes)

一、导读

1.1 本章学习目标

本章是 Kubernetes 的实战入门。通过一个完整的应用部署流程,你将学习 Kubernetes 最核心的三个资源:Pod、Deployment 和 Service。本章采用"先手动操作,再理解自动化"的方式——先手动创建 Pod,理解其局限性,然后引入 Deployment 管理 Pod 副本,最后通过 Service 暴露网络访问。

本章的核心学习目标包括:

  • 掌握 Pod 的创建和管理:理解 Pod 是 K8s 的最小调度单元,学会使用 kubectl 操作 Pod。
  • 理解 Deployment 的工作原理:Deployment 如何管理 Pod 副本、如何实现滚动更新和回滚。
  • 掌握 Service 的网络模型:ClusterIP、NodePort、LoadBalancer 三种类型的区别和使用场景。
  • 理解 Label 和 Selector 机制:K8s 资源之间如何通过标签关联。
  • 学会使用 Namespace 进行逻辑隔离:多环境、多团队的资源隔离。
  • 掌握 kubectl 的常用命令:日常运维的基本操作。

1.2 为什么本章如此重要

本章是你从"理解概念"到"动手实践"的转折点。Pod、Deployment、Service 是 Kubernetes 中使用频率最高的三个资源,几乎每个应用都会用到它们。不理解这三个资源,就无法在 K8s 中运行任何应用。

更重要的是,这三个资源之间的关系体现了 Kubernetes 的核心设计模式:Pod 是工作负载,Deployment 管理 Pod 的生命周期,Service 为 Pod 提供网络抽象。理解这个模式,你就能举一反三地理解其他资源(StatefulSet、DaemonSet、Ingress 等)。

1.3 核心问题引导

为什么不直接创建 Pod,而要通过 Deployment 来管理?

Pod 重启后 IP 地址会变化吗?Service 如何解决这个问题?

Label 和 Selector 的关系是什么?为什么 K8s 使用这种松耦合的关联方式?

滚动更新时,如何保证服务不中断?

如何在不停机的情况下回滚到上一版本?


二、核心概念详解

2.1 Pod:最小调度单元

Pod 是 Kubernetes 中最小的可部署单元。一个 Pod 包含一个或多个容器,这些容器共享:

  • 网络命名空间:相同的 IP 地址和端口空间
  • 存储卷:可以挂载相同的 Volume
  • 生命周期:一起创建、调度、终止
yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: my-app
spec:
  containers:
    - name: main
      image: nginx:1.25
      ports:
        - containerPort: 80
bash
# 创建 Pod
kubectl apply -f pod.yaml

# 查看 Pod
kubectl get pods
kubectl get pods -o wide  # 显示更多信息
kubectl get pods -o yaml  # 完整 YAML

# 查看 Pod 详情
kubectl describe pod my-pod

# 查看 Pod 日志
kubectl logs my-pod
kubectl logs my-pod -c main  # 多容器时指定容器名
kubectl logs -f my-pod       # 实时跟踪

# 进入 Pod
kubectl exec -it my-pod -- /bin/bash

# 端口转发
kubectl port-forward pod/my-pod 8080:80

# 删除 Pod
kubectl delete pod my-pod

2.1.1 Pod 的生命周期

Pod 的状态转换:

  • Pending:Pod 已被接受,但容器尚未全部运行
  • Running:至少一个容器正在运行
  • Succeeded:所有容器正常退出(退出码 0)
  • Failed:至少一个容器异常退出
  • Unknown:无法获取 Pod 状态
bash
# 查看 Pod 状态
kubectl get pods
# NAME     READY   STATUS    RESTARTS   AGE
# my-pod   1/1     Running   0          5m

# 查看 Pod 事件
kubectl describe pod my-pod
# Events:
#   Type    Reason     Age   From               Message
#   Normal  Scheduled  5m    default-scheduler  Successfully assigned default/my-pod to node-1
#   Normal  Pulled     5m    kubelet            Container image "nginx:1.25" already present
#   Normal  Created    5m    kubelet            Created container main
#   Normal  Started    5m    kubelet            Started container main

2.2 Deployment:管理 Pod 的副本

直接创建 Pod 有几个问题:

  • Pod 被删除后不会自动重建
  • 无法管理多个 Pod 副本
  • 无法实现滚动更新

Deployment 解决了这些问题。它通过管理 ReplicaSet 来间接管理 Pod。

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: nginx:1.25
          ports:
            - containerPort: 80
bash
# 创建 Deployment
kubectl apply -f deployment.yaml

# 查看 Deployment
kubectl get deployments
# NAME     READY   UP-TO-DATE   AVAILABLE   AGE
# my-app   3/3     3            3           30s

# 查看 ReplicaSet
kubectl get replicasets
# NAME                DESIRED   CURRENT   READY   AGE
# my-app-abc12        3         3         3       30s

# 查看 Pod
kubectl get pods
# NAME                      READY   STATUS    RESTARTS   AGE
# my-app-abc12-xyz1         1/1     Running   0          30s
# my-app-abc12-xyz2         1/1     Running   0          30s
# my-app-abc12-xyz3         1/1     Running   0          30s

2.2.1 Deployment → ReplicaSet → Pod

Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。这个层级关系是 K8s 的核心设计模式:

  • Deployment:定义期望状态(副本数、镜像版本、更新策略)
  • ReplicaSet:维持指定数量的 Pod 副本
  • Pod:实际运行的工作负载

当你更新 Deployment 的镜像版本时:

Deployment Controller 创建新的 ReplicaSet(新版本)

新的 ReplicaSet 逐步创建新版本的 Pod

旧的 ReplicaSet 逐步缩减 Pod 数量

最终,旧 ReplicaSet 的副本数为 0

2.2.2 滚动更新

bash
# 更新镜像
kubectl set image deployment/my-app my-app=nginx:1.26

# 查看更新状态
kubectl rollout status deployment/my-app
# Waiting for deployment "my-app" rollout to finish:
#   1 of 3 updated replicas are available...
#   2 of 3 updated replicas are available...
#   deployment "my-app" successfully rolled out

# 查看更新历史
kubectl rollout history deployment/my-app
# REVISION  CHANGE-CAUSE
# 1         <none>
# 2         kubectl set image deployment/my-app my-app=nginx:1.26

# 回滚到上一版本
kubectl rollout undo deployment/my-app

# 回滚到指定版本
kubectl rollout undo deployment/my-app --to-revision=1

# 暂停/恢复更新
kubectl rollout pause deployment/my-app
kubectl rollout resume deployment/my-app

2.2.3 更新策略

yaml
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1         # 更新时最多多出1个Pod
      maxUnavailable: 0   # 更新时不允许有Pod不可用
  • maxSurge:更新过程中可以超出期望副本数的最大数量
  • maxUnavailable:更新过程中允许不可用的最大 Pod 数量

2.3 Service:稳定的网络入口

Pod 的 IP 是动态的——Pod 重启后 IP 会变化。Service 提供稳定的 IP 地址和 DNS 名称,并通过 Label Selector 自动发现后端 Pod。

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - port: 80          # Service 的端口
      targetPort: 80    # Pod 的端口
  type: ClusterIP

2.3.1 Service 类型

ClusterIP(默认):

  • 分配一个集群内部 IP
  • 只能在集群内部访问
  • 适合内部服务间通信
bash
kubectl expose deployment my-app --type=ClusterIP --port=80
# 其他 Pod 可以通过 http://my-service:80 访问

NodePort:

  • 在每个节点上开放一个端口(30000-32767)
  • 可以通过 <NodeIP>:<NodePort> 从外部访问
  • 适合开发测试环境
bash
kubectl expose deployment my-app --type=NodePort --port=80
kubectl get services
# NAME         TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
# my-service   NodePort   10.96.100.1    <none>        80:31234/TCP   10s
# 可以通过 http://<any-node-ip>:31234 访问

LoadBalancer:

  • 在 NodePort 基础上,调用云厂商 API 创建负载均衡器
  • 提供外部 IP 地址
  • 适合生产环境
bash
kubectl expose deployment my-app --type=LoadBalancer --port=80
kubectl get services
# NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
# my-service   LoadBalancer   10.96.100.1    203.0.113.50    80:31234/TCP   1m

2.3.2 Service 的 DNS

Kubernetes 内部 DNS 为 Service 提供域名解析:

bash
# 在集群内的 Pod 中
curl http://my-service.default.svc.cluster.local
curl http://my-service.default.svc
curl http://my-service
# 三种格式都可以访问,K8s DNS 会自动解析

# 跨 Namespace 访问
curl http://my-service.other-namespace.svc.cluster.local

2.4 Label 和 Selector

Label 是附加在 K8s 资源上的键值对,用于标识和组织资源。Selector 是通过 Label 筛选资源的机制。

yaml
metadata:
  labels:
    app: my-app
    env: production
    version: v1.0
    team: backend
bash
# 通过 Label 筛选资源
kubectl get pods -l app=my-app
kubectl get pods -l app=my-app,env=production
kubectl get pods -l 'app in (my-app,other-app)'
kubectl get pods -l 'version notin (v1.0)'

# 给资源添加 Label
kubectl label pod my-pod env=production

# 删除 Label
kubectl label pod my-pod env-

# 查看 Label
kubectl get pods --show-labels
kubectl get pods -L app,env

Service 通过 Label Selector 自动发现后端 Pod:

yaml
spec:
  selector:
    app: my-app  # 选择所有 app=my-app 的 Pod

当你创建或删除带有 app: my-app 标签的 Pod 时,Service 会自动更新后端列表。这种松耦合的关联方式是 Kubernetes 的核心设计模式。

2.5 Namespace

Namespace 提供逻辑隔离,用于区分不同的项目、团队或环境。

bash
# 创建 Namespace
kubectl create namespace development
kubectl create namespace production
kubectl create namespace staging

# 在指定 Namespace 中操作
kubectl get pods -n development
kubectl apply -f deployment.yaml -n production

# 查看所有 Namespace
kubectl get namespaces
# NAME           STATUS   AGE
# default        Active   30d
# kube-system    Active   30d
# kube-public    Active   30d
# development    Active   10m
# production     Active   10m
# staging        Active   10m

# 设置默认 Namespace
kubectl config set-context --current --namespace=development

三、实战案例

3.1 完整的应用部署

bash
# 1. 创建 Namespace
kubectl create namespace demo

# 2. 创建 Deployment
kubectl create deployment web --image=nginx:1.25 --replicas=3 -n demo

# 3. 查看资源
kubectl get all -n demo
# NAME                       READY   STATUS    RESTARTS   AGE
# pod/web-abc12-xyz1         1/1     Running   0          30s
# pod/web-abc12-xyz2         1/1     Running   0          30s
# pod/web-abc12-xyz3         1/1     Running   0          30s
#
# NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
# deployment.apps/web   3/3     3            3           30s
#
# NAME                             DESIRED   CURRENT   READY   AGE
# replicaset.apps/web-abc12        3         3         3       30s

# 4. 创建 Service
kubectl expose deployment web --port=80 --type=NodePort -n demo

# 5. 访问应用
kubectl get svc web -n demo
# NAME   TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
# web    NodePort   10.96.100.1   <none>        80:31234/TCP   10s

# 6. 扩缩容
kubectl scale deployment web --replicas=5 -n demo

# 7. 滚动更新
kubectl set image deployment/web web=nginx:1.26 -n demo
kubectl rollout status deployment/web -n demo

# 8. 回滚
kubectl rollout undo deployment/web -n demo

3.2 使用 YAML 声明式部署

yaml
# complete-app.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.25-alpine
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: demo
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
  type: NodePort
bash
# 一次性创建所有资源
kubectl apply -f complete-app.yaml

# 查看
kubectl get all -n demo

四、本章小结

本章通过实战学习了 Kubernetes 的三个核心资源:

Pod 是最小调度单元,包含一个或多个共享网络/存储的容器。

Deployment 管理 Pod 的副本数、滚动更新和回滚,是生产环境管理无状态应用的标准方式。

Service 为 Pod 提供稳定的网络入口,通过 Label Selector 自动发现后端 Pod。

Label 和 Selector 是 K8s 资源关联的核心机制,实现松耦合的声明式管理。

Namespace 提供逻辑隔离,支持多租户和多环境。


五、思考与练习

思考题

为什么 Pod 被删除后,Deployment 会自动创建新的 Pod?这个过程的内部机制是什么?

滚动更新时,如果新版本的 Pod 启动失败(Liveness Probe 失败),会发生什么?

Service 的 ClusterIP 是如何实现负载均衡的?iptables 模式和 IPVS 模式有什么区别?

如果两个 Deployment 使用了相同的 Label,Service 会将流量路由到哪些 Pod?

Namespace 之间的资源是否完全隔离?有哪些资源是跨 Namespace 的?

练习题

创建一个3副本的 Nginx Deployment,配置 NodePort Service,从集群外部访问。

修改 Deployment 的镜像版本,观察滚动更新过程。然后回滚到上一版本。

手动删除一个 Pod,观察 Deployment 的行为。记录从删除到重建的时间。

创建两个 Namespace(dev 和 prod),分别在两个 Namespace 中部署应用,验证隔离性。

3/8在 Ku
0%