TOC

Service资源

    Pod资源在Pod控制器的控制下,可能随时会重启或重构,其实严格意义上来讲Pod是不能被重建的,所谓的重建就是指在原来的基础之上建立一个,但Pod不是这样的,因为在Pod控制器下,一旦这个Pod终止了,我们会发现它和原来的Pod是没有关系的,所以从某种意义上来讲,这是不叫重建的,所以Pod资源存在生命周期,且不可重建,必要时,我们仅能够创建一个替代者,而不是在原来的基础上重构出来的,从某种意义上来讲这叫新建和替代的关系,所以严格意义上来说不是重建,只是一个替代者而已;
    Pod控制器是可以实现规模伸缩的,此前使用的scale命令或者直接去修改deployment控制器的副本数量,就能够调整Pod副本数量的,一旦Pod数量增加或减少了,也会导致Pod在访问时的可用及不可用状态的变动,因此Pod相对于这种动态性在很大程度上会给客户端去访问Pod带来困扰,如果使用Pod的IP直接访问Pod,就可能会出现一会儿有,一会儿没有,所以在必要时我们需要增加一个中间层,这个中间层是固定的,只要你不删除和人为修改,这个中间层我们就称为Service,Service本身通过标签选择器,关联拥有相匹配的Pod对象,Service其实也不是直接关联Pod的,甚至说Service压根就不是关联Pod的,而是关联到端点Endpoint;
Endpoint
    Endpoint是k8s集群中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址,一个Service由一组backend Pod组成。这些Pod通过endpoints暴露出来,结果被POST到一个名称为Service-hello的Endpoint 对象上。 当Pod终止后,它会自动从Endpoint中移除,新的能够匹配上Service Selector的Pod将自动地被添加到Endpoint中。检查该Endpoint,注意到IP地址与创建的Pod是相同的。现在能够从集群中任意节点上使用curl命令请求hello Service<CLUSTER-IP>:<PORT> 。 注意 Service IP 完全是虚拟的,它从来没有走过网络;
    Kubernetes在创建Service时,根据Service的标签选择器(Label Selector)来查找Pod,据此创建与Service同名的EndPoints对象。当Pod的地址发生变化时,EndPoints也随之变化。Service接收到请求时,就能通过EndPoints找到请求转发的目标地址;
    Service不仅可以代理Pod,还可以代理任意其他后端,比如运行在Kubernetes外部Mysql、Oracle等。这是通过定义两个同名的service和endPoints来实现的;
    在实际的生产环境使用中,通过分布式存储来实现的磁盘在mysql这种IO密集性应用中,性能问题会显得非常突出。所以在实际应用中,一般不会把mysql这种应用直接放入kubernetes中管理,而是使用专用的服务器来独立部署。而像web这种无状态应用依然会运行在kubernetes当中,这个时候web服务器要连接kubernetes管理之外的数据库,有两种方式,第一是直接连接数据库所在物理服务器IP,另一种方式就是借助kubernetes的Endpoints直接将外部服务器映射为kubernetes内部的一个服务;
    Endpoint引用了Pod,Endpoint就是IP+端口,是实现实际服务的端点集合,比如指定了一个某一个Pod的IP+端口,这就是一个Pod的Endpoint,因此Service引用的Endpoint,而Endpoint引用了Pod,因此这个时候客户端向Service请求时,Service能够将其代理并调度至后端不同的Endpoint从而把流量转发给这个Endpoint后端引用的IP地址和端口;
    Endpoint后端也不一定是Pod,有的时候,我们的MySQL数据库服务器可能并没有运行在Pod中,而是直接运行在我们的Kubernetes集群之外的主机中,那么这个时候,我们也可以对于这个MySQL做一个端点,让Pod里面服务能直接通过Endpoint访问集群外部的MySQL服务器,这就是为什么集群中加一个中间层的原因,因为被引用的不一定是Pod,也有可能是集群之外其他地方的服务,这就是为什么Service后端是Endpoint的这个中间层,而不是直接的Pod的原因;
Service底层实现
    Service也是一个标准的kubernetes资源,对它的增删改查的管理操作,依然需要借助于ApiServer进行,但是其实每一个Service,对一个kubernetes实际上来讲,它应该是被转换为每一个节点上的Iptables或ipvs规则,每一个节点都有,因此,以某一个节点为例,这个节点上的Pod它是一个Client客户端,它试图访问某一个Service时,这个Service的IP地址一定在存在于这个Client节点所在的内核的netfilter框架的iptables或者ipvs规则上的,所以一旦这个Pod中的进程,对外发出的网络请求,如果目标的某一个Service,那这个目标地址一定会被当前节点上的iptables或者ipvs匹配到,而这个规则,可能是一个DNAT规则,也可能是一个简单的转发,或者代理规则,它未必是一个DNAT,一般而言不会使用DNAT逻辑,因为Pod到Pod是直接可达的,只有必要的时候才会给它做DNAT;
    所以这个时候,我们的内核中的规则,会根据规则中的逻辑,假如它访问的服务的后端有多个Pod,那么这个时候,我们的Service会基于挑选算法或叫调度算法,从这个众多被访问的服务端Pod中挑选出一个Pod出来,并把挑选出来的Endpoint对应的地址,返回给客户端,或者说把流量直接转过去,因此就能使得Client与目标后端Pod直接进行通信了,Service后端有可能是当前集群中的一个Pod,也可能是当前集群之外的具体的服务器提供的服务,不管怎么样,Service会把流量代理带指定的节点上去的,如果就是当前节点上的其他Pod,只不过Service做的仅仅是一个转发而已,如果后端有多个Pod,DNAT虽然有调度功能,但是DNAT支持的调度算法是有限的,我们想实现更强大调度算法应该使用ipvs,因此这儿的规则也可以不是iptables规则,也可以是ipvs规则。
    我们的Service随时有可能会被创建、修改、删除,那也就意味着我们每一个节点上的iptables或ipvs规则随时有可能会发生变化,那这个变动如何及时的反映到谋个节点上呢,那么整个集群中的每一个节点上都会运行一个守护进程,如果使用kubeadm去部署,它就是运行为一个Pod,就是我们的kube-proxy,它会在每一个节点上运行为一个Pod,它是一个DaemonSet控制器所控制的Pod,因此每个节点只能有一个,在kubernetes上如果是使用kubeadm部署集群的话,它甚至于会容忍主节点的污点,所以会在主节点上也运行一个;
    kube-proxy其实就是ApiServer的客户端,运行在每一个节点上的一个守护进程,并告诉ApiServer监听所有Service资源的变动,一旦ApiServer上某一个Service资源发生变动,ApiServer会立即通知到每一个监视到这个Service的所有kube-proxy,而后kube-proxy转换为iptables或ipvs规则,所以支持Service实现的重要组件就是每一个节点上的kube-proxy进程,这个进程监听所有Service资源的变动,实时将其变动转换为节点上的规则;
Service及Proxy Mode
    简单来讲,一个Service对象就是工作节点上的iptables或ipvs规则,用户将到底Service对象IP地址的流量转发至想用的Endpoint对象指向的IP地址和端口之上;
    工作于每个工作节点的kube-proxy组件,通过API Server持续监控着各Service及其关联的Pod对象,并将其创建或变动实时反映至当前工作节点上的响应的iptables或ipvs规则,kube-proxy把请求代理至少相应端点的方式有三种:userspace(用户空间)、iptables、ipvs
iptables
    对于每个Service对象,kube-proxy会创建iptables规则直接捕获到达ClusterIP和Port的流量,并将其重定向至当前的Service对象的后端Pod资源,对于每一个Endpoint对象,Service资源会为其创建iptables规则并关联至挑选的后端Pod资源对象,相对于用户控件模型来说,iptables无需讲流量在用户控件和内核空间来回切换,因此也就更为高效,kube-proxy只负责维护iptables规则链,当客户端访问到我们的Service的时候,当请求报文到达内核的iptables规则链上,一经匹配,立马转发给后端的Pod;

ipvs
    Kubernetes自1.9-alpha起引入ipvs代理模型,且自1.11版本成为默认设置,kube-proxy跟踪API server上Service和Endpoints对象的变动,根据此来调用netlink接口创建ipvs规则,并确保与API server中的变动保持同步;
    它与iptables规则的不同之处仅在于其请求流量的调度功能由ipvs实现,余下的其他功能仍由iptables完成,ipvs支持众多调度算法,例如rr、lc、dh、sh、sed和nq等;

Service类型

    Service其实是有多种类型的,在定义Service资源时,还有个字段叫type,它就是指定Service类型的,支持ClusterIP、ExternalName、NodePort、LoadBalancer,默认为ClusterIP;
ClusterIP
    ClusterIP就是指,给一个Service分配一个当前集群上的位于Service可用网段内的动态地址,如果它是ClusterIP类型,就表示这个服务只可以被集群内机器所访问,脱离集群无法访问,那因此,如果这个Service期望暴露给集群外部的主机访问,那使用ClusterIP的不合适的;
[root@node1 ~]# cat service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ccesvc
spec:
  ports:
  - port: 80 # Service端口
    protocol: TCP
    targetPort: 80 # 后端Pod端口
  selector: # 利用标签选择器来关联后端Pod
    app: cce
        # 我们的Service也不是直接到后端的Pod对象的,而是通过我们的Endpoint,Endpoints表示一个Service对应的所有Pod副本的访问地址,而Endpoints Controller就是负责生成和维护所有Endpoints对象的控制器.它负责监听Service和对应的Pod副本的变化,如果检测到Service被删除,则删除和该Service同名的Endpoints对象。如果检测到新的Service被创建或者修改则根据该Service信息获得相关的Pod列表,然后创建或者更新Service对应的Endpoints对象;
        # 创建一个Service会立马创建一个endpoint,kube-proxy会监控当前主机的Pod的变化,同时也会监控endpoint相关数据,一旦有后端的Pod的label符合endpoint的label就会将其加入endpoint作为后端服务端之一,如果一旦该Pod发生变化会立即剔除;
        # Service关联的是Endpoint,而Endpoint通过IP地址+端口关联Pod;
# 查看当前的Pod
[root@node1 ~]# kubectl get pods --show-labels                             
NAME READY STATUS RESTARTS AGE LABELS
cce-fcd4c8cff-bqvrt 1/1 Running 0 26m app=cce,pod-template-hash=fcd4c8cff
# 查看当前的endpoint
[root@node1 ~]# kubectl get endpoints                                      
NAME ENDPOINTS AGE
ccesvc <none> 4m40s  # 此时ccesvc后端没有Pod
kubernetes 172.16.1.2:6443 11d
# 修改Pod的label让endpoint能关联到
[root@node1 ~]# kubectl label pods cce-fcd4c8cff-bqvrt --overwrite app=ce  
pod/cce-fcd4c8cff-bqvrt labeled
# 重新查看,Pod已经关联至Endpoint
[root@node1 ~]# kubectl get endpoints                                     
NAME ENDPOINTS AGE
ccesvc 10.244.2.4:80 4m46s
kubernetes 172.16.1.2:6443 11d

NodePort
    所谓NodePort是指,对于一个Service来说,它该有的ClusterIP还是有的,但除此之外,我们会在每一个节点上生成一个iptables或ipvs规则,它允许集群外部客户端去访问节点的IP地址和端口,这个节点端口是通过iptables规则进行操作的,比如说节点的80端口映射到clusterIP的80端口,然后由ClusterIP转发到Endpoint继而转发给Pod,所以外部主机通过访问节点的IP和端口就能直接访问到Pod里面的服务了,由于Service的iptables或者ipvs规则是配置的每一个节点上的,所以每一个节点的同一个端口都为NodePort提供服务;
[root@node1 ~]# cat service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ccesvc
spec:
  type: NodePort
  ports:
  - port: 80 # Service端口,NodePort不对丢球ClusterIP原有的特性
    protocol: TCP
    nodePort: 30080 # 只有type为NodePort的时候才能使用nodePort字段,这个字段一般不写,随机
    targetPort: 80 # 后端Pod端口
  selector:
    app: cce
查看创建的Service
[root@node1 ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ccesvc NodePort 10.106.142.19 <none> 80:30080/TCP 2m38s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11d
使用clusterip的方式访问,nodeport不会丢弃clusterip的特性
[root@node1 ~]# curl 10.106.142.19/hostname.html
cce-fcd4c8cff-95sch
使用nodeport的方式访问
[root@node1 ~]# curl 127.0.0.1:30080/hostname.html
cce-fcd4c8cff-95sch
LoadBalancer
    LoadBalancer是kubernetes为NodePort类型引入自动管理的外部负载均衡器,它会向底层cloud provider的API发送请求,由其按需创建,用户也可以手动提供负载均衡器,但它不再属于LoadBalancer类型;
ExternalName
    ExternalName即为外部名称,它将集群外部Service引入集群内供各Pod客户端使用,我们在创建一个Service时,不指定标签选择器,不选Pod,而是直接指明类型为ExternalName,并externalName定义一个域名,这个域名能够被我们的DNS服务器解析为外部的IP地址,而这个外部的IP地址就是外部提供服务的对应的端点,那这个时候,客户端对此服务的访问就都被转给外部的服务了。这种方式是使用名称的方式来引用的;
    Service标签选择器关联Pod的主要作用在于,它能够自动的为关联到的所有Pod创建一个Endpoint资源,那对于ExternalName类型来说,没有标签选择器了,它就不会自动创建Pod,那因此我们可以手动创建一个endpoint,把它的IP地址和端口指向集群之外的主机的IP地址和端口,然后再把这个endpoint作为ExternalName这个Service的引用放,因此所有发送给这个Service的请求都会被转发到手动创建的这个endpoint上面来,从而就调度到了集群外部的主机了;

Service代理

    Service能够正常的把客户请求的流量,代理并调度到后端的资源,但是Service与Pod之间并不存在必然的关系,因为Service和Pod之间间隔着一个空间层Endpoint,而Endpoint在我们使用Service时,它会自动帮我们的生成Endpoint,当然我们也可以手动去管理Endpoint,尤其是在后端资源不是Pod的情况下,Endpoint如果手动创建的话,我们可用尝试让Endpoint的名称和Service一样,使得Service能够关联至相关的Endpoint资源,从而在定义Endpoint时引入非标准的客户端Pod,因此我们就可以把集群外部的服务以手工维护Endpoint的形式,映射进集群内部来,也把它组织为Service;
    同时Service也能够实现会话保持功能,常见的会话保持有三种方式,通常是会话粘性、会话集群、会话服务器,其中Service本身也能支持会话粘性的方式,它可以实现把来自同一客户端IP地址的请求,始终绑定到同一个后端Pod上,需要在我们定义Service的时候加上一个专用属性sessionAffinity,有两个选项,Node为使用调度算法进行调度,不做会话粘性,ClientIP表示使用ClientIP来做会话粘性,和nginx的iphash,lvs的sh算法;
    每一个Service都是有ClusterIP的,ClusterIP也作为Service的访问入口,但是有些场景当中,我们也可以不为Service添加ClusterIP,这种Service叫做无头Service,那么存在ClusterIP的Service时,会在kubernetes的DNS当中将Service Name解析为ClusterIP,一旦没有ClusterIP,会直接将Service Name解析为后端的Pod的IP,而Pod的IP可能不止一个,因此会使用DNS的方式生成多个A记录,实现DNS轮询,这种就叫做无头Service;
    Service即便能够代理,并调度客户端请求,但是它只是一个四层代理,无论是iptables还是ipvs,都是由kube-proxy来实现,所以要想实现ipvs的规则类型,需要手动修改kube-proxy的配置,因为kube-proxy决定了你的节点是映射至iptables还是ipvs规则的,在Kubernetes之上我们配置容器应用,靠的是一种资源,这种资源叫做ConfigMap,我们称为配置映射,我们可以在容器外部改一个配置映射中的配置映射的定义,这个定义能够被自动注入到,相关的Pod中的应用之上,被容器所加载和使用,而当前Kubernetes集群所使用的kube-proxy配置是位于kube-system这个名称空间中(kubectl get configmap -n kube-system),当中的kube-proxy其实就是kube-proxy容器中的应用程序所加载的配置信息,而且这是一种集中配置,我们一旦修改了这个配置文件,对所有的kube-proxy容器都是生效的,因此有一种集中配置或者说配置中心的说法;
手动修改代理方式为ipvs
启用ipvs模块
[root@node1 ~]# cat /etc/sysconfig/modules/ipvs.modules          
#!/bin/bash
ipvs_mods_dir="/usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs"
for mod in $(ls $ipvs_mods_dir | grep -o "^[^.]*"); do
    /sbin/modinfo -F filename $mod &> /dev/null
    if [ $? -eq 0 ]; then
        /sbin/modprobe $mod
    fi
done
修改configmap里面的代理方式mode: "ipvs",还可以在ipvs.scheduler字段定义调度算法,默认是rr;
[root@node1 ~]# kubectl edit configmap -n kube-system kube-proxy
安装ipvsadm控制器管理工具
[root@node1 ~]# yum install -y ipvsadm
因为创建集群时使用的iptables创建的kube-proxy,现手动删除原Pod,会自动重建
[root@node1 ~]# kubectl delete pods -n kube-system -l controller-revision-hash=68594d95c
此时我们的ipvsadm就可以看到数据了
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.0.1:443 rr
  -> 172.16.1.2:6443 Masq 1 0 0         
TCP 10.96.0.10:53 rr
  -> 10.244.1.3:53 Masq 1 0 0         
  -> 10.244.2.3:53 Masq 1 0 0         
TCP 10.96.0.10:9153 rr
  -> 10.244.1.3:9153 Masq 1 0 0         
  -> 10.244.2.3:9153 Masq 1 0 0         
UDP 10.96.0.10:53 rr
  -> 10.244.1.3:53 Masq 1 0 0         
  -> 10.244.2.3:53 Masq 1 0 0   

示例一

    利用deployment控制器创建三个web服务pod,然后自定义Service,利用externalIPs向外部暴露kubernetes集群内部的Pod服务,这种Service属于ClusterIP;
[root@node1 ~]# cat pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    author: caichangen
  name: ccepod
  namespace: ccenamespace
spec:
  replicas: 3
  selector:
    matchLabels:
      podtype: deploy
  template:
    metadata:
      name: ccepod
      namespace: ccenamespace
      labels:
        podtype: deploy
    spec:
      restartPolicy: Always
      containers:
      - image: ikubernetes/myapp:v1
        name: ccecontainers
[root@node1 ~]# cat service.yaml
apiVersion: v1
kind: Service
metadata:
  name: cceservice
  namespace: ccenamespace
spec:
  externalIPs: # externalIPs向外部暴露集群内部的服务的通讯地址,暴露给外部的IP
  - 172.16.1.2
  - 172.16.1.3
  ports:
  - protocol: TCP # 传输协议
    port: 80 # 暴露给外部的端口
    targetPort: 80 # 集群内部Pod的端口
  selector:
    podtype: deploy # 标签选择器,用于选择Pod
[root@node1 ~]# curl -o /dev/null -s -w %{http_code} http://172.16.1.2
200

Endpoint与Service结合

    其实在上面已经讲过,一个Service最终都是需要借助于Endpoint,来找到后端的Pod的,那么接下来就做一个基本的演示,主要是两个方向,第一,通过Endpoint结合Service向外部暴露Pod,虽然直接创建Service就能够实现,那么为了更接近底层,下面就分别来创建Service和Endpoint;
    第二,通过Endpoint结合Service,将外部的服务,或者说安装在宿主机上面的服务,映射进Kubernetes集群,虽然我们可以直接通过宿主机的IP访问,但是为了统一化,容器化,标准化的目的,在此就将外部的服务直接整合进Kubernetes集群,作为Kubernetes集群的一个资源来使用;

注意:因为Service创建会直接创建相应的,同名的Endpoint,所以在此我们需要先创建Endpoint,然后再创建Service;

内部端点集合
    内部端点集合,说白了,就是将所有Pod,融合到一个Endpoint,然后统一由一个Service来向外输出服务接口,为了详细介绍其步骤,下面就进行分段介绍,具体如下;
# 创建两个自主式Pod
[root@node01 ~]# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod1
  labels:
    app: pod1
spec:
  containers:
    - name: pod1
      image: ikubernetes/myapp:v1
      imagePullPolicy: IfNotPresent
  restartPolicy: Always
---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
  labels:
    app: pod2
spec:
  containers:
    - name: pod2
      image: ikubernetes/myapp:v2
      imagePullPolicy: IfNotPresent
  restartPolicy: Always
[root@node01 ~]# kubectl apply -f pod.yaml
[root@node01 ~]# kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP           NODE             NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          24s   172.20.3.2   node03.cce.com   <none>           <none>
pod2   1/1     Running   0          24s   172.20.3.3   node03.cce.com   <none>           <none>
# 分别请求两个Pod,可以看到分别是v1和v2
[root@node02 ~]# curl 172.20.3.2
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@node02 ~]# curl 172.20.3.3
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
# 创建Endpoint,将这两个Pod集合到一个端点
[root@node01 ~]# cat endpoints.yaml 
apiVersion: v1
kind: Endpoints
metadata:
  name: pod
subsets:
  - addresses:
      - ip: 172.20.3.2
      - ip: 172.20.3.3
    ports:
      - port: 80
        protocol: TCP
[root@node01 ~]# kubectl apply -f endpoints.yaml 
[root@node01 ~]# kubectl get endpoints pod 
NAME   ENDPOINTS                     AGE
pod    172.20.3.2:80,172.20.3.3:80   34s
# 上面已经将所有的Pod集合在一个端点了,那么接下来就创建一个Servce,对外提供服务,Service需要与Endpoint同名
[root@node01 ~]# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: pod
spec:
  ports:
    - port: 8080
      protocol: TCP
      targetPort: 80
[root@node01 ~]# kubectl apply -f service.yaml
# 看到下面,可以看出,当我们的Service创建之后,自动关联我们在上面创建的Endpoint
[root@node01 ~]# kubectl describe svc pod 
Name:              pod
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"pod","namespace":"default"},"spec":{"ports":[{"port":8080,"protoc...
Selector:          <none>
Type:              ClusterIP
IP:                10.10.17.77
Port:              <unset>  8080/TCP
TargetPort:        80/TCP
Endpoints:         172.20.3.2:80,172.20.3.3:80
Session Affinity:  None
Events:            <none>
# 测试请求Service的地址
[root@node02 ~]# curl 10.10.17.77:8080
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@node02 ~]# curl 10.10.17.77:8080
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
# 可以发现是v1和v2,到此为止,我们的内部服务已经成功通过Endpoint结合Service进行了服务暴露
外部端点集合
    Kubernetes出世还不久,总的来说,还不是特别的稳定,依旧会出现各种各样的问题,所以在很多时候,我们的比较重要的服务,比如我们的MySQL服务,对于一般没有较强的二次开发能力的企业,是不会将MySQL服务放在Kubernetes之上的,所以在这个时候,我们一般都会是将其部署在Kubernetes集群之外;
    所以在此处,就涉及到外部流量接入Kubernetes集群内部的问题,虽然我们的Pod是可以直接访问到我们的Node IP的,但是不如和我们追求Kubernetes化、标准化的需求,所以在此了,就利用Endpoint和Service将外部流量引入Kubernetes集群内部;
    此案例就直接使用Mariadb来做测试,随便找一台宿主机,然后将其接入Kubernetes集群;
# 在集群之外的主机安装Mariadb用于测试
[root@node04 ~]# yum install -y mariadb-server
[root@node04 ~]# systemctl start mariadb
# 授权任何主机都可以连接
[root@node04 ~]# mysql -e "grant all on *.* to 'root'@'%';"
# 创建Endpoint,将node04上面Maradb加入该Endpoint
[root@node01 ~]# cat endpoints.yaml 
apiVersion: v1
kind: Endpoints
metadata:
  name: mysqlconn
subsets:
  - addresses:
      - ip: 172.16.1.4
    ports:
      - port: 3306
[root@node01 ~]# kubectl apply -f endpoints.yaml
# 创建Service,将上面的Endpoint提供一个固定的访问端点
[root@node01 ~]# cat service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysqlconn
spec:
  clusterIP: 10.10.0.10
  ports:
    - port: 30000
      protocol: TCP
      targetPort: 3306
[root@node01 ~]# kubectl apply -f service.yaml
# 查看创建出来的Service,可以看到我们的ServiceIP,并且我们的连接端口为30000
[root@node01 ~]# kubectl describe svc mysqlconn 
Name:              mysqlconn
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"mysqlconn","namespace":"default"},"spec":{"clusterIP":"10.10.0.10...
Selector:          <none>
Type:              ClusterIP
IP:                10.10.0.10
Port:              <unset>  30000/TCP
TargetPort:        3306/TCP
Endpoints:         172.16.1.4:3306
Session Affinity:  None
Events:            <none>
# 创建一个测试的自主式Pod,用于测试连接
[root@node01 ~]# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mclient
  labels:
    app: mclient
spec:
  nodeSelector:
    kubernetes.io/hostname: node02.cce.com
  containers:
    - name: mclient
      image: widdpim/mysql-client:latest
      imagePullPolicy: IfNotPresent
      command:
        - /bin/sleep
        - '10000000000'
  restartPolicy: Always
[root@node01 ~]# kubectl apply -f pod.yaml
# 进入Pod,测试通过ServiceIP连接外部的Mariadb
[root@node01 ~]# kubectl exec -it mclient -- bash
[root@mclient /]# mysql -h 10.10.0.10 -P 30000
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.5.60-MariaDB MariaDB Server
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
# 测试连接成功

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注