3.2 K8s如何引入外部服务
直接访问ip+port,缺点:如果更换ip,应用需要修改配置文件并重载
ExternalName类型的Service,缺点:无法进行端口映射
定义没有选择器的service+同名的endpoints,缺点:如果更换ip,需要更新endpoints资源配置
针对k8s原生应用,k8s提供了一个简单的Endpoints API,当服务中的一组pod发生更改时,该API就会更新。对于非本机应用程序,Kubernetes提供了一个基于虚拟ip的服务桥接器,服务将重定向到后端pod。
endpoint 是k8s集群中一个资源对象,存储在etcd里面,用来记录一个service对应的所有pod的访问地址。service配置了selector,endpoint controller才会自动创建对应的endpoint对象,否则是不会生产endpoint 对象。services-without-selectors
一个service由一组后端的pod组成,这些后端的pod通过service endpoint暴露出来,如果有一个新的pod创建创建出来,且pod的标签名称(label:pod)跟service里面的标签(label selector 的label)一致会自动加入到service的endpoints 里面,如果pod对象终止后,pod 会自动从edponts 中移除。在集群中任意节点可以使用curl请求service的<CLUSTER-IP>:<PORT>
endpoints: 实际上servce服务后端的pod端点集合。
service不仅可以代理pod,还可以代理任意其它的后端,比如运行在k8s集群外部的服务mysql mysql (如果需要从k8s里面链接外部服务(mysql)需要定义同名的service和endpoint)。这种组合可以理解为是一种静态服务,如果后期需要将有状态服务迁移到k8s里面,则代码不需要任何修改,只需要修改svc即可。
在集群外部托管自己的数据库,例如在阿里云的ECS实例中。如果您在 Kubernetes 内部运行无状态的应用和外部运行一些数据库服务,并且希望未来某个时候您可以将所有服务都移入集群内,但在此之前将是“内外混用”的状态。幸运的是,您可以使用静态 Kubernetes 服务来缓解上述痛点。如此,根本不需要在代码中使用 IP 地址!如果以后 IP 地址发生变化,您可以为endpoints更新 IP 地址,而应用无需进行任何更改。
- 创建service (mysql-service-extenal)
此服务没有 Pod 选择器。此操作将创建一个服务,但它不知道往哪里发送流量。这样一来,您可以手动创建一个将从此服务接收流量的 Endpoints 对象(条件是svc和ep的name是相同的,就会自动关联)。
kind: Service
apiVersion: v1
metadata:
name: mysql
namespace: default
spec:
ports:
- port: 3306
name: mysql
targetPort: 3306
- 创建 endpoint(mysql-endpoint)
service 和endpoint的名称相同, 且在一个命名空间下面
kind: Endpoints
apiVersion: v1
metadata:
name: mysql
namespace: default
subsets:
- addresses:
- ip: 192.168.3.24
ports:
- port: 3306
name: mysql
- 创建静态service
[root@vm-3-30 aa]# kubectl apply -f svc.yaml
service/mysql created
[root@vm-3-30 aa]# kubectl describe svc mysql
Name: mysql
Namespace: default
Labels: <none>
Annotations: Selector: <none>
Type: ClusterIP
IP: 10.43.195.223
Port: mysql 3306/TCP
TargetPort: 3306/TCP
Endpoints: <none> #因为没有标签选择器,此处为none
Session Affinity: None
Events: <none>
[root@vm-3-30 aa]# kubectl apply -f ep.yaml
endpoints/mysql created
[root@vm-3-30 aa]# kubectl get ep mysql
NAME ENDPOINTS AGE
mysql 192.168.3.24:3306 9s
[root@vm-3-30 aa]# kubectl describe svc mysql
Name: mysql
Namespace: default
Labels: <none>
Annotations: Selector: <none>
Type: ClusterIP
IP: 10.43.195.223
Port: mysql 3306/TCP
TargetPort: 3306
Endpoints: 192.168.3.24:3306 #配置同名的endpoints后,svc自动关联ep。
Session Affinity: None
Events: <none>
Kubernetes 将 Endpoints 中定义的所有 IP 地址视为与常规 Kubernetes Pod 一样。
如果您使用的是来自第三方的托管数据库服务,例如阿里云的rds,它们可能会为您提供可用于连接的统一资源标识符 (URI)。这些数据库的连接字符串如下所示:
mongodb://
: @ds149763.mlab.com:49763/devmongodb:// : @ds145868.mlab.com:45868/prodmLab
可以看到上面的连接中提供了动态URI和动态端口。这种情景中。我们可以创建一个 “ExternalName” Kubernetes 服务,此服务为您提供将流量重定向到外部服务的静态 Kubernetes 服务。此服务在内核级别执行简单的 CNAME 重定向,因此对性能的影响非常小。
kind: Service
apiVersion: v1
metadata:
name: mongo
spec:
type: ExternalName
externalName: ds149763.mlab.com #必须是FQDN格式,不能是ip
现在,您可以使用更简化的连接字符串:
mongodb://
: @mongo: /dev
由于 “ExternalName” 使用 CNAME 重定向,因此无法执行端口重映射。对于使用静态端口的服务来说,这可能不成问题,然而本例中使用的是动态端口。这意味着您需要对开发和生产数据库使用不同的连接字符串。但如果您可以获取 IP 地址,就可以执行端口重映射,详见下例。
首先我们对rds的连接字串中的host进行dns解析,获取到期其IP。然后创建一个重新映射rds端口的服务,并为此 IP 地址创建端点。
kind: Service
apiVersion: v1
metadata:
name: mongo
spec:
ports:
– port: 27017
targetPort: 49763
—--
kind: Endpoints
apiVersion: v1
metadata:
name: mongo
subsets:
– addresses:
– ip: 35.188.8.12
ports:
– port: 49763
apiVersion: v1
kind: Service
metadata:
name: "baiducom" # 设置IP(14.215.177.39)和域名(www.baidu.com)错误,不允许使用'.'
spec:
type: ExternalName
externalName: "www.baidu.com" # 设置 IP 14.215.177.39 无效
# 所以 externalName 的意义就是为域名设置一个别名,如上为"www.baidu.com"设置别名"baiducom",以便pod内容器使用
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
labels:
app: busybox
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "curl baiducom"]
上例中,externalName除了设置为真实的域名"www.baidu.com"外,也可以使用svc的域名;可以通过这种方式实现为其他namespace的svc在自己的ns中定义一个svc的别名,实现用访问自己ns中的svc来访问其他ns的中svc。例如:
apiVersion: v1
kind: Service
metadata:
name: "mysql"
namespace: app-ns
spec:
type: ExternalName
externalName: "mysql.db-ns.svc.cluster.local"
db-ns名称空间中有一个服务为:mysql.db-ns.svc.cluster.local
app-ns名称空间中有一个app要访问上面的mysql,可以直接使用mysql -h mysql访问即可,无需使用mysql -h mysql.db-ns的方式。