1. 程式人生 > >kubernetes ingress 在物理機上的nodePort和hostNetwork兩種部署方式解析及比較

kubernetes ingress 在物理機上的nodePort和hostNetwork兩種部署方式解析及比較

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錯誤。而訪問繫結的域名,就可以正確導流了,返回正確結果。

參考資料