前言

在Kubernetes中,服务和Pod的IP地址仅可以在集群网络内部使用,对于集群外的应用是不可见的。为了使外部的应用能够访问集群内的服务,在Kubernetes 目前 提供了以下几种方案:

  1. NodePort,基于 k8s service 实现
  2. Ingress
  3. LoadBalancer

K8s Service 由来

每个 Pod 都有自己的 IP 地址。当 controller 用新 Pod 替代发生故障的 Pod 时,新 Pod 会分配到新的 IP 地址。这样就产生了一个问题:如果一组 Pod 对外提供服务(比如 HTTP),它们的 IP 很有可能发生变化,那么客户端如何找到并访问这个服务呢?Kubernetes 给出的解决方案是 Service。Kubernetes Service 从逻辑上代表了一组 Pod,具体是哪些 Pod 则是由 label 来挑选。Service 有自己 IP,而且这个 IP 是不变的。Kubernetes 则负责建立和维护 Service 与 Pod 的映射关系。无论后端 Pod 如何变化,对客户端不会有任何影响,因为 Service 没有变。

Service是K8S中最核心的资源对象之一,就是用于解决上面提到的问题。一旦Service被创建,K8S会为其分配一个集群内唯一的IP,叫做ClusterIP,而且在Service的整个生命周期中,ClusterIP不会发生变更,这样一来,就可以用与Docker Swarm类似的操作,建立一个ClusterIP到服务名的DNS域名映射即可。值得注意的是,ClusterIP是一个虚拟的IP地址,无法被Ping,仅仅只限于在K8S的集群内使用。而Service对客户端,屏蔽了底层Pod的寻址的过程。并且由kube-proxy进程将对Service的请求转发到具体的Pod上,具体到哪一个,由具体的调度算法决定。这样以来,就实现了负载均衡。

 

接下来开始构建 K8s Service

 

1. 构建多副本的Pod服务

 

1.1 创建rc-ng.yaml,添加如下内容:

apiVersion: v1
kind: ReplicationController
metadata:       #设置rc的元数据
  name: nginx
  namespace: esign-dev      #指定rc的命名工作
spec:   #设置rc的具体规格
  replicas: 2   #设置Pod的副本数量
  selector:     #通过selector来匹配相应的Pod的label
    app: esign-dev-ng
  template:     #设置Pod的模板
    metadata:
      labels:
        app: esign-dev-ng       #以命名空间+服务名,给服务添加标签。rc副本管理、service服务代理都根据命名空间和标签来寻找需要其管理的Pod
      namespace: esign-dev      #指定Pod的命名空间
    spec:
      containers:
      - name: nginx
        image: 192.168.56.104:5000/nginx
        ports:
        - containerPort: 80     #指定Pod内部访问容器的端口

1.2 启动服务

[root@master k8s]# kubectl create -f rc-ng.yaml 
replicationcontroller "nginx" created

 

2. 构建 Service 服务

 

2.1 创建k8s-service.yaml,添加如下配置

apiVersion: v1
kind: Service           #定义资源类型为Service
metadata:               #定义Service元信息
  name: nginx-service      #名称
  namespace: esign-dev     #工作空间
  labels:
    app: nginx-service     #定义标签
spec:                   #定义工作内容
  selector:
    app: esign-dev-ng      #在service命名空间中,按标签去过滤去所需要代理的服务。
  ports:
  - port: 80            #配合service的cluster IP使用的端口
    targetPort: 80      #要代理的目标Pod内部容器的端口
    nodePort: 30001     #分配给Node节点可以对外提供访问的端口
    protocol: TCP       #TCP,UDP。需要大写

2.2 启动服务

[root@master k8s]# kubectl apply -f k8s-service.yaml 
service "nginx-service" created

2.3 查看Service服务,它会生成一个Service的集群IP。通过这个IP就能访问到它代理的Pod集群中的容器服务了

k8s 保证statefulset ip不变 k8s service 固定ip_Pod

2.4 查看Service详情,请注意这个IP

[root@master k8s]# kubectl describe svc nginx-service -n esign-dev
Name:                   nginx-service
Namespace:              esign-dev
Labels:                 app=nginx-service
Selector:               app=esign-dev-ng
Type:                   ClusterIP
IP:                     10.254.72.42
Port:                   <unset> 80/TCP
Endpoints:              10.8.82.2:80,10.8.82.2:80
Session Affinity:       None
No events.
[root@master k8s]#

 

3. 验证Service

 

3.1 测试代理效果,它生成的这个地址,和主机及虚拟机的网段不一样。所以不能从浏览器访问了。我们在虚拟机内部来访问(master访问不通,只能从Node节点机器能访问通)。

k8s 保证statefulset ip不变 k8s service 固定ip_IP_02

 

3.2 继续验证该代理能否完成负载均衡工作

首先我们先看一下RC副本构建的Pod

k8s 保证statefulset ip不变 k8s service 固定ip_IP_03

看一下两个Pod分别被分配到了slave1、slave2节点

k8s 保证statefulset ip不变 k8s service 固定ip_kubernetes_04

k8s 保证statefulset ip不变 k8s service 固定ip_kubernetes_05

在Master节点,分别监控两个Pod的日志:

kubectl logs -f nginx-85w5x -n esign-dev -c nginx
kubectl logs -f nginx-pfthm -n esign-dev -c nginx

我们分别监控这两个Pod内部Nginx容器的日志。发现了一个很神奇的问题:

  1. 这个地址从K8s Master机器上访问不通。我必须切换到 Node1、Node2节点上才能访问到。这是因为master节点没有部署 kube-proxy 的原因吗?
  2. Pod(nginx-85w5x)构建在节点为slave1的机器上,我在 slave1 上执行: curl 10.254.72.42,哪怕访问100次,请求也全部转发到该机器的Pod的Nginx。
  3. Pod(nginx-pfthm)被构建在点为slave2的机器上,我在 slave2 上访问也只被转发到本机的 Pod。并没有预期中的随机分发。