目錄
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倉庫找到,下面給出一個詳細地址:
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 <none> 80/TCP 13d
kibana 10.254.213.110 <none> 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
然后我們通過kubectl exec -it