转 https://mritd.me/2017/03/04/how-to-use-nginx-ingress/
最近发现好多人问Ingress,同时一直也没去用Nginx的Ingress,索性鼓捣了一把,发现跟原来确实有了点变化,在这里写篇文章记录一下
一,Ingress介绍
Kubernetes暴露服务的方式目前只有三种:LoadBlancer Service,NodePort Service,Ingress;前两种估计都应该很熟悉,具体的可以参考下这篇文章 ;下面详细的唠一下这个Ingress
1.1,Ingress是个什么玩意
可能从大致印象上Ingress就是能利用Nginx,Haproxy啥的负载均衡器暴露集群内服务的工具;那么问题来了,集群内服务想要暴露出去面临着几个问题:
1.2,Pod漂移问题
众所周知Kubernetes具有强大的副本控制能力,能保证在任意副本(Pod)挂掉时自动从其他机器启动一个新的,还可以动态扩容等,总之一句话,这个Pod可能在任何时刻出现在任何节点上,也可能在任何时刻死在任何节点上;那么自然随着Pod的创建和销毁,Pod IP肯定会动态变化; 那么如何把这个动态的Pod IP暴露出去?这里借助于Kubernetes的服务机制,服务可以以标签的形式选定一组带有指定标签的Pod,并监控和自动负载他们的Pod IP,那么我们向外暴露只暴露服务IP就行了 ;这就是NodePort模式:即在每个节点上开起一个端口,然后转发到内部Pod IP上,如下图所示
1.3,端口管理问题
采用NodePort方式暴露服务面临一个坑爹的问题是,服务一旦多起来,NodePort在每个节点上开启的端口会及其庞大,而且难以维护;这时候引出的思考问题是“能不能使用Nginx啥的只监听一个端口,比如80,然后按照域名向后转发?“这思路很好,简单的实现就是使用DaemonSet在每个节点上监听80,然后写好规则,因为Nginx外面绑定了宿主机80端口(就像NodePort),本身又在集群内,那么向后直接转发到相应服务IP就行了,如下图所示
1.4,域名分配及动态更新问题
从上面的思路,采用Nginx似乎已经解决了问题,但是其实这里面有一个很大缺陷:每次有新服务加入怎么改Nginx配置?总不能手动改或者来个滚动更新前端Nginx Pod吧?这时候“伟大而又正直勇敢的”Ingress登场,如果不算上面的Nginx,Ingress只有两大组件:Ingress Controller和Ingress
Ingress这个玩意,简单的理解就是你原来要改Nginx配置,然后配置各种域名对应哪个服务,现在把这个动作抽象出来,变成一个Ingress对象,你可以用yml创建,每次不要去改Nginx了,直接改yml然后创建/更新就行了 ;那么问题来了:“Nginx咋整?”
Ingress控制器这东西就是解决“Nginx咋整”的; Ingress Controoler通过与Kubernetes API交互,动态的去感知集群中Ingress规则变化,然后读取他,按照他自己模板生成一段Nginx配置,再写到Nginx Pod里,最后重装一下,工作流程如下图
当然在实际应用中,最新版本Kubernetes已经将Nginx与Ingress Controller合并为一个组件,所以Nginx无需单独部署,只需要部署Ingress Controller即可
二,怼一个Nginx Ingress
上面啰嗦了那么多,只是为了讲明白Ingress的各种理论概念,下面实际部署很简单
2.1,部署默认后端
我们知道前端的Nginx最终要负载到后端服务上,那么如果访问不存在的域名咋整?官方给出的建议是部署一个默认后端,对于未知请求全部负载到这个默认后端上;这个后端啥也不干,就是返回404,部署如下
➜ ~ kubectl create -f default-backend.yaml
deployment "default-http-backend" created
service "default-http-backend" created
这个default-backend.yaml
文件可以在官方Ingress仓库找到,由于篇幅限制这里不贴了,仓库位置如下
2.2,部署Ingress Controller
部署完了后端就得把最重要的组件Nginx + Ingres Controller(官方统一称为Ingress Controller)部署上
➜ ~ kubectl create -f nginx-ingress-controller.yaml
daemonset "nginx-ingress-lb" created
注意:官方的Ingress Controller有个坑,至少我看了DaemonSet方式部署的有这个问题:没有绑定到宿主机80端口,也就是说前端Nginx没有监听宿主机80端口(这还玩个卵啊) ;所以需要把配置搞下来自己加hostNetwork
一下,截图如下
同样配置文件自己找一下,地址点这里,仓库截图如下
当然它支持以deamonset的方式部署,这里用的就是(个人喜欢而已),所以你发现我上面截图是部署,但是链接给的却是daemonset,因为我截图截错了......
2.3,部署Ingress
这个可就厉害了,这个部署完就能装逼了
咳咳,回到正题,从上面可以知道Ingress就是个规则,指定哪个域名转发到哪个服务,所以说首先我们得有个服务,当然服务去哪找这里就不管了;这里默认为已经有了两个可用的服务,以下以Dashboard和kibana为例
先写一个Ingress文件,语法格式啥的请参考官方文档,由于我的Dashboard和Kibana都在kube-system这个命名空间,所以要指定namespace,写之前服务分布如下
vim dashboard-kibana-ingress.yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: dashboard-kibana-ingress
namespace: kube-system
spec:
rules:
- host: dashboard.mritd.me
http:
paths:
- backend:
serviceName: kubernetes-dashboard
servicePort: 80
- host: kibana.mritd.me
http:
paths:
- backend:
serviceName: kibana-logging
servicePort: 5601
装逼成功截图如下
三,部署Ingress TLS
上面已经搞定了Ingress,下面就顺便把TLS怼上;官方给出的样例很简单,大致步骤就两步:创建一个含有证书的秘密,在Ingress开启证书 ;但是我不得不喷一下,文档就提那么一嘴,大坑一堆,比如多域名配置,还有下面这文档特么的是逗我玩呢?
3.1,创建证书
首先第一步当然要有个证书,由于我这个Ingress有两个服务域名,所以证书要支持两个域名;生成证书命令如下:
# 生成 CA 自签证书
mkdir cert && cd cert
openssl genrsa -out ca-key.pem 2048
openssl req -x509 -new -nodes -key ca-key.pem -days 10000 -out ca.pem -subj "/CN=kube-ca"
# 编辑 openssl 配置
cp /etc/pki/tls/openssl.cnf .
vim openssl.cnf
# 主要修改如下
[req]
req_extensions = v3_req # 这行默认注释关着的 把注释删掉
# 下面配置是新增的
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = dashboard.mritd.me
DNS.2 = kibana.mritd.me
# 生成证书
openssl genrsa -out ingress-key.pem 2048
openssl req -new -key ingress-key.pem -out ingress.csr -subj "/CN=kube-ingress" -config openssl.cnf
openssl x509 -req -in ingress.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out ingress.pem -days 365 -extensions v3_req -extfile openssl.cnf
3.2,创建秘密
创建好证书以后,需要将证书内容放到秘密中,秘密中全部内容需要base64编码,然后注意去掉换行符(变成一行);以下是我的秘密样例(上一步中ingress.pem是证书, ingress-key.pem是证书的关键)
vim ingress-secret.yml
apiVersion: v1
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM5akNDQWQ2Z0F3SUJBZ0lKQU5TR2dNNnYvSVd5TUEwR0NTcUdTSWIzRFFFQkJRVUFNQkl4RURBT0JnTlYKQkFNTUIydDFZbVV0WTJFd0hoY05NVGN3TXpBME1USTBPRFF5V2hjTk1UZ3dNekEwTVRJME9EUXlXakFYTVJVdwpFd1lEVlFRRERBeHJkV0psTFdsdVozSmxjM013Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUM2dkNZRFhGSFpQOHI5Zk5jZXlkV015VVlELzAwQ2xnS0M2WjNpYWZ0QlRDK005TmcrQzloUjhJUE4KWW00cjZOMkw1MmNkcmZvQnBHZXovQVRIT0NJYUhJdlp1K1ZaTzNMZjcxZEVLR09nV21LMTliSVAzaGpSeDZhWQpIeGhEVWNab3ZzYWY1UWJHRnUydEF4L2doMTFMdXpTZWJkT0Y1dUMrWHBhTGVzWWdQUjhFS0cxS0VoRXBLMDFGCmc4MjhUU1g2TXVnVVZmWHZ1OUJRUXExVWw0Q2VMOXhQdVB5T3lMSktzbzNGOEFNUHFlaS9USWpsQVFSdmRLeFYKVUMzMnBtTHRlUFVBb2thNDRPdElmR3BIOTZybmFsMW0rMXp6YkdTemRFSEFaL2k1ZEZDNXJOaUthRmJnL2NBRwppalhlQ01xeGpzT3JLMEM4MDg4a0tjenJZK0JmQWdNQkFBR2pTakJJTUM0R0ExVWRFUVFuTUNXQ0VtUmhjMmhpCmIyRnlaQzV0Y21sMFpDNXRaWUlQYTJsaVlXNWhMbTF5YVhSa0xtMWxNQWtHQTFVZEV3UUNNQUF3Q3dZRFZSMFAKQkFRREFnWGdNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0SUJBUUNFN1ByRzh6MytyaGJESC8yNGJOeW5OUUNyYVM4NwphODJUUDNxMmsxUUJ1T0doS1pwR1N3SVRhWjNUY0pKMkQ2ZlRxbWJDUzlVeDF2ckYxMWhGTWg4MU9GMkF2MU4vCm5hSU12YlY5cVhYNG16eGNROHNjakVHZ285bnlDSVpuTFM5K2NXejhrOWQ1UHVaejE1TXg4T3g3OWJWVFpkZ0sKaEhCMGJ5UGgvdG9hMkNidnBmWUR4djRBdHlrSVRhSlFzekhnWHZnNXdwSjlySzlxZHd1RHA5T3JTNk03dmNOaQpseWxDTk52T3dNQ0h3emlyc01nQ1FRcVRVamtuNllLWmVsZVY0Mk1yazREVTlVWFFjZ2dEb1FKZEM0aWNwN0sxCkRPTDJURjFVUGN0ODFpNWt4NGYwcUw1aE1sNGhtK1BZRyt2MGIrMjZjOVlud3ROd24xdmMyZVZHCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBdXJ3bUExeFIyVC9LL1h6WEhzblZqTWxHQS85TkFwWUNndW1kNG1uN1FVd3ZqUFRZClBndllVZkNEeldKdUsramRpK2RuSGEzNkFhUm5zL3dFeHpnaUdoeUwyYnZsV1R0eTMrOVhSQ2hqb0ZwaXRmV3kKRDk0WTBjZW1tQjhZUTFIR2FMN0duK1VHeGhidHJRTWY0SWRkUzdzMG5tM1RoZWJndmw2V2kzckdJRDBmQkNodApTaElSS1N0TlJZUE52RTBsK2pMb0ZGWDE3N3ZRVUVLdFZKZUFuaS9jVDdqOGpzaXlTcktOeGZBREQ2bm92MHlJCjVRRUViM1NzVlZBdDlxWmk3WGoxQUtKR3VPRHJTSHhxUi9lcTUycGRadnRjODJ4a3MzUkJ3R2Y0dVhSUXVhelkKaW1oVzRQM0FCb28xM2dqS3NZN0RxeXRBdk5QUEpDbk02MlBnWHdJREFRQUJBb0lCQUJtRmIzaVVISWVocFYraAp1VkQyNnQzVUFHSzVlTS82cXBzenpLVk9NTTNLMk5EZUFkUHhFSDZhYlprYmM4MUNoVTBDc21BbkQvMDdlQVRzClU4YmFrQ2FiY2kydTlYaU5uSFNvcEhlblFYNS8rKys4aGJxUGN6cndtMzg4K0xieXJUaFJvcG5sMWxncWVBOW0KVnV2NzlDOU9oYkdGZHh4YzRxaUNDdmRETDJMbVc2bWhpcFRKQnF3bUZsNUhqeVphdGcyMVJ4WUtKZ003S1p6TAplYWU0bTJDR3R0bmNyUktodklaQWxKVmpyRWoxbmVNa3RHODFTT3QyN0FjeDRlSnozbmcwbjlYSmdMMHcwU05ZCmlwd3I5Uk5PaDkxSGFsQ3JlWVB3bDRwajIva0JIdnozMk9Qb2FOSDRQa2JaeTEzcks1bnFrMHBXdUthOEcyY00KLzY4cnQrRUNnWUVBN1NEeHRzRFFBK2JESGdUbi9iOGJZQ3VhQ2N4TDlObHIxd2tuTG56VVRzRnNkTDByUm1uZAp5bWQ4aU95ME04aUVBL0xKb3dPUGRRY240WFdWdS9XbWV5MzFVR2NIeHYvWlVSUlJuNzgvNmdjZUJSNzZJL2FzClIrNVQ1TEMyRmducVd2MzMvdG0rS0gwc0J4dEM3U2tSK3Y2UndVQk1jYnM3c0dUQlR4NVV2TkVDZ1lFQXlaaUcKbDBKY0dzWHhqd1JPQ0FLZytEMlJWQ3RBVmRHbjVMTmVwZUQ4bFNZZ3krZGxQaCt4VnRiY2JCV0E3WWJ4a1BwSAorZHg2Z0p3UWp1aGN3U25uOU9TcXRrZW04ZmhEZUZ2MkNDbXl4ZlMrc1VtMkxqVzM1NE1EK0FjcWtwc0xMTC9GCkIvK1JmcmhqZW5lRi9BaERLalowczJTNW9BR0xRVFk4aXBtM1ZpOENnWUJrZGVHUnNFd3dhdkpjNUcwNHBsODkKdGhzemJYYjhpNlJSWE5KWnNvN3JzcXgxSkxPUnlFWXJldjVhc0JXRUhyNDNRZ1BFNlR3OHMwUmxFMERWZWJRSApXYWdsWVJEOWNPVXJvWFVYUFpvaFZ0U1VETlNpcWQzQk42b1pKL2hzaTlUYXFlQUgrMDNCcjQ0WWtLY2cvSlplCmhMMVJaeUU3eWJ2MjlpaWprVkVMRVFLQmdRQ2ZQRUVqZlNFdmJLYnZKcUZVSm05clpZWkRpNTVYcXpFSXJyM1cKSEs2bVNPV2k2ZlhJYWxRem1hZW1JQjRrZ0hDUzZYNnMyQUJUVWZLcVR0UGxKK3EyUDJDd2RreGgySTNDcGpEaQpKYjIyS3luczg2SlpRY2t2cndjVmhPT1Z4YTIvL1FIdTNXblpSR0FmUGdXeEcvMmhmRDRWN1R2S0xTNEhwb1dQCm5QZDV0UUtCZ0QvNHZENmsyOGxaNDNmUWpPalhkV0ZTNzdyVFZwcXBXMlFoTDdHY0FuSXk5SDEvUWRaOXYxdVEKNFBSanJseEowdzhUYndCeEp3QUtnSzZmRDBXWmZzTlRLSG01V29kZUNPWi85WW13cmpPSkxEaUU3eFFNWFBzNQorMnpVeUFWVjlCaDI4cThSdnMweHplclQ1clRNQ1NGK0Q5NHVJUmkvL3ZUMGt4d05XdFZxCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
kind: Secret
metadata:
name: ingress-secret
namespace: kube-system
type: Opaque
创建完成后创一下就可
➜ ~ kubectl create -f ingress-secret.yml
secret "ingress-secret" created
其实这个配置比如证书转码啥的没必要手动去做,可以直接使用下面的命令创建,这里写这么多只是为了把步骤写清晰
kubectl create secret tls ingress-secret --key cert/ingress-key.pem --cert cert/ingress.pem
3.3,重新部署Ingress
生成完成后需要在Ingress中开启TLS,Ingress修改后如下
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: dashboard-kibana-ingress
namespace: kube-system
spec:
tls:
- hosts:
- dashboard.mritd.me
- kibana.mritd.me
secretName: ingress-secret
rules:
- host: dashboard.mritd.me
http:
paths:
- backend:
serviceName: kubernetes-dashboard
servicePort: 80
- host: kibana.mritd.me
http:
paths:
- backend:
serviceName: kibana-logging
servicePort: 5601
注意:一个Ingress只能使用一个秘密(secretName段只能有一个),也就是说只能用一个证书,更直白的说就是如果你在一个Ingress中配置了多个域名,那么使用TLS的话必须保证证书支持该Ingress下所有域名;并且这个secretName
一定要放在上面域名列表最后位置,否则会报错did not find expected key
无法创建;同时上面的hosts
段下域名必须跟下面的rules
中完全匹配
更需要注意一点:之所以这里单独开一段就是因为有大坑; Kubernetes Ingress默认情况下,当你不配置证书时,会默认给你一个TLS证书的,也就是说你Ingress中配置错了,比如写了2个secretName
,或者hosts
段中缺了某个域名,对于那么写了多个secretName
的情况,所有域名全会走默认证书;对于hosts
缺了某个域名的情况,缺失的域名将会走默认证书,部署时一定要验证一下证书,不能“有了就行”;更新Ingress证书可能需要等一段时间才会生效
最后重新部署一下即可
➜ ~ kubectl delete -f dashboard-kibana-ingress.yml
ingress "dashboard-kibana-ingress" deleted
➜ ~ kubectl create -f dashboard-kibana-ingress.yml
ingress "dashboard-kibana-ingress" created
注意:部署TLS后80端口会自动重定向到443,最终访问截图如下
历时5个小时鼓捣,到此结束
转载请注明出处,本文采用CC4.0协议授权