最新:获取项目的更新在推特Mastodon

保护 NGINX-ingress

本教程将详细介绍如何使用 NGINX 安装和保护对集群的 ingress。

步骤 1 - 安装 Helm

如果您已安装 helm,请跳过此部分。

安装 cert-manager 最简单的方法是使用 Helm,这是一个用于 Kubernetes 资源的模板和部署工具。

首先,请按照 Helm 安装说明 确保已安装 Helm 客户端。

例如,在 MacOS 上

brew install kubernetes-helm

步骤 2 - 部署 NGINX Ingress 控制器

一个 kubernetes ingress 控制器 被设计为集群中运行的软件的 HTTP 和 HTTPS 流量的访问点。 ingress-nginx-controller 通过提供由云提供商负载均衡器支持的 HTTP 代理服务来实现这一点。

您可以从 关于 ingress-nginx 的文档 中获得有关 ingress-nginx 以及其工作原理的更多详细信息。

添加 ingress-nginx 的最新 helm 仓库

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

使用最新图表更新 helm 仓库

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the "stable" chart repository
...Successfully got an update from the "ingress-nginx" chart repository
...Successfully got an update from the "coreos" chart repository
Update Complete. ⎈ Happy Helming!

使用 helm 安装 NGINX Ingress 控制器

$ helm install quickstart ingress-nginx/ingress-nginx
NAME: quickstart
... lots of output ...

云提供商可能需要一两分钟才能提供并链接一个公网 IP 地址。完成之后,您可以使用 kubectl 命令查看外部 IP 地址

$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 13m
quickstart-ingress-nginx-controller LoadBalancer 10.0.114.241 <pending> 80:31635/TCP,443:30062/TCP 8m16s
quickstart-ingress-nginx-controller-admission ClusterIP 10.0.188.24 <none> 443/TCP 8m16s

此命令显示了集群中所有服务(在 default 命名空间中),以及它们拥有的任何外部 IP 地址。当您首次创建控制器时,您的云提供商还没有通过 LoadBalancer 分配和分配 IP 地址。在完成之前,服务的外部 IP 地址将列为 <pending>

您的云提供商可能提供在创建 ingress 控制器之前保留 IP 地址的选项,并使用该 IP 地址而不是从池中分配 IP 地址。请阅读云提供商的文档,了解如何安排此操作。

步骤 3 - 分配 DNS 名称

分配给 ingress 控制器的外部 IP 是所有传入流量应该被路由到的 IP。要启用此功能,请将其添加到您控制的 DNS 区域,例如 www.example.com

此快速入门假设您知道如何将 DNS 条目分配给 IP 地址,并将执行此操作。

步骤 4 - 部署示例服务

您的服务可能拥有自己的图表,或者您可能直接使用清单文件进行部署。此快速入门使用清单文件来创建和公开示例服务。示例服务使用 kuard,一个演示应用程序。

快速入门示例使用三个清单文件作为示例。前两个是示例部署和相关服务

apiVersion: apps/v1
kind: Deployment
metadata:
name: kuard
spec:
selector:
matchLabels:
app: kuard
replicas: 1
template:
metadata:
labels:
app: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
imagePullPolicy: Always
name: kuard
ports:
- containerPort: 8080
apiVersion: v1
kind: Service
metadata:
name: kuard
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
selector:
app: kuard

您可以创建、下载并本地引用这些文件,或者您可以从本教程文件的 GitHub 源代码仓库引用它们。要从教程文件直接从 GitHub 安装示例服务,请执行以下操作

kubectl apply -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/deployment.yaml
# expected output: deployment.extensions "kuard" created
kubectl apply -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/service.yaml
# expected output: service "kuard" created

一个 Ingress 资源 是 Kubernetes 用于公开此示例服务在集群外部的资源。您将需要下载并修改示例清单文件以反映您拥有或控制的域名以完成此示例。

您可以从以下示例 ingress 开始

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kuard
annotations: {}
#cert-manager.io/issuer: "letsencrypt-staging"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.example.com
secretName: quickstart-example-tls
rules:
- host: example.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 80

您可以从 GitHub 下载示例清单文件,编辑它,并使用以下命令将清单文件提交给 Kubernetes。在您的编辑器中编辑文件,保存后

kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/ingress.yaml
# expected output: ingress.networking.k8s.io/kuard created

注意:我们上面显示的 ingress 示例在其中有一个 host 定义。 ingress-nginx-controller 将在请求的主机名与 ingress 中的定义匹配时路由流量。您可以在规则中不带 host 定义的情况下部署 ingress,但这种模式不能与 TLS 证书一起使用,因为 TLS 证书期望一个完全限定的域名。

部署后,您可以使用命令 kubectl get ingress 查看 ingress 的状态

NAME HOSTS ADDRESS PORTS AGE
kuard * 80, 443 17s

根据您的服务提供商,ingress 可能需要几分钟才能完全创建。当它被创建并链接到适当的位置时,ingress 将显示一个地址

NAME HOSTS ADDRESS PORTS AGE
kuard * 203.0.113.2 80 9m

注意:ingress 上的 IP 地址可能ingress-nginx-controller 拥有的 IP 地址不匹配。这是正常的,是托管您的 Kubernetes 集群的服务提供商的一个怪癖/实现细节。由于我们使用的是 ingress-nginx-controller 而不是任何云提供商特定的 ingress 后端,请使用为 quickstart-ingress-nginx-controller LoadBalancer 资源定义和分配的 IP 地址作为您服务的首要访问点。

确保服务可以在您上面添加的域名上访问,例如 http://www.example.com。最简单的方法是打开浏览器并输入您在 DNS 中设置的名称,我们刚刚为此添加了 ingress。

您也可以使用命令行工具,如 curl 来检查 ingress。

$ curl -kivL -H 'Host: www.example.com' 'http://203.0.113.2'

此 curl 命令的选项将提供详细的输出,跟踪任何重定向,在输出中显示 TLS 标头,并且在不安全证书的情况下不会报错。使用 ingress-nginx-controller,服务将使用 TLS 证书可用,但它将使用从 ingress-nginx-controller 提供的默认自签名证书。浏览器将显示一个警告,说明这是一个无效证书。这是预期的并且正常的,因为我们还没有使用 cert-manager 为我们的网站获取一个完全可信的证书。

警告:确保您的 ingress 在互联网上可用并正确响应至关重要。此快速入门示例使用 Let's Encrypt 来提供证书,它期望并验证服务是否可用,以及在颁发证书的过程中,该验证被用作证明域名请求属于对域名有足够控制权的人的证据。

步骤 5 - 部署 cert-manager

我们需要安装 cert-manager 来完成与 Kubernetes 的工作,以便请求证书并响应验证挑战。我们可以使用 Helm 或普通的 Kubernetes 清单文件来安装 cert-manager。

由于我们之前安装了 Helm,因此我们将假设您希望使用 Helm;请遵循 Helm 指南。对于其他方法,请阅读 cert-manager 的安装文档

cert-manager 主要使用两种不同的自定义 Kubernetes 资源(称为 CRDs)来配置和控制其操作方式,以及存储状态。这些资源是 Issuer 和 Certificate。

Issuer

Issuer 定义了 cert-manager 将如何请求 TLS 证书。Issuer 针对 Kubernetes 中的单个命名空间,但也有一个 ClusterIssuer,它被认为是集群范围内的版本。

请确保您的 Issuer 创建在与您要创建的证书相同的命名空间中。您可能需要在 kubectl create 命令中添加 -n my-namespace

您的另一个选择是用 ClusterIssuers 替换您的 IssuersClusterIssuer 资源适用于集群中的所有 Ingress 资源。如果使用 ClusterIssuer,请记住将 Ingress 注释 cert-manager.io/issuer 更新为 cert-manager.io/cluster-issuer

如果您在 issuer 中遇到问题,请遵循 Issuer 颁发 ACME 证书故障排除 指南。

有关 IssuersClusterIssuers 之间的差异的更多信息(包括您何时可能选择使用每个信息)可以在 Issuer 概念 上找到。

证书

证书资源允许您指定您想要请求的证书的详细信息。它们引用一个 issuer 来定义将如何颁发它们。

有关更多信息,请参阅 证书概念

步骤 6 - 配置 Let's Encrypt 发行者

在这个示例中,我们将为 Let's Encrypt 设置两个发行者:staging 和 production。

Let's Encrypt production 发行者有非常严格的速率限制。当你进行实验和学习时,很容易达到这些限制。考虑到这种风险,我们将从 Let's Encrypt staging 发行者开始,一旦我们确信它工作正常,我们将切换到 production 发行者。

请注意,您将看到有关 staging 发行者颁发的不可信证书的警告,但这完全是预期的。

在本地创建此定义,并将电子邮件地址更新为您的地址。Let's Encrypt 需要此电子邮件,并将其用于通知您证书到期和更新。

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-staging
spec:
acme:
# The ACME server URL
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-staging
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx

编辑完成后,应用自定义资源

kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/staging-issuer.yaml
# expected output: issuer.cert-manager.io "letsencrypt-staging" created

还要创建一个 production 发行者并将其部署。与 staging 发行者一样,您需要更新此示例并添加自己的电子邮件地址。

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx
kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/production-issuer.yaml
# expected output: issuer.cert-manager.io "letsencrypt-prod" created

这两个发行者都配置为使用HTTP01 挑战提供者。

创建发行者后,检查其状态

$ kubectl describe issuer letsencrypt-staging
Name: letsencrypt-staging
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"cert-manager.io/v1","kind":"Issuer","metadata":{"annotations":{},"name":"letsencrypt-staging","namespace":"default"},(...)}
API Version: cert-manager.io/v1
Kind: Issuer
Metadata:
Cluster Name:
Creation Timestamp: 2018-11-17T18:03:54Z
Generation: 0
Resource Version: 9092
Self Link: /apis/cert-manager.io/v1/namespaces/default/issuers/letsencrypt-staging
UID: 25b7ae77-ea93-11e8-82f8-42010a8a00b5
Spec:
Acme:
Private Key Secret Ref:
Key:
Name: letsencrypt-staging
Server: https://acme-staging-v02.api.letsencrypt.org/directory
Solvers:
Http 01:
Ingress:
Class: nginx
Status:
Acme:
Uri: https://acme-staging-v02.api.letsencrypt.org/acme/acct/7374163
Conditions:
Last Transition Time: 2018-11-17T18:04:00Z
Message: The ACME account was registered with the ACME server
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>

您应该看到发行者与注册的帐户一起列出。

步骤 7 - 部署 TLS Ingress 资源

所有先决条件配置到位后,我们现在可以执行请求 TLS 证书的步骤。主要有两种方法:使用 Ingress 上的注释和ingress-shim 或直接创建证书资源。

在本示例中,我们将向 Ingress 添加注释,并利用 ingress-shim 让它代表我们创建证书资源。在创建证书后,cert-manager 将更新或创建 Ingress 资源,并使用它来验证域名。验证和颁发后,cert-manager 将创建或更新在证书中定义的密钥。

注意:Ingress 中使用的密钥应与证书中定义的密钥匹配。没有明确的检查,因此任何拼写错误会导致ingress-nginx-controller 回退到其自签名证书。在我们的示例中,我们使用 Ingress(和 ingress-shim)上的注释,这将代表您创建正确的密钥。

编辑 Ingress 并添加在之前示例中注释掉的注释

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kuard
annotations:
cert-manager.io/issuer: "letsencrypt-staging"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.example.com
secretName: quickstart-example-tls
rules:
- host: example.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 80

并将其应用

kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/ingress-tls.yaml
# expected output: ingress.networking.k8s.io/kuard configured

cert-manager 将读取这些注释并使用它们来创建证书,您可以请求并查看该证书

$ kubectl get certificate
NAME READY SECRET AGE
quickstart-example-tls True quickstart-example-tls 16m

cert-manager 在证书对象中反映了每个请求的处理状态。您可以使用kubectl describe 命令查看此信息

$ kubectl describe certificate quickstart-example-tls
Name: quickstart-example-tls
Namespace: default
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Cluster Name:
Creation Timestamp: 2018-11-17T17:58:37Z
Generation: 0
Owner References:
API Version: networking.k8s.io/v1
Block Owner Deletion: true
Controller: true
Kind: Ingress
Name: kuard
UID: a3e9f935-ea87-11e8-82f8-42010a8a00b5
Resource Version: 9295
Self Link: /apis/cert-manager.io/v1/namespaces/default/certificates/quickstart-example-tls
UID: 68d43400-ea92-11e8-82f8-42010a8a00b5
Spec:
Dns Names:
www.example.com
Issuer Ref:
Kind: Issuer
Name: letsencrypt-staging
Secret Name: quickstart-example-tls
Status:
Acme:
Order:
URL: https://acme-staging-v02.api.letsencrypt.org/acme/order/7374163/13665676
Conditions:
Last Transition Time: 2018-11-17T18:05:57Z
Message: Certificate issued successfully
Reason: CertIssued
Status: True
Type: Ready
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CreateOrder 9m cert-manager Created new ACME order, attempting validation...
Normal DomainVerified 8m cert-manager Domain "www.example.com" verified with "http-01" validation
Normal IssueCert 8m cert-manager Issuing certificate...
Normal CertObtained 7m cert-manager Obtained certificate from ACME server
Normal CertIssued 7m cert-manager Certificate issued Successfully

与该资源关联的事件列在describe 结果的底部,显示了请求的状态。在上面的示例中,证书在几分钟内得到验证和颁发。

完成后,cert-manager 将根据 Ingress 资源中使用的密钥创建一个包含证书详细信息的密钥。您也可以使用 describe 命令查看一些详细信息

$ kubectl describe secret quickstart-example-tls
Name: quickstart-example-tls
Namespace: default
Labels: cert-manager.io/certificate-name=quickstart-example-tls
Annotations: cert-manager.io/alt-names=www.example.com
cert-manager.io/common-name=www.example.com
cert-manager.io/issuer-kind=Issuer
cert-manager.io/issuer-name=letsencrypt-staging
Type: kubernetes.io/tls
Data
====
tls.crt: 3566 bytes
tls.key: 1675 bytes

现在我们有信心一切配置正确,您可以更新 Ingress 中的注释以指定 production 发行者

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kuard
annotations:
cert-manager.io/issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.example.com
secretName: quickstart-example-tls
rules:
- host: example.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 80
$ kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/ingress-tls-final.yaml
ingress.networking.k8s.io/kuard configured

您还需要删除现有的密钥,cert-manager 正在监视它,这将导致它使用更新的发行者重新处理请求。

$ kubectl delete secret quickstart-example-tls
secret "quickstart-example-tls" deleted

这将启动获取新证书的过程,您可以使用 describe 查看状态。production 证书更新后,您应该看到示例 KUARD 在您的域名上运行,并具有签名的 TLS 证书。

$ kubectl describe certificate quickstart-example-tls
Name: quickstart-example-tls
Namespace: default
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Cluster Name:
Creation Timestamp: 2018-11-17T18:36:48Z
Generation: 0
Owner References:
API Version: networking.k8s.io/v1
Block Owner Deletion: true
Controller: true
Kind: Ingress
Name: kuard
UID: a3e9f935-ea87-11e8-82f8-42010a8a00b5
Resource Version: 283686
Self Link: /apis/cert-manager.io/v1/namespaces/default/certificates/quickstart-example-tls
UID: bdd93b32-ea97-11e8-82f8-42010a8a00b5
Spec:
Dns Names:
www.example.com
Issuer Ref:
Kind: Issuer
Name: letsencrypt-prod
Secret Name: quickstart-example-tls
Status:
Conditions:
Last Transition Time: 2019-01-09T13:52:05Z
Message: Certificate does not exist
Reason: NotFound
Status: False
Type: Ready
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Generated 18s cert-manager Generated new private key
Normal OrderCreated 18s cert-manager Created Order resource "quickstart-example-tls-889745041"

您可以通过在 cert-manager 为您的证书创建的 Order 资源上运行kubectl describe 来查看 ACME Order 的当前状态

$ kubectl describe order quickstart-example-tls-889745041
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Created 90s cert-manager Created Challenge resource "quickstart-example-tls-889745041-0" for domain "www.example.com"

在这里,我们可以看到 cert-manager 创建了 1 个“Challenge”资源来完成 Order。您可以通过在自动创建的 Challenge 资源上运行kubectl describe 来深入了解当前 ACME 挑战的状态

$ kubectl describe challenge quickstart-example-tls-889745041-0
...
Status:
Presented: true
Processing: true
Reason: Waiting for http-01 challenge propagation
State: pending
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 15s cert-manager Challenge scheduled for processing
Normal Presented 14s cert-manager Presented challenge using http-01 challenge mechanism

从上面我们可以看到挑战已经“呈现”,cert-manager 正在等待挑战记录传播到 Ingress 控制器。您应该注意 Challenge 资源上的新事件,因为大约一分钟后(取决于您的 Ingress 控制器更新规则的速度)应该会打印一个“成功”事件

$ kubectl describe challenge quickstart-example-tls-889745041-0
...
Status:
Presented: false
Processing: false
Reason: Successfully authorized domain
State: valid
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 71s cert-manager Challenge scheduled for processing
Normal Presented 70s cert-manager Presented challenge using http-01 challenge mechanism
Normal DomainVerified 2s cert-manager Domain "www.example.com" verified with "http-01" validation

注意:如果您的挑战没有变得“有效”,并且一直处于“待处理”状态(或进入“失败”状态),则可能是配置错误。阅读Challenge 资源参考文档,了解有关调试失败挑战的更多信息。

挑战完成后,其对应的挑战资源将被删除,并且“Order”将更新以反映 Order 的新状态

$ kubectl describe order quickstart-example-tls-889745041
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Created 90s cert-manager Created Challenge resource "quickstart-example-tls-889745041-0" for domain "www.example.com"
Normal OrderValid 16s cert-manager Order completed successfully

最后,“Certificate”资源将更新以反映颁发过程的状态。如果一切顺利,您应该能够“描述”证书并看到类似下面的内容

$ kubectl describe certificate quickstart-example-tls
Status:
Conditions:
Last Transition Time: 2019-01-09T13:57:52Z
Message: Certificate is up to date and has not expired
Reason: Ready
Status: True
Type: Ready
Not After: 2019-04-09T12:57:50Z
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Generated 11m cert-manager Generated new private key
Normal OrderCreated 11m cert-manager Created Order resource "quickstart-example-tls-889745041"
Normal OrderComplete 10m cert-manager Order "quickstart-example-tls-889745041" completed successfully