Kubernetes 系列文章:

  1. 从 RunC 聊起
  2. Kubernetes 介绍与安装
  3. Pod
  4. Service

从第三节:的示例中,已经运行起来了一个 Nginx 的 Pod,处于好奇,当运行起来之后及时看到状态是 Runing 的,也会想尝试用浏览器访问试试看是否真的运行了。但是,事情就是这么尴尬,刚打开浏览器,然后就发现不知道该访问什么地址,第三篇文章里面也没说啊,假设我告诉你查询 Pod 可以加一个参数,可以看到 IP:

[root@liqiang.io]# kubectl get pods first-pod -o wide
NAME        READY     STATUS    RESTARTS   AGE       IP           NODE
first-pod   1/1       Running   0          14h       172.17.0.2   host-79

这里有一个 IP: 172.17.0.2,然而,当你尝试访问的时候,一般情况下是访问不通的(当然,不排除你网络设置等原因可以访问通),这就很尴尬了啊,我运行的应用不就是想被访问的吗,现在访问不了是几个意思?

这个时候就要聊聊 K8S 对于 Pod 的设计理念了,Pod 是最小的单元,往往当我们运行一个应用的时候,对 Pod 有一些额外的要求,例如高可用或者多实例,这就意味着如果你只记住一个 Pod 的 IP,那么很多时候都是会有问题的,例如高可用,可能会因为 Pod 的重建而改变,这样你记住的那个 IP 就失效了。一个很自然的想法就是,能不能把这个 IP 固定住,这是个好的想法,但是实现的时候有一些可以扩展的地方,例如,高可用类型的 Pod 可以固定 IP,那么我多个实例的 IP 做负载均衡要不要固定 IP 呢?于是乎,就到了这篇文章要介绍的新的资源:Service。

为什么要存在 Service

从前面的描述中,我们已经知道了 Service 存在的意义,总结起来一下亮点:

所以可以用这个图来展示一下 Pod 和 Service 的关系:

Service 操作

按照惯例,要开始介绍一些例子了,承接前面将 Pod 的描述,在 K8S 中,资源通用的字段 Service 都会有,下面就给出一个示例:

[root@liqiang.io]# cat /home/liqiang.io/blog/kubernetes/guide/chap04/00-simple-service.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: first-svc
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 5580
      targetPort: 80
      nodePort: 32280
[root@liqiang.io]# kubectl apply -f 00-simple-service.yaml
service "first-svc" created
[root@liqiang.io]# kubectl get svc first-svc
NAME        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
first-svc   10.110.87.116   <nodes>       5580:32280/TCP   3m

这里有几个地方需要说明一下:

可能你会认为这个时候打开浏览器就可以通过 10.110.87.116 这个 IP 访问到这个 Service 了,但是事实上,一般情况下也是不行的。这个 IP 是集群内部使用的,集群外部也是访问不了的,那么如果你想测试验证一下该怎么办?你需要访问的是 K8S 在的集群的 IP,以及上面这里指定的 32280 端口,也就是说:

[root@liqiang.io]# curl -I 192.168.62.255:32280
HTTP/1.1 200 OK
Server: nginx/1.17.0
Date: Sun, 30 Jun 2019 05:53:09 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 21 May 2019 14:23:57 GMT
Connection: keep-alive
ETag: "5ce409fd-264"
Accept-Ranges: bytes

这样就访问通了这个 Pod,这个 IP 可以是集群中任意一个节点的 IP,都没有关系。

外部访问 Service 的几种方式

这里之所以我们在集群外部能访问到 Service,是因为我指定了 Service 的 Type 是 NodePort,在 K8S 中,如果不指定这个 Type 的话,Service 是只能在内部访问的,集群外部是访问不了的。要想在集群外部访问,就需要特别的设置,这里有几种可以设置的方式:

NodePort 的使用就如示例中介绍的那样就可以访问了。LoadBalancer 一般也不在私有云中使用,所以比较难以尝试,就不介绍了。最后一种就是 Ingress,在一般的尝试中,我们都是通过 Nginx-Ingress 的方式进行介绍,这里因为入门的缘故,也就不介绍得那么深了,可以理解成我们将以前写 Nginx 的配置的方式转化成写 K8S 资源配置的形式来处理就可以了。

内部访问 Service

那么在我们集群内部又要如何访问 Service 呢?例如我设置了一个名字叫做 MySQL 的 Service,在我的一个应用 service-blog 中要怎么访问 MySQL?是指定 Service 的 IP 的形式吗?但是在开发的时候我都不知道部署在哪个集群,这个集群里面 MySQL 的 service ip 是什么,这就没法写了。

K8S 的设计肯定是考虑到了这种情况,对于 Service 的访问,内部都是通过 DNS 来进行的,也就是说,当你定义了一个 Service 之后,K8S 就响应得生成了一个 DNS 记录,这个 Service 对应的内部域名就是: <cluster-name>.<namespace>.svc.cluster.local,在没有特别指定 Service 类型的时候,访问这个 A 记录,它返回的是 Service 的 ClusterIP;如果你设置了 Service 的 ClusterIP 为 None,那么返回的就是所有被代理的 Pod 的 IP 地址的集合。

小结

在这篇文章中,我介绍了什么是一个 Service 以及 Service 存在的意义,还有 Service 是怎么和 Pod 联系起来并且对外提供服务的。但是,还有一些内容没有提及,这个都不能简单的属于入门内容,后面有入门之后的进阶系列,你可以多关注了解一下。