Skip to content

在 Kubernetes 中首次接入 HTTPS:TLS Secret 与入口边界 ​

本文适用于证书已签发、网站已运行在 Kubernetes 中,需要首次配置 HTTPS 入口的客户。通过 KeyManager 导出证书与私钥,手动创建 TLS Secret,再由入口 Controller 加载。本文不配置自动申请或续期工具。

操作流程: 确认业务与入口 → KeyManager 导出材料 → 创建 TLS Secret → 配置 HTTPS 引用 → 验证访问。

下文输出摘自测试集群的实际执行结果,域名统一替换为 trustasia.com,资源名称与本文示例对齐,省略无关记录和字段。IP、运行时长以你的实际输出为准。

第一步:确认网站和现有入口 ​

  • 在能够使用 kubectl 管理该集群的终端操作,确认原网站正常。
  • 记下网站所在的 Namespace、Service 名称和端口,以及现有入口是 Ingress 还是 Gateway。
  • 已签发网站域名的证书,能从 KeyManager 导出证书和私钥。
  • 保存原入口配置,后面沿用它,不重建网站。

入口控制器需已运行,且客户端可达其 HTTPS 端口。缺少 Gateway 组件或裸机入口地址时,再看文末对应说明;已有 Ingress 不安装 Gateway,手动部署也不需要 cert-manager。

在集群管理终端确认当前访问配置:

bash
kubectl config current-context

实测输出:

text
default

default 是访问配置的名称,不是 Namespace;确认它连接的是你要操作的集群。再查询网站使用的入口类型:

bash
kubectl get ingress -A
kubectl get gateway -A

Ingress 输出节选:

text
NAMESPACE   NAME          CLASS              HOSTS           ADDRESS   PORTS     AGE
web         web-ingress   tutorial-traefik   trustasia.com             80, 443   34m

找到 HOSTS 为网站域名的那一行,记下 NAMESPACE=web、NAME=web-ingress。本次查询的是已启用 HTTPS 的测试入口,因此显示 80, 443;首次启用前可能只有 80。ADDRESS 为空不一定表示不可用,本测试入口通过节点端口访问。

Gateway 输出节选:

text
NAMESPACE   NAME          CLASS       ADDRESS      PROGRAMMED   AGE
web         web-gateway   envoy-web   30.82.1.88   True         3d13h

这里记下 NAMESPACE=web、NAME=web-gateway,CLASS=envoy-web 是第三步沿用的 GatewayClass。Gateway 列表不显示网站域名,需结合原网站清单,或执行下面命令查看监听:

bash
kubectl describe gateway web-gateway -n web

输出中网站监听的节选(本次测试入口已启用 HTTPS):

text
Spec:
  Listeners:
    Hostname:  trustasia.com
    Name:      https
    Port:      443
    Protocol:  HTTPS

看 Hostname 是否为目标网站域名,Name 是该监听的名称;首次启用前,在原配置中找到对应的 HTTP 监听,第三步在旁边添加 HTTPS。使用 Ingress 的集群没有 Gateway 资源类型时,跳过 Gateway 查询。

以下以网站位于 web 为例,查看后端 Service:

bash
kubectl get service -n web

实测输出:

text
NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
web-service   ClusterIP   10.43.38.255    <none>        80/TCP    3d13h

在原路由中确认后端是这个 Service,记下 NAME=web-service、服务端口 80。CLUSTER-IP 是集群内部地址,不填写为网站公网解析地址。

记录什么本文示例后续用途
网站入口所在 NamespacewebSecret 和入口放在同一 Namespace
网站实际域名trustasia.comHTTPS 入口配置和浏览器访问
原后端 Service 及其端口web-service:80沿用原路由中的后端,不是 Pod 端口
新证书 Secret 名称web-tls第二步创建,第三步引用

后面的名称统一替换为实际值。保存原 Ingress,或 Gateway 和 HTTPRoute 的部署清单副本。

第二步:导出材料并创建 TLS Secret ​

在 CertCloud 已签发订单中点击“下载证书 → 打开 KeyManager”,同步到保存了匹配私钥的 KeyManager 环境。在“证书管理 → 更多 → 导出证书”选择 Nginx(PEM),取得完整证书链和匹配私钥。这里仅使用该格式的 PEM 文件,不要求 Kubernetes 安装 Nginx。图示见KeyManager 下载与导出指南。

将导出文件传到运行 kubectl 的那台机器,保存在受限目录中。下面的 --cert 填完整证书链路径,--key 填同一次导出的私钥路径,文件名按实际导出结果填写。

示例使用已有 web Namespace、名为 web-tls 的新 Secret、trustasia.com 和已有 web-service:80,请统一替换为实际值。先确认目标 Secret 不存在;已存在时先核对其用途及管理方,不直接覆盖。

bash
kubectl get namespace web
kubectl get secret web-tls -n web

确认查询返回目标 Secret 的 NotFound,而不是权限或连接错误后,再创建:

未创建时的实测输出(名称已对齐):

text
Error from server (NotFound): secrets "web-tls" not found

这里的 NotFound 表示这个名字尚未使用,可以继续创建:

bash
kubectl create secret tls web-tls -n web \
  --cert=/root/cert-files/fullchain.pem \
  --key=/root/cert-files/privkey.pem
kubectl get secret web-tls -n web

确认 Secret 类型为 kubernetes.io/tls,不打印私钥。本文将 Secret 与 Gateway 或 Ingress 放在同一 Namespace;Service 也在该 Namespace 中,不涉及跨命名空间引用。Secret 创建成功不等于入口已经加载证书,继续第三步。

创建成功的实测输出(名称已对齐):

text
secret/web-tls created
NAME      TYPE                DATA   AGE
web-tls   kubernetes.io/tls   2      0s

记下 NAME=web-tls,第三步的证书引用填写这个名称。DATA=2 表示含证书和私钥两个数据项,不代表入口已经加载。

第三步:首次配置 HTTPS 入口 ​

按当前入口选择下面一种方式。已有 Gateway 或 Ingress 时只合并证书引用,不为导入证书创建第二套入口。

3.1 使用 Gateway ​

打开网站原有的 Gateway 和 HTTPRoute 部署清单,在原 HTTP listener 旁新增 HTTPS listener,并让该网站的 HTTPRoute 同时关联它。下面展示修改后的上下文,示例名称须换成原清单中的值,保留其他监听和路由。将修改后的完整清单保存为 gateway.yaml:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web-gateway
  namespace: web
spec:
  gatewayClassName: REPLACE_WITH_GATEWAY_CLASS
  listeners:
    - name: http
      hostname: trustasia.com
      port: 80
      protocol: HTTP
      allowedRoutes:
        namespaces:
          from: Same
    # 新增 HTTPS listener,引用第二步的 Secret
    - name: https
      hostname: trustasia.com
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - group: ""
            kind: Secret
            name: web-tls
      allowedRoutes:
        namespaces:
          from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web
  namespace: web
spec:
  parentRefs:
    - name: web-gateway
      sectionName: http
    # 让原网站路由也接收新 HTTPS listener 的请求
    - name: web-gateway
      sectionName: https
  hostnames:
    - trustasia.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: web-service
          port: 80

按以下对应关系填写,不要用示例覆盖网站完整业务配置:

  • namespace、Gateway 和 HTTPRoute 的 name:沿用原资源的 Namespace 和名称。
  • gatewayClassName:沿用原 Gateway 的值。
  • 所有 trustasia.com:网站实际域名。
  • certificateRefs.name:第二步创建的 Secret 名称。
  • backendRefs:沿用原 Service 名称和服务端口,以及原匹配规则。

示例原路由只关联 http,因此增加了关联 https 的一项。原 parentRefs 没有 sectionName 时会关联适用的监听,无需为同一个 Gateway 重复添加;原来使用其他监听名称时沿用实际值。

Gateway 终止 HTTPS,向 Service 转发 HTTP;后端也要求 HTTPS 时需另行配置,不把本例解释成双向 TLS。

bash
kubectl apply --dry-run=server -f gateway.yaml
kubectl apply -f gateway.yaml
kubectl describe gateway web-gateway -n web
kubectl describe httproute web -n web

确认 Gateway Accepted=True、Programmed=True,listener ResolvedRefs=True;HTTPRoute 的父级条件 Accepted=True、ResolvedRefs=True。裸机入口地址不明确时,先按本文裸机入口说明确认实际可达地址,不先改 DNS。

describe gateway 的实测输出节选如下。每组 Status 和后面的 Type 要一起看:

text
Status:
  Conditions:
    Status: True
    Type:   Accepted
    Status: True
    Type:   Programmed
  Listeners:
    Conditions:
      Status: True
      Type:   ResolvedRefs
    Name: https

表示 Gateway 已接受并配置完成,https 监听引用的证书已解析。describe httproute 则看 Status → Parents → Conditions:

text
Status:
  Parents:
    Conditions:
      Status: True
      Type:   Accepted
      Status: True
      Type:   ResolvedRefs

这两项为 True 表示路由已接受且后端引用有效,接着检查网站访问。

3.2 使用已有 Ingress ​

打开网站原有的 Ingress 部署清单,在 spec 下增加 tls,与原 rules 同级。下面展示添加后的上下文:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  namespace: web
spec:
  ingressClassName: REPLACE_WITH_EXISTING_INGRESS_CLASS
  # 新增:引用第二步创建的 TLS Secret
  tls:
    - hosts:
        - trustasia.com
      secretName: web-tls
  # 以下沿用原网站路由
  rules:
    - host: trustasia.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-service
                port:
                  number: 80

name、namespace、ingressClassName 和 rules 使用原配置,只为目标域名增加 tls 引用。hosts 填原 rules.host 中的网站域名,secretName 填第二步的 Secret 名称。已有其他 TLS 配置时继续保留。

将修改后的完整清单保存为 ingress.yaml,在集群管理终端执行:

bash
kubectl apply --dry-run=server -f ingress.yaml
kubectl apply -f ingress.yaml
kubectl describe ingress web-ingress -n web

名称和 Namespace 换成实际值。确认 TLS 部分列出对应域名和 Secret,后端仍是原网站 Service,然后进入第四步。

describe ingress 实测输出节选:

text
Name:             web-ingress
Namespace:        web
Ingress Class:    tutorial-traefik
TLS:
  web-tls terminates trustasia.com
Rules:
  Host           Path  Backends
  ----           ----  --------
  trustasia.com
                 /     web-service:80 (10.42.0.17:80,10.42.2.8:80)

重点看 TLS 行的 web-tls 和 trustasia.com 是否对应第二步的 Secret 与网站域名;Backends 的 web-service:80 应仍是原后端。括号里是当前后端地址,不需要抄到证书配置中。

第四步:检查网站访问 ​

首次域名解析应指向实际入口,已有解析不因导入证书直接覆盖。Cloudflare 代理或其他负载均衡终止 HTTPS 时,浏览器可能显示边缘证书,需分别验收入口和源站。

在自己的电脑上打开浏览器,访问 https://trustasia.com/。域名换成第三步配置的网站域名;入口端口不是 443 时加上实际端口,例如 https://trustasia.com:8443/。

  1. 打开的是自己的网站,浏览器没有“连接不安全”或证书错误提示。
  2. 点击地址栏左侧的网站信息按钮查看证书,确认域名、有效期与本次部署的证书一致。
  3. 浏览常用页面,确认图片、样式和登录等功能正常。

以上均正常,就完成了 HTTPS 部署。网站显示旧证书或使用 CDN 等代理时,按证书检查说明核对实际入口。

入口配置失败时,按原发布流程撤回本次新增 listener / Route 或恢复原 Gateway / Ingress 清单,确认原 HTTP 或其他已有业务恢复。不要删除共享 Secret、Namespace 或 Controller。本次新建且已不被任何入口使用的 Secret,可核对后单独清理;日常换证不重复应用建站清单。

后续自动更新 ​

手动部署完成后,自行安排证书更新。如需自动化,继续Kubernetes 自动申请与更新指南。接入前核对现有 Secret 的管理方、域名与入口引用,避免手动任务与自动化工具同时更新同一 Secret。

可选:Gateway 环境准备 ​

以下仅用于缺少网关组件或裸机入口地址的环境。已有正常 Gateway / Ingress 无需执行;按集群原安装与配置管理方式操作,不覆盖其他人管理的资源。

补齐 Gateway 组件 ​

Gateway API CRD 只定义资源,还需 Gateway Controller 处理流量。以下 Envoy Gateway v1.9.1 示例仅适用于集群版本兼容、资源充足,且既未安装 Gateway API CRD、也未安装 Envoy Gateway 的环境。已有 CRD 或 Controller 时由管理员沿用原安装方式维护,不重复应用。

bash
kubectl apply --server-side -f \
  https://github.com/envoyproxy/gateway/releases/download/v1.9.1/install.yaml
kubectl wait -n envoy-gateway-system --timeout=10m \
  --for=condition=Available deployment/envoy-gateway

若 kubectl get gatewayclass 为空,保存以下内容为 gatewayclass.yaml,由管理员应用:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-web
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
bash
kubectl apply -f gatewayclass.yaml
kubectl describe gatewayclass envoy-web

确认 Accepted=True,第三步 Gateway 的 spec.gatewayClassName 使用实际确认的 GatewayClass,例如 envoy-web。安装控制器不会部署网站 Service,也不保证自动获得对外入口地址;继续按第三步配置并检查 Gateway。

裸机集群如何提供入口 ​

云环境通常由负载均衡器分配入口地址。裸机或本地虚拟机没有该能力时,即使证书与路由正确,代理 Service 的 EXTERNAL-IP 也可能一直为 <pending>。生产应采用适合网络的负载均衡或入口方案;以下节点 IP 示例仅用于可路由的实验网络,不能把私网地址当成公网地址。

下面仅适用于 Envoy Gateway。用 192.0.2.10 占位,请换为客户端可达、且 443 不与其他服务冲突的实际节点 IP。若 externalTrafficPolicy 为 Local,无本地代理 Pod 的节点可能丢弃请求;示例用 Cluster 策略允许跨节点转发。

保存以下内容为 envoy-proxy.yaml,Namespace 按网站实际位置替换:

yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: web-proxy
  namespace: web
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyService:
        externalTrafficPolicy: Cluster
bash
kubectl apply -f envoy-proxy.yaml

在第三步 gateway.yaml 的 Gateway spec 中合并 infrastructure 和 addresses,不加到 HTTPRoute 中:

yaml
spec:
  gatewayClassName: envoy-web
  infrastructure:
    parametersRef:
      group: gateway.envoyproxy.io
      kind: EnvoyProxy
      name: web-proxy
  addresses:
    - type: IPAddress
      value: 192.0.2.10  # 替换为实际入口节点 IP
  listeners:
    # 保留第三步示例中完整的 HTTPS listener

这是插入位置示意,不是独立可应用清单。保留原 listener 和 HTTPRoute,应用完整 gateway.yaml,再核对入口状态。Cluster 可能丢失原始客户端源 IP;不要只手工 Patch Controller 管理的 Service,重建可能覆盖 Patch。

bash
kubectl get gateway web-gateway -n web
kubectl get svc -n envoy-gateway-system \
  -l gateway.envoyproxy.io/owning-gateway-namespace=web,gateway.envoyproxy.io/owning-gateway-name=web-gateway \
  -o custom-columns=NAME:.metadata.name,POLICY:.spec.externalTrafficPolicy,EXTERNAL-IP:.spec.externalIPs

此示例的 POLICY 应为 Cluster。修改 DNS 前可临时指定域名连接到实际节点,保留域名与信任校验:

bash
curl --noproxy '*' --resolve trustasia.com:443:192.0.2.10 \
  --connect-timeout 10 --max-time 20 -fS https://trustasia.com/

替换域名、IP 和业务路径。--resolve 只影响本次请求,不改 DNS;--noproxy '*' 避免本机代理绕开指定地址,不使用 -k。确认返回目标业务后,按第四步完成验收。超时先检查路由、防火墙、443 及代理 Pod;其他网关实现不照搬 EnvoyProxy 配置。