更新已弃用的 API 清单

KubernetesBeginner
立即练习

介绍

Kubernetes API 会不断演进。在旧集群上运行正常的清单文件,可能会因为 API 版本被移除或字段结构发生变化而在新集群上失效。在 CKAD 的工作中,这通常表现为一项实际的维护任务:阅读错误信息、确定当前的 API 版本、更新清单,并验证新版本已被集群接受。

在本实验中,你将把一个旧的 Ingress 清单从 networking.k8s.io/v1beta1 迁移到 networking.k8s.io/v1。你将首先创建一个供 Ingress 引用的 Service,观察旧清单报错,使用 kubectl 发现命令检查当前的 API,重写清单,最后应用修正后的 Ingress。

准备 Service 引用

在此步骤中,你将准备一个干净的命名空间和一个简单的 Service。旧的和新的 Ingress 清单都将指向此 Service,这样迁移工作的重点将保持在 API 结构上,而不是 Pod 的就绪状态或流量路由上。

从 /home/labex/project 开始:

mkdir -p /home/labex/project
cd /home/labex/project

检查 Kubernetes API 服务器是否可达。如果虚拟机刚启动且节点尚未就绪,请等待几秒钟后再运行该命令。

kubectl get nodes --request-timeout=5s

预期输出包括:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

创建一个名为 ckad-prep-api-migration 的命名空间,并将其设置为当前命名空间:

kubectl create namespace ckad-prep-api-migration
kubectl config set-context --current --namespace=ckad-prep-api-migration

创建一个名为 app-web 的 ClusterIP Service。--tcp=80:80 选项表示 Service 监听端口 80,如果存在匹配的 Pod,它会将流量转发到目标端口 80。对于本次迁移实验,Service 对象本身就足够了,因为你验证的是 Ingress API 字段,而不是 HTTP 流量。

kubectl create service clusterip app-web --tcp=80:80

确认 Service 已存在:

kubectl get service app-web

预期输出包括:

NAME      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
app-web   ClusterIP   ...          <none>        80/TCP    ...

观察已弃用清单的失败情况

在此步骤中,你将创建一个旧的 Ingress 清单,并让当前集群拒绝它。这是一种很有用的练习,因为迁移任务中的第一个线索通常就是 API 服务器的错误消息。

创建 old-ingress.yaml:

cat > old-ingress.yaml <<'EOF'
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: app-web
spec:
  rules:
  - host: app.example.local
    http:
      paths:
      - path: /
        backend:
          serviceName: app-web
          servicePort: 80
EOF

读取文件的前几行。apiVersion 字段告诉 Kubernetes 应该由哪个 API 组和版本来处理此对象。

sed -n '1,20p' old-ingress.yaml

预期输出包括:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
...
          serviceName: app-web
          servicePort: 80

请求 API 服务器验证该清单而不创建它。--dry-run=server 会将对象发送到 API 服务器进行验证,而 || true 可以让教程继续进行,因为这种失败是预料之中的。

kubectl apply --dry-run=server -f old-ingress.yaml || true

预期输出包含类似以下的错误:

error: resource mapping not found for name: "app-web" namespace: "" from "old-ingress.yaml": no matches for kind "Ingress" in version "networking.k8s.io/v1beta1"

重要的证据是 no matches for kind "Ingress" in version "networking.k8s.io/v1beta1"。这意味着当前集群不再提供此 API 版本。

发现当前的 Ingress API 结构

在此步骤中,你将在重写文件之前检查当前的 Ingress API。这是保持迁移工作规范化的关键部分:当集群可以向你展示可接受的结构时,不要去猜测新的字段。

列出当前提供的 Ingress 资源:

kubectl api-resources | grep -i '^ingresses'

预期输出包括:

ingresses   ing   networking.k8s.io/v1   true   Ingress

使用 kubectl explain 检查当前的后端 Service 字段。路径很长,因为 Service 后端嵌套在 spec.rules[].http.paths[].backend.service 下。

kubectl explain ingress.spec.rules.http.paths.backend.service --recursive | sed -n '1,80p'

预期输出包括:

GROUP:      networking.k8s.io
KIND:       Ingress
VERSION:    v1

FIELD: service <IngressServiceBackend>
...
  name        <string> -required-
  port        <ServiceBackendPort> -required-

现在创建迁移后的 current-ingress.yaml。新清单使用了 networking.k8s.io/v1,添加了 pathType,并将 serviceName 和 servicePort 替换为嵌套的 service.name 和 service.port.number 字段。

cat > current-ingress.yaml <<'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-web
spec:
  rules:
  - host: app.example.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-web
            port:
              number: 80
EOF

通过服务端试运行(server-side dry run)验证迁移后的清单:

kubectl apply --dry-run=server -f current-ingress.yaml

预期输出:

ingress.networking.k8s.io/app-web created (server dry run)

应用并检查迁移后的 Ingress

在此步骤中,你将创建迁移后的 Ingress 并检查 Kubernetes 存储的字段。应用资源是证明清单现在符合当前 API 服务器要求的最终证据。

应用迁移后的清单:

kubectl apply -f current-ingress.yaml

预期输出:

ingress.networking.k8s.io/app-web created

列出 Ingress:

kubectl get ingress app-web

预期输出包括:

NAME      CLASS   HOSTS               ADDRESS   PORTS   AGE
app-web   ...     app.example.local    ...       80      ...

直接从存储的对象中检查迁移后的后端字段:

kubectl get ingress app-web -o jsonpath='{.apiVersion}{" "}{.spec.rules[0].http.paths[0].pathType}{" "}{.spec.rules[0].http.paths[0].backend.service.name}{":"}{.spec.rules[0].http.paths[0].backend.service.port.number}{"\n"}'

预期输出:

networking.k8s.io/v1 Prefix app-web:80

此输出同时证明了三件事:对象使用了当前的 API 版本,路径具有明确的 pathType,并且后端使用了 networking.k8s.io/v1 所要求的嵌套 Service 引用。

总结

你练习了一个实用的 API 迁移工作流。你创建了一个旧的 networking.k8s.io/v1beta1 Ingress 清单,使用服务端试运行确认当前集群拒绝了它,检查了当前提供的 networking.k8s.io/v1 API,通过添加 pathType 和嵌套的 Service 引用重写了后端字段,最后应用并检查了迁移后的 Ingress。