k8s之service与ingress

k8s之service与ingress

service资源及模型

​ 各类pod控制器只负责管理pod的运行,其生命周期,但运行的pod到底还是需要提供给客户端访问才有价值,如nginx,tomcat,httpd,因此需要引入service和ingress资源,做pod服务的暴露工做;

​ 单一节点的hostPort,可以借助pod共享节点的网络ns实现pod中服务的暴露,但不够灵活;

​ 集群内pod默认只能集群内其他pod,或集群内节点可达,集群外部根本没有到pod的相关路由, 但service的nodeport和loadbalancer类型,ingress的七层调度,可将集群外部的流量引入pod,为(集群内部或外部都)提供稳定的、统一的访问入口,和负载均衡能力;

service概述

​ service是标准k8s资源,本质是节点上的iptable或ipvs规则,利用规则实现流量的转发到pod上,且也基于selector标签选择器选择、标识后端pod;

​ pod的动态性和提供服务要求的固定入口是冲突的,因此引入service,可以做很好的中间层,隐藏了pod动态的地址,且可以提供负载均衡调度;

service图示:

image-20201116191643571

service与pod关系:

  1. service与pod关系为松耦合,先创建service并不会报错(生产中可实现,service由运维定义,pod由开发定义的解耦)
  2. service和pod中间层:还隔了一个endpoint资源,其是一个ip:port的列表,来自于后端颇多的ip:port,创建service后,其endpoint会自动创建
  3. service会通过api-server,借助watch机制,持续监测其selector所匹配到的pod,从创建、扩缩容,ip变化,删除;从而实时追踪,

service、控制器、pod图示:

image-20201116192049803

虚拟ip和代理模型

​ 向api-server提交创建service资源后,此时一直检测的每个节点上kube-proxy,监测到了service资源的创建,就会根据该service定义在其节点上,创建iptabls或ipvs规则,用于流量转发;

​ service的ip称为clusterip,就是iptables规则中的前端ip而已,不存在任何设备上,因此也叫虚拟ip

​ kube-proxy代理流量有3种方式:

  • userspace
  • iptables
  • ipvs

userspace

k8s1.1版本之前的默认转发模型

  1. kube-proxy监测api-server上的service和endpoint资源变化,
  2. 检测到后,会在本地节点创建对应iptables或ipvs规则,并监听一个宿主机节点上的端口,接收流量,然后调度,默认轮询
  3. 流量流向:客户端-》节点的内核空间接收-》内存copy给节点上kube-proxy用户空间-》处理后再给节点的内核处理,根据规则转发,(2次内核与用户空间的复制,效率低下)

iptables

k8s1.2版本后默认转发模型

  1. 每个工作节点的kube-proxy监测着api-server上service和endpoint资源的变化,
  2. 监测到变动后,创建对应规则,对于service:创建对外的iptables规则并捕获到达clusterip的流量。对于endpoint:创建向后的iptables规则并关联到具体的后端pod;
  3. 无需在节点的用户空间和内核空间反复切换,挺高效率
  4. 且后端pod无响应时,可以自动切换

ipvs

k8s1.11版本后默认转发模型

  1. 每个工作节点的kube-proxy监测着api-server上service和endpoint资源的变化,
  2. 不同的是,监测到变化后,kube-proxy调用netlink接口创建的是ipvs规则,但ipvs规则只负责流量调度部分,剩余的nat等功能仍有iptables实现
  3. ipvs优点:ipvs专做流量调度,速度快,效率高,且支持多种调度算法

图示

image-20201116202902842

image-20201116202814171

service基础应用

创建service

创建service资源2种方式:

  • kubectl expose命令行
  • yaml文件

1、yaml文件示例

语法:重要字段是ports和selector
[root@client service]# kubectl explain svc.spec

示例:
[root@client service]# cat service-dep1.yaml 
apiVersion: v1
kind: Service
metadata: 
 name: dep1-svc
spec:
 selector: #和dep1控制器采用一样的标签选择器
  app: dep1-app
 ports:
  - protocol: TCP
    port: 80 # service对外暴露的端口
    targetPort: 80 #后端pod端口

[root@client service]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   7d1h
[root@client service]# kubectl apply -f service-dep1.yaml 
service/dep1-svc created
[root@client service]# kubectl get svc
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
dep1-svc     ClusterIP   10.99.38.23   <none>        80/TCP    3s
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   7d1h

2、service与endpoint查看

​ 创建service时,指定了selector,该selector关联到的pod对象,其套接字信息被抽取出来作为service的后端,endpoint资源,是一个ip+port的列表

service一般需结合deploment使用,也可以:定义service不指定selector,手动为其创建endpoints资源列表供service关联

service:

[root@client service]# kubectl get svc -o wide
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE    SELECTOR
dep1-svc     ClusterIP   10.99.38.23   <none>        80/TCP    11m    app=dep1-app
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   7d1h   <none>


endpoint:
# 看到相关pod没创建时,创建service后,随机endpoint就会被创建,只是其中关联的endpoint列表为空而已;
[root@client service]# kubectl get endpoints
NAME         ENDPOINTS             AGE
dep1-svc     <none>                11m
kubernetes   192.168.80.101:6443   7d1h
[root@client service]# kubectl apply -f ../deployment/dep1.yaml 
deployment.apps/dep1 created
[root@client service]# kubectl get endpoints
NAME         ENDPOINTS                                       AGE
dep1-svc     10.244.1.71:80,10.244.2.103:80,10.244.4.47:80   11m
kubernetes   192.168.80.101:6443                             7d1h

向service请求服务

1、启动交互式容器cirros做客户端

[root@client service]# kubectl run c1 -it --rm --image=cirros -- /bin/sh
kubectl run --generator=deployment/apps.v1beta1 is DEPRECATED and will be removed in a future version. Use kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # ls

2、访问clusterip,查看调度效果

​ 默认是随机调度

/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-npjkj
/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-npjkj
/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-7bb5s
/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-rv8nd
/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-npjkj

service实现会话粘性

概念:

​ service能够基于四层ip实现会话粘性的调度,默认的粘性保持时长为10800s,但对于同一个公网ip出口的访问来说,它们采用的都是同一个出口ip做源ip,此时基于客户端源ip调度粒度较粗糙,不建议使用

语法:

[root@client k8s_yaml]# kubectl explain svc.spec.sessionAffinity
KIND:     Service
VERSION:  v1

FIELD:    sessionAffinity <string>

DESCRIPTION:
     Supports "ClientIP" and "None". Used to maintain session affinity. Enable
     client IP based session affinity. Must be ClientIP or None. Defaults to
     None. More info:

默认none,即不采用会话粘性,clientIP表现根据客户端ip做粘性
[root@client k8s_yaml]# kubectl explain svc.spec.sessionAffinityConfig
KIND:     Service
VERSION:  v1

RESOURCE: sessionAffinityConfig <Object>
配置会话粘性时长的,默认10800

测试:

[root@client service]# cat service-dep1.yaml 
apiVersion: v1
kind: Service
metadata: 
 name: dep1-svc
spec:
 selector:
  app: dep1-app
 ports:
  - protocol: TCP
    port: 80
    targetPort: 80
 sessionAffinity: ClientIP
 sessionAffinityConfig: 
  clientIP:
   timeoutSeconds: 5
[root@client service]# kubectl apply -f service-dep1.yaml

#可以看到,会话粘性效果
[root@client service]# kubectl run c1 -it --rm --image=cirros -- /bin/sh
/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-7bb5s
/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-7bb5s
/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-7bb5s
/ # curl 10.99.38.23/hostname.html
dep1-7b96746498-7bb5s

服务发现

服务发现概述

​ pod的动态性决定了它ip的不固定性,虽然service为其提供了固定的访问入口ip,但是客户端仍需通过某种机制知道自己要访问的service的ip和port,因此引入 服务发现

​ 尤其对于微服务应用,被拆分后的大量的小服务之间通信,通过运维人员手动逐一配置要访问的应用地址显然是不现实的,因此需要服务发现

服务发现的逻辑:

​ 借助一个服务总线,服务发布方通过服务总线发布自己提供的服务,客户端需要访问时,通过服务总线查询自己需要的服务,进而得到服务方的地址信息,如ip,url等其他信息。

服务发现的模型:

  • 客户端发现:客户端自己完成到服务总线的查询;需要实现服务查询的代码逻辑
  • 服务端发现;借助一个中间查询服务,代替客户端查询并返回客户端结果,客户端无需实现服务查询逻辑

常用的服务总线实现:

  • zookeeper和etcd;传统的键值存储系统
  • netfix的Eureka和HashiCorp的Consul:专用的服务发现系统
  • kubedns和coredns:基于go,借助etcd做后端存储实现,coredns在k8s1.11后,代替kubedns成为k8s默认的服务发现的附件
  • k8s也支持环境变化做服务发现

服务发现方式:环境变量

​ 创建pod时,k8s会自动把同一个名称空间,在该pod之前存在的service对象作为环境变量注入到pod的环境变量中,共有2种形式的变量:

  • k8s service环境变量
  • docker link形式变量

缺点:pod之后创建的service变量无法注入到其中

查看环境变量

[root@client service]# kubectl exec  dep1-7b96746498-7bb5s -it -- /bin/sh
# printenv |grep -i dep1_svc

DEP1_SVC_PORT_80_TCP_ADDR=10.99.38.23
DEP1_SVC_PORT_80_TCP_PORT=80
DEP1_SVC_PORT_80_TCP_PROTO=tcp
DEP1_SVC_PORT_80_TCP=tcp://10.99.38.23:80

DEP1_SVC_SERVICE_HOST=10.99.38.23
DEP1_SVC_SERVICE_PORT=80

DEP1_SVC_PORT=tcp://10.99.38.23:80

其中dep1_svc是service的名字,注意即便是定义service名字是用的是短横线,也会统一转为下划线
带有service字符串的是k8s service环境变量,不带的是docker link形式变量

服务发现与clusterDns

​ k8s集群的服务发现实现主要有kubedns,coredns,后者为前者的替换产品,是k8s众多功能依赖的基础服务,集群安装后应该立即部署,kubeadm初始化集群时,会自动部署为pod

​ 以下为3种service类型,对应的资源记录resources record格式:

1、clusterip类型:常见的资源记录格式

2、headless类型:资源记录格式

3、Externalname类型;资源记录格式

image-20201117175148583

服务发现方式:dns

service资源的ip对应的dns名称格式:

  • ..cluster.local
  • ..svc.cluster.local
  • 依次是:service对象名.所在名称空间名.svc(可选).cluster.local(为集群的域名,部署集群时由--cluster-domain参数指定)

部署集群时,--cluster.dns用于指定dns的ip地址。

启动pod时,会将该dns服务器的ip和默认搜索的域注入到容器的/etc/resolve.conf文件中。

查看如下:

/ # cat /etc/resolv.conf 
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

其中dns服务器的ip为10.96.0.10
搜索的域依次为: default.svc.cluster.local svc.cluster.local cluster.local

/ # nslookup dep1-svc
nslookup: can't resolve '(null)': Name does not resolve

Name:      dep1-svc
Address 1: 10.99.38.23 dep1-svc.default.svc.cluster.local 《主机所在域名》
主机名也配置了某域的话,主机所在域名会加在最后一个
查询某service的名称时,根据给定的域名后缀,补全然后搜索,dep1-svc默认在default名称空间,第一个域名后缀就可以匹配搜到,进而解析出对应的ip

服务暴露

service的类型

service共有四种类型:

  • clusterip:默认类型。仅集群内部的pod或节点可达;
  • nodeport:构建在clusterip之上,在每个节点监听一个端口,访问该端口的流量会被转到clusterip和其端口,集群外部客户端通过访问节点的该端口(nodeip:nodeport),进而转到clusterip,再到pod;
  • loadbalancer:构建在nodeport之上,依赖于集群外部的负载均衡器,如lvs,haproxy等,或云平台的相关产品,如阿里的slb,该slb将所有集群节点的ip:nodeport作为后端列表,可以起到流量负载均衡转发之用,(相比nodeport,用于无需知道访问哪个节点的ip和nodeport,只需访问固定的负载均衡器即可,且负载均衡其可以将nodeport的非标准端口转为知名标准端口且避免了访问nodeport时的节点故障,需手动切换其他节点的nodeport的问题)
  • externalname:前三者是将集群内部服务暴露给集群外部客户端,而externalname是将集群外部的服务暴露给集群内部,

类型解释:

[root@client service]# kubectl explain svc.spec.type
KIND:     Service
VERSION:  v1

FIELD:    type <string>

DESCRIPTION:
     type determines how the Service is exposed. Defaults to ClusterIP. Valid
     options are ExternalName, ClusterIP, NodePort, and LoadBalancer.
     "ExternalName" maps to the specified externalName. "ClusterIP" allocates a
     cluster-internal IP address for load-balancing to endpoints. Endpoints are
     determined by the selector or if that is not specified, by manual
     construction of an Endpoints object. If clusterIP is "None", no virtual IP
     is allocated and the endpoints are published as a set of endpoints rather
     than a stable IP. "NodePort" builds on ClusterIP and allocates a port on
     every node which routes to the clusterIP. "LoadBalancer" builds on NodePort
     and creates an external load-balancer (if supported in the current cloud)
     which routes to the clusterIP.

clusterIP

​ 默认的service类型即为clusterip

[root@client service]# curl 10.99.38.23

[root@node1 ~]# curl 10.99.38.23
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@node1 ~]#

[root@client service]# kubectl get svc -o wide
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE    SELECTOR
dep1-svc     ClusterIP   10.99.38.23   <none>        80/TCP    173m   app=dep1-app

相比集群内节点,单独的客户端节点对于clusterip是路由不可达的

Nodeport

[root@client service]# vim service-dep1.yaml 
[root@client service]# grep type -C 3 service-dep1.yaml 
metadata: 
 name: dep1-svc
spec:
 type: NodePort
 selector:
  app: dep1-app
 ports:

类型设置为nodeport即可,在节点开放的端口范围是30000到32767,可以手动指定,但不建议,防止人为指定造成的冲突

[root@client service]# kubectl apply -f service-dep1.yaml 
service/dep1-svc configured
[root@client service]# kubectl get svc
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
dep1-svc     NodePort    10.99.38.23   <none>        80:31029/TCP   176m
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP        7d4h

[root@client service]# curl 192.168.80.106:31029
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
此时集群外部节点,就可以通过 节点ip+nodeport 访问pod中服务

Loadbalancer

​ nodeport类型的service的缺点:

  • 客户端需要知道,一个节点的ip和其开放的不规则随机端口
  • 节点发生故障时,客户端需要手动切换到另一个节点ip
  • k8s集群物理是在公有云还是机房,节点肯定都是私有地址,对于互联网客户端是不可达的
  • 因此:需要配置一个具有公网ip的loadbalancer,后端挂所有集群的私有ip,客户端只需访问负载均衡器的公网ip,即可被其将流量均匀的调度到各个节点的nodeport,再到clusterip,再到pod
  • 流量流向:公网客户端-》有公网ip的loadbalancer-》各个节点的nodeport-》集群的clusterip-》具体的pod

​ 测试:修改type为loadbalancer即可,可以看到externalip为pending状态,因为集群外部并未配置相应的外部ip,实践中:外部负载均衡器可以是:

  • 调用云厂商接口生产的软均衡器,如阿里云的slb;
  • 机房中,自己搭建的高可用均衡器,如lvs,haproxy,nginx;
[root@client service]# vim service-dep1.yaml 
[root@client service]# kubectl apply -f service-dep1.yaml 
service/dep1-svc configured
[root@client service]# kubectl get svc
NAME         TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
dep1-svc     LoadBalancer   10.99.38.23   <pending>     80:31029/TCP   3h6m

Externalname

​ externalname类型的service用于将集群外部的服务,暴露给集群内部的pod使用,

类型改为externalname,并在externalname字段定义一个cname别名,用于解析外部的服务ip地址
注意:该类型,不能有selector关联到任何pod


[root@client service]# kubectl apply -f service-dep1.yaml 
service/external created
[root@client service]# cat service-dep1.yaml 
apiVersion: v1
kind: Service
metadata: 
 name: external
spec:
 type: ExternalName
 externalName: somehost.bo.io
 ports:
  - protocol: TCP
    port: 80
    targetPort: 80

headless类型service

​ 需求:有时客户端需要直接访问后端的pod的ip,而不是经由clusterip做一层转发,此时客户端解析service名称时,获得的是所有后端ip组成的列表;

​ 所有pod的ip由endpoint controller管理,生成后作为service名称解析的A记录;此时service没有clusterip这一ip地址;

创建headless型service

有问题?正常的定义后,查看svc详细,应该发现没有clusterip地址,只有被关联到的所有pod的ip,客户端pod方式测试,用nslookup解析该svc名称,得到的应该是一个pod的ip的列表;

​ headless的service,通过其名称解析,会被dns服务器轮询的方式返回一个个pod的ip,然后客户端直接通过pod的ip进行访问,不再经由service转发

[root@client service]# kubectl apply -f service-dep1.yaml 
The Service "my-service" is invalid: spec.clusterIP: Invalid value: "None": field is immutable
[root@client service]# cat service-dep1.yaml 
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  clusterIP: None
  selector:
    app: dep1-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

ingress资源

​ service用于在四层引入外部流量,而ingress则实现了在七层引入外部流量,其具有的功能:

  • tls会话加密,卸载
  • 基于七层url等做流量调度分发
  • 后端基于url的健康监测等

ingress与ingress controller

ingress:

是k8s资源的一种,是一组规则的集合,定义了如何做流量分发,如何做健康检测,配置tls信息等,是概念性的东西;

ingress-controller:

​ **解析并实现ingress所定义规则的软件实现,是软件实体,**具有七层调度功能的软件都可以做ingress-controller,如nginx,haproxy,envoy,虽说叫控制器,但不是controller-manager的一部分,而是作为运行在k8s集群上的pod存在,由于本质还是pod!就可以由deployment控制器控制,

​ ingress-controller依赖service的标签选择器标识后端pod,但可以不经由service转发流量,而类似headless service一样,直接访问到后端的pod端点

图示:

image-20201117195920948

创建ingress

https://kubernetes.io/docs/concepts/services-networking/ingress/

语法:

[root@client ~]# kubectl explain ingress.spec
KIND:     Ingress
VERSION:  extensions/v1beta1

RESOURCE: spec <Object>

DESCRIPTION:
     Spec is the desired state of the Ingress. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status

     IngressSpec describes the Ingress the user wishes to exist.

FIELDS:
   backend	<Object>
     A default backend capable of servicing requests that don't match any rule.
     At least one of 'backend' or 'rules' must be specified. This field is
     optional to allow the loadbalancer controller or defaulting logic to
     specify a global default.

   rules	<[]Object>
     A list of host rules used to configure the Ingress. If unspecified, or no
     rule matches, all traffic is sent to the default backend.

   tls	<[]Object>
     TLS configuration. Currently the Ingress only supports a single TLS port,
     443. If multiple members of this list specify different hosts, they will be
     multiplexed on the same port according to the hostname specified through
     the SNI TLS extension, if the ingress controller fulfilling the ingress
backend定义默认后端
rules定义转发规则,类比nginx的location,
tls配置https时需要

示例:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: my-ingress
  annotations:
   kubernetes.io/ingress.class: "nginx"
spec:
 rules:
  - host: www.ilinux.io
    http:
     paths:
      - backend:
         serviceName: my-svc
         servicePort: 80
annotations描述了nginx-controller是由nginx实现
rules定义了转发规则
定义了请求www.ilinux.io的http流量,对/的访问,都转到my-svc的80端口

ingress的类型

​ 基于http暴露的每个service资源,可以发布在一个独立的域名上,如www.ilinux.io,也可以是一个域名加uri后缀,如www.ilinux.io/wordpress。

​ 定义ingress规则时的定义方式共4种,分别是:

  • 基于一个完整的fqdn域名分发
  • 基于url路径做流量分发
  • 基于主机名的虚拟主机
  • tls类型的ingress资源,用于https

基于完整域名

​ 此种情况,一个完整的fqdn的转发,对应的后端是一个service资源,

示例:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: my-ingress
spec:
 backend:
  serviceName: my-svc
  esrvicePort: 80

基于url

​ 此时,各个应用会聚集在域名下,以第一层的uri路径做区分,如www.bo.com/pc,www.bo.com/mobile

示例:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: my-ingress
spec:
 rules:
  - host: www.ilinux.io
    http:
     paths:
      - path: /pc
        backend: 
         serviceName: pc
         servicePort: 80
      - path: /mobile
        backend:
         serviceName: mobile
         servicePort: 80
         

基于虚拟主机主机名

​ 多个应用可采用不同的域名,但解析到同一个ip,如具有公网ip的external LB,类似nginx的虚拟主机,

示例:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: my-ingress
spec:
 rules:
  - host: pc.bo.io
    http:
     paths:
      - backend:
         serviceName: pc
         servicePort: 80
  - host: mobile.bo.io
    http:
     paths:
      - backend:
         serviceName: mobile
         servicePort: 80

tls加密

​ 在ingress定义中,引用事先定义好的secret资源,其中包括了私钥和证书,用于https的,之后ingress-controller在解析ingress规则同时,也会引入其中证书和私钥,配置https会话

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: my-ingress
spec:
 tls:
  - secretName: httpsecret
  backend:
   serviceName: my-https
   servicePort: 80

管理ingress控制器

ingress-controller工作过程:

  1. pod中进程,如nginx,haproxy,时刻监测着api-server上,ingress资源的变化
  2. 监测到后,将ingress定义的规则集合,翻译成进程的配置文件,如nginx.conf,haproxy.cfg
  3. 然后,nginx等进程根据配置文件进行流量的调度

ingress-controller说到底,就是跑在k8s集群上的pod应用,其中就是nginx、haproxy、envoy这种具有七层调度能力的进程,

​ 因其还是pod,那么自然默认只有私有的pod地址,因此外部客户端向通过ingress-controller调度七层流量,就还需为ingress-controller所在pod,定义service资源:nodeport类型或loadbalancer类型,来帮助ingress-controller引入外部流量,

​ 因其还是pod,就可用控制器来管理运行,有两种控制器常用于ingress-controller的pod

  • deployment
  • daemonset

deployment控制

​ 用deploment控制器控制ingress-controller的pod的运行,此时ingress-controller是私有pod的ip,为引入外部流量:需要为其创建nodeport或loadbanlancer类型的service资源;

图示:

image-20201118124544054

daemoset控制

​ 采用ds控制器,使得每个节点都运行一个ingress-controller的pod,并采用hostport或共享host net nameSpace的方式,为其引入外部流量,此种方式,无需再为其定义service资源

图示:

image-20201118124607045

部署nginx版的ingress-controller

问题?应该是版本不匹配导致,

  1. 应用官方定义了ingress-controller的yaml文件(默认deployment方式管理)

    kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.41.2/deploy/static/provider/baremetal/deploy.yaml
    
  2. 查看生成的pod,在ingress-nginx名称空间

    [root@client ingress]# kubectl get pods -n ingress-nginx
    NAME                                        READY   STATUS             RESTARTS   AGE
    ingress-nginx-admission-create-g6xg9        0/1     Completed          0          3m3s
    ingress-nginx-admission-patch-p5rs4         0/1     CrashLoopBackOff   3          3m3s
    ingress-nginx-controller-58975c55bc-nxvzn   0/1     Pending            0          3m3s
    
  3. 为pod定义nodePort类型的service资源,引入外部流量

  4. 集群外客户端访问,某节点的nodeport,进而访问到ingress-controller的pod

排错:

  1. apply时,报了一个不支持的字段,应该是版本问题,
  2. 还有ingress-controller的pod一直pending状态,排查过程:
    1. describe查看pod的event事件为,没有可用节点,调度阶段就失败
    2. 然后看到该pod定义了一个nodeselecter,标签是:kubernetes.io/os=linux
    3. 再查看节点标签时,发现无该标签,给节点补全后,pod可以正常调度
  3. 可以调度后,pod一直显示重启失败,问题?

示例:ingress发布tomcat

实验拓扑

客户端访问流量流向:

  1. 集群外部客户端通过nodePort或loadbalancer访问到集群内某节点的nodePort;
  2. nodePort收到后,将其转发给关联的service的clusterIp,该service是ingress-controller pod的引流口;
  3. 通过service,ingress-controller所在pod收到流量,由内部的nginx进程分析处理;
  4. 再经由tomcat pod的service识别过滤出后端的tomcat所在pod;(只靠tomcat的service识别,不经由它转发)
  5. 然后直接与tomcat所在pod通信;(ingress-controller和tomcat所在pod,属于集群内pod间通信,都是pod)

image-20201118125710211

准备名称空间

将后续pod,都部署在此test名称空间中

[root@client ingress-demo]# kubectl apply -f ns-test.yaml 
namespace/test created
[root@client ingress-demo]# kubectl get ns
NAME            STATUS   AGE
default         Active   7d23h
ingress-nginx   Active   8m
kube-public     Active   7d23h
kube-system     Active   7d23h
test            Active   2s
[root@client ingress-demo]# cat ns-test.yaml 
apiVersion: v1
kind: Namespace
metadata:
 name: test
 labels:
  env: test

部署tomcat

运行tomcat镜像为pod,生产中,应该包含了开发打包进程序war包的tomcat镜像

[root@client ingress-demo]# cat dep-tomcat.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
 name: dep-tomcat
 namespace: test
spec:
 replicas: 2
 selector: 
  matchLabels:
   app: tomcat
 template:
  metadata:
   labels:
    app: tomcat
  spec:
   containers:
    - name: tomcat
      image: tomcat:8.0.50-jre8-alpine
      ports:
      - containerPort: 8080
        name: httpport
      - containerPort: 8009
        name: ajpport

创建service

[root@client ingress-demo]# vim svc-tomcat.yaml
[root@client ingress-demo]# kubectl apply -f svc-tomcat.yaml 
service/svc-tomcat created
[root@client ingress-demo]# cat svc-tomcat.yaml 
apiVersion: v1
kind: Service
metadata:
 name: svc-tomcat
 namespace: test
spec:
 selector:
  app: tomcat
 ports:
  - name: http
    port: 80
    targetPort: 8080

[root@client ingress-demo]# kubectl get svc -n test
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
svc-tomcat   ClusterIP   10.99.79.167   <none>        80/TCP    17s

创建ingress

[root@client ingress-demo]# vim ingress-for-tomcat.yaml
[root@client ingress-demo]# cat ingress-for-tomcat.yaml 
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: ingress-tomcat
 namespace: test
 annotations:
  kubernetes.io/ingress.class: "nginx"
spec:
 rules:
 - host: tomcat.ilinux.io
   http:
    paths:
    - path:
      backend:
       serviceName: svc-tomcat
       servicePort: 80
[root@client ingress-demo]# kubectl apply -f ingress-for-tomcat.yaml 
ingress.extensions/ingress-tomcat created
[root@client ingress-demo]# kubectl get ingress -n test
NAME             HOSTS              ADDRESS   PORTS   AGE
ingress-tomcat   tomcat.ilinux.io             80      10s

配置tls加密的ingress

tls加密可以在外部的loadbalancer实现,若外部的loadbalancer是在四层,则可以在ingress配置,即配置tls加密类型的ingress

  1. 生成key和证书
  2. 用key和证书,生成secret
  3. 将secret配置到ingress的tls字段中,重新apply即可

小结:

  • service资源通过标签选择器,选择识别一组pod,并为其创建一个访问入口,将客户端请求代理到pod
  • service是四层调度,默认为随机调度算法
    • 本质是节点上的iptables或ipvs规则
  • service实现模型三种:userspace,iptables,ipvs
  • service共分4种类型:
    • clusterip
    • nodeport
    • loadbalancer
    • externalname
  • headless service为一种特殊的service,用于pod发现,解析service的名称,得到的是一组后端pod的ip列表,而不是一个固定的clusterip
  • ingress资源是发布service资源的另一种方式,需结合ingress控制器工作
  • ingress控制器的实现有:
    • nginx
    • haproxy
    • envoy
    • traefik
updatedupdated2020-11-182020-11-18
加载评论