Ingress是kubernetes中用來對集羣外部進來的請求進行負載、路由控制的一種機制。經過ingress,能夠方便的將集羣內的service以http或https方式對外提供服務,並且不用將各個服務再單獨暴露。Ingress功能由Ingress resource和Ingress Controllers共同協做完成。java
Ingress resource是kubernetes中的一種資源,是相似於pod、deployment的一種API對象,能夠經過kubectl命令建立、銷燬、更新,其對應的Kind是Ingress,能夠在其spec屬性中定義服務路由的規則信息。一般Ingress resource中定義的規則須要支持如下的特性:node
基於內容路由nginx
對每個主機均可以單獨設置TLS/SSL鏈接信息git
Ingress resource只是定義了路由的規則信息,真正利用這些信息對請求進行控制是經過Ingress Controllers來實現的。不像kube-controller-manager中的其餘controller組件,它們是被做爲集羣的一部分隨着集羣直接安裝好,Ingress controllers 須要咱們本身來安裝啓動,而且kubernetes支持不少種實現來知足不一樣需求,具體參考Ingress Controllers。github
在kubernetes中,Ingress Controller以pod形式運行,監控API Server的/ingress接口後端的backend services,若是service發生變化,Ingress Controller自動更新轉發規則。如Nginx Ingress Controller的工做過程以下:spring
端口競爭docker
一般狀況下,咱們部署在kubernetes集羣中的應用須要給外部訪問,這時咱們須要在kubernetes中定義NodePort、LoadBalancer等類型的Servcie來實現,具體參考Service。其中LoadBalancer須要在提供相應機制的雲環境中才能使用,因此在自建的kubernetes集羣中都是經過NodePort類型的Service來實現,就是將宿主機的port的Service的Port作個映射,經過訪問宿主機的端口來對service進行訪問。在kubernetes集羣中只有一個應用,或者應用數量比較少,可以正確分配各個應用對應的宿主機端口時還能夠應付。隨着應用的追加,端口的映射就變的混亂起來,有的應用還會由於限制必須使用特定的端口號,而這些端口號可能前期已經分配給了別的應用,這時就出現了端口競爭後端
服務動態更新api
爲了不端口競爭,能夠在系統中部署反向代理服務(nginx、HAproxy)等,這時能夠把對外的集羣服務都經過反向代理服務來暴露,這樣就帶來了另外一個問題,當有新的服務追加進來,或者舊的服務須要刪除,這時還要從新編輯反向代理的配置文件,而後對反向代理服務進行升級。上線/下線一個應用卻須要編輯另外一個應用的配置文件,服務的升級、回滾等等都須要考慮,不只麻煩還容易出錯服務器
當採用Ingress機制時,部署新應用,只須要建立針對新應用的Ingress resource,Ingress Controllers就會自動把Ingress resource中的規則合併起來,做爲總體路由規則對外服務,而且服務都經過Ingress Controllers統一對外提供,也解決了端口競爭的問題。接下來以nginx-ingress爲例來說解具體的原理以及在集羣中部署Ingress。
nginx-ingress實現的原理
部署nginx-ingress-controller
目前部署nginx-ingress-controller有兩個官方的安裝方式:nginxinc/kubernetes-ingress、kubernetes/ingress-nginx,具體對比參考區別
部署前可參閱下這個異同,下面以kubernetes/ingress-nginx爲例,進行nginx-ingress-controller的部署
下載啓動文件
$ mkdir -p /opt/k8s/yml/ingress-nginx $ cd /opt/k8s/yml/ingress-nginx $ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
下載鏡像,上傳到本身的私有鏡像倉庫
mandatory.yaml中指定的原始鏡像是quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0,在國內可能沒有辦法下載,改爲從阿里鏡像倉庫下載,爲了啓動時不用從新從互聯網下載,把這個鏡像push到咱們本身的鏡像倉庫中
$ docker pull registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.30.0 $ docker tag registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.30.0 192.168.0.107/k8s/nginx-ingress-controller:0.30.0 $ docker push 192.168.0.107/k8s/nginx-ingress-controller:0.30.0
修改下載的mandatory.yaml,將鏡像名稱改爲咱們私有倉庫中的鏡像名稱,以後啓動服務
``` $ cd /opt/k8s/yml/ingress-nginx $ kubectl create -f mandatory.yaml namespace/ingress-nginx created configmap/nginx-configuration created configmap/tcp-services created configmap/udp-services created serviceaccount/nginx-ingress-serviceaccount created clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created role.rbac.authorization.k8s.io/nginx-ingress-role created rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created deployment.apps/nginx-ingress-controller created limitrange/ingress-nginx created ``` * 主要是發佈了deployment類型的nginx-ingress-controller,默認replicate是1,權限經過nginx-ingress-serviceaccount來設置 * 建立了一個serviceaccount:nginx-ingress-serviceaccount,並賦予相關的權限
由於咱們是在裸機上部署的kubernetes,還須要部署一個service,將nginx-ingress-controller暴露出去,使集羣外的服務可以訪問,此處採用NodePort類型的service
$ cd /opt/k8s/yml/ingress-nginx $ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml $ kubectl create -f service-nodeport.yaml service/ingress-nginx created
這樣咱們經過建立一個service,把nginx-ingress-controller暴露出去,經過以下命令查看具體暴露的端口號
$ kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.254.10.208 <none> 80:18797/TCP,443:29468/TCP 108s
驗證安裝狀況
$ kubectl get pods -n ingress-nginx NAME READY STATUS RESTARTS AGE nginx-ingress-controller-7fdc95bf86-rgmdn 1/1 Running 0 22m
查看安裝的controller版本
$ kubectl exec -it nginx-ingress-controller-7fdc95bf86-rgmdn -n ingress-nginx -- /nginx-ingress-controller --version NGINX Ingress controller Release: 0.30.0 Build: git-7e65b90c4 Repository: https://github.com/kubernetes/ingress-nginx nginx version: nginx/1.17.8
利用官方給咱們提供的例子http-svc,啓動一個服務,經過Ingress controller的端口訪問咱們的服務。由於官方例子http-svc.yaml中的鏡像在gcr.io中,國內沒法訪問,須要修改爲阿里倉庫中的鏡像registry.aliyuncs.com/google_containers/echoserver:1.4,這個鏡像的做用是接收客戶端的請求,返回服務端和客戶端的header信息。
啓動文件:
$ cd /opt/k8s/yml/ingress-nginx $ cat > http-svc.yaml<< EOF apiVersion: apps/v1 kind: Deployment metadata: apiVersion: apps/v1 name: http-svc spec: replicas: 1 selector: matchLabels: app: http-svc template: metadata: labels: app: http-svc spec: containers: - name: http-svc image: registry.aliyuncs.com/google_containers/echoserver:1.4 ports: - containerPort: 8080 env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP --- apiVersion: v1 kind: Service metadata: name: http-svc labels: app: http-svc spec: ports: - port: 80 targetPort: 8080 protocol: TCP name: http selector: app: http-svc EOF
啓動命令
$ cd /opt/k8s/yml/ingress-nginx $ kubectl create -f http-svc.yaml
建立一個Ingress resources
目前咱們只建立了一個後端服務,因此建立一個簡單的Ingress,把全部請求都路由到http-svc:80
$ cd /opt/k8s/yml/ingress-nginx $ cat > single-ingress.yml <<EOF apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: single-ingress annotations: kubernetes.io/ingress.class: "nginx" spec: backend: serviceName: http-svc servicePort: 80 EOF
建立 ingress
$ cd /opt/k8s/yml/ingress-nginx $ kubectl create -f single-ingress.yml
查看ingress信息
$ kubectl get ingress -o wide NAME HOSTS ADDRESS PORTS AGE single-ingress * 10.254.10.208 80 11m $ kubectl describe ingresses single-ingress Name: single-ingress Namespace: default Address: 10.254.10.208 Default backend: http-svc:80 (172.30.22.3:8080) Rules: Host Path Backends ---- ---- -------- * * http-svc:80 (172.30.22.3:8080) Annotations: kubernetes.io/ingress.class: nginx Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 11m nginx-ingress-controller Ingress default/single-ingress Normal UPDATE 11m nginx-ingress-controller Ingress default/single-ingress
經過Ingress controller暴露出來的http端口或者https端口訪問http-svc服務,查看Ingress controller服務暴露的地址(按照前面的部署流程,咱們是經過一個NodePort類型的service來暴露的)
$ kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.254.10.208 <none> 80:18797/TCP,443:29468/TCP 12h
經過18797端口訪問服務
# http端口訪問 $ curl http://192.168.0.107:18797 # https端口訪問 $curl --insecure https://192.168.0.107:29468 CLIENT VALUES: client_address=172.30.22.7 command=GET real path=/ query=nil request_version=1.1 request_uri=http://192.168.0.107:8080/ SERVER VALUES: server_version=nginx: 1.10.0 - lua: 10001 HEADERS RECEIVED: accept=*/* host=192.168.0.107:18797 user-agent=curl/7.58.0 x-forwarded-for=172.30.22.1 x-forwarded-host=192.168.0.107:18797 x-forwarded-port=80 x-forwarded-proto=http x-real-ip=172.30.22.1 x-request-id=9c9b6f86b5a0d0a664c0f9f01a0bde47 x-scheme=http BODY: -no body in request-
這樣咱們就完成了經過nginx-ingress-controller對後端服務的訪問(若是用spring cloud作過微服務開發,會對這個操做很熟悉,由於和zuul、getway等網關功能很相似)。
假如咱們又在集羣中新啓動了一個服務,爲了能從外部訪問這個服務,咱們也要把這個新服務經過nginx-ingress-controller對外暴露,咱們能夠新追加一個ingress source對象,定義咱們這個新服務相關的信息.
啓動新服務,咱們這個新服務是本身編寫的一個spring boot 工程,裏面只有一個restful接口
@RequestMapping("/header/list") public String listHeader(HttpServletRequest request) { log.info("host is" + request.getHeader("host")); log.info("remoteAddr is " + request.getRemoteHost()); log.info("remotePort is " + request.getRemotePort()); return "OK"; }
啓動這個新服務
$ cd /opt/k8s/yml/ingress-nginx $ cat > clientip.yml <<EOF apiVersion: v1 kind: Service metadata: name: clientip spec: selector: app: clientip ports: - name: http port: 8080 targetPort: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: clientip-deployment spec: selector: matchLabels: app: clientip replicas: 1 template: metadata: labels: app: clientip spec: nodeSelector: sample: slave containers: - name: clientip image: 192.168.0.107/k8s/client-ip-test:0.0.2 ports: - containerPort: 8080 EOF
啓動服務
$ cd /opt/k8s/yml/ingress-nginx $ kubectl create -f clientip.yml
新服務對應的ingress source文件
$ cd /opt/k8s/yml/ingress-nginx $ cat > clientip-ingress.yml<< EOF apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: clientip-ingress annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/rewrite-target: /$2 spec: rules: - http: paths: - path: /clientip backend: serviceName: clientip servicePort: 8080 path: /clientip(/|$)(.*) EOF
建立新的ingress source
$ cd /opt/k8s/yml/ingress-nginx $ kubectl create -f clientip-ingress.yml ingress.networking.k8s.io/clientip-ingress created
啓動後經過nginx-ingress-controller訪問
# 訪問新服務,注意咱們在請求路徑中的clientip $ curl http://192.168.0.107:18797/clientip/header/list OK # 訪問原來部署好的服務 $ curl http://192.168.0.107:18797 CLIENT VALUES: client_address=172.30.22.7 command=GET real path=/ query=nil request_version=1.1 request_uri=http://192.168.0.107:8080/ SERVER VALUES: server_version=nginx: 1.10.0 - lua: 10001 HEADERS RECEIVED: accept=*/* host=192.168.0.107:18797 user-agent=curl/7.58.0 x-forwarded-for=172.30.22.1 x-forwarded-host=192.168.0.107:18797 x-forwarded-port=80 x-forwarded-proto=http x-real-ip=172.30.22.1 x-request-id=883570a193258f151a6d8bd5f96761af x-scheme=http BODY: -no body in request-