目录
- Ingress介绍
- 1、Pod漂移问题
- 2、端口管理问题
- 3、域名分配及动态更新问题
- Nginx Ingress配置
- 1、部署默认后端
- 2、部署Ingress Controller
- 3、部署Ingress
- 4、配置Ingress TLS
- 创建secret
- 修改ingress,开启tls
- 5、通过ingress暴露tcp服务
- 6、通过configmap修改nginx controller的一些全局变量
- 定义一个名为nginx-controller-configmap的yaml内容如下:
- 修改nginx-controller的yml文件nginx-ingress-daemonset.yaml启动参数如下:
- 应用该yaml文件:
Ingress介绍
Kubernetes暴露服务的方式目前有三种:LoadBlancer Service、NodePort Service、Ingress。
在详细说明Ingress之前,我们先大概的说一说,kubernetes集中内服务想要暴露出去需要面临的几个问题:
1、Pod漂移问题
众所周知,kubernetes具有强大的副本控制能力,能保证在任意副本(Pod)挂掉时自动从其他 节点上启动一个新的,还可以动态扩缩容等。也就是说单个pod可能在任何时刻出现在任何节点上,也可能在任何时刻死在任何节点上。那么随着pod的创建和销毁,pod ip也会随着动态变化。那么如何把这个动态的pod ip暴露出去?为了解决这个问题,kubernetes引入了service机制。service可以以标签的形式选定一组带有指定标签的pod,并监控和自动负载他们的pod ip,这样一来,我们就只需要向外暴露service的ip就可以了,这就是nodeport的模式:即在每个节点上开启一个端口,然后转发到内部service ip上。如下图:
2、端口管理问题
在上面的问题中,我们通过引入Service并暴露nodeport的方式解决了pod的漂移问题。但是我们在引入nodeport后,又会面临一个新的问题:随着服务越来越多,我们在每个node节点上开启的nodeport也会越来越多,最终变得难以维护。这时候,就引入了一种新的思考方式:能不能使用nginx或者haproxy等负载均衡的方式只监听一个端口,比如80,然后按照域名往后端转发?当然可以,最简单的实现就是使用daemonset的方式在node上监听80,然后配置好转发规则。因为nginx外网绑定了宿主机80端口(就像nodeport),本身又在集群内,直接向后转发到相应的service ip即可。如下图所示:
3、域名分配及动态更新问题
从上面的思路,采用nginx似乎已经解决问题了。但其实这里面有一个很大的缺陷:每次有新服务加入,怎么样修改nginx配置?总不能每次都手动改下nginx镜像,然后再来个rolling update前端的nginx pod吧?
由此,kubernetes引入了ingress。ingress简单的理解就是,你原来要改nginx配置,然后配置各种域名对应哪个service,现在把这个动作抽象出来,变成了一个ingress对象,可以直接使用yml来创建。这样就不用不每次去修改nginx了,直接修改yml,然后更新即可。
Ingress一共包含三大组件:Ingress、Ingress Controller以及Nginx。Ingress Controller通过与kubernetes api交互,动态的去感知集群中ingress规则变化,然后读取它,再按照自己的模板生成一段nginx配置,再写到nginx pod里,最后reload一下nginx。工作流如下图:
在实际部署中,kubernetes已经将nginx和ingress controller合并为一个组件,所以nginx无需单独部署,只需要部署ingress controller即可。
Nginx Ingress配置
1、部署默认后端
我们知道前端的Nginx最终要负载到后端service上,那么如果访问到不存在的域名怎么办?官方给出的建议是部署一个默认后端,对于未知请求全部负载到这个默认后端上,这个后端别的事啥也不干,只是返回一个404。
部署如下:
kubectl create -f default-backend.yml
default-backend.yml文件可以直接在官方Ingress仓库找到,下面给出一个详细地址:
https://raw.githubusercontent.com/kubernetes/ingress/master/examples/deployment/nginx/default-backend.yaml
2、部署Ingress Controller
Ingress Controller官方提供了多种方式部署。个人还是推荐使用Daemonset的方式,官方也有给出的示例文件,可以参考这里
我的配置相对于官方示例文件,有一点小小的改动,贴在下面:
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: nginx-ingress-lb
labels:
name: nginx-ingress-lb
namespace: kube-system
spec:
template:
metadata:
labels:
name: nginx-ingress-lb
annotations:
prometheus.io/port: '10254'
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 60
#hostNetwork: true #看到网上有人讲,需要指定该配置项才能生效,但我在实际测试中,并不需要此项,可能是因为采用的网络架构不一样,备注一下,以做说明。
containers:
- image: dk-reg.op.douyuyuba.com/library/nginx-ingress-controller:0.9.0-beta.8
name: nginx-ingress-lb
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
livenessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 1
ports:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 443
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- /nginx-ingress-controller
- --default-backend-service=kube-system/default-http-backend
- --apiserver-host=http://10.1.61.132:8080 #ingress默认通过https连接api server,因为我这里api server仅支持https,所以需要指定--apiserver-host的地址
部署:
kubectl create -f ingress-nginx-daemonset.yml
3、部署Ingress
前面我们说到Ingress其实就是个规则,指定哪个域名转发到哪个service,所以说首先得有个service。不过service的具体配置这里不作说明。我们就以kubernetes-dashboard和kibana为例:
kubectl get svc kubernetes-dashboard --namespace=kube-system
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes-dashboard 10.254.213.109 80/TCP 13d
kibana 10.254.213.110 5601/TCP 13d
创建一个dashboard-kibana-ingress.yml如下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: dashboard-kibana-ingress
namespace: kube-system
spec:
rules:
- host: dashboard.dz11.com
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 80
- host: kibana.dz11.com
http:
paths:
- backend:
serviceName: kibana
servicePort: 5601
部署:
kubectl create -f dashboard-kibana-ingress.yml
4、配置Ingress TLS
默认情况下,ingress只提供了http服务,而没有https服务,要部署一个https服务,首先得有https证书。证书的生成,这里不做说明。
创建secret
假定,我们现在已经有了一个ca.crt的证书文件和一个server.key的密钥文件。我们需要创建一个secret。在创建secret之前,先要把证书及密钥内容通过base64编码。如下:
cat ca.crt | base64 -w 0
cat server.key|base64 -w 0
创建ingress-secret.yml文件,内容如下:
apiVersion: v1
kind: Secret
metadata:
name: ingress-secret
namespace: default
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlIWlRDQ0JrMmdBd0lCQWdJTUxQbnRoUStHZlJJOTNHd0dNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1HQXhDekFKDQpCZ05WQkFZVEFrSkZNUmt3RndZRFZRUUtFeEJIYkc5aVlXeFRhV2R1SUc1MkxYTmhNVFl3TkFZRFZRUURFeTFIDQpiRzlpWVd4VGFXZHVJRVJ2YldGcGJpQldZV3hwWkdGMGFXOXVJRU5CSUMwZ1UwaEJNalUySUMwZ1J6SXdIaGNODQpNVGN3TlRJMU1EWTBNRFEyV2hjTk1UZ3hNVEk0TWpNMU9UVTVXakE0TVNFd0h3WURWUVFMRXhoRWIyMWhhVzRnDQpRMjl1ZEhKdmJDQldZV3hwWkdGMFpXUXhFekFSQmdOVkJBTU1DaW91WkhveE1TNWpiMjB3Z2dFaU1BMEdDU3FHDQpTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDaTA5bjg0WGdhWGo0YStvaERyQXJONWVLeEpYbXV0QmJNDQpqNVFJdEZDa2l0dUg3OWxtNmtMcThSL2E0ZHdEc1h6czZXVWNRRHBjbUlqNzdOYlBQYzJrZVZlcDMxeVZLSUpKDQpkYWhFd2V5NlVFBQeWwwS2xOeFdMODhETGtMZUFvby8rNVNBaUIzUktsUUswMXREWnIrem4rYkxZVUQ4YzU4DQ
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBb3RQWi9PRjRHbDQrR3ZxSVE2d0t6ZVhpc1NWNXJyUVd6SStVQ0xSUXBJcmJoKy9aClp1cEM2dkVmMnVIY0E3Rjg3T2xsSEVBNlhKaUkrK3pXenozTnBIbFhxZDljbFNpQ1NYV29STUhzdWtKL1RFbUcKRUhsOTI3M1BFZU1QclhqSUpEeC85K1ZQRUlFRnlHc3hYamFaR2FtZnJYNmJvMVVFaExlMlEySVpWMDh1UU1EMQpUTVArb0VyZHY1MkUzZlAyxxxxxNBSjNUNWtrUm5IaE5TWDFIankySnFBcHNDYW5pKzI4MmV2NGlYYkwwCks1NVA4N3BqdGw4WGtWTGZDbXJYTSt6dEs4aGNkQ3ZCOHU0NkpNRWQ2R1JjeXpBWHJ6b1dYM3RxUGVDdGxrazgKcEVMRXVFSmJpV2hYRjZEVUtlK1NpeHIyMTJHdm5JcncreTBxendJREFRQUJBb0lCQVFDTFhTSWQ1R2xrd0RjTgo1bE1OQU1xNmtrRmw5N3BmZ25wbEdack5uRy9OZGFBU2lJS2VLSEdnSDBOeGw1RTFoQXQxeHdvb2xQeWUxbHVnCnJJVHJHbTNSa1o0cm9pYmU
执行创建:
kubect create -f ingress-secret.yml
也可以通过命令行的方式直接创建一个secret:
kubectl create secret tls ingress-secret --key server.key --cert ca.crt
修改ingress,开启tls
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: dashboard-kibana-ingress
namespace: kube-system
spec:
tls:
- hosts:
- dashboard.dz11.com
- kibana.dz11.com
secretName: ingress-secret
rules:
- host: dashboard.dz11.com
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 80
- host: kibana.dz11.com
http:
paths:
- path: /
backend:
serviceName: kibana
servicePort: 5601
注意:一个 Ingress 只能使用一个 secret(secretName 段只能有一个),也就是说只能用一个证书,更直白的说就是如果你在一个 Ingress 中配置了多个域名,那么使用 TLS 的话必须保证证书支持该 Ingress 下所有域名;并且这个 secretName 一定要放在上面域名列表最后位置,否则会报错 did not find expected key 无法创建;同时上面的 hosts 段下域名必须跟下面的 rules 中完全匹配
更需要注意一点:Kubernetes Ingress默认情况下,当你不配置证书时,会默认给你一个 TLS 证书的,也就是说你 Ingress 中配置错了,比如写了2个 secretName、或者 hosts 段中缺了某个域名,那么对于写了多个 secretName 的情况,所有域名全会走默认证书;对于 hosts 缺了某个域名的情况,缺失的域名将会走默认证书,部署时一定要验证一下证书,不能 “有了就行”;更新 Ingress 证书可能需要等一段时间才会生效
一旦部署了https,默认请求的http会自动跳转到https,所以在同时需要https和http并存的应用场景,也需要注意
最后重新部署下ingress即可:
kubectl delete -f dashboard-kibana-ingress.yml
kubectl create -f dashboard-kibana-ingress.yml
5、通过ingress暴露tcp服务
通过ingress暴露tcp服务,我们需要先定义一个nginx-tcp-ingress-configmap.yaml的configmap,示例如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: tcp-configmap-example
data:
9000: "default/redis:6379"
配置ingress controller的yaml文件,如下:
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: nginx-ingress-lb
labels:
name: nginx-ingress-lb
namespace: kube-system
spec:
template:
metadata:
labels:
name: nginx-ingress-lb
annotations:
prometheus.io/port: '10254'
prometheus.io/scrape: 'true'
spec:
terminationGracePeriodSeconds: 60
containers:
- image: dk-reg.op.douyuyuba.com/library/nginx-ingress-controller:0.9.0-beta.8
name: nginx-ingress-lb
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
livenessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
timeoutSeconds: 1
ports:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 443
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- /nginx-ingress-controller
- --default-backend-service=kube-system/default-http-backend
- --apiserver-host=http://10.1.61.132:8080
- --tcp-services-configmap=default/nginx-tcp-ingress-configmap
以上表示暴露 default namespace 下服务名为 redis,端口为 6379 的服务到 nginx-ingress-lb 所在节点的 9000 端口。
6、通过configmap修改nginx controller的一些全局变量
通过上面启动nginx controller的yaml文件,其实我们可以看出来,在启动controller的时候,向启动命令传递了一大堆参数,包括--default-backend-service以及--apiserver-host等。更多的参数,可以直接参考相关文档
我们知道,nginx controller本质上就是一个nginx代理,这个代理使用了一大堆nginx默认参数启动。而在某些特定场景下,这些我们需要定制这些参数以更适用于我们的需求。在controller启动的时候,提供了一个--configmap的参数,我们可以将需要定制的参数保存到一个configmap中,并在controller启动的时候,来读取这个configmap,获取其值,应用到controller中。具体哪些值可以通过configmap来传递,可以直接参考相关文档
下面是一个简单的示例:
定义一个名为nginx-controller-configmap的yaml内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-ingress-configmap
namespace: default
data:
proxy-body-size: 1024m
这个configmap定义了一个proxy-body-size的大小为1024m,即nginx中client_max_body_size的参数为1024m。
修改nginx-controller的yml文件nginx-ingress-daemonset.yaml启动参数如下:
args:
- /nginx-ingress-controller
- --default-backend-service=kube-system/default-http-backend
- --apiserver-host=http://10.1.61.132:8080
- --configmap=default/nginx-ingress-configmap
- --tcp-services-configmap=default/nginx-tcp-ingress-configmap
应用该yaml文件:
kubectl apply -f nginx-ingress-daemonset.yaml
需要说明的直接apply daemonset并不会立即生效,在这里我们提供一种简单的方法让其生效,就是手动通过kuecetl delete 的方式删除旧的pod ,然后k8s会自动创建新的pod,新pod会自动应用新的配置。
然后我们通过kubectl exec -it /bin/sh的方式登录到新的pod,查看/etc/nginx/nginx.conf文件,可以看到client_max_body_size由之前的默认1m修改为了1024m。