第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
- 生命周期:一起创建、调度、终止
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: my-app
spec:
containers:
- name: main
image: nginx:1.25
ports:
- containerPort: 80# 创建 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-pod2.1.1 Pod 的生命周期
Pod 的状态转换:
- Pending:Pod 已被接受,但容器尚未全部运行
- Running:至少一个容器正在运行
- Succeeded:所有容器正常退出(退出码 0)
- Failed:至少一个容器异常退出
- Unknown:无法获取 Pod 状态
# 查看 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 main2.2 Deployment:管理 Pod 的副本
直接创建 Pod 有几个问题:
- Pod 被删除后不会自动重建
- 无法管理多个 Pod 副本
- 无法实现滚动更新
Deployment 解决了这些问题。它通过管理 ReplicaSet 来间接管理 Pod。
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# 创建 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 30s2.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 滚动更新
# 更新镜像
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-app2.2.3 更新策略
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。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- port: 80 # Service 的端口
targetPort: 80 # Pod 的端口
type: ClusterIP2.3.1 Service 类型
ClusterIP(默认):
- 分配一个集群内部 IP
- 只能在集群内部访问
- 适合内部服务间通信
kubectl expose deployment my-app --type=ClusterIP --port=80
# 其他 Pod 可以通过 http://my-service:80 访问NodePort:
- 在每个节点上开放一个端口(30000-32767)
- 可以通过
<NodeIP>:<NodePort>从外部访问 - 适合开发测试环境
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 地址
- 适合生产环境
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 1m2.3.2 Service 的 DNS
Kubernetes 内部 DNS 为 Service 提供域名解析:
# 在集群内的 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.local2.4 Label 和 Selector
Label 是附加在 K8s 资源上的键值对,用于标识和组织资源。Selector 是通过 Label 筛选资源的机制。
metadata:
labels:
app: my-app
env: production
version: v1.0
team: backend# 通过 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,envService 通过 Label Selector 自动发现后端 Pod:
spec:
selector:
app: my-app # 选择所有 app=my-app 的 Pod当你创建或删除带有 app: my-app 标签的 Pod 时,Service 会自动更新后端列表。这种松耦合的关联方式是 Kubernetes 的核心设计模式。
2.5 Namespace
Namespace 提供逻辑隔离,用于区分不同的项目、团队或环境。
# 创建 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 完整的应用部署
# 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 demo3.2 使用 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# 一次性创建所有资源
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 中部署应用,验证隔离性。