Kubernetes 实战指南:从零部署到熟练使用

Kubernetes(K8s)已经成为容器编排的事实标准。但对于初次接触的人,它的概念体系庞大,学习曲线陡峭——你可能在还没有运行第一个 Pod 之前,就已经被 etcd、Controller Manager、Scheduler、CNI、CRI 这些词淹没了。

本文换一个思路:先动手,再理解原理。我们会从搭建一个本地集群开始,依次完成部署应用、暴露服务、滚动更新、水平扩缩容、配置管理这些最核心的实战操作,让你在完成一件件真实的事情中建立对 K8s 的直觉。

一、核心概念速览(先记住名字,边用边理解)

Kubernetes 的核心是一个声明式系统:你告诉它"我要 3 个 nginx 副本",K8s 负责让现实与你的声明保持一致,无论期间发生什么故障。

集群的两类节点

graph TD
    subgraph Cluster[K8s 集群]
        subgraph CP[Control Plane Master Node]
            API[API Server]
            ETCD[etcd]
            SCH[Scheduler]
            CM[Controller Manager]
        end
        subgraph W1[Worker Node 1]
            KB1[kubelet]
            KP1[kube-proxy]
            RT1[container runtime]
        end
        subgraph W2[Worker Node 2]
            KB2[kubelet]
            KP2[kube-proxy]
            RT2[container runtime]
        end
        CP --> W1
        CP --> W2
    end
  • Control Plane:集群大脑。API Server 是所有操作的入口,etcd 存储集群所有状态,Scheduler 决定 Pod 跑在哪个节点,Controller Manager 负责保持现实符合声明
  • Worker Node:实际跑容器的机器。kubelet 在每个节点上执行 Control Plane 的指令,kube-proxy 处理网络规则

最常用的 6 类资源

资源一句话说明类比
PodK8s 最小调度单位,包含一个或多个容器一组住在一起的室友,共享网络和存储
Deployment管理无状态应用的副本数量与更新策略工厂的生产线,负责维持 N 条流水线同时运行
Service为一组 Pod 提供稳定的网络访问入口前台接待员,无论后端服务器怎么换,对外电话号码不变
Ingress集群的 HTTP 入口,基于域名/路径路由大厦的门卫,根据来访目的把人引导到不同楼层
ConfigMap存储非敏感配置数据(键值对/文件)应用的配置文件,可以在不重新打镜像的情况下修改
Secret存储敏感数据(密码、Token、证书)保险箱,Base64 编码存储(注意:不是加密)

二、搭建本地集群

生产环境用 kubeadm 或托管 K8s(GKE/EKS/AKS),本地学习用 minikubekind(Kubernetes IN Docker)最方便。这里用 minikube。

安装 minikube 和 kubectl

# macOS
brew install minikube kubectl

# Linux(Ubuntu/Debian)
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -m 0755 kubectl /usr/local/bin/kubectl

启动集群

# 启动单节点集群(默认用 Docker 作为驱动)
minikube start

# 验证集群状态
kubectl cluster-info
# 输出:
# Kubernetes control plane is running at https://127.0.0.1:63790
# CoreDNS is running at ...

# 查看节点
kubectl get nodes
# NAME       STATUS   ROLES           AGE   VERSION
# minikube   Ready    control-plane   1m    v1.31.0

看到 STATUS=Ready 就成功了。kubectl 是与 K8s 集群交互的命令行工具,几乎所有操作都通过它完成。

三、部署第一个应用

方法一:命令式(快速验证)

# 创建一个 nginx Deployment,运行 2 个副本
kubectl create deployment nginx --image=nginx:1.25 --replicas=2

# 查看 Deployment
kubectl get deployment nginx
# NAME    READY   UP-TO-DATE   AVAILABLE   AGE
# nginx   2/2     2            2           30s

# 查看 Pod(2 个副本自动创建)
kubectl get pods
# NAME                     READY   STATUS    RESTARTS   AGE
# nginx-7f456874f4-6x2kp   1/1     Running   0          35s
# nginx-7f456874f4-m9r8t   1/1     Running   0          35s

方法二:声明式 YAML(生产推荐)

命令式操作难以版本控制,生产环境应该用 YAML 文件描述期望状态,再通过 kubectl apply 提交。

# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "200m"
kubectl apply -f nginx-deployment.yaml
# deployment.apps/nginx created(或 configured,如果已存在)

YAML 中几个关键字段:

  • spec.replicas:期望运行的 Pod 数量
  • spec.selector.matchLabels:Deployment 通过标签找到它管理的 Pod
  • spec.template:Pod 模板,每个副本按此创建
  • resources.requests/limits:CPU 和内存的请求量与限制量,建议始终设置

四、暴露服务(Service)

Pod 的 IP 是动态分配的,扩缩容时会变化。Service 提供一个稳定的访问端点,并做负载均衡。

Service 的三种常用类型

类型适用场景说明
ClusterIP集群内部服务间通信默认类型,仅集群内可访问
NodePort本地开发/测试在每个节点上开放一个端口(30000-32767),可从集群外访问
LoadBalancer云环境生产部署调用云厂商 API 创建外部负载均衡器(AWS ELB / GCP LB 等)
# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  selector:
    app: nginx          # 匹配带有 app=nginx 标签的 Pod
  ports:
  - protocol: TCP
    port: 80            # Service 对外暴露的端口
    targetPort: 80      # 转发到 Pod 的端口
  type: NodePort
kubectl apply -f nginx-service.yaml

kubectl get service nginx-svc
# NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
# nginx-svc   NodePort   10.108.73.142   <none>        80:31234/TCP   10s

# minikube 环境获取访问 URL
minikube service nginx-svc --url
# http://127.0.0.1:31234

curl http://127.0.0.1:31234
# <!DOCTYPE html> ... nginx 欢迎页面

五、滚动更新与回滚

K8s Deployment 默认使用滚动更新(Rolling Update)策略:先启动新版本的 Pod,等它 Ready 后再停止旧版本,保证服务不中断。

执行更新

# 更新镜像版本
kubectl set image deployment/nginx nginx=nginx:1.26

# 观察更新过程
kubectl rollout status deployment/nginx
# Waiting for deployment "nginx" rollout to finish: 1 out of 2 new replicas have been updated...
# Waiting for deployment "nginx" rollout to finish: 1 old replicas are pending termination...
# deployment "nginx" successfully rolled out

# 查看更新历史
kubectl rollout history deployment/nginx
# REVISION  CHANGE-CAUSE
# 1         <none>
# 2         <none>

回滚到上一版本

# 回滚到上一个版本
kubectl rollout undo deployment/nginx

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

# 验证当前镜像版本
kubectl get deployment nginx -o jsonpath='{.spec.template.spec.containers[0].image}'
# nginx:1.25

更新策略配置

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 更新期间最多多创建 1 个 Pod(超出 replicas 的数量)
      maxUnavailable: 0  # 更新期间最多允许 0 个 Pod 不可用(保证零停机)

maxSurge: 1, maxUnavailable: 0 是最保守的零停机配置:每次先启动 1 个新 Pod,等它 Ready 后再停 1 个旧 Pod,始终保持 replicas 个 Pod 可用。

六、水平扩缩容

手动扩缩容

# 扩容到 5 个副本
kubectl scale deployment nginx --replicas=5

kubectl get pods
# NAME                     READY   STATUS    RESTARTS   AGE
# nginx-xxx-aaa            1/1     Running   0          5m
# nginx-xxx-bbb            1/1     Running   0          5m
# nginx-xxx-ccc            1/1     Running   0          10s  ← 新创建
# nginx-xxx-ddd            1/1     Running   0          10s  ← 新创建
# nginx-xxx-eee            1/1     Running   0          10s  ← 新创建

# 缩容回 2 个
kubectl scale deployment nginx --replicas=2

HPA:基于 CPU 自动扩缩容

Horizontal Pod Autoscaler(HPA)根据 CPU 或自定义指标自动调整副本数。

# 先确保 metrics-server 已安装(minikube 内置)
minikube addons enable metrics-server

# 创建 HPA:CPU 使用率超过 50% 时扩容,最多 10 个副本
kubectl autoscale deployment nginx --cpu-percent=50 --min=2 --max=10

kubectl get hpa
# NAME    REFERENCE          TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
# nginx   Deployment/nginx   2%/50%    2         10        2          30s

或者用 YAML 声明(更推荐):

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

七、配置管理(ConfigMap 与 Secret)

把配置从镜像中分离出来,是云原生应用的基本原则(12-Factor App)。

ConfigMap:非敏感配置

# app-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  # 也可以存整个文件
  nginx.conf: |
    server {
        listen 80;
        location / {
            root /usr/share/nginx/html;
        }
    }

在 Pod 中使用 ConfigMap:

spec:
  containers:
  - name: app
    image: myapp:1.0
    env:
    # 方式一:逐个引用
    - name: APP_ENV
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: APP_ENV
    envFrom:
    # 方式二:整个 ConfigMap 注入为环境变量
    - configMapRef:
        name: app-config
    volumeMounts:
    # 方式三:挂载为文件
    - name: config-volume
      mountPath: /etc/nginx/conf.d
  volumes:
  - name: config-volume
    configMap:
      name: app-config
      items:
      - key: nginx.conf
        path: default.conf

Secret:敏感数据

# 命令行创建 Secret(值会自动 Base64 编码)
kubectl create secret generic db-secret \
  --from-literal=DB_HOST=mysql.default.svc.cluster.local \
  --from-literal=DB_PASSWORD=supersecret123
# 在 Pod 中使用 Secret(与 ConfigMap 方式相同)
env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: DB_PASSWORD

注意:Secret 默认只是 Base64 编码,不是加密。生产环境应配合 etcd 加密、Vault、或 External Secrets Operator 使用。

八、健康检查(Probe)

K8s 通过三种 Probe 判断容器状态:

  • livenessProbe:容器是否还活着。失败则重启容器
  • readinessProbe:容器是否准备好接收流量。失败则从 Service 的 Endpoints 中摘除
  • startupProbe:容器是否完成启动(适合启动慢的应用)。未通过前不执行 liveness/readiness
spec:
  containers:
  - name: app
    image: myapp:1.0
    ports:
    - containerPort: 8080
    livenessProbe:
      httpGet:
        path: /healthz      # 请求这个路径,返回 2xx 则认为健康
        port: 8080
      initialDelaySeconds: 10   # 容器启动后等待 10s 再开始检查
      periodSeconds: 10          # 每 10s 检查一次
      failureThreshold: 3        # 连续失败 3 次才重启
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3
    startupProbe:
      httpGet:
        path: /healthz
        port: 8080
      failureThreshold: 30      # 允许启动最多等 30 * 10s = 5 分钟
      periodSeconds: 10

这三个 Probe 组合使用是最佳实践:startupProbe 保护慢启动,readinessProbe 保护流量只到就绪的 Pod,livenessProbe 自动重启卡死的容器。

九、常用 kubectl 命令速查

查看资源

# 查看所有 Pod(加 -A 看所有命名空间)
kubectl get pods
kubectl get pods -A
kubectl get pods -o wide    # 显示节点和 IP

# 查看详细信息(排查问题的主要命令)
kubectl describe pod <pod-name>
kubectl describe deployment nginx

# 查看日志
kubectl logs <pod-name>
kubectl logs <pod-name> -f          # 实时追踪
kubectl logs <pod-name> --previous  # 查看上次崩溃的日志

# 进入容器 shell
kubectl exec -it <pod-name> -- /bin/bash
kubectl exec -it <pod-name> -- sh   # 如果没有 bash

调试命令

# 端口转发(临时访问集群内服务,不用创建 Service)
kubectl port-forward pod/<pod-name> 8080:80
kubectl port-forward service/nginx-svc 8080:80

# 复制文件
kubectl cp <pod-name>:/path/to/file ./local-file

# 查看事件(Pod 异常时必看)
kubectl get events --sort-by='.lastTimestamp'
kubectl get events --field-selector reason=Failed

# 查看资源使用(需要 metrics-server)
kubectl top pods
kubectl top nodes

命名空间(Namespace)

# 查看所有命名空间
kubectl get namespaces

# 在指定命名空间下操作
kubectl get pods -n kube-system
kubectl apply -f app.yaml -n production

# 设置默认命名空间(避免每次都加 -n)
kubectl config set-context --current --namespace=production

十、完整示例:部署一个 Web 应用

把前面学到的所有内容串起来,部署一个有配置、有健康检查、能自动扩缩容的 Web 应用。

# complete-app.yaml
---
# 1. ConfigMap:应用配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"

---
# 2. Deployment:应用本体
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: nginx:1.25
        ports:
        - containerPort: 80
        envFrom:
        - configMapRef:
            name: webapp-config
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

---
# 3. Service:稳定访问入口
apiVersion: v1
kind: Service
metadata:
  name: webapp-svc
spec:
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP

---
# 4. HPA:自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: webapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: webapp
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60
# 一键部署所有资源
kubectl apply -f complete-app.yaml

# 验证
kubectl get all
# NAME                          READY   STATUS    RESTARTS   AGE
# pod/webapp-xxx-aaa            1/1     Running   0          1m
# pod/webapp-xxx-bbb            1/1     Running   0          1m
# pod/webapp-xxx-ccc            1/1     Running   0          1m
#
# NAME                 TYPE        CLUSTER-IP      PORT(S)   AGE
# service/webapp-svc   ClusterIP   10.100.10.100   80/TCP    1m
#
# NAME                     READY   UP-TO-DATE   AVAILABLE
# deployment.apps/webapp   3/3     3            3
#
# NAME                                    REFERENCE           TARGETS   MINPODS   MAXPODS
# horizontalpodautoscaler.autoscaling/webapp-hpa   Deployment/webapp   2%/60%    3         20

十一、关键点总结

  • K8s 是声明式系统:你描述期望状态,它持续将现实状态向期望状态靠拢
  • Pod 是最小调度单元,但直接管理 Pod 不推荐——用 Deployment 管理无状态应用
  • 标签(Label)是 K8s 内部关联的核心机制:Service 通过 selector 找 Pod,Deployment 通过 matchLabels 认领 Pod
  • 滚动更新maxSurge: 1, maxUnavailable: 0 实现零停机更新;kubectl rollout undo 随时回滚
  • ConfigMap/Secret 将配置与镜像解耦,是 12-Factor App 的核心实践;Secret 默认 Base64 编码不等于加密
  • 三类 Probe 组合使用:startupProbe 保护慢启动,readinessProbe 控制流量,livenessProbe 自动恢复
  • kubectl describekubectl get events 是排查 Pod 异常的第一工具
  • 生产部署:始终设置 resources.requests/limits,始终配置 readinessProbe,始终用 YAML 而不是命令式操作