系列目录
概述
向外网暴露集群内服务, 以使客户端能够访问, 有以下几种方法, 本文重点描述 Ingress.
LoadBalancer
LoadBalancer 一般由云服务供应商提供或者用户自定义, 运行在集群之外. 在创建 service 时为其配置 LoadBalancer 相关参数, 当从外网访问集群内 servcie 时, 用户直接连接到 LoadBalancer 服务器, LoadBalancer 服务器再将流量转发到集群内 service.Loadbalancer 配置及使用方法与各云服务供应商有关, 本文不详细描述.
NodePort
这种方式要求集群中部分节点有被外网访问的能力. Kubernetes 为每个 NodePort 类型的服务在集群中的每个节点上分配至少一个主机网络端口号. 客户通过能被外网访问的节点 IP 加上节点端口的方式访问服务. 大多数情况下不会通过这种方式向集群外暴露服务, 原因有四.
其一: 大多情况下, 为了安全起见, 集群中的节点位于完全内网环境中, 不应该有被外网直接访问的能力. 一般外网访问集群中的节点都是通过边界服务器如网关, 跳板等, 而这种边界服务器需要通过各种方式进行安全加固.
其二: 如果集群内节点可以从外网直接访问的话, 则会将集群内节点地址, 服务名称, 端口号等信息直接暴露在外, 非常不安全.
其三: 服务端口号一般由系统自动分配, 并非固定, 而服务名称也可能发生变更, 此时外部客户端需要跟踪变更并修改, 属于重试耦合.
其四: 这种方式, 每个服务至少向外网暴露一个端口号, 当服务很多时不易于管理.
Ingress
Ingress 不是某种产品, 组件的名称, 它应该是 kubernetes 向集群外暴露服务的一种思路, 技术, 用户完全可以根据这种思路提供自己的 Ingress 实现, 当然 kubernetes 提供了默认 Ingress 实现还有其它第三方实现, 一般无需自己开发. 它的思路是这样的, 首先在集群内运行一个服务或者 pod 也可以是容器, 不管是什么它至少应该有一个外网可以访问的 IP, 至少向外网开放一个端口号, 让它充当反向代理服务器. 当外网想要访问集群内 service 时, 只需访问这个反向代理服务器并指定相关参数, 代理服务器根据请求参数并结合内部规则, 将请求转发到 service. 这种思路与 LoadBalancer 的不同之处是它就位于集群内, 而 LoadBalancer 位于集群外. 与 NodePort 的不同之处是集群只向外暴露一个服务或者 pod 等, 而 NodePort 是暴露全部 service.
Kubernetes 用 nginx 实现反向代理服务器, 称为 Ingress Controller, 是 pod 类型资源. 同时提供了 Ingress 类型对象, 通过创建 Ingress 对象配置 nginx 反向代理服务器的转发规则. Nginx 反向代理服务器收到来自外网的请求后, 用请求的 URL 地址, 请求头字段区别不同 service, 然后转发请求.
部署 Ingress Controller
在 Kubernetes 中, Ingress Controller 典型是 pod 类型资源, 其部署方式与普通 pod 相同, 通过 Deployment,DaemonSet 等副本控制器部署, 其中更值推荐的是 DaemonSet 方式. Ingress Controller 需要部署在具备连通外网能力的节点上, 首先在目标节点打上 Ingress Controller 专用标签, 然后在 DaemonSet 的配置文件中配置节点选择器选中此类标签, 控制 pod 实例可以部署的节点, 通过为节点增减相关标签控制 Ingress Controller 的 pod 实例个数. Ingress Controller 一般占用两个节点主机端口, http 用 80,https 用 443. 详细参考: 这里 https://git.k8s.io/ingress-nginx/README.md . 外网通过 http:// 节点外网 ip:80/... 或者 https:// 节点外网 ip:80/... 就可以访问内部服务了, 当前首先需要创建 Ingress 对象配置访问策略.
创建 Ingress 对象
本节通过创建各种 Ingress 对象, 展示 Ingress 的各种典型用法.
Single Service Ingress
配置文件:
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: test-ingress
- spec:
- backend:
- serviceName: testsvc
- servicePort: 80
然后通过 kubectl create -f 创建对象, 这同创建一般对象并没有很多区别, 前面已经多次提到过, 这里不再详细描述
查看对象:
- $ kubectl get ing
- NAME RULE BACKEND ADDRESS
- test-ingress - testsvc:80 107.178.254.228
以上配置中没有具体的 rule, 所以诸如 http(s)://107.178.254.228/xxx 之类的请求都转发到 testsvc 的 80 端口.
其于 URL 转发 (Simple fanout)
假如要实现以下目标:
- foo.bar.com -> 178.91.123.132 -> / foo s1:80
- / bar s2:80
其中 foo.bar.com 是 http 请求体头部中的 host 字段, 178.91.123.132 是 Ingress Controller 外网地址, 当请求路径与 / foo 匹配时转发到 s1 服务的 80 端口, 当与 / bar 匹配时转发到 s2 服务的 80 端口, 其最核心逻辑是用 URL 区分不同服务.
配置如下:
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: test
- annotations:
- nginx.ingress.kubernetes.io/rewrite-target: /
- spec:
- rules:
- - host: foo.bar.com
- http:
- paths:
- - path: /foo
- backend:
- serviceName: s1
- servicePort: 80
- - path: /bar
- backend:
- serviceName: s2
- servicePort: 80
查看对象:
- $ kubectl get ing
- NAME RULE BACKEND ADDRESS
- test -
- foo.bar.com
- /foo s1:80
- /bar s2:80
基于名称的虚拟主机 (Name based virtual hosting)
实现如下目录:
- foo.bar.com --| |-> foo.bar.com s1:80
- | 178.91.123.132 |
- bar.foo.com --| |-> bar.foo.com s2:8
这种方式的核心逻辑是用 http 请求中的 host 字段区分不同服务, 而不是 URL. 如 host: foo.bar.com 的请求被转发到 s1 服务 80 端口, 如 host: bar.foo.com 的请求被转发到 s2 服务 80 端口.
配置:
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: test
- spec:
- rules:
- - host: foo.bar.com
- http:
- paths:
- - backend:
- serviceName: s1
- servicePort: 80
- - host: bar.foo.com
- http:
- paths:
- - backend:
- serviceName: s2
- servicePort: 80
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: test
- spec:
- rules:
- - host: foo.bar.com
- http:
- paths:
- - backend:
- serviceName: s1
- servicePort: 80
- - host: bar.foo.com
- http:
- paths:
- - backend:
- serviceName: s2
- servicePort: 80
- TLS
利用 Secret 类型对象为 Ingress Controller 提供私钥及证书, 对通信链路加密.
Secret 配置:
- apiVersion: v1
- data:
- tls.crt: base64 encoded cert
- tls.key: base64 encoded key
- kind: Secret
- metadata:
- name: testsecret
- namespace: default
- type: Secret
在 Ingress 对象中引用
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: no-rules-map
- spec:
- tls:
- - secretName: testsecret
- backend:
- serviceName: s1
- servicePort: 80
** 更新 Ingress 对象 **
使用 kubectl edit 命令编辑 Ingress 实时对象:
- $ kubectl get ing
- NAME RULE BACKEND ADDRESS
- test - 178.91.123.132
- foo.bar.com
- /foo s1:80
- $ kubectl edit ing test
在弹出的编辑器中修改:
- spec:
- rules:
- - host: foo.bar.com
- http:
- paths:
- - backend:
- serviceName: s1
- servicePort: 80
- path: /foo
- - host: bar.baz.com
- http:
- paths:
- - backend:
- serviceName: s2
- servicePort: 80
- path: /foo
- ..
保存关稍后确认更新:
- $ kubectl get ing
- NAME RULE BACKEND ADDRESS
- test - 178.91.123.132
- foo.bar.com
- /foo s1:80
- bar.baz.com
- /foo s2:80
参考:
- $ kubectl get ing
- NAME RULE BACKEND ADDRESS
- test - 178.91.123.132
- foo.bar.com
- /foo s1:80
- bar.baz.com
- /foo s2:80
Ingress-NginX 传递自定义 header
问题现场:
配置好 Ingress 之后可以通过 Ingress 正常访问系统, 但是输入用户名密码之后登陆失败. 但是通过 NodePort 暴露服务时可以正常访问和登录. 接下来进过调试发现是在获取用户信息时出错, 无法从 Request header 中取到自定义的用户信息字段.
参考此文章发现, NginX 默认会将用户自定义的 header 过滤掉, 除非开启 underscores_in_headers , 经过测试, 在 NginX 中开启 underscores_in_headers 之后系统登录正常. 那么如何在 Ingress-NginX 中开启此项呢:
- kind: ConfigMap
- apiVersion: v1
- metadata:
- name: nginx-configuration
- namespace: ingress-nginx
- labels:
- App: ingress-nginx
- data:
- enable-underscores-in-headers: "true"
参考链接 1:
参考链接 2
来源: https://www.cnblogs.com/tylerzhou/p/11003787.html