前言
在Kubernetes中,服务和Pod的IP地址仅可以在集群网络内部使用,对于集群外的应用是不可见的。为了使外部的应用能够访问集群内的服务,在Kubernetes 目前 提供了以下几种方案:
- NodePort,基于 k8s service 实现
- Ingress
- 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集群中的容器服务了
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节点机器能访问通)。
3.2 继续验证该代理能否完成负载均衡工作
首先我们先看一下RC副本构建的Pod
看一下两个Pod分别被分配到了slave1、slave2节点
在Master节点,分别监控两个Pod的日志:
kubectl logs -f nginx-85w5x -n esign-dev -c nginx
kubectl logs -f nginx-pfthm -n esign-dev -c nginx
我们分别监控这两个Pod内部Nginx容器的日志。发现了一个很神奇的问题:
- 这个地址从K8s Master机器上访问不通。我必须切换到 Node1、Node2节点上才能访问到。这是因为master节点没有部署 kube-proxy 的原因吗?
- Pod(nginx-85w5x)构建在节点为slave1的机器上,我在 slave1 上执行: curl 10.254.72.42,哪怕访问100次,请求也全部转发到该机器的Pod的Nginx。
- Pod(nginx-pfthm)被构建在点为slave2的机器上,我在 slave2 上访问也只被转发到本机的 Pod。并没有预期中的随机分发。