7

关于Kubernetes中如何访问集群外服务的一些笔记

 1 year ago
source link: https://liruilongs.github.io/2022/12/10/%E5%BE%85%E5%8F%91%E5%B8%83/%E5%85%B3%E4%BA%8EKubernetes%E4%B8%AD%E5%A6%82%E4%BD%95%E8%AE%BF%E9%97%AE%E9%9B%86%E7%BE%A4%E5%A4%96%E6%9C%8D%E5%8A%A1%E7%9A%84%E4%B8%80%E4%BA%9B%E7%AC%94%E8%AE%B0/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

关于Kubernetes中如何访问集群外服务的一些笔记

是故不应取法,不应取非法。以是义故,如来常说:汝等比丘,知我说法,如筏喻者,法尚应舍何况非法。 ———-《金刚经》


  • 分享一些 k8s 中服务如何访问集群外服务的笔记
  • 博文内容涉及:
    • 如何访问集群外服务
    • 创建外部服务代理 SVC(IP+PORT情况)
    • Endponts/EndpointSlice 实现 Demo
    • 外部服务为 单体/集群 的访问 Demo
    • 创建 ExternalName 类型 SVC(域名的情况)
  • 理解不足小伙伴帮忙指正

是故不应取法,不应取非法。以是义故,如来常说:汝等比丘,知我说法,如筏喻者,法尚应舍何况非法。 ———-《金刚经》


如何访问集群外服务

在 K8s 中,考虑某些稳定性问题,希望把数据库部署到 物理机或者虚机上,或许系统正在一点点迁移到 K8s 平台,某些服务在非 k8s 集群部署,或者上游系统是别人的,和我们没有直接关系。那么我们如何实现 K8s 集群上的服务访问 这些外部服务。

外部服务是IP端口的方式

在 K8s 中,我们可以定义一个没有 lable SelectorService 来代替 非当前集群的服务。通过 IP 端口映射的方式把外部服务映射到内部集群中。

这样可以正常接入外部服务的同时,添加了一个类似外部服务的代理服务。之后如果外部服务发生 IP 端口变更,只需要修改映射关系即可,不需要修改应用相关的配置。同时对访问他的pod 隐藏了实际的IP端口,以后如果服务移入集群内,则不需要更改任何代码。

外部服务是域名的方式

当 外部服务提供的方式是域名的时候,我们可以创建一个 Service 类型为 ExternalName 的SVC,同样没有lable Selector, 类型为 ExternalName 的服务将外部服务域名映射到集群内部服务的 DNS 名称,而不是对应的 Pod 。

创建外部服务代理服务

适用于外部服务为 IP:Port的方式,定义一个没有 选择器的 Service ,对应这样的 Servicek8s 不会自动创建对应的 EndpointEndpointSlice ,其他的和正常的 Service 没有区别,所以我们需要提供 Service 对应的 endpoint 或者是 EndpointSlice 来对外部服务做映射。类似与通过 iptables 做了 DNAT 的映射,实现 IP 端口转发。

资源文件的定义

┌──[[email protected]]-[~/ansible]
└─$cat not-service.yaml
apiVersion: v1
kind: Serviceca
metadata:
name: external-service
spec:
ports:
- protocol: TCP
port: 30056
targetPort: 3306

定义了一个普通的 Service ,没有选择器,在 Service 内部做了转发,暴露的端口为 30056 转发到端口 3306, 这里的 3306 为代理的外部服务的端口。

┌──[[email protected]]-[~/ansible]
└─$kubectl describe svc external-service
Name: external-service
Namespace: awx
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.103.93.20
IPs: 10.103.93.20
Port: <unset> 30056/TCP
TargetPort: 3306/TCP
Endpoints: <none>
Session Affinity: None
Events: <none>

可以看到当前的 Service 类型为 ClusterIP, 对应的集群 IP 为 :10.103.93.20,Endpoints 为 None。

对于这样的 Service 系统不会自动创建 EndpointEndpointSlice,因此需要手动创建一个和该 Service 同名的Endpoint 或者带序号的 EndpointSlice 对象 ,用于指向实际的 后端访问地址

1.21 版本之前的只能通过创建 Endponits 的方式,创建 Endpoint 的配置文件内容如下:

Endponits 方式单体服务

┌──[[email protected]]-[~/ansible]
└─$cat external-service.yaml
kind: Endpoints
apiVersion: v1
metadata:
name: external-service
subsets:
- addresses:
- ip: 192.168.26.81
ports:
- port: 3306

这里定义 集群外的服务 IP 为 192.168.26.81,端口为 3306, 这个 endpoint 即表示集群外的服务,生产环境中,我们需要打通相关的网络。

┌──[[email protected]]-[~/ansible]
└─$kubectl apply -f external-service.yaml
endpoints/external-service created
┌──[[email protected]]-[~/ansible]
└─$kubectl get endpoints external-service
NAME ENDPOINTS AGE
external-service 192.168.26.81:3306 57s

在集群外通过 python 模块发布一个 简单的 http 服务,暴露端口 3306,做简单测试。

┌──[[email protected]]-[~/ansible]
└─$coproc python -m SimpleHTTPServer 3306
[2] 109525
┌──[[email protected]]-[~/ansible]
└─$kubectl get svc external-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
external-service ClusterIP 10.103.93.20 <none> 30056/TCP 26m

通过访问集群服务,实现对集群外部服务的访问

┌──[[email protected]]-[~/ansible]
└─$curl 10.103.93.20:30056 -s -w "%{http_code}\n" -o /dev/null
192.168.26.81 - - [10/Dec/2022 03:26:30] "GET / HTTP/1.1" 200 -
200

EndpointSlice 方式(集群服务)

对于 1.21 版本及之后的版本来讲,我们可以通过 EndpointSlice 来实现,资源文件的定义

┌──[[email protected]]-[~/ansible]
└─$cat external-service-1.yaml
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: external-service-1 # 按惯例将服务的名称用作 EndpointSlice 名称的前缀
labels:
# 你应设置 "kubernetes.io/service-name" 标签。
# 设置其值以匹配服务的名称
kubernetes.io/service-name: external-service
addressType: IPv4
ports:
- name: '' # 留空,因为 port 9376 未被 IANA 分配为已注册端口
appProtocol: http
protocol: TCP
port: 3306
endpoints:
- addresses:
- "192.168.26.82" # 此列表中的 IP 地址可以按任何顺序显示
- addresses:
- "192.168.26.81"

这里我们提供了两个 ip ,来模拟外部服务集群的情况。

┌──[[email protected]]-[~/ansible]
└─$kubectl apply -f external-service-1.yaml
endpointslice.discovery.k8s.io/external-service-1 created
┌──[[email protected]]-[~/ansible]
└─$kubectl get endpointslices.discovery.k8s.io external-service-1
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
external-service-1 IPv4 3306 192.168.26.81,192.168.26.82 20s

在集群外 81,82 两台机器通过 python 模块发布一个 简单的 http 服务,暴露端口 3306,做简单测试。

┌──[[email protected]]-[~/ansible]
└─$coproc python -m SimpleHTTPServer 3306
[2] 9084
┌──[[email protected]]-[~]
└─$coproc python -m SimpleHTTPServer 3306

测试可以看到在81 和 82两个外部服务轮询访问,默认情况下 Serviec 的 负载均衡策略为,sessionAffinity: None ,即 RoundRobin 将客户端请求代理到合适的后端合适的 Pod 上

┌──[[email protected]]-[~/ansible]
└─$while true;do curl 10.103.93.20:30056 -s -w "%{http_code}\n" -o /dev/null ;sleep 2;done
192.168.26.81 - - [10/Dec/2022 03:56:52] "GET / HTTP/1.1" 200 -
200
200
200
192.168.26.81 - - [10/Dec/2022 03:56:58] "GET / HTTP/1.1" 200 -
200
200
192.168.26.81 - - [10/Dec/2022 03:57:02] "GET / HTTP/1.1" 200 -
200
192.168.26.81 - - [10/Dec/2022 03:57:04] "GET / HTTP/1.1" 200 -
200
200
192.168.26.81 - - [10/Dec/2022 03:57:08] "GET / HTTP/1.1" 200 -
200
192.168.26.81 - - [10/Dec/2022 03:57:10] "GET / HTTP/1.1" 200 -
200

这里我们修改一下,修改为会话保持 sessionAffinity: ClientIP

┌──[[email protected]]-[~/ansible]
└─$kubectl edit svc external-service
service/external-service edited
┌──[[email protected]]-[~/ansible]
└─$kubectl get svc external-service -o json | jq .spec.sessionAffinity
"ClientIP"

可以看到当前 访问只到 81 上面

┌──[[email protected]]-[~/ansible]
└─$while true;do curl 10.103.93.20:30056 -s -w "%{http_code}\n" -o /dev/null ;sleep 2;done
192.168.26.81 - - [10/Dec/2022 04:00:56] "GET / HTTP/1.1" 200 -
200
192.168.26.81 - - [10/Dec/2022 04:00:58] "GET / HTTP/1.1" 200 -
200
192.168.26.81 - - [10/Dec/2022 04:01:00] "GET / HTTP/1.1" 200 -
200
192.168.26.81 - - [10/Dec/2022 04:01:02] "GET / HTTP/1.1" 200 -
200
192.168.26.81 - - [10/Dec/2022 04:01:04] "GET / HTTP/1.1" 200 -
200
192.168.26.81 - - [10/Dec/2022 04:01:06] "GET / HTTP/1.1" 200 -
200
192.168.26.81 - - [10/Dec/2022 04:01:08] "GET / HTTP/1.1" 200 -
200

DNS 解析测试,可以看到 对于没有选择器的服务来讲,同样可以通过 服务名对应的域名来解析到对应的 集群 IP 地址,这与 有选择器的相同。

┌──[[email protected]]-[~/ansible]
└─$kubectl run pod-test -it --rm --image=yauritux/busybox-curl --image-pull-policy=IfNotPresent
If you don't see a command prompt, try pressing enter.
/home # nslookup external-service.awx.svc.cluster.local.
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: external-service.awx.svc.cluster.local.
Address 1: 10.103.93.20 external-service.awx.svc.cluster.local
/home # Session ended, resume using 'kubectl attach pod-test -c pod-test -i -t' command when the pod is running
pod "pod-test" deleted

域名的方式:ExternalName

这里假设 集群外的服务为 我的 个人主页 https://liruilongs.github.io/

创建一个 ExternalName 类型的 SVC,当然也可以设置端口,这里我们不需要。

┌──[[email protected]]-[~/ansible]
└─$cat external-service.yaml
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
type: ExternalName
externalName: liruilongs.github.io

查看详细信息,CLUSTER-IP 为none

┌──[[email protected]]-[~/ansible]
└─$kubectl apply -f external-service.yaml
service/external-service created
┌──[[email protected]]-[~/ansible]
└─$kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
external-service ExternalName <none> liruilongs.github.io <none> 5s
┌──[[email protected]]-[~/ansible]
└─$kubectl describe svc external-service
Name: external-service
Namespace: liruilong-deploy-create
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ExternalName
IP Families: <none>
IP:
IPs: <none>
External Name: liruilongs.github.io
Session Affinity: None
Events: <none>

解析域名测试,可以发现,external-service 经过 k8s 的内部 DNS 记录为 liruilongs.github.io,解析获得的 ipv4和ipv6 完全相同。所以pod 可以通过域名连接到外部服务,而不是使用服务的实际 FQDN

┌──[[email protected]]-[~/ansible]
└─$kubectl run pod-test -it --rm --image=yauritux/busybox-curl --image-pull-policy=IfNotPresent
If you don''t see a command prompt, try pressing enter.
/home # nslookup external-service
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: external-service
Address 1: 2606:50c0:8001::153
Address 2: 2606:50c0:8003::153
Address 3: 2606:50c0:8000::153
Address 4: 2606:50c0:8002::153
Address 5: 185.199.111.153 cdn-185-199-111-153.github.com
Address 6: 185.199.109.153 cdn-185-199-109-153.github.com
Address 7: 185.199.110.153 cdn-185-199-110-153.github.com
Address 8: 185.199.108.153 cdn-185-199-108-153.github.com
/home # nslookup liruilongs.github.io
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: liruilongs.github.io
Address 1: 2606:50c0:8003::153
Address 2: 2606:50c0:8001::153
Address 3: 2606:50c0:8000::153
Address 4: 2606:50c0:8002::153
Address 5: 185.199.111.153 cdn-185-199-111-153.github.com
Address 6: 185.199.110.153 cdn-185-199-110-153.github.com
Address 7: 185.199.108.153 cdn-185-199-108-153.github.com
Address 8: 185.199.109.153 cdn-185-199-109-153.github.com

ExternalName 服务仅在 DNS 级别实现,为该服务创建一个简单的 CNAME DNS 记录。因此,连接到服务的客户端将直接连接到外部服务,完全绕过服务代理。出于这个原因,这些类型的服务甚至没有获得集群 IP。所以对于域名的解析,实际上是依赖于 节点机器。


https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/

https://stackoverflow.com/questions/74795408/clean-way-to-connect-to-services-running-on-the-same-host-as-the-kubernetes-clus

《Kubernetes 实战》


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK