如何在 Kubernetes 上配置带有自动生成的 Let's Encrypt 证书的 nginx Ingress
背景:一开始我搜索了一下,发现有很多教程教人怎么做,但没有一个能直接用,要么是教程过时了,要么是用头盔(其实不需要头盔,而且我想尽可能少用工具),所以我不得不查阅很多资料才搞定。基本上,这篇文章是我整理的网上文章合集,里面的方法对我有用。
这里我省略了很多背景信息和解释,因为这些内容可以在相关的教程中找到。本文仅作为一份简要的操作指南,帮助您顺利完成设置。
此外,我们还将设置端口转发,使任何任意服务(非 http/https 服务,例如数据库)都可以通过与网站相同的 IP/负载均衡器从互联网访问。
我使用的是 DigitalOcean 提供的 Kubernetes 服务,如果您是新手,欢迎使用我的链接注册。您可以以每月 10 美元起的价格搭建一个 Kubernetes 集群。
先决条件
- 对 Kubernetes 对象/类型的基本理解
- 一个已准备就绪/已设置好的 Kubernetes 集群,kubectl 已就绪/已设置
- 需要访问 DNS 提供商,以便设置一些 DNS 条目指向您的集群。
来源
本文所引用资料来源如下(排名不分先后):
- https://kubernetes.github.io/ingress-nginx/deploy/
- https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nginx-ingress-with-cert-manager-on-digitalocean-kubernetes
- https://cert-manager.io/docs/installation/kubernetes/
- https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/
创建一些虚拟的“echo”部署
我们需要一些虚拟的 Web 服务来响应 HTTP 请求。请创建以下两个文件:
# echo1.yaml
apiVersion: v1
kind: Service
metadata:
name: echo1
spec:
ports:
- port: 80
targetPort: 5678
selector:
app: echo1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo1
spec:
selector:
matchLabels:
app: echo1
replicas: 2
template:
metadata:
labels:
app: echo1
spec:
containers:
- name: echo1
image: hashicorp/http-echo
args:
- "-text=echo1"
ports:
- containerPort: 5678
# echo2.yaml
apiVersion: v1
kind: Service
metadata:
name: echo2
spec:
ports:
- port: 80
targetPort: 5678
selector:
app: echo2
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo2
spec:
selector:
matchLabels:
app: echo2
replicas: 1
template:
metadata:
labels:
app: echo2
spec:
containers:
- name: echo2
image: hashicorp/http-echo
args:
- "-text=echo2"
ports:
- containerPort: 5678
同时采用这两种部署方式:
$ kubectl apply -f echo1.yaml
$ kubectl apply -f echo2.yaml
这将创建 2 个部署和 2 个服务,监听集群内部端口 80:
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
echo1 ClusterIP 10.245.164.177 <none> 80/TCP 1h
echo2 ClusterIP 10.245.77.216 <none> 80/TCP 1h
配置 Ingress nginx
只需两条命令即可启动并运行 ingress-nginx:
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.apps/nginx-ingress-controller created
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
service/ingress-nginx created
此时 nginx-ingress 已配置完成,负载均衡器(注意:云服务提供商会对每个负载均衡器收取费用)将启动。这可能需要几分钟时间才能显示出来。您可以使用以下命令检查状态:
$ kubectl get -n ingress-nginx service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx LoadBalancer 10.245.209.25 155.241.87.123 80:30493/TCP,443:30210/TCP 1h
更新:
目前存在一个关于网络路由的未解决的问题,当 pod 尝试请求证书时,如果尝试对请求的域进行自我检查而超时,则会导致问题。
因此,我们需要修改刚刚创建的“ingress-nginx”服务的“externalTrafficPolicy”。创建一个名为“Service_ingress-nginx.yaml”的文件,并添加以下内容:
# Service_ingress-nginx.yaml
kind: Service
apiVersion: v1
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
# default for externalTrafficPolicy = 'Local', but due to an issue
# (https://stackoverflow.com/questions/59286126/kubernetes-cluterissuer-challenge-timeouts,
# https://github.com/danderson/metallb/issues/287)
# it has to be 'Cluster' for now
externalTrafficPolicy: Cluster
type: LoadBalancer
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
- name: https
port: 443
protocol: TCP
targetPort: https
当你比较/应用此文件时,区别应该在于 externalTrafficPolicy 现在为“Cluster”。这样做的缺点是可能会丢失请求服务的原始 IP 地址,但对我来说这并不重要,而且解决了证书请求问题。
kubectl diff -f Service_ingress-nginx.yaml # show differences
kubectl apply -f Service_ingress-nginx.yaml # activate changes
请记下您的外部 IP 地址,我们将使用它作为我们向公众开放服务的唯一入口。
现在前往您的 DNS 提供商,创建两条记录(任何类型的 A 记录)指向您的外部 IP 地址。
创建一个名为“echo_ingress.yaml”的文件,并对其进行调整以匹配刚刚创建的DNS条目:
# echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: echo-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: echo1.yourdomain.com
http:
paths:
- backend:
serviceName: echo1
servicePort: 80
- host: echo2.yourdomain.com
http:
paths:
- backend:
serviceName: echo2
servicePort: 80
通过应用以下方式创建入口:
$ kubectl apply -f echo_ingress.yaml
现在这两个网站应该可以通过http访问了。请尝试在浏览器中访问它们。
证书管理器
下一步是安装 cert-manager,它稍后将使用 Issuers 获取我们的证书。
$ kubectl create namespace cert-manager
$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.12.0/cert-manager.yaml
这将安装 cert-manager。您可以检查是否有正在运行的 pod:
$ kubectl get pods --namespace cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-5c47f46f57-n9bb6 1/1 Running 0 31s
cert-manager-cainjector-6659d6844d-6zjh5 1/1 Running 0 31s
cert-manager-webhook-547567b88f-9hj5f 1/1 Running 0 31s
为了检查 cert-manager 是否运行正常,我们现在可以颁发自签名证书进行测试。生成一个名为“test-cert-manager.yaml”的文件:
# test-cert-manager.yaml
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager-test
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: test-selfsigned
namespace: cert-manager-test
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: selfsigned-cert
namespace: cert-manager-test
spec:
commonName: example.com
secretName: selfsigned-cert-tls
issuerRef:
name: test-selfsigned
应用此方法并检查输出结果:
$ kubectl apply -f test-cert-manager.yaml
$ kubectl describe certificate -n cert-manager-test
describe 输出中的事件应该显示类似“证书已成功颁发”的内容。
如果可以,请删除测试资源:
$ kubectl delete -f test-cert-manager.yaml
cert-manager 启动并运行后,我们还缺少最后一块拼图;一个 Issuer(或者在我们的例子中,我们将选择一个 ClusterIssuer,这样我们就不需要指定命名空间,它就可以全局工作),用于生成有效的证书。
集群发行者的
我们将创建两个颁发者。第一个(测试环境)用于测试一切是否正常运行。否则,如果出现诸如 DNS 设置错误之类的问题,而我们直接使用生产环境的颁发者,则可能会暂时被 Let's Encrypt 服务器拒绝。
创建以下两个文件并修改您的电子邮件地址:
# staging_issuer.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
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
email: me@example.com
# 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:
class: nginx
# prod_issuer.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: me@example.com
# 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:
class: nginx
创建发行者:
$ kubectl create -f staging_issuer.yaml
$ kubectl create -f prod_issuer.yaml
扩展之前创建的“echo_ingress.yaml”文件,使其内容如下所示:
# echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: echo-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-staging"
spec:
tls:
- hosts:
- echo1.yourdomain.com
- echo2.yourdomain.com
secretName: letsencrypt-staging
rules:
- host: echo1.yourdomain.com
http:
paths:
- backend:
serviceName: echo1
servicePort: 80
- host: echo2.yourdomain.com
http:
paths:
- backend:
serviceName: echo2
servicePort: 80
应用调整:
$ kubectl apply -f echo_ingress.yaml
就我而言,证书颁发花了数分钟时间。请查看以下命令的事件部分:
$ kubectl describe certificate letsencrypt-staging
过一段时间后,应该会显示类似“证书颁发成功”的信息。然后您可以重新加载浏览器中的网站并检查证书。此时,证书应该是由 Let's Encrypt 的虚假机构颁发的。
如果成功,我们就可以进行最后一步,将发行者替换为生产环境的发行者。再次调整 'echo_ingress.yaml' 文件,切换到 'letsencrypt-prod':
# echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: echo-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- echo1.yourdomain.com
- echo2.yourdomain.com
secretName: letsencrypt-prod
rules:
- host: echo1.yourdomain.com
http:
paths:
- backend:
serviceName: echo1
servicePort: 80
- host: echo2.yourdomain.com
http:
paths:
- backend:
serviceName: echo2
servicePort: 80
再次应用这些调整:
$ kubectl apply -f echo_ingress.yaml
现在,可能稍等几分钟,您的网站应该就会获得有效的证书。
$ kubectl describe certificate letsencrypt-prod
恭喜!我们的网站终于都通过了有效的证书。现在最棒的是,它具有可扩展性。从现在开始,添加或删除网站都非常容易。
证书颁发故障排除
如果证书在某个阶段无法颁发,了解哪些组件参与其中会有所帮助,直到证书成功颁发。例如,我遇到的情况是挑战未能通过。因此,只需按照官方证书管理器网站上的指南操作,直到找到根证书即可:
https://cert-manager.io/docs/faq/acme/
额外功能——在集群之外提供其他服务
假设你的集群中运行着一些网站以外的服务(例如数据库),并且你想让它能够从外部访问。
一种方法是直接附加一个“NodePort”服务,但这会有一些限制,例如只能使用 30000 以上的端口,而且如果你的某个节点宕机或被替换,则需要调整访问该服务的 IP 地址。
相反,我们可以直接在现有的负载均衡 nginx-ingress 服务(TCP 或 UDP)中添加任意端口。因此,只需要进行一些小的修改。
以下是一个打开 TCP 端口 9000 并将其转发到在端口 27017 上运行的内部服务的实例。
之前我们只是应用了来自 URL ' https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml ' 和 ' https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml ' 的资源。
它们包含两个已激活但需要调整和重新应用的资源。首先,创建文件“ingress-nginx-tcp-configmap.yaml”:
# ingress-nginx-tcp-configmap.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: tcp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
data:
9000: "default/someservice:27017"
第二,创建一个名为“ingress-nginx-service.yaml”的文件:
# ingress-nginx-service.yaml
kind: Service
apiVersion: v1
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
externalTrafficPolicy: Local
type: LoadBalancer
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
- name: https
port: 443
protocol: TCP
targetPort: https
- name: proxied-tcp-9000
port: 9000
targetPort: 9000
protocol: TCP
应用调整。
$ kubectl apply -f ingress-nginx-tcp-configmap.yaml
$ kubectl apply -f ingress-nginx-service.yaml
您的服务不应能从集群外部通过与网站相同的 IP 地址访问。
文章来源:https://dev.to/chrisme/setting-up-nginx-ingress-w-automatically- generated-letsencrypt-certificates-on-kubernetes-4f1k