---
title: 使用 cert-manager 加密 Kubernetes Ingress
description: 展示使用 cert-Manager 为 Kubernetes Ingress 获取 Let's Encrypt TLS 证书的过程。
weight: 70
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 提供了更强大的功能，例如 [Gateway](/docs/reference/config/networking/v1alpha3/gateway) 和 [Virtual service](/docs/reference/config/networking/v1alpha3/virtual-service)，它们可以用于更加高级的流量管理功能，而可选的 Kubernetes Ingress 控制器支持则可以简单的把传统应用和第三方解决方案集成到服务网格之中，并由此获得 Istio 提供的遥测和跟踪能力。

首先要从一个全新安装的 Istio 入手，创建一个示例应用，并利用 Kubernetes `Ingress` 资源将服务开放出去，Istio 可以为这一过程提供加密服务，它调用自带的 cert-manager 管理 TLS 证书的签发和续期，然后把证书分发给 Istio 的 Ingress [gateway](/docs/reference/config/networking/v1alpha3/gateway)，并在必要时使用 [SDS](https://www.envoyproxy.io/docs/envoy/latest/configuration/secret) 进行证书的热交换。

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

[安装 Istio](/zh/docs/setup) 并确认已经启用 Ingress [Gateway](/docs/reference/config/networking/v1alpha3/gateway) 的 Kubernetes Ingress 支持、[SDS](https://www.envoyproxy.io/docs/envoy/latest/configuration/secret) 以及 [cert-manager](https://docs.cert-manager.io/)。下面的例子展示了使用 [Helm template](/zh/docs/setup/kubernetes/install/helm/#方案-1-使用-helm-template-进行安装) 完成设置这些依赖项目的方法：

{{< text bash >}}
$ helm template $HOME/istio-fetch/istio \
  --namespace=istio-system \
  --set gateways.istio-ingressgateway.sds.enabled=true \
  --set global.k8sIngress.enabled=true \
  --set global.k8sIngress.enableHttps=true \
  --set global.k8sIngress.gatewayName=ingressgateway \
  --set certmanager.enabled=true \
  --set certmanager.email=mailbox@donotuseexample.com \
  > $HOME/istio-fetch/istio.yaml
{{< /text >}}

{{< tip >}}
缺省情况下， `istio-ingressgateway` 会以 `LoadBalancer` 的服务类型开放出来。根据本地环境条件，可以考虑把 `gateways.istio-ingressgateway.type` 参数设置为 `NodePort`。
{{< /tip >}}

## 配置 DNS 名称和 Gateway {#domain-name-and-gate-way}

记录一下 `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](/docs/reference/config/networking/v1alpha3/gateway)，用于给 Kubernetes `Ingress` 资源提供路由服务。缺省情况下，它不会使用 [SDS](https://www.envoyproxy.io/docs/envoy/latest/configuration/secret)，所以需要对其进行修改，让 [SDS](https://www.envoyproxy.io/docs/envoy/latest/configuration/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-staging", "mode": "SIMPLE", "privateKey": "sds", "serverCertificate": "sds"}}]'
{{< /text >}}

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

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

接下来使用一个简单的 `helloworld` 应用来进行演示。下面的命令会为示例应用创建 `Deployment` 和 `Service` 对象，并使用 `istio-ingressgateway` 所支持的 `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: extensions/v1beta1
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: extensions/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-cert-using-cert-manager}

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

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

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

注意这里的 `secretName` 要匹配前面配置 [Gateway](/docs/reference/config/networking/v1alpha3/gateway) 资源时其中的 `credentialName` 字段值。`Certificate` 对象会被 cert-manager 处理，最终会签发新证书。可以通过对 `Certificate` 对象状态的查询来得知整个过程的进展：

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

这样一来就可以使用 HTTPS 进行访问了：

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

注意，因为演练环境签发的证书不受信任，这里用了 `--insecure` 参数。
