ingress controller在物理機上的兩種部署方式
ingress controller(ingress-nginx)負責k8s中的7層負載均衡。其在物理機中有多種部署方式。本文中主要選擇了nodePort和hostNetwork兩種部署方式進行介紹。主要原因是這兩種部署方式不需要藉助於其他元件,直接使用的是k8s的基礎元件和使用方式,較為容易理解和排障。
注:本文中的kube-proxy使用的是iptables
本文使用的ingress-nginx的commit版本是51ad0bc54b1475384b67bee9e8a8e41e26b18bc4。該版本的部署方式是在NGINX: 0.24.1版本後重構了deploy資料夾中的ingress-nginx的部署相關檔案。採用了kustomize進行部署配置。因此推薦使用kubectl 1.14以上的版本。且特別注意,下面的命令都是使用了kubectl apply -k
的方式執行,而不是-f
引數。
deploy各個資料夾走讀
ingress-nginx專案deploy資料夾下有多個資料夾,為不同的環境提供支援。這裡我們主要介紹的是與在物理機部署相關的幾個資料夾。
cluster-wide
├── cluster-wide
│ ├── cluster-role-binding.yaml
│ ├── cluster-role.yaml
│ └── kustomization.yaml
cluster-wide資料夾主要用於建立cluster-role和cluster-role-binding,為ingress-controller提供apiserver的cluster訪問許可權。這裡cluster-role-binding.yaml要作一下修改,指定namespace: ingress-nginx
。因為在後面建立的ServiceAccount等其他資源都是預設在該namespace下。
[root@local cluster-wide]# cat cluster-role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: nginx-ingress-clusterrole-nisa-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
cloud-generic
├── cloud-generic
│ ├── deployment.yaml
│ ├── kustomization.yaml
│ ├── role-binding.yaml
│ ├── role.yaml
│ ├── service-account.yaml
│ └── service.yaml
cloud-generic資料夾提供了通用的一些部署檔案。其中deployment.yaml是負責建立ingress-controller的deployment,預設副本數為1,可以進行調節。service.yaml是為ingress-controller建立的service。其他的主要是與賬戶相關的內容。
baremetal
├── baremetal
│ ├── kustomization.yaml
│ └── service-nodeport.yaml
baremetal資料夾主要是建立了一個NodePort型別的svc,然後向ingress-controller的容器進行導流的。因為該部署方式需要依賴cloud-generic裡的資源,因此在kustomization.yaml中描述了對cloud-generic的依賴。可以參看kustomization.yaml中的bases。
[root@local baremetal]# cat deploy/baremetal/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../cloud-generic
patchesStrategicMerge:
- service-nodeport.yaml
大致瞭解了相關部署檔案的功能,那麼結合具體的兩種部署圖(部署圖來自官網),來進行一下具體介紹。
nodePort部署
nodePort的部署思路就是通過在每個節點上開闢nodePort的埠,將流量引入進來,而後通過iptables首先轉發到ingress-controller容器中(圖中的nginx容器),而後由nginx根據ingress的規則進行判斷,將其轉發到對應的應用web容器中。因此採用nodePort部署較為簡單,直接使用以下命令即可。
kubectl apply -k deploy/baremetal/
kubectl apply -k deploy/cluster-wide/
hostNetwork部署
相比較起來,hostNetwork模式不再需要建立一個nodePort的svc,而是直接在每個節點都建立一個ingress-controller的容器,而且將該容器的網路模式設為hostNetwork。也就是說每個節點物理機的80和443埠將會被ingress-controller中的nginx容器佔用。當流量通過80/443埠進入時,將直接進入到nginx中。而後nginx根據ingress規則再將流量轉發到對應的web應用容器中。
這裡需要對cloud-generic/deployment.yaml進行一下改動,將其資源型別從deployment改為daemonset,並且在spec中新增hostNetwork: true
,從而使其可以使用物理機網路。
[root@local deploy]# cat cloud-generic/deployment.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: nginx-ingress-controller
spec:
template:
metadata:
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
labels:
nginx-ingress-controller: 0.24.1
spec:
serviceAccountName: nginx-ingress-serviceaccount
hostNetwork: true
...
修改完成後,使用以下命令即可完成hostNetwork模式的部署。
kubectl apply -k deploy/cloud-generic/
kubectl apply -k deploy/cluster-wide/
兩種部署方式的比較
相比較起來,nodePort部署模式中需要部署的ingress-controller容器較少。一個叢集可以部署幾個就可以了。而hostNetwork模式需要在每個節點部署一個ingress-controller容器,因此總起來消耗資源較多。另外一個比較直觀的區別,nodePort模式主要佔用的是svc的nodePort埠。而hostNetwork則需要佔用物理機的80和443埠。
從網路流轉來說,通過nodePort訪問時,該node節點不一定部署了ingress-controller容器。因此還需要iptables將其轉發到部署有ingress-controller的節點上去,多了一層流轉。
另外,通過nodePort訪問時,nginx接收到的http請求中的source ip將會被轉換為接受該請求的node節點的ip,而非真正的client端ip。
而使用hostNetwork的方式,ingress-controller將會使用的是物理機的DNS域名解析(即物理機的/etc/resolv.conf)。而無法使用內部的比如coredns的域名解析。
因此具體使用哪種部署方式,需要根據實際情況和需求進行選擇。
ingress controller試用
在部署好ingress controller後,可以通過一個樣例進行測試使用。首選建立一個應用容器和以及一個對應的svc。
kubectl run web --image=gcr.azk8s.cn/google-samples/hello-app:1.0 --port=8080
kubectl expose deployment web --target-port=8080
然後建立ingress,將通過hello-world.info域名訪問ingress的請求轉發到該容器中去。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: hello-world.info
http:
paths:
- path: /*
backend:
serviceName: web
servicePort: 8080
這一切完成後,在/etc/hosts裡繫結域名,127.0.0.1 hello-world.info
。
sed -i '$a 127.0.0.1 hello-world.info' /etc/hosts
然後通過curl命令進行測試。
root@i-5i2mhmaus9v67pz19zmahp07u:~# curl 127.0.0.1
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.15.10</center>
</body>
</html>
root@i-5i2mhmaus9v67pz19zmahp07u:~# curl hello-world.info
Hello, world!
Version: 1.0.0
Hostname: web-77f97c6cc7-g7qft
這裡可以看到,我們訪問本地127.0.0.1的時候,會返回404錯誤。而訪問繫結的域名,就可以正確導流了,返回正確結果。