K8s 访问集群外部的服务
从 docker 到 compose 再到 k8s,容器化是越来越火了,应该也有不少小伙伴和我一样跃跃欲试。但是马克思告诉我们要一分为二地看待问题。容器化固然好,不过也不是所有服务都适合这么搞。典型的,数据库服务是否适合放在 k8s 中就一直饱受争议。
这次的问题也起于此。通过 Service 暴露内部服务到外部已经轻车熟路了,倒过来则讨论少了许多。究其原因,我觉得是太简单,甚至不值得讨论——直接访问域名和 ip 不就行咯。
确实可以,但不够「优雅」, 尤其当我们不确定某个服务是否会在某天也容器化时。这非常符合处于过渡阶段的项目。
服务间通讯
首先来看一下集群内部服务之间的通讯。官方文档传送门🌀。
这个太简单了,默认情况下,k8s 会根据服务名,为每个服务都创建一个内部的 FQDN,格式为 my-svc.my-namespace.svc.cluster-domain.example
。集群内的其他服务可以通过此域名直接访问。
访问集群外服务
那么我们想,如果把这种思路用于外部服务,岂不是太好了。
这样一来,随着容器化的推进,就可以无缝切换,不需要重新调整服务地址的配置。
Service - ExternalName
搂一眼配置:
kind: Service
apiVersion: v1
metadata:
name: pgsql
namespace: default
ports:
- port: 5432
spec:
type: ExternalName
externalName: pgsql.mydomain.com
这也是一个 Service,只不过没有选择器。访问这个服务所对应的 FQDN 时,实际将解析到 externalName
所指定的域名对应的 IP。最常见的用例应该就是购买云计算厂商的数据库,通常会给我们一个地址用于连接。通过这种方式,相当于把外部地址和内部地址做了映射。
这种方式内部其实利用了 DNS 的 CNAME 技术。这就意味着 externalName
必须是一个有效的域名,而不能 IP 地址。 如果... 就是想要 IP 呢?
Endpoint
其实 Service 一直都有 Endpoint,只不过因为是自动的而被我们忽略。通常,Service 通过选择器与 Pod 关联,这时候 k8s 就会根据 Pod 的信息搞出一个 Endpoint 作为访问点。那... 我是不是可以手动指定它呢?
apiVersion: v1
kind: Service
metadata:
name: pgsql
spec:
ports:
- port: 5432
---
apiVersion: v1
kind: Endpoints
metadata:
name: pgsql
subsets:
- addresses:
- ip: 200.1.2.3
ports:
- port: 5432
这同样是一个没有选择器的 Service,但被手动指定了 Endpoint。可以类比地理解为,手动给 FQDN 设置 A 记录。
通过上面两种方式,我们成功在外部服务和 Pod 中创建了一个抽象层。未来如果外部服务也容器化,只需要改变 Service 的选择器即可,使用到这些服务的组件可以自动适配。