Kubernetes的網絡結構
K8s的網絡相對比較復雜, 包含了如下幾類IP地址:
Host Network
運行K8s集群的宿主服務器的內網IP, 其網段在配置宿主機時設置. 這些服務器可能是物理機, 也可能是ESXi或KVM虛機. 可以根據這個IP在K8s初始化時設置--apiserver-advertise-address, 配置外網訪問時, 前端的負載均衡要通過這些IP訪問服務.
Docker Bridge Network
運行Docker服務時啟動的虛擬網卡docker0的IP, 通常為 172.17.0.1/16, 對應docker內部默認的bridge網段. 這個網絡在K8s中不會用到.
Pod Network
K8s中Pod單元的IP, 其網段在K8s初始化時使用--pod-network-cidr設置, 此處使用172.16.0.0/16, 在每個宿主(node節點)上, 都會有一個對應的二級網段, 例如 172.16.1.0/24, 如果使用的是flannel, 那么在宿主機上會對應成對出現的兩個虛擬網卡 cni0 和 flannel.1 (如果宿主機上還沒部署pod, cni0可能不會出現)
Pod網絡在集群內可以跨節點相互ping通, 在master和node主機上也可以直接訪問Pod的IP
Service Network
K8s中Service單元的IP, 其網段在K8s初始化時使用--service-cidr設置, 在master和node主機上不能直接訪問. 這里使用10.1.0.0/16.
Ingress的機制
要解決的問題
因為K8s中Pod是易變的, Pod IP在更新中會自動修改, 使用Service能使訪問入口相對固定, 但是Service IP在集群外不能訪問, 要對外提供訪問, 只能把Service以NodePort, LoadBalancer這些方式Expose出去, 但是NodePort會與每一個Node主機綁定, 而LoadBalancer需要雲服務商提供相應的服務(或自己安裝).
原理
Ingress 啟動一個獨立的Pod來運行七層代理, 可以是 Nginx, Traefik 或者是 Envoy. Ingress Pod會直接代理后端提供服務的Pod, 為了能監聽后端Pod的變化, 需要一個 Headless Service 通過Selector選擇指定的Pod, 並收集到Pod對應的IP. 一旦后端Pod產生變化, Headless Service 會自動根據變化更改配置文件並重載.
如果使用的是 Nginx 類型的 Ingress Pod, 則每次變化后通過reload修改過的配置文件實現規則更新.
Ingress Controller
在kubernetes集群內節點上運行web七層代理所對應的Pod, 由此Pod代理集群內部的Service, Service再把流量轉發給集群內部對應的Pod, 這就叫做 Ingress Controller, Ingress Controller 基於 DaemonSet 控制器實現, DaemonSet用於保證集群內部每個節點上都運行一個指定的Pod.
部署Ingress Nginx
軟件版本
這里使用的環境是 K8s 1.17, Docker 18.06.3, Ingress Nginx 0.26.2
Ingress Nginx項目地址: https://github.com/kubernetes/ingress-nginx/
Ingress Nginx的安裝文檔: https://kubernetes.github.io/ingress-nginx/deploy/ 使用 https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/
安裝前提
已經配置好K8s集群
部署公共部分
現在最新的標簽是0.26.2, 使用部署模板
https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.26.2/deploy/static/mandatory.yaml
下載后在master主機上執行
kubectl apply -f mandatory.yaml
可以使用下面的命令查看部署的結果
kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx --watch kubectl get services
部署Nodeport部分
因為不使用雲服務商的服務, 所以要使用Nodeport的方式, 對應的部署模板在
這一步實際上是創建一個Nodeport將ingress-nginx服務發布到Node節點的Host Network上, 原模板中未指定端口, 創建后會隨機設置端口, 可以修改一下, 只能使用端口范圍 3000 ~ 32767. 修改后的service-nodeport.yaml, 將80端口映射到了30080, 443映射到了30443
apiVersion: v1 kind: Service metadata: name: ingress-nginx namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: type: NodePort ports: - name: http port: 80 targetPort: 80 protocol: TCP nodePort: 30080 - name: https port: 443 targetPort: 443 protocol: TCP nodePort: 30443 selector: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx
可以使用下面的命令查看發布的結果, 以及映射出去的實際端口
kubectl get services -n ingress-nginx
創建服務並測試
以下都是使用defalut的namespace. 都使用kubectl apply -f 模板名 命令進行發布
Deployment, 創建兩個nginx的pod
# nginx-deployment.yml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 # tells deployment to run 2 pods matching the template template: metadata: labels: app: nginx spec: containers: - name: nginx image: registry.cn-shanghai.aliyuncs.com/jovi/nginx:alpine ports: - containerPort: 80
Service, 將上面創建的兩個Pod發布為Servce, 將Pod的80端口映射到Service的8080端口
# nginx-service.yml apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: nginx ports: - protocol: TCP port: 8080 targetPort: 80
查看service詳情
$ kubectl describe service my-service Name: my-service Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"my-service","namespace":"default"},"spec":{"ports":[{"port":8080,... Selector: app=nginx Type: ClusterIP IP: 10.1.116.99 Port: <unset> 8080/TCP TargetPort: 80/TCP Endpoints: 172.16.1.6:80,172.16.1.7:80 Session Affinity: None Events: <none>
Ingress, 將上面的Service的8080端口映射到Ingress的http
# ingress-test.yaml apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: test-ingress annotations: # use the shared ingress-nginx kubernetes.io/ingress.class: "nginx" spec: rules: - http: paths: - backend: serviceName: my-service servicePort: 8080
查看Ingress詳情
$ kubectl get services -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.1.60.152 <none> 80:30080/TCP,443:30443/TCP 69m milton@k8s00:~/backup$ kubectl describe ingress test-ingress Name: test-ingress Namespace: default Address: 10.1.60.152 Default backend: default-http-backend:80 (<none>) Rules: Host Path Backends ---- ---- -------- * my-service:8080 (172.16.1.6:80,172.16.1.7:80) Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"networking.k8s.io/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"test-ingress","namespace":"default"},"spec":{"rules":[{"http":{"paths":[{"backend":{"serviceName":"my-service","servicePort":8080}}]}}]}} kubernetes.io/ingress.class: nginx Events: <none>
這時候, 就可以通過 http://宿主機IP:30080 訪問到這兩個Pod的Nginx默認頁.