Ingress资源

官方文档:https://kubernetes.io/zh/docs/concepts/services-networking/ingress/

  • 注意:因使用版本不同,与官方文档存在细微差异。
    Service虽然解决了服务发现和负载均衡的问题,但是它在使用上还是有着很多的限制,比如,只支持4层的负载均衡,没有7层功能,而对于http的服务来说,不同的URL地址通常对应到不同的后端服务或者虚拟服务器,这些应用层的转发机制仅通过Service是无法实现的,所以在kubernetes1.1版本开始,新增Ingress资源对象,用于将不同URL的访问请求转发到后端不同的Service,以实现HTTP层的业务路由机制。
    Ingress就是为了解决Service的这些限制而引入的新资源,主要用来将服务暴漏到集群外部,并且可以定义服务的访问策略。比如想通过负载均衡器实现不同子域名的访问。Service实现的是”TCP负载均衡器”,Ingress实现的是”HTTP(S)”负载均衡器
  • TCP负载均衡器:无论是iptables还是ipvs模型的Service资源都配置于Linux内核中的netfilter之上进行四层调度,是一种类型更为通用的调度器,它支持调度http,mysql等应用层服务,不过,由于正是其工作于四层从而能使得它无法做到类似卸载HTTPS中的SSL会话等一类操作,也不支持基于URL的请求调度机制,而且K8S也不支持为此类负载均衡器配置任何类型的监控状态检查机制
  • HTTP(S)负载均衡器:HTTP(S)负载均衡器是应用层负载均衡机制的一种,支持根据环境做出更好的调度决策。与传输层调度器相比,它提供了诸如可自动义URL映射和TLS卸载等功能,并支持多种类型的后端服务器健康状态检测机制

Ingress和Ingress Controller

K8S中,Service资源和Pod资源的IP地址仅能用于集群网络内部的通信,所有的网络流量都无法穿透边界路由器以实现集群内外通信。尽管可以为Service使用NodePort或LoadBalancer类型通过节点引入外部流量,但它依然是4层流量转发,可用的负载均衡器也为传输层负载均衡机制 Ingress是K8S API的标准资源类型之一,它其实就是一组基于DNS名称(host)或URL路径把请求转发至指定的Service资源的规则,用于将集群外部的请求流量转发至集群内布完成服务发布。但是Ingress资源自身并不能进行”流量穿透”,它仅是一组路由规则的集合,这些规则要想真正发挥作用还需要其他功能的辅助。如监听某套接字,然后根据这些规则的匹配机制路由请求流量。这种能够为Ingress资源监听套接字并转发流量的组件叫Ingress控制器。通俗点讲就是,Ingress可以配置为提供外部服务可访问的URL,负载均衡流量,SSL\TLS,以及提供各种基于名称的虚拟主机。而如果要实现Ingress则必须配置Ingress Controller 不同于Deployment控制器等,Ingress控制器并不直接运行为kube-controller-manager的一部分,它是K8S集群的一个重要附件,类似于CoreDNS,需要在集群上单独部署 Ingress控制器可以由任何具有反向代理(HTTP/HTTPS)功能的服务程序实现,如Nginx,Envoy,HAproxy,vulcand和Traefik等。Ingress控制器自身也是运行于集群中的Pod资源对象,它与被代理的运行为Pod资源的应用运行于同一网络中,如下图所示 Servicee和Ingress(五)之Ingress - 图1 另一方面,使用Ingress资源进行流量分发时,Ingress控制器可基于某Ingress资源定义的规则将客户端的请求流量直接转发至与Service对应的后端Pod资源之上,这种转发机制会绕过Service资源,从而省去了由kube-proxy实现的端口代理的开销。如上图所示,Ingress规则需要由一个Service对象辅助识别相关的所有Pod对象,但Ingress-nginx控制器可经由api.ilinux.io规则的定义直接将请求流量调度至Pod3或Pod4上,而无须经由Service对象API的再次转发,WAP相关规则的作用方式于此类同。Service的主要作用就是用于关联后端的Pod资源对象。Ingress通过识别Service资源的目标名称和端口进行流量转发

创建Ingress资源

Ingress资源是基于HTTP虚拟主机或URL的转发规则,它再资源配置清单的spec字段中嵌套了rules、backend和tls等字段进行定义。与其他K8S资源一样,Ingress也必须要使用apiVersion,kind和metadata字段。 Ingress 对象的命名必须是合法的 DNS 子域名名称。下面的示例中定义了一个Ingress资源,它包含了一个转发规则,把发往www.ilinux.io的请求代理给myapp-svc的Service资源的80端口,并且定义了一个默认后端,所有不匹配的规则都将发往这个默认后端default.ilinux.io

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: my-ingress
  5. annotations:
  6. kubernetes.io/ingress.class: "nginx"
  7. spec:
  8. backend:
  9. serviceName: default.ilinux.io
  10. servicePort: 80
  11. rules:
  12. - host: www.ilinux.io
  13. http:
  14. paths:
  15. - path: /
  16. backend:
  17. serviceName: myapp-svc
  18. servicePort: 80

上面资源清单中的annotations用于识别其所属的ingress控制器的类别,这一点在集群上部署有多个Ingress控制器时尤为重要。Ingress spec中的字段是定义ingress资源的核心组成部分,它主要嵌套如下三个字段

  • rules<[]Object>:用于定义Ingress资源的转发规则列表;未由rules定义规则或者没有匹配到任何规则时,所有流量都会转发到由backend定义的默认后端
  • backend:默认的后端用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,至少应该定义backend或rules两者之一;此字段用于让负载均衡器指定一个全局默认的后端。通常都是在ingress控制器中配置默认后端,用于服务那些不符合任何匹配规则的请求
  • tls:TLS配置,目前仅支持通过默认端口443提供服务;如果要配置指定的列表成员指向了不同的主机,则必须通过SNI TLS扩展机制来支持此功能

    backend对象的定义由两个必选的内嵌字段组成:serviceName和servicePort,分别用于指定流量转发的后端目标service资源的名称和端口。

    1. spec:
    2. backend:
    3. serviceName: <string>
    4. servicePort: <string>

    rules对象由一系列配置Ingress资源的host规则组成,这些host规则用于将一个主机上的某个URL路径映射至相关的后端service对象,它的格式如下:

    1. spec:
    2. rules:
    3. - host: <string>
    4. http:
    5. paths:
    6. - path: <string>
    7. backend:
    8. serviceName: <string>
    9. servicePort: <string>

    注意:.spec.rules.host属性值目前不支持使用IP地址,也不支持地址后面跟端口格式的地址,而且此字段留空表示通配所有的主机名。在K8S 1.18版本中path字段加入了新的内嵌字段,具体使用方式可以参考官方文档,且host字段支持使用IP地址

    tls对象由两个内嵌字段组成:hosts和secretName。仅在定义TLS主机的转发规则时才需要定义此类对象

    1. spec:
    2. tls:
    3. hosts: <[]string>
    4. secretName: <string>

    Ingress资源类型

    基于HTTP暴漏的每个Service资源均可发布于一个独立的FQDN的主机名之上,如www.ilinux.io;也可以发布某主机的URL路径之上,从而将他们整合到一个web站点,如www.ilinux.io/grafana。至于是否需要发布为HTTPS类型的应用则取决于用户的业务需求

    单Service资源型Ingress

    暴露单个服务的方法有很多种,如服务类型中的Nodeport、LoadBalancer等,不过,一样可以考虑使用Igress来暴露服务,此时只需要为Ingress指定”default backend”即可。例如下面的配置

    1. apiVersion: extensions/v1beta1
    2. kind: Ingress
    3. metadata:
    4. name: my-ingress
    5. spec:
    6. backend:
    7. serviceName: my-svc
    8. servicePort: 80

    Ingress控制器会为其分配一个IP地址介入请求流量,并将请求流量转至示例中的my-svc的后端Pod资源

    基于URL路径进行流量分发

    垂直拆分或微服务架构中,每个小的应用都有其专用的Service资源暴露服务,但在对外开放的站点上,它们可能是财经、新闻、电商、无线端或API接口等一类的独立应用,可通过主域名的URL路径分别接入,例如,www.ilinux.io/apiwww.ilinux.io/wap等,用于发布集群内名称为api和wap的service资源。于是,可对应的创建一个如下的Ingress资源,它将对www.ilinux.io/api的请求统统转发至API Service资源,将对www.ilinux.io/wap的请求转发至wap service资源

    1. apiVersion: extensions/v1beta1
    2. kind: Ingress
    3. metadata:
    4. name: test
    5. annotations:
    6. nginx.ingress.kubernetes.io/rewrite-taget: /
    7. spec:
    8. rules:
    9. - host: www.ilinux.io
    10. http:
    11. paths:
    12. - backend:
    13. serviceName: myapp-svc-api
    14. servicePort: 80
    15. path: /api
    16. - backend:
    17. serviceName: myapp-svc-wap
    18. servicePort: 80
    19. path: /wap

    基于主机名称的Ingress资源

    上面的类型中的需求,也可以将每个应用分别以独立的FQDN主机名进行输出,如wap.ilinux.ioapi.ilinux.io。这两个主机名能够被DNS所解析,并且解析到external LB的IP地址之上,分别用于发布集群内部的wap和api这两个Service资源。这种实现方案类似于web站点中的“基于域名的虚拟主机”,将多个FQDN解析至同一个IP地址,然后根据主机头进行转发。下面是一个独立FQDN主机形式发布服务的Ingress资源示例

    1. apiVersion: extensions/v1beta1
    2. kind: Ingress
    3. metadata:
    4. name: test
    5. spec:
    6. rules:
    7. - host: api.ilinux.io
    8. http:
    9. paths:
    10. - backend:
    11. serviceName: api
    12. servicePort: 80
    13. - host: wap.ilinux.io
    14. http:
    15. paths:
    16. - backend:
    17. serviceName: wap
    18. servicePort: 80

    TLS类型的Ingress资源

    这种类型用于以HTTPS发布Service资源,基于一个含有私钥和证书的secret对象即可配置TLS协议的Ingress资源,目前来说,Ingress资源仅支持单TLS端口,并且还会卸载TLS会话。在Ingress资源中引用此secret即可让Ingress控制器加载并配置为HTTPS服务 关于TLS类型的会在后面详细讲到

    1. apiVersion: extensions/v1beta1
    2. kind: Ingress
    3. metadata:
    4. name: no-rules-map
    5. spec:
    6. tls:
    7. - secretName: ikubernetesSecret
    8. backend:
    9. serviceName: homesite
    10. servicePort: 80

    Ingress 控制器

    官网:https://kubernetes.io/zh/docs/concepts/services-networking/ingress-controllers/ 为了能够让Ingress资源正常工作,集群内部必须有一个正在运行的Ingress控制器。而且Ingress控制器自身也是运行于Pod中的容器应用,一般可以是nginx或者envoy等一类的具有代理及负载均衡功能的守护进程,它监视着来自于API Server的Ingress对象状态,并以其规则生成相应的应用程序专有的格式的配置文件并通过重载或重启守护进程而使配置生效。例如,对于Nginx来说,Ingress规则需要转换为Nginx的配置信息。简单来说,Ingress控制器其实就是托管于K8S系统之上的用于实现在应用层发布服务的Pod资源,它将跟踪Ingress资源并实时生成配置规则。与作为 kube-controller-manager 可执行文件的一部分运行的其他类型的控制器不同,Ingress 控制器不是随集群自动启动的。通常我们需要单独安装它,才能使用。 同样运行为Pod资源的Ingress控制器进程又该如何接入外部的请求流量呢?常用的解决方案有如下两种

    • 以Deployment控制器管理Ingress控制器的Pod资源,并通过NodePort或LoadBalancer类型的service对象为其接入集群外部的请求流量,这就意味着,定义一个Ingress控制器时,必须在其前端定义一个专用的service资源。如下图
      Servicee和Ingress(五)之Ingress - 图2
    • 借助于DaemonSet控制器,将Ingress控制器的Pod资源以单一实例的方式运行于集群的所有或部分工作节点之上,并配置这类Pod对象以hostPort或hostNetwork的方式在当前节点接入外部流量。如下图
      Servicee和Ingress(五)之Ingress - 图3

    以ingress-nginx项目为例子,部署ingress Nginx控制的配置文件被切割存放在了多个不同的文件中,并集中存放于其源码deploy子目录下,同时,为了方便用户部署,它还将所有需要的资源全部集成为一个配置文件mandatory.yaml 站点:https://github.com/kubernetes/ingress-nginx/tree/nginx-0.17.0/deploy 注意:因为有两个Deployment控制器,我们需要确保能够正常拉取到它所需要的镜像,如果无法拉取到请自行修改为可以拉取到镜像的地址。我这里只需要修改第一个deployement的镜像地址docker.io/chenliujin/defaultbackend:1.4第二个镜像地址我可以正常访问,所以无需更换 当然也可以使用更新版本的ingress 控制器

    1. ---
    2. apiVersion: v1
    3. kind: Namespace
    4. metadata:
    5. name: ingress-nginx
    6. ---
    7. apiVersion: apps/v1
    8. kind: Deployment
    9. metadata:
    10. name: default-http-backend
    11. labels:
    12. app: default-http-backend
    13. namespace: ingress-nginx
    14. spec:
    15. replicas: 1
    16. selector:
    17. matchLabels:
    18. app: default-http-backend
    19. template:
    20. metadata:
    21. labels:
    22. app: default-http-backend
    23. spec:
    24. terminationGracePeriodSeconds: 60
    25. containers:
    26. - name: default-http-backend
    27. # Any image is permissible as long as:
    28. # 1. It serves a 404 page at /
    29. # 2. It serves 200 on a /healthz endpoint
    30. image: docker.io/chenliujin/defaultbackend:1.4
    31. livenessProbe:
    32. httpGet:
    33. path: /healthz
    34. port: 8080
    35. scheme: HTTP
    36. initialDelaySeconds: 30
    37. timeoutSeconds: 5
    38. ports:
    39. - containerPort: 8080
    40. resources:
    41. limits:
    42. cpu: 10m
    43. memory: 20Mi
    44. requests:
    45. cpu: 10m
    46. memory: 20Mi
    47. ---
    48. apiVersion: v1
    49. kind: Service
    50. metadata:
    51. name: default-http-backend
    52. namespace: ingress-nginx
    53. labels:
    54. app: default-http-backend
    55. spec:
    56. ports:
    57. - port: 80
    58. targetPort: 8080
    59. selector:
    60. app: default-http-backend
    61. ---
    62. kind: ConfigMap
    63. apiVersion: v1
    64. metadata:
    65. name: nginx-configuration
    66. namespace: ingress-nginx
    67. labels:
    68. app: ingress-nginx
    69. ---
    70. kind: ConfigMap
    71. apiVersion: v1
    72. metadata:
    73. name: tcp-services
    74. namespace: ingress-nginx
    75. ---
    76. kind: ConfigMap
    77. apiVersion: v1
    78. metadata:
    79. name: udp-services
    80. namespace: ingress-nginx
    81. ---
    82. apiVersion: v1
    83. kind: ServiceAccount
    84. metadata:
    85. name: nginx-ingress-serviceaccount
    86. namespace: ingress-nginx
    87. ---
    88. apiVersion: rbac.authorization.k8s.io/v1beta1
    89. kind: ClusterRole
    90. metadata:
    91. name: nginx-ingress-clusterrole
    92. rules:
    93. - apiGroups:
    94. - ""
    95. resources:
    96. - configmaps
    97. - endpoints
    98. - nodes
    99. - pods
    100. - secrets
    101. verbs:
    102. - list
    103. - watch
    104. - apiGroups:
    105. - ""
    106. resources:
    107. - nodes
    108. verbs:
    109. - get
    110. - apiGroups:
    111. - ""
    112. resources:
    113. - services
    114. verbs:
    115. - get
    116. - list
    117. - watch
    118. - apiGroups:
    119. - "extensions"
    120. resources:
    121. - ingresses
    122. verbs:
    123. - get
    124. - list
    125. - watch
    126. - apiGroups:
    127. - ""
    128. resources:
    129. - events
    130. verbs:
    131. - create
    132. - patch
    133. - apiGroups:
    134. - "extensions"
    135. resources:
    136. - ingresses/status
    137. verbs:
    138. - update
    139. ---
    140. apiVersion: rbac.authorization.k8s.io/v1beta1
    141. kind: Role
    142. metadata:
    143. name: nginx-ingress-role
    144. namespace: ingress-nginx
    145. rules:
    146. - apiGroups:
    147. - ""
    148. resources:
    149. - configmaps
    150. - pods
    151. - secrets
    152. - namespaces
    153. verbs:
    154. - get
    155. - apiGroups:
    156. - ""
    157. resources:
    158. - configmaps
    159. resourceNames:
    160. # Defaults to "<election-id>-<ingress-class>"
    161. # Here: "<ingress-controller-leader>-<nginx>"
    162. # This has to be adapted if you change either parameter
    163. # when launching the nginx-ingress-controller.
    164. - "ingress-controller-leader-nginx"
    165. verbs:
    166. - get
    167. - update
    168. - apiGroups:
    169. - ""
    170. resources:
    171. - configmaps
    172. verbs:
    173. - create
    174. - apiGroups:
    175. - ""
    176. resources:
    177. - endpoints
    178. verbs:
    179. - get
    180. ---
    181. apiVersion: rbac.authorization.k8s.io/v1beta1
    182. kind: RoleBinding
    183. metadata:
    184. name: nginx-ingress-role-nisa-binding
    185. namespace: ingress-nginx
    186. roleRef:
    187. apiGroup: rbac.authorization.k8s.io
    188. kind: Role
    189. name: nginx-ingress-role
    190. subjects:
    191. - kind: ServiceAccount
    192. name: nginx-ingress-serviceaccount
    193. namespace: ingress-nginx
    194. ---
    195. apiVersion: rbac.authorization.k8s.io/v1beta1
    196. kind: ClusterRoleBinding
    197. metadata:
    198. name: nginx-ingress-clusterrole-nisa-binding
    199. roleRef:
    200. apiGroup: rbac.authorization.k8s.io
    201. kind: ClusterRole
    202. name: nginx-ingress-clusterrole
    203. subjects:
    204. - kind: ServiceAccount
    205. name: nginx-ingress-serviceaccount
    206. namespace: ingress-nginx
    207. ---
    208. apiVersion: apps/v1
    209. kind: Deployment
    210. metadata:
    211. name: nginx-ingress-controller
    212. namespace: ingress-nginx
    213. spec:
    214. replicas: 1
    215. selector:
    216. matchLabels:
    217. app: ingress-nginx
    218. template:
    219. metadata:
    220. labels:
    221. app: ingress-nginx
    222. annotations:
    223. prometheus.io/port: '10254'
    224. prometheus.io/scrape: 'true'
    225. spec:
    226. serviceAccountName: nginx-ingress-serviceaccount
    227. containers:
    228. - name: nginx-ingress-controller
    229. image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.17.0
    230. args:
    231. - /nginx-ingress-controller
    232. - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
    233. - --configmap=$(POD_NAMESPACE)/nginx-configuration
    234. - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
    235. - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
    236. - --publish-service=$(POD_NAMESPACE)/ingress-nginx
    237. - --annotations-prefix=nginx.ingress.kubernetes.io
    238. securityContext:
    239. capabilities:
    240. drop:
    241. - ALL
    242. add:
    243. - NET_BIND_SERVICE
    244. # www-data -> 33
    245. runAsUser: 33
    246. env:
    247. - name: POD_NAME
    248. valueFrom:
    249. fieldRef:
    250. fieldPath: metadata.name
    251. - name: POD_NAMESPACE
    252. valueFrom:
    253. fieldRef:
    254. fieldPath: metadata.namespace
    255. ports:
    256. - name: http
    257. containerPort: 80
    258. - name: https
    259. containerPort: 443
    260. livenessProbe:
    261. failureThreshold: 3
    262. httpGet:
    263. path: /healthz
    264. port: 10254
    265. scheme: HTTP
    266. initialDelaySeconds: 10
    267. periodSeconds: 10
    268. successThreshold: 1
    269. timeoutSeconds: 1
    270. readinessProbe:
    271. failureThreshold: 3
    272. httpGet:
    273. path: /healthz
    274. port: 10254
    275. scheme: HTTP
    276. periodSeconds: 10
    277. successThreshold: 1
    278. timeoutSeconds: 1

    上述文件修改完成之后,创建Ingress控制器

    1. [root@k8s-master01 ingress]# kubectl apply -f mandatory.yaml
    2. namespace/ingress-nginx created
    3. deployment.apps/default-http-backend created
    4. service/default-http-backend created
    5. configmap/nginx-configuration created
    6. configmap/tcp-services created
    7. configmap/udp-services created
    8. serviceaccount/nginx-ingress-serviceaccount created
    9. clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
    10. role.rbac.authorization.k8s.io/nginx-ingress-role created
    11. rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
    12. clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
    13. deployment.apps/nginx-ingress-controller created
    14. [root@k8s-master01 ingress]#

    如果我们在执行这个资源列表前已经手动将镜像拉取下来了,那么可以直接使用kubectl get命令查看相关资源,如果没有拉取下来,则会自动下载相关镜像,但是需要一定的时间,所以Pod资源的创建需要等待一段才能完成,具体多长时间还是取决于网络状况,所以这里还是推荐先手动把镜像拉取下来。我们可以使用如下命令监控创建过程。待其状态为”Running”之后表示运行正常

    1. [root@k8s-master01 ingress]# kubectl get pods -n ingress-nginx --watch
    2. NAME READY STATUS RESTARTS AGE
    3. default-http-backend-c8c565f69-9nlvg 1/1 Running 0 3m43s
    4. nginx-ingress-controller-5758bfd5fc-b5chq 1/1 Running 0 3m42s

    上面的配置清单中采用的是基于Deployment控制器部署Ingress Nginx的方式,因此接入外部流量之前还需要手动为其创建相关的NodePort或Loadbalancer类型的service资源对象。下面的配置清单中定义了类型Nodeport类型的service资源,并明确指定了clusterIP和一个比较容易记忆的端口

    1. apiVersion: v1
    2. kind: Service
    3. metadata:
    4. name: nginx-ingress-controller
    5. namespace: ingress-nginx
    6. spec:
    7. type: NodePort
    8. clusterIP: 10.99.88.88
    9. ports:
    10. - port: 80
    11. name: http
    12. nodePort: 10080
    13. - port: 443
    14. name: https
    15. nodePort: 10443
    16. selector:
    17. app: ingress-nginx

    将上面的内容保存到文件nginx-ingress-service.yaml中,然后再创建service资源。 注意: 其标签选择器应该与mandatory.yaml配置清单中的Deployment控制器nginx-ingress-controller的选择器保持一致

    1. [root@k8s-master01 ingress]# kubectl apply -f nginx-ingress-service.yaml
    2. service/nginx-ingress-controller created
    3. [root@k8s-master01 ingress]#

    确认service对象nginx-ingress-controller的状态没有问题后即可于集群外部对其发起访问测试。目标URL为http://:10080或者http://:10443,确认可接收到响应报文后即表示ingress nginx部署完成;不过,本示例中尚且缺少一个可用的外部负载均衡器。因此,访问测试只能使用http://:进行

    1. [root@k8s-master01 ingress]# kubectl get svc -n ingress-nginx
    2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    3. default-http-backend ClusterIP 10.99.194.137 <none> 80/TCP 52m
    4. nginx-ingress-controller NodePort 10.99.88.88 <none> 80:10080/TCP,443:10443/TCP 8m16s
    5. [root@k8s-master01 ingress]#
    6. [root@k8s-master01 ingress]#
    7. #因为我们这边没有后端应用服务,所以访问会报错
    8. [root@k8s-master01 ingress]# curl http://172.18.15.114:10080
    9. default backend - 404
    10. [root@k8s-master01 ingress]#
    11. [root@k8s-master01 ingress]#

    案例

    发布一个tomcat应用

    建设有这样一套环境,K8S集群上的tomcat-deploy控制器生成了两个运行于Pod资源中的tomcat副本,tomcat-svc是将它们统一暴露于集群中的访问入口。现在需要通过Ingress资源将tomcat-svc发布给集群外部的客户端访问。具体的需求如下图 Servicee和Ingress(五)之Ingress - 图4 为了便于理解,下面的测试操作过程把每一步都分解开进行

    准备名称空间

    本示例当中的所有资源都位于testing名称空间中,与其他的资源在逻辑上进行隔离,以方便管理。下面的配置信息保存在tomcat-testing-ns.yaml资源清单文件中

    1. apiVersion: v1
    2. kind: Namespace
    3. metadata:
    4. name: testing
    5. labels:
    6. env: testing
    1. [root@k8s-master01 tomcat]# kubectl apply -f tomcat-testing-ns.yaml
    2. namespace/testing created
    3. [root@k8s-master01 tomcat]#
    4. [root@k8s-master01 tomcat]# kubectl get ns
    5. NAME STATUS AGE
    6. default Active 35d
    7. ingress-nginx Active 17h
    8. kube-node-lease Active 35d
    9. kube-public Active 35d
    10. kube-system Active 35d
    11. testing Active 43s
    12. [root@k8s-master01 tomcat]# kubectl get ns testing
    13. NAME STATUS AGE
    14. testing Active 50s
    15. [root@k8s-master01 tomcat]#

    部署tomcat实例

    在此示例中,tomcat应用本身代表着运行于tomcat容器的一个应用程序,这个应用程序通常应该包含了某应用程序的war文件的镜像文件。下面的配置清单中使用了Deployment控制器部署了2个tomcat的Pod应用,并且名称空间属于testing。它保存于tomcat-testing-deploy.yaml文件中

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: tomcat-deploy
    5. namespace: testing
    6. labels:
    7. app: tomcat
    8. spec:
    9. replicas: 2
    10. selector:
    11. matchLabels:
    12. app: tomcat
    13. template:
    14. metadata:
    15. labels:
    16. app: tomcat
    17. spec:
    18. containers:
    19. - image: docker.io/tomcat:8.5.56-jdk14-openjdk-oracle
    20. imagePullPolicy: IfNotPresent
    21. name: tomcat
    22. ports:
    23. - containerPort: 8080
    24. name: httpport
    25. - containerPort: 8009
    26. name: ajpport
    27. lifecycle:
    28. postStart:
    29. exec:
    30. command: ["/bin/sh","-c","echo '健哥说非要echo一个页面,不然就是搞学问不严谨,哈哈哈哈' > /usr/local/tomcat/webapps/aaa/a.txt"]

    资源清单配置文件配置完成后使用kubectl apply命令创建deployment。如果需要更多的Pod资源承载用户访问,那么使用deployment控制器的规模伸缩机制即可完成,或者直接修改上面的配置文件中的replicas然后执行kubectl apply命令重新进行应用

    1. [root@k8s-master01 tomcat]# kubectl apply -f tomcat-testing-deploy.yaml
    2. deployment.apps/tomcat-deploy created
    3. [root@k8s-master01 tomcat]#
    4. [root@k8s-master01 tomcat]# kubectl get pods -n testing
    5. NAME READY STATUS RESTARTS AGE
    6. tomcat-deploy-778769699c-jcptt 0/1 ContainerCreating 0 14s
    7. tomcat-deploy-778769699c-nfxxb 0/1 ContainerCreating 0 14s
    8. [root@k8s-master01 tomcat]# kubectl get pods -n testing
    9. NAME READY STATUS RESTARTS AGE
    10. tomcat-deploy-778769699c-jcptt 1/1 Running 0 58s
    11. tomcat-deploy-778769699c-nfxxb 1/1 Running 0 58s
    12. [root@k8s-master01 tomcat]#

    创建Service资源

    Ingress资源仅通过Service资源识别相应的Pod资源,获取其IP地址和端口,而后Ingress控制器即可直接使用各Pod对象的IP地址与它直接进行通信,而无需经由Service资源的代理和调度。因此Service资源的ClusterIP对Ingress控制器来说并不起作用。不过,若集群内的其他Pod客户端需要与其通信,那么保留ClusterIP地址也是很有必要的。 下面的配置文件中定义了Service资源的tomcat-testing-svc,它通过标签选择器将相关的Pod资源对象归于一组,并通过80/TCP端口暴露Pod资源的8080/TCP端口。如果还需要暴露8009/TCP端口,那么只需要将其以类似的格式配置在列表中即可。将如下内容保存到tomcat-testing-svc.yaml文件中

    1. apiVersion: v1
    2. kind: Service
    3. metadata:
    4. name: tomcat-testing-svc
    5. namespace: testing
    6. labels:
    7. app: tomcat-svc
    8. spec:
    9. selector:
    10. app: tomcat
    11. ports:
    12. - name: http
    13. port: 80
    14. targetPort: 8080
    15. protocol: TCP
    16. - name: ajp
    17. port: 8009
    18. targetPort: 8009
    19. protocol: TCP

    资源清单文件配置完成之后即可创建service

    1. [root@k8s-master01 tomcat]# kubectl apply -f tomcat-testing-svc.yaml
    2. service/tomcat-testing-svc created
    3. [root@k8s-master01 tomcat]#
    4. #查看svc资源,可以看到两个tomcat已经被关联到了该service资源,并且我们为该service资源定义了一个静态的IP地址,目的是如果集群内部有其他Pod资源可以直接使用该Servie地址进行通信
    5. [root@k8s-master01 tomcat]# kubectl get svc -n testing
    6. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    7. tomcat-testing-svc ClusterIP 10.99.85.85 <none> 80/TCP,8009/TCP 46s
    8. [root@k8s-master01 tomcat]#
    9. [root@k8s-master01 tomcat]# kubectl describe svc tomcat-testing-svc -n testing
    10. Name: tomcat-testing-svc
    11. Namespace: testing
    12. Labels: app=tomcat-svc
    13. Annotations: kubectl.kubernetes.io/last-applied-configuration:
    14. {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"tomcat-svc"},"name":"tomcat-testing-svc","namespace":"te...
    15. Selector: app=tomcat
    16. Type: ClusterIP
    17. IP: 10.99.85.85
    18. Port: http 80/TCP
    19. TargetPort: 8080/TCP
    20. Endpoints: 10.244.38.5:8080,10.244.59.6:8080
    21. Port: ajp 8009/TCP
    22. TargetPort: 8009/TCP
    23. Endpoints: 10.244.38.5:8009,10.244.59.6:8009
    24. Session Affinity: None
    25. Events: <none>
    26. [root@k8s-master01 tomcat]#

    创建Ingress资源

    通过Ingress资源的FQDN主机名或URL路径等类型发布的服务,只要用户的访问请求能够匹配到其.spec.rules.host字段定义的主机时才能被相应的规则处理。如果要明确匹配用户的处理请求,比如希望将那些发往tomcat.ilinux.io主机的所有请求代理至tomcat-testing-svc资源的后端Pod,则可以使用如下命令配置文件中的内容。将如下内容保存到tomcat-testing-ingress.yaml文件中

    1. apiVersion: extensions/v1beta1
    2. kind: Ingress
    3. metadata:
    4. name: tomcat
    5. namespace: testing
    6. annotations:
    7. kubernetes.io/ingress.class: "nginx"
    8. spec:
    9. rules:
    10. - host: tomcat.ilinux.io
    11. http:
    12. paths:
    13. - backend:
    14. serviceName: tomcat-testing-svc
    15. servicePort: 80

    资源清单文件配置文成之后执行创建ingress资源

    1. [root@k8s-master01 tomcat]# kubectl apply -f tomcat-testing-ingress.yaml
    2. ingress.extensions/tomcat created
    3. [root@k8s-master01 tomcat]#

    Ingress资源创建完成之后,通过详细信息确认是否成功创建,并且确认是否正确关联到了对应的service资源

    1. [root@k8s-master01 tomcat]# kubectl describe ingress -n testing
    2. Name: tomcat
    3. Namespace: testing
    4. Address:
    5. Default backend: default-http-backend:80 (<none>)
    6. Rules:
    7. Host Path Backends
    8. ---- ---- --------
    9. tomcat.ilinux.io
    10. tomcat-testing-svc:80 (10.244.38.5:8080,10.244.59.6:8080)
    11. Annotations:
    12. kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"tomcat","namespace":"testing"},"spec":{"rules":[{"host":"tomcat.ilinux.io","http":{"paths":[{"backend":{"serviceName":"tomcat-testing-svc","servicePort":80}}]}}]}}
    13. kubernetes.io/ingress.class: nginx
    14. Events:
    15. Type Reason Age From Message
    16. ---- ------ ---- ---- -------
    17. Normal CREATE 113s nginx-ingress-controller Ingress testing/tomcat
    18. [root@k8s-master01 tomcat]#

    接下来就可以通过Ingress 控制器的前端Service资源的NodePort来访问此服务,前面的示例中,该Ingress Controller的Service资源已经被创建出来了,并且IP地址被定义为10.99.88.88,节点端口为10080映射控制器的端口为80。因此,这里使用Ingress中定义的主机名tomcat.ilinux.io即可进行访问。为什么不使用在tomcat案列中的service访问呢?因为该Service的主要作用仅仅是用于关联后端Pod资源 如下图: Servicee和Ingress(五)之Ingress - 图5

    我们通过本地浏览器访问tomcat.ilinux.io Servicee和Ingress(五)之Ingress - 图6

    不过,用户对tomcat.ilinux.io主机外的地址发起的请求被此Ingress规则匹配到后将被发往Ingress控制器的默认的后端,即default-http-backend,它通常只有一个404的状态提示信息。用户也可以按需自定义默认后端。例如,下面的配置文件片段所示,它通过.spec.backend定义了所有无法由此Ingress匹配到的访问请求都由相应的后端default-svc这个service来处理

    1. spec
    2. backend:
    3. serviceName: default-svc
    4. servicePort: 80

    发布TLS类型应用

    一般来说,如果有基于HTTPS通信的需求,那么它应该由外部的负载均衡器予以实现,并在SSL会话卸载后将访问请求转发到Ingress控制器。不过,如果外部负载均衡器工作于传输层而不是工作于应用层的反响代理服务器,或者存在直接通过Ingress控制器接收客户端请求的需求,又期望它们能够提供HTTPS服务时,就应该配置TLS类型的Ingress资源 将此类服务公开发布到互联网时,HTTPS服务用到的证书应该是由公信CA签署并颁发。因为是出于测试目的,所以我们直接使用openssl生成自签证书和测试使用的私钥 注意:TLS Secret中包含的证书必须以tls.crt作为其键名,私钥文件必须以tls.key为键名,因此上面生成的私钥文件和证书文件名将直接保存为键名形式,以便于后面创建Secret对象时直接作为键名引用

    1. [root@k8s-master01 tomcat]# openssl genrsa -out tls.key 2048
    2. Generating RSA private key, 2048 bit long modulus
    3. .+++
    4. ..........................................................................+++
    5. e is 65537 (0x10001)
    6. [root@k8s-master01 tomcat]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Shanghai/L=Shanghai/O=DevOps/CN=tomcat.ilinux.io -days 3600
    7. [root@k8s-master01 tomcat]#

    在Ingress控制器上配置HTTPS主机时,不能直接使用私钥和证书文件,而是需要使用secret资源对象来传递相关的数据。所以,接下来要根据私钥和证书生成用于配置TLS Ingress的Secret资源,在创建Ingress规则时由其将用到的Secret资源中的信息注入到Ingrss控制器的Pod资源对象中,用于为其配置HTTPS虚拟主机提供相应的私钥和证书。下面的命令会创建一个TLS类型名为tomcat-testing-ingress-secret的secret资源

    1. [root@k8s-master01 tomcat]# kubectl create secret tls tomcat-testing-ingress-secret --cert=tls.crt --key=tls.key -n testing
    2. secret/tomcat-testing-ingress-secret created
    3. [root@k8s-master01 tomcat]#
    4. [root@k8s-master01 tomcat]# kubectl get secret tomcat-testing-ingress-secret -n testing
    5. NAME TYPE DATA AGE
    6. tomcat-testing-ingress-secret kubernetes.io/tls 2 45s
    7. [root@k8s-master01 tomcat]#

    而后去定义创建TLS类型Ingress资源的配置清单。下面的配置清单通过spec.rules定义了一组转发规则,并通过.spec.tls将此主机定义为了HTTPS类型的虚拟主机,用到的私钥和证书信息则来自于Secret资源tomcat-testing-ingress-secret 为了演示方便,我们直接修改之前创建的tomcat-testing-ingress.yaml资源清单文件。加上了.spec.tls字段的配置即可

    1. apiVersion: extensions/v1beta1
    2. kind: Ingress
    3. metadata:
    4. name: tomcat
    5. namespace: testing
    6. annotations:
    7. kubernetes.io/ingress.class: "nginx"
    8. spec:
    9. tls:
    10. - hosts:
    11. - tomcat.ilinux.io
    12. secretName: tomcat-testing-ingress-secret
    13. rules:
    14. - host: tomcat.ilinux.io
    15. http:
    16. paths:
    17. - backend:
    18. serviceName: tomcat-testing-svc
    19. servicePort: 80

    资源清单文件修改完成之后直接应用一下文件科技

    1. [root@k8s-master01 tomcat]# kubectl apply -f tomcat-testing-ingress.yaml
    2. ingress.extensions/tomcat configured
    3. [root@k8s-master01 tomcat]#

    然后查看详细信息确认是否被成功修改,且已经正确关联到了相应的service资源

    1. [root@k8s-master01 tomcat]# kubectl describe ingress tomcat -n testing
    2. Name: tomcat
    3. Namespace: testing
    4. Address:
    5. Default backend: default-http-backend:80 (<none>)
    6. TLS:
    7. tomcat-testing-ingress-secret terminates tomcat.ilinux.io
    8. Rules:
    9. Host Path Backends
    10. ---- ---- --------
    11. tomcat.ilinux.io
    12. tomcat-testing-svc:80 (10.244.38.5:8080,10.244.59.6:8080)
    13. Annotations:
    14. kubernetes.io/ingress.class: nginx
    15. kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"tomcat","namespace":"testing"},"spec":{"rules":[{"host":"tomcat.ilinux.io","http":{"paths":[{"backend":{"serviceName":"tomcat-testing-svc","servicePort":80}}]}}],"tls":[{"hosts":["tomcat.ilinux.io"],"secretName":"tomcat-testing-ingress-secret"}]}}
    16. Events:
    17. Type Reason Age From Message
    18. ---- ------ ---- ---- -------
    19. Normal UPDATE 84s nginx-ingress-controller Ingress testing/tomcat
    20. [root@k8s-master01 tomcat]#

    接下来就可以通过Ingress控制器的前端Service资源的NodePort来访问此服务,同样使用之前创建的servie资源来访问,此Service资源以节点端口10443映射控制器的443端口。因此,这里使用Ingress中定义的主机名tomcat.ilinux.io:443即可访问tomcat应用,其访问到的效果跟之前一样,会出现一个404页面。因为tomcat的webapps目录里面没有任何内容 Servicee和Ingress(五)之Ingress - 图7 或者我们可以直接使用curl命令进行访问测试,只要对应的主机能够正确解析tomcat.ilinux.io主机名即可。

    1. [root@k8s-master01 tomcat]# curl -k -v https://tomcat.ilinux.io:10443/aaa/a.txt
    2. * About to connect() to tomcat.ilinux.io port 10443 (#0)
    3. * Trying 172.18.15.114...
    4. * Connected to tomcat.ilinux.io (172.18.15.114) port 10443 (#0)
    5. * Initializing NSS with certpath: sql:/etc/pki/nssdb
    6. * skipping SSL peer certificate verification
    7. * SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    8. * Server certificate:
    9. * subject: CN=tomcat.ilinux.io,O=DevOps,L=Shanghai,ST=Shanghai,C=CN
    10. * start date: Jun 30 08:53:53 2020 GMT
    11. * expire date: May 09 08:53:53 2030 GMT
    12. * common name: tomcat.ilinux.io
    13. * issuer: CN=tomcat.ilinux.io,O=DevOps,L=Shanghai,ST=Shanghai,C=CN
    14. > GET /aaa/a.txt HTTP/1.1
    15. > User-Agent: curl/7.29.0
    16. > Host: tomcat.ilinux.io:10443
    17. > Accept: */*
    18. >
    19. < HTTP/1.1 200
    20. < Server: nginx/1.13.12
    21. < Date: Wed, 01 Jul 2020 03:03:48 GMT
    22. < Content-Type: text/plain
    23. < Content-Length: 80
    24. < Connection: keep-alive
    25. < Accept-Ranges: bytes
    26. < ETag: W/"80-1593514308928"
    27. < Last-Modified: Tue, 30 Jun 2020 10:51:48 GMT
    28. < Strict-Transport-Security: max-age=15724800; includeSubDomains
    29. <
    30. 健哥说非要echo一个页面,不然就是搞学问不严谨,哈哈哈哈
    31. * Connection #0 to host tomcat.ilinux.io left intact
    32. [root@k8s-master01 tomcat]#

    到此为止,实践配置目标已经全部达成。需要再次提醒的是,在实际使用中,在集群之外应该存在一个用于调度用户请求至各节点上Ingress控制器相关的NodePort的负载均衡器。这个负载均衡器可以是云厂商提供,也可以是用户基于Nginx、Haproxy、LVS等手动构建并通过keepalive等解决方式实现其服务高可用配置的反向代理服务器

    总结

    Service通过标签选择器为一组Pod资源创建一个统一的访问入口,其可以将客户端请求代理调度至后端的Pod资源 Service资源是四层调度机制,默认调度算法为随机调度 Service的实现模式有三种:userspace(已经不再使用)、iptables和ipvs Service共有四种类型: ClusterIP、NodePort、Loadbalancer和ExternalName,用于发布服务 Headless Service是一种特殊的service资源,可用于Pod发现 Ingress资源是发布Service资源的另一种方式,它需要结合Ingress控制器才能正常工作 Ingress Controller的实现除了Nginx之外,还有Envoy、Haproxy、Traefik等 Ingress资源需要依赖Service资源用于关联后端Pod资源