---
title: 使用 Cert-Manager 加密 Kubernetes Ingress
description: 演示如何使用 Cert-Manager 为 Kubernetes Ingress 自动获取 Let's Encrypt TLS 证书。
weight: 40
keywords: [traffic-management,ingress,https,cert-manager,acme,sds]
aliases:
  - /zh/docs/examples/advanced-gateways/ingress-certmgr/
---

这个例子演示了在 Istio 中使用 [Let's Encrypt](https://letsencrypt.org/) 签发 TLS 证书为 Kubernetes Ingress controller 提供安全加固的过程。

您将从全新的 Istio 安装开始，创建示例服务，使用 Kubernetes Ingress 把它开放出去，并使用 cert-manager（与 Istio 捆绑在一起）管理 TLS 证书的签发和续订来确保它的安全，这个证书之后会给 Istio ingress [gateway](/zh/docs/reference/config/networking/gateway) 使用，并根据需要通过 [Secrets Discovery Service (SDS)](https://www.envoyproxy.io/docs/envoy/latest/configuration/security/secret) 提供 hot-swapped 功能。

## 开始之前{#before-you-begin}

1. [安装 Istio](/zh/docs/setup/) 并确认已经启用支持 Kubernetes Ingress 和 [SDS](https://www.envoyproxy.io/docs/envoy/latest/configuration/security/secret) 的 ingress [gateway](/zh/docs/reference/config/networking/gateway)。下面是例子:

    {{< text bash >}}
    $ istioctl manifest apply \
      --set values.gateways.istio-ingressgateway.sds.enabled=true \
      --set values.global.k8sIngress.enabled=true \
      --set values.global.k8sIngress.enableHttps=true \
      --set values.global.k8sIngress.gatewayName=ingressgateway
    {{< /text >}}

    {{< tip >}}
    默认情况下， `istio-ingressgateway` 会以 `LoadBalancer` 的服务类型开放出来。您可以根据自己的 Kubernetes 环境把 `gateways.istio-ingressgateway.type` 设置为 `NodePort`。
    {{< /tip >}}

1. [安装 cert-manager](https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html) 以便实现证书的自动管理。

## 配置 DNS 域名和 gateway{#configuring-DNS-name-and-gateway}

记录一下 `istio-ingressgateway` 服务的外部 IP：

{{< text bash >}}
$ kubectl -n istio-system get service istio-ingressgateway
{{< /text >}}

对您的 DNS 进行设置，给 `istio-ingressgateway` 服务的外部 IP 分配一个合适的域名。为了能让例子正常执行，您需要一个真正的域名，用于获取 TLS 证书。让我们把域名保存为环境变量，以便于后面的使用：

{{< text bash >}}
$ INGRESS_DOMAIN=mysubdomain.mydomain.edu
{{< /text >}}

Istio 安装中包含了一个自动生成的 [gateway](/zh/docs/reference/config/networking/gateway) ，用于提供 Kubernetes `Ingress` 定义的路由服务。默认情况下，它不会使用 [SDS](https://www.envoyproxy.io/docs/envoy/latest/configuration/security/secret)，所以您需要对其进行修改，使其能通过 [SDS](https://www.envoyproxy.io/docs/envoy/latest/configuration/security/secret) 来为 `istio-ingressgateway` 签发 TLS 证书：

{{< text bash >}}
$ kubectl -n istio-system edit gateway
{{< /text >}}

然后修改 `https-default` 端口对应的 `tls` 内容：

{{< text bash >}}
$ kubectl -n istio-system \
  patch gateway istio-autogenerated-k8s-ingress --type=json \
  -p='[{"op": "replace", "path": "/spec/servers/1/tls", "value": {"credentialName": "ingress-cert", "mode": "SIMPLE", "privateKey": "sds", "serverCertificate": "sds"}}]'
{{< /text >}}

现在就可以部署一个演示应用了。

## 部署演示应用{#setting-up-a-demo-application}

接下来使用一个简单的 `helloworld` 应用来进行演示。下面的命令会为示例应用创建 `Deployment` 和 `Service`，并使用 Istio Ingress 开放服务。

{{< text bash >}}
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld
spec:
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - name: helloworld
        image: istio/examples-helloworld-v1
        resources:
          requests:
            cpu: "100m"
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5000
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: istio
  name: helloworld-ingress
spec:
  rules:
    - host: "$INGRESS_DOMAIN"
      http:
        paths:
          - path: /hello
            backend:
              serviceName: helloworld
              servicePort: 5000
---
EOF
{{< /text >}}

{{< tip >}}
注意，这里用了前面定义的 `INGRESS_DOMAIN` 变量。
{{< /tip >}}

现在，您应该能够通过 HTTP 访问演示应用：

{{< text bash >}}
$ curl http://$INGRESS_DOMAIN/hello
Hello version: v1, instance: helloworld-5d498979b6-jp2mf
{{< /text >}}

因为没有配置任何的 TLS 证书，所以现在还不能使用 HTTPS 访问，下面就开始进行配置。

## 使用 cert-manager 获取 Let's Encrypt 签发的证书{#getting-a-let-s-encrypt-certificate-issued-using-cert-manager}

目前，您的 Istio 中应该已经启动了 cert-manager，并带有两个 `ClusterIssuer` 对象（分别对应 [Let's Encrypt](https://letsencrypt.org/) 的生产和测试 ACME-endpoints）。这个例子中使用测试 ACME-endpoint（将 `letsencrypt-staging` 替换为 `letsencrypt` 就能获得浏览器信任的证书）。

为了用 cert-manager 进行证书的签发和管理，需要创建一个 Certificate 对象：

{{< text bash >}}
$ cat <<EOF | kubectl apply -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: ingress-cert
  namespace: istio-system
spec:
  secretName: ingress-cert
  issuerRef:
    name: letsencrypt-staging
    kind: ClusterIssuer
  commonName: $INGRESS_DOMAIN
  dnsNames:
  - $INGRESS_DOMAIN
  acme:
    config:
    - http01:
        ingressClass: istio
      domains:
      - $INGRESS_DOMAIN
---
EOF
{{< /text >}}

注意这里的 `secretName` 要匹配前面配置 [gateway](/zh/docs/reference/config/networking/gateway) 时的 `credentialName` 字段值。`Certificate` 对象会被 cert-manager 处理，最终会签发新证书。为了看到这个过程我们可以查询 `Certificate` 对象的状态：

{{< text bash >}}
$ kubectl -n istio-system describe certificate ingress-cert
-> 状态最终会切换为 'Certificate issued successfully'
{{< /text >}}

这样一来，该服务也应通过 HTTPS 访问了：

{{< text bash >}}
$ curl --insecure https://$INGRESS_DOMAIN/hello
Hello version: v1, instance: helloworld-5d498979b6-jp2mf
{{< /text >}}

注意，您需要使用 `--insecure` 参数，因为测试 ACME-endpoints 签发的证书不受信任。

## 从测试到生产{#moving-to-production-from-staging}

现在换成生产 `letsencrypt`。首先，我们重新申请一下证书。

{{< text bash >}}
$ cat <<EOF | kubectl apply -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: ingress-cert
  namespace: istio-system
spec:
  secretName: ingress-cert
  issuerRef:
    name: letsencrypt
    kind: ClusterIssuer
  commonName: $INGRESS_DOMAIN
  dnsNames:
  - $INGRESS_DOMAIN
  acme:
    config:
    - http01:
        ingressClass: istio
      domains:
      - $INGRESS_DOMAIN
---
EOF
{{< /text >}}

{{< text plain>}}
certificate.certmanager.k8s.io/ingress-cert configured
{{< /text >}}

现在删除 secret 来强制 cert-manager 从生产 ACME-endpoints 请求新证书：

{{< text bash >}}
$ kubectl delete secret -n istio-system ingress-cert
{{< /text >}}

等等看证书是否成功签发了：

{{< text bash >}}
$ watch -n1 kubectl describe cert ingress-cert -n istio-system
{{< /text >}}

您应该能看到如下显示：

{{< text plain>}}
Normal  CertIssued     13m   cert-manager  Certificate issued successfully
{{< /text >}}
