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-