6.2 Kubernetes 容器编排入门

预计阅读时间:14 分钟

📖 目录

学习目标

  • 理解 Kubernetes 集群架构:Control Plane 各组件职责、Worker Node 角色
  • 掌握核心工作负载资源:Pod、Deployment、Service、Ingress 的编写与操作
  • 学会使用 ConfigMap / Secret 管理配置,用 Helm 部署复杂应用
  • 能够在单机用 K3s 搭建 K8s 环境并部署应用
  • 了解常见错误模式与生产最佳实践

核心知识

本章建立在 6.1:容器底层原理 容器底层原理 之上——K8s 本质是 container runtime(containerd)的上层编排引擎。后续可衔接 6.7:GitOps 入门 GitOps 入门,用 ArgoCD/Flux 实现 Git 驱动的自动化部署。

概念一句话关联资源
Pod最小调度单元,一个或多个共享网络的容器Deployment / StatefulSet / DaemonSet
Deployment声明式管理 Pod 副本数与滚动更新ReplicaSet(自动管理)
ServicePod 的稳定网络入口 + 负载均衡ClusterIP / NodePort / LoadBalancer
Ingress七层 HTTP/HTTPS 路由到 ServiceIngress Controller(nginx / traefik)
ConfigMap / Secret将配置从镜像中解耦Pod 通过 env / volume 注入
HelmK8s 包管理器,Chart = YAML 模板 + 参数values.yaml + 模板渲染
K3s轻量 K8s(<100MB),适合单机/边缘默认 SQLite 存储(可换 etcd)+ containerd

知识关联

  • 前置知识6.1:容器底层原理 容器底层原理(Pod 依赖 namespace/cgroups)、3.1:Docker 容器入门 Docker 容器入门(容器镜像与运行时基础)
  • 后续影响6.7:GitOps 入门 GitOps 入门(ArgoCD/Flux 在 K8s 上实现 Git 驱动部署)、4.9:CI/CD 持续部署 CI/CD 持续部署(K8s 是 CI/CD 的常见目标平台)
  • 配套技术:K3s 轻量 K8s 适合单机学习,Helm 包管理器简化应用部署,Prometheus 是 K8s 生态标准监控方案

原理讲解

为什么 K8s 要用声明式 API 而不是命令式

K8s 采用声明式 API(你声明期望状态,系统自动收敛)而非命令式(你告诉系统每一步做什么),这个设计决策解决了分布式系统的核心难题:并发控制和状态一致性。在命令式模型中,如果你告诉系统"创建 3 个 Pod",系统执行后某个 Pod 崩溃,系统不知道应该重建——因为"创建 3 个 Pod"是一个已完成的动作。在声明式模型中,你声明"始终运行 3 个 Pod",控制器持续监控实际状态与期望状态的差异,Pod 崩溃后自动重建。这种"Reconcile Loop(调谐循环)"让 K8s 具备了自愈能力——你只需声明意图,系统负责实现。

声明式 API 的另一个优势是幂等性:多次 apply 同一个 YAML 文件,结果相同。这简化了 GitOps 工作流——Git 中的 YAML 文件是唯一的真相来源,任何偏离都会被自动修正。命令式操作(如 kubectl run)则不具备幂等性,重复执行会创建多个资源。

控制器循环(Reconcile Loop)如何工作

K8s 的核心架构是"控制循环":控制器持续观察实际状态,将其与 etcd 中存储的期望状态对比,执行操作缩小差距。例如 Deployment Controller 观察到期望 3 个 Pod 但实际只有 2 个(某 Pod 崩溃),立即创建新 Pod;Node Controller 观察到某节点心跳超时,驱逐该节点上的所有 Pod。这个循环永不停止,确保系统始终收敛到期望状态。控制器之间通过 API Server 通信,从不直接读写 etcd——这简化了访问控制和审计,但也意味着 API Server 是集群的单点瓶颈。

为什么推荐用 etcd 而不是 Consul

etcd 和 Consul 都是分布式 KV 存储,但设计目标不同。etcd 专为 Kubernetes 设计:强一致性(Raft 算法)、高可用(奇数节点集群)、简单的 API(PUT/GET/DELETE + Watch)。Consul 功能更丰富(内置服务发现、健康检查、多数据中心),但复杂度更高。K8s 选择 etcd 的原因:① etcd 的 Watch 机制天然适合 K8s 的事件驱动架构(API Server 通过 Watch 推送变更给控制器);② etcd 的 Raft 实现简单可靠,适合存储集群核心状态;③ etcd 的性能在小数据量(K8s 集群状态通常 <1GB)下足够好。对于纯服务发现场景(非 K8s),Consul 的内置功能更完整。

K8s 集群分为控制面(Control Plane)和工作节点(Worker Node)。控制面决定"做什么",工作节点负责"执行"。

Control Plane 核心组件:

  • API Server(kube-apiserver)——集群唯一入口,提供 REST API 和认证授权。etcd 是它的后端存储,所有组件通过 API Server 通信,从不直接读写 etcd。
  • etcd——分布式键值存储,保存集群全部状态(Pod 调度信息、Secret、ConfigMap 等)。唯一有状态组件,必须定期备份
  • Scheduler(kube-scheduler)——为新 Pod 选择合适的节点。调度算法考虑资源请求、亲和性、污点容忍、数据本地性等。
  • Controller Manager——运行一系列控制循环(Deployment Controller、Node Controller、Endpoint Controller 等),确保实际状态收敛到期望状态。

Worker Node 核心组件:

  • kubelet——节点上的代理,接收 API Server 指令并管理 Pod 生命周期(启停、健康检查)。
  • kube-proxy——维护节点 iptables/IPVS 规则,实现 Service 的负载均衡和网络路由。
  • Container Runtime——实际运行容器的底层引擎(containerd / CRI-O)。
# 控制面与工作节点交互流程
# 1. kubectl apply -f deployment.yaml → API Server
# 2. API Server 写入 etcd
# 3. Controller Manager 检测到期望状态,创建 ReplicaSet
# 4. Scheduler 为新 Pod 选择合适的 Worker Node
# 5. 目标节点的 kubelet 收到指令 → 调用 Container Runtime 启动容器
# 6. kube-proxy 更新 Service 规则,新 Pod 开始接收流量
⚠️ etcd 备份是生命线 ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db。无备份=无灾备。

调度原理与 Pod 生命周期

理解"一个 Pod 从提交到运行经历了什么",是排查一切 K8s 问题的根基:

  1. Pod 创建kubectl apply → API Server 校验并写入 etcd(Pod 状态 Pending
  2. 调度(Scheduling):Scheduler 从队列中取 Pod,执行"过滤(Feasibility)→ 打分(Scoring)"两阶段。过滤阶段排除资源不足、taint 不匹配的节点;打分阶段按资源利用率、亲和性、反亲和性等策略排序,选出最优节点。节点亲和性(nodeAffinity)、Pod 反亲和性(podAntiAffinity)和污点(taint)是三个最常用的调度控制手段
  3. kubelet 执行:节点 kubelet 观察到 Pod 绑定到本节点 → 调用 CRI(containerd)创建容器 → 执行 initContainer → 启动主容器(状态 Running
  4. 探针与就绪:livenessProbe 失败 → 重启容器;readinessProbe 失败 → 从 Service Endpoints 摘除(不接流量)
  5. 终态:任务完成(Completed)或节点故障被驱逐(Evicted)→ 控制器重建(Deployment 保证副本数)
# 观察调度决策(Events 中能看到 Scheduler 的选择)
kubectl describe pod nginx-deploy-xxxxx | sed -n '/Events:/,$p'
# 输出:
# Events:
#   Type    Reason     Age   From               Message
#   ----    ------     ----  ----               -------
#   Normal  Scheduled  12s   default-scheduler  Successfully assigned ... to node1
#   Normal  Pulling    11s   kubelet            Pulling image "nginx:alpine"
#   Normal  Started    9s    kubelet            Started container nginx

# 反亲和性示例:同一应用的多个副本尽量分散到不同节点
# affinity:
#   podAntiAffinity:
#     preferredDuringSchedulingIgnoredDuringExecution:
#       - weight: 100
#         podAffinityTerm:
#           labelSelector:
#             matchLabels:
#               app: nginx
#           topologyKey: kubernetes.io/hostname

示例代码

kubectl 基础操作

kubectl version --client
# 输出: Client Version: v1.30.0

kubectl get nodes
# 输出: NAME   STATUS   ROLES           AGE   VERSION
#       node1  Ready    control-plane   10d   v1.30.0

kubectl get pods -A
# 输出: NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE
#       kube-system   coredns-5d78c9869d-7j9k2   1/1     Running   0          10d

Deployment YAML

# nginx-deploy.yaml — Deployment 定义了 Pod 的期望状态
apiVersion: apps/v1          # Deployment 属于 apps 组 v1 版本
kind: Deployment             # 资源类型:Deployment(管理 ReplicaSet + 滚动更新)
metadata:
  name: nginx-deploy
spec:
  replicas: 3                # 期望运行 3 个 Pod 副本
  selector:
    matchLabels:
      app: nginx             # 必须与 template.metadata.labels 一致
  template:
    metadata:
      labels:
        app: nginx           # Pod 标签,Service 通过此标签选择 Pod
    spec:
      containers:
        - name: nginx
          image: nginx:alpine # 使用 alpine 精简镜像(约 40MB vs 标准 180MB)
          ports:
            - containerPort: 80
          resources:
            requests:        # 调度器据此预留资源(保证能调度到节点)
              cpu: "100m"    # 100m = 0.1 核 CPU
              memory: "128Mi"
            limits:          # 运行时上限,超出则 OOMKilled
              cpu: "500m"
              memory: "256Mi"
      terminationGracePeriodSeconds: 30  # 给 Pod 30 秒完成清理后强制终止
  strategy:
    type: RollingUpdate      # 滚动更新策略(默认)
    rollingUpdate:
      maxUnavailable: 1      # 更新时最多 1 个 Pod 不可用
      maxSurge: 1            # 更新时最多多出 1 个 Pod
kubectl apply -f nginx-deploy.yaml
kubectl rollout status deployment/nginx-deploy
# 输出: deployment "nginx-deploy" successfully rolled out

kubectl scale deployment nginx-deploy --replicas=5
kubectl set image deployment/nginx-deploy nginx=nginx:1.25-alpine
kubectl rollout history deployment/nginx-deploy
# 输出: REVISION  CHANGE-CAUSE
#        1        <none>
#        2        <none>

kubectl rollout undo deployment/nginx-deploy --to-revision=1

Service 暴露

# nginx-svc.yaml — Service 为 Pod 提供稳定的网络入口
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  type: ClusterIP                # 集群内部访问(默认);其他类型:NodePort(节点端口)、LoadBalancer(云 LB)
  selector:
    app: nginx                   # 匹配 Pod 标签,将流量转发到匹配的 Pod
  ports:
    - port: 80                   # Service 暴露端口(客户端访问端口)
      targetPort: 80             # Pod 容器监听端口(必须与 containerPort 一致)

kubectl apply -f nginx-svc.yaml
kubectl get svc nginx-svc
# 输出: NAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
#       nginx-svc   ClusterIP   10.96.123.45    <none>        80/TCP    5s

kubectl expose deployment nginx-deploy --port=80 --type=NodePort --name=nginx-nodeport
kubectl get svc nginx-nodeport
# 输出: NAME            TYPE       CLUSTER-IP     PORT(S)        AGE
#       nginx-nodeport  NodePort   10.96.66.77    80:30123/TCP   5s

Ingress

# example-ingress.yaml — Ingress 定义七层 HTTP/HTTPS 路由规则
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /  # 重写请求路径
spec:
  ingressClassName: nginx            # 指定 Ingress Controller(必须已安装)
  rules:
    - host: app.example.com          # 域名匹配(HTTP Host 头)
      http:
        paths:
          - path: /api               # 路径前缀匹配(pathType: Prefix)
            pathType: Prefix
            backend:
              service:
                name: api-svc        # 后端 Service 名称
                port:
                  number: 8080       # Service 端口
          - path: /                  # 兜底路由(优先级低于 /api)
            pathType: Prefix
            backend:
              service:
                name: web-svc
                port:
                  number: 80
  tls:                               # HTTPS 配置(需提前创建 TLS Secret)
    - hosts:
        - app.example.com
      secretName: app-tls            # 包含 tls.crt 和 tls.key 的 Secret
kubectl get ingress # 输出: NAME CLASS HOSTS ADDRESS PORTS AGE # example-ingress nginx app.example.com 80, 443 10s

ConfigMap

# app-config.yaml — ConfigMap 将配置从镜像中解耦(不敏感数据用 ConfigMap,敏感数据用 Secret)
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # 键值对形式:注入为环境变量
  DB_HOST: "db-service"             # 数据库 Service 名称(同命名空间内 DNS 自动解析)
  DB_PORT: "3306"
  LOG_LEVEL: "INFO"
  # 文件形式:通过 Volume 挂载为配置文件
  nginx.conf: |
    server {
      listen 80;
      root /usr/share/nginx/html;
    }

# 注入方式一:环境变量(在 Pod spec.containers 中添加)
# env:
#   - name: DB_HOST
#     valueFrom:
#       configMapKeyRef:
#         name: app-config          # ConfigMap 名称
#         key: DB_HOST              # 键名

# 注入方式二:Volume 挂载(适合配置文件)
# volumes:
#   - name: nginx-config
#     configMap:
#       name: app-config
#       items:
#         - key: nginx.conf
#           path: nginx.conf

kubectl get configmap app-config -o yaml
# 输出: (展开所有 data 字段)

Helm 安装

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

helm search repo nginx
# 输出: NAME                    CHART VERSION   APP VERSION   DESCRIPTION
#       bitnami/nginx          15.0.0          1.25.0        Chart for the nginx server

helm install my-nginx bitnami/nginx --namespace web --create-namespace --set service.type=LoadBalancer
# 输出: NAME: my-nginx
#       LAST DEPLOYED: ...
#       NAMESPACE: web
#       STATUS: deployed

helm list -n web
# 输出: NAME      NAMESPACE   REVISION   STATUS      CHART         APP VERSION
#       my-nginx  web         1          deployed    nginx-15.0.0  1.25.0

K3s 搭建

# 安装 K3s(单行命令)
curl -sfL https://get.k3s.io | sh -

sudo systemctl status k3s
# 输出: ● k3s.service - Lightweight Kubernetes
#        Active: active (running)

sudo k3s kubectl get nodes
# 输出: NAME    STATUS   ROLES                  AGE   VERSION
#       node1   Ready    control-plane,master   1m    v1.30.1+k3s1

# 部署测试应用
kubectl create deployment hello --image=nginx:alpine
kubectl expose deployment hello --port=80 --type=NodePort
kubectl get svc hello
# 输出: NAME    TYPE       CLUSTER-IP      PORT(S)        AGE
#       hello   NodePort   10.43.xxx.xxx   80:3xxxx/TCP   5s

# 清理
# /usr/local/bin/k3s-uninstall.sh

kubectl 高频命令速查

# 查看与诊断
kubectl get pods -o wide                    # 附带节点/IP 信息
kubectl get all -n myns                     # 查看命名空间内所有资源
kubectl describe pod                   # Events + 配置详情(排错第一步)
kubectl logs  -f --previous            # 查看上一个容器实例的日志
kubectl exec -it  -- sh                # 进入容器调试
kubectl top node / kubectl top pod          # 资源用量(需 metrics-server)

# 资源操作
kubectl apply -f deploy.yaml                # 声明式创建/更新
kubectl delete -f deploy.yaml               # 按清单删除
kubectl rollout restart deployment/nginx    # 滚动重启(改配置后常用)
kubectl rollout status deployment/nginx     # 等待滚动完成
kubectl scale deployment/nginx --replicas=5
kubectl port-forward svc/nginx-svc 8080:80  # 本地端口转发到集群内服务

# 上下文与命名空间
kubectl config get-contexts                 # 查看所有集群上下文
kubectl config use-context prod-cluster
kubectl get ns && kubectl create ns staging

故障排查五步法

# 第一步:定位问题资源
kubectl get pods -A | grep -v Running | grep -v Completed

# 第二步:查看事件(80% 的问题在这里一眼可见)
kubectl describe pod  | tail -20

# 第三步:看日志(应用层错误)
kubectl logs  --tail=100

# 第四步:验证 Service 链路
kubectl get endpoints             # Endpoints 为空 = selector 没匹配上
kubectl exec -it  -- wget -qO- http://:80
# 第五步:检查 DNS 与网络
kubectl exec -it  -- nslookup ..svc
💡 排查顺序口诀get(什么状态)→ 再 describe(发生了什么)→ 后 logs(应用为什么)→ 最后 exec(进容器验证)。大多数"Service 不通"的案例,90% 是 selector 标签写错——先查 Endpoints,别急着查网络。

常见错误

错误现象原因解决方案
Pod 一直 Pending节点资源不足(CPU/内存)或 PVC 未绑定kubectl describe pod 看 Events 段;检查节点 kubectl top nodes
Pod 反复 CrashLoopBackOff容器启动后立即退出(配置错误/app bug)kubectl logs 看退出原因;检查 readinessProbe 配置
Service 无法访问selector 不匹配 Pod 标签,或 targetPort 错误检查 kubectl describe svc 的 Endpoints 字段;确保 endpoints 非空
Ingress 返回 404Ingress Controller 未安装,或 path 前缀不匹配确认有 Ingress Controller Pod 在运行;检查 annotation 配置
ImagePullBackOff镜像名拼错、私有仓库未配置 imagePullSecretskubectl describe pod 看具体错误;配置 imagePullSecrets
Helm 安装失败values.yaml 参数不合法,或 Chart 依赖未下载helm lint 检查 Chart;helm dependency build 下载依赖

最佳实践

领域实践说明
资源限制始终设置 requests + limits防止单 Pod 耗尽节点资源,影响其他服务
健康检查配置 livenessProbe + readinessProbeliveness 决定是否重启,readiness 决定是否接流量
优雅关闭设置 terminationGracePeriodSeconds + preStop hook给容器足够时间完成清理、断连、刷盘
标签规范使用有意义的 labels(app/tier/env)便于 Service selector、监控、成本分摊
配置管理ConfigMap 存配置,Secret 存敏感数据不硬编码在镜像或 Deployment 中
权限最小化RBAC 不分配 cluster-admin每个角色只有必需的 resources+verbs
Secret 加密启用 etcd 加密 + Sealed Secretsbase64 不是加密,谁有 etcd 权限谁就能读
版本管理所有 YAML 入 Git,用 GitOps 工具同步审计追踪、回滚即时、漂移自动修正

练习题

  1. 排错实战:创建一个 Deployment,故意设置错误的 image 名称(如 nginx:nonexistent),观察 Pod 状态。用 kubectl describe pod 找到错误原因并修复。
  2. Service 联通:创建两个 Deployment(nginx + busybox),通过 ClusterIP Service 使 busybox 用 wget 访问 nginx。验证 Endpoints 正确性。
  3. Ingress 路由:部署一个 nginx Ingress Controller,配置一个 Ingress 将 /app1 路由到 svc1、/app2 路由到 svc2。用 curl -H "Host: example.com" 测试。
  4. ConfigMap 注入:创建一个 ConfigMap 包含 NGINX_HOST=myapp.local,通过 env 注入到 Pod。验证容器内 echo $NGINX_HOST 输出正确。
  5. K3s 实战:在一台 Ubuntu 虚拟机或树莓派上用 K3s 搭建单节点集群,部署 WordPress(Helm Chart bitnami/wordpress),通过 NodePort 访问。
点击查看答案
  1. 创建 Deployment 设置 image: nginx:nonexistent → Pod 状态 ImagePullBackOffkubectl describe pod 的 Events 段显示 "Failed to pull image" + 具体 404 错误 → 修正镜像名为正确 tag 后 kubectl apply -f 自动拉取并启动。
  2. 创建 nginx Deployment + 创建 ClusterIP Service → 创建 busybox Deployment 并在其 Pod 内执行 wget -O- http://nginx-svc 应拿到 nginx 页面。kubectl describe svc nginx-svc 的 Endpoints 字段应列出 nginx Pod 的 IP。
  3. 安装 nginx Ingress Controller → 创建两个 Service(svc1/svc2)→ Ingress YAML 中配置两条 path 规则 → curl -H "Host: example.com" http://localhost/app1 路由到 svc1,/app2 路由到 svc2。
  4. ConfigMap 中 data: NGINX_HOST: myapp.local → Deployment 中 env: - name: NGINX_HOST valueFrom: configMapKeyRef: name: my-config key: NGINX_HOST → Pod 内 echo $NGINX_HOST 输出 myapp.local
  5. 安装 K3s → helm repo add bitnamihelm install wordpress bitnami/wordpress --set service.type=NodePortkubectl get svc wordpress 获取 NodePort → 浏览器访问 http://node-ip:NodePort 完成 WordPress 安装。

学习检查点

学完本章后,请检验自己是否掌握以下内容:

检查项自测问题验证方法
概念理解能用自己的话解释 K8s 的 Pod、Service、Deployment 核心概念尝试向他人讲解
命令操作能不查文档完成 kubectl 常用命令和 YAML 资源定义编写在终端实际执行
原理掌握能说出 K8s 的控制平面组件和调度器工作原理画出流程图
故障排查能独立排查 Pod 处于 Pending 或 CrashLoopBackOff 状态模拟故障并修复
最佳实践能说明为什么需要配置资源限制和健康检查探针对比不同方案

本章总结

速查表

资源API 版本一句话kubectl 前缀
Podv1最小调度单元(1+N 容器)kubectl get pods
Deploymentapps/v1管理 ReplicaSet + 滚动更新kubectl get deployments
Servicev1稳定的 Pod 网络入口kubectl get svc
Ingressnetworking.k8s.io/v1七层 HTTP 路由kubectl get ingress
ConfigMapv1非敏感配置kubectl get configmap
Secretv1敏感数据(base64 编码)kubectl get secrets
Helm ReleaseHelm v3Chart 部署的实例helm list

学习路径:

  • 掌握本章 → 6.7:GitOps 入门 GitOps 入门(ArgoCD/Flux 自动化同步)
  • 深入了解容器网络 → k8s05 CNI 网络插件(Calico / Cilium / Flannel)
  • 生产集群运维 → k8s01-k8s04(etcd 备份、集群升级、HPA/VPA、NetworkPolicy)
  • 进阶主题 → k8s06 Service Mesh 入门(Istio)、Operator 模式(Kubebuilder)、eBPF(Cilium)

排错四步曲kubectl get → describe → logs → exec。善用 -o wide-o yaml 获取详细信息。

延伸阅读

  • Kubernetes 官方文档 — 概念 + 任务 + 教程,最权威的参考
  • Helm 官方文档 — Chart 开发指南、最佳实践
  • K3s 官网 — 轻量 K8s 安装与配置文档
  • Kubernetes in Action (Marko Lukša) — K8s 实战经典教材
  • The Kubernetes Book (Nigel Poulton) — 入门友好,配图丰富
  • KubeWiz — 交互式 kubectl 练习平台

常见问题

K3s 和完整 K8s 有什么区别?
K3s 是 Rancher 开发的轻量 K8s 发行版,移除云服务商插件(负载均衡器)、弃用 etcd(默认用 SQLite)、简化证书管理、打包为单文件。K3s 适合边缘计算、IoT、开发环境和资源受限场景。完整 K8s 适合生产集群、大规模部署和高可用需求。K3s API 完全兼容 K8s,迁移成本很低。
Pod 一直处于 Pending 状态怎么办?
用 kubectl describe pod <name> 看 Events 部分定位原因。常见原因:① 节点资源不足(CPU/内存不够),用 kubectl top nodes 查看;② PVC 未绑定(PersistentVolumeClaim 找不到对应 PV);③ 节点有 taints 且 Pod 没有 tolerations;④ 镜像拉取失败(ImagePullBackOff)。kubectl describe 是最重要的排障工具。
Service 类型 ClusterIP、NodePort、LoadBalancer 的区别?
ClusterIP:集群内可访问(默认),外部无法访问。NodePort:在每台节点上开放一个高位端口(30000-32767),集群外通过 NodeIP:Port 访问。LoadBalancer:云服务商提供的外部负载均衡器(如阿里云 SLB),分配公网 IP。Ingress 不是 Service 类型,是七层路由规则(类似 Nginx 反向代理)。
↑ 回到顶部