学练结合,快速掌握 Kubernetes Service

微信扫一扫,分享到朋友圈

学练结合,快速掌握 Kubernetes Service

今天这篇文章里我们来讲一下 Kubernetes
里的 Service
对象。其实前面的文章《 Kubernetes初体验–部署运行Go项目
》里我们已经与 Service
有过一次短暂接触了,在那篇文章里我说用 Deployment
对象部署完应用后还需要向外界暴露入口才能通过HTTP访问到 Kubernetes
集群里的应用 Pod
,当时使用的是这样一条命令,其实就是创建的 Service
对象:

kubectl expose deployment my-go-app --type=NodePort ...

那么在这篇文章里我们就来聊一下:

  • 什么是 Service
    对象,在 Kubernetes
    里它是干什么用的;
  • Kubernetes
    里怎么发现 Service
  • 如何创建和使用 Service
  • nodePort,port,targetPort都是啥;

文章前面半部分理论知识多一点,稍显枯燥,后半部分会用一个实践练习给之前用 Deployment
部署好的应用 Pod
们加上 Service
,让外部请求能访问到 Kubernetes
集群里的应用,并为 Pod
提供负载均衡。

Kubernetes Service

和之前文章里介绍的Pod, ReplicaSet
,Deployment一样, Service
也是 Kubernetes
里的一个API对象,而 Kubernetes
之所以需要 Service
,一方面是因为 Pod
的 IP 不是固定的,另一方面则是因为一组 Pod
实例需要 Service
提供复杂均衡功能。
所以 Service
是在逻辑抽象层上定义了一组 Pod
,为他们提供一个统一的固定IP和访问这组 Pod
的负载均衡策略

下面是 Service
对象的常用属性设置:

  • 使用label selector,在集群中查找目标 Pod
    ;
  • ClusterIP设置Service的集群内IP让 kube-proxy
    使用;
  • 通过prot和targetPort将访问端口与目标端口建议映射(不指定targetPort时默认值和port设置的值一样);

  • Service支持多个端口映射

  • Service支持HTTP(默认),TCP和UDP协议;

下面是一个典型的 Service
定义:


apiVersion: v1
kind: Service
metadata:
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80
targetPort: 9376

都有哪些类型的Service

Kubernetes
中有四种Service类型:

  • ClusterIP
    。这是默认的Service类型,会将Service对象通过一个内部IP暴露给集群内部,这种类型的Service只能够在集群内部使用 <ClusterIP>:<port>
    访问。
  • NodePort
    。会在每个宿主机节点的一个指定的固定端口上暴露Service,与此同时还会自动创建一个ClusterIP类型的Service,NodePort类型的Service会将集群外部的请求路由给ClusterIP类型的Service。你可以使用 <NodeIP>:<NodePort>
    访问NodePort类型的Service,NodePort的端口范围为30000-32767。
  • LoadBalancer
    。适用于公有云上的 Kubernetes
    服务,使用公有云服务的 CloudProvider
    创建 LoadBalancer
    类型的 Service
    ,同时会自动创建 NodePort
    ClusterIP
    类型的 Service
    LoadBalancer
    会把请求路由到 NodePort
    ClusterIP
    类型的 Service
    上。
  • ExternalName。ExternalName 类型的 Service,是在 kube-dns 里添加了一条 CNAME 记录。这个CNAME记录是在Service的spec.externalName里指定的,

以上四种类型除了 ExternalName
Kubernetes
kube-proxy
组件都会为 Service
提供VIP(虚拟IP), kube-proxy
支持两种模式: iptables
ipvs
。涉及到不少知识,感兴趣的可以去极客时间上看这篇文章: Service, DNS与服务发现
[1]

上面的第三和第四种类型的 Service
在本地试验不了,所以后面的例子我们主要通过 NodePort
类型的 Service
学习它的基本用法。

怎么发现Service

Kubernetes
里的内部组件 kube-dns
会监控Kubernetes API,当有新的 Service
对象被创建出来后, kube-dns
会为 Service
对象添加DNS A记录(从域名解析 IP 的记录)

对于 ClusterIP
模式的 Service
来说,它的 A 记录的格式是:

serviceName.namespace.svc.cluster.local,当你访问这条 A 记录的时候,它解析到的就是该 Service 的 VIP 地址。

对于指定了 clusterIP=None 的 Headless Service来说,它的A记录的格式跟上面一样,但是访问记录后返回的是Pod的IP地址集合。Pod 也会被分配对应的 DNS A 记录,格式为: podName.serviceName.namesapce.svc.cluster.local

我们会在后面的实践练习里通过 nslookup
印证DNS记录是否符合这里说的格式

创建和使用Service

跟其他 Kubernetes
里的API对象, Service
也是通过 YAML
文件定义然后提交给 Kubernetes
后由 ApiManager
创建完成。一个典型的 NodePort
类型的 Service
的定义如下所示:

apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
type: NodePort
selector:
app: go-app
ports:
- name: http
protocol: TCP
nodePort: 30080
port: 80
targetPort: 3000

这里定义的Service对象会去管控我们在之前的文章《 K8s上的Go服务怎么扩容、发版更新、回滚、平滑重启?教你用Deployment全搞定!
》里用 Deployment
创建的 Go
应用的三个 Pod
副本。

➜ kubectl get pods -l app=go-app 
NAME READY STATUS RESTARTS AGE
my-go-app-864496b67b-6hm7r 1/1 Running 1 16d
my-go-app-864496b67b-d87kl 1/1 Running 1 16d
my-go-app-864496b67b-qxrsr 1/1 Running 1 16d

我们用 kubectl apply -f service.yaml
命令把定义好的 Service
提交给 Kubernetes

➜ kubectl apply -f service.yaml 
service/app-service created

Service
selector
选中的 Pod
,就称为 Service
Endpoints
,可以使用 kubectl get ep
命令看到它们,如下所示:

➜  kubectl get ep app-service
NAME ENDPOINTS AGE
app-service 172.17.0.6:3000,172.17.0.7:3000,172.17.0.8:3000 8m38s

需要注意的是,只有处于 Running
状态,且readinessProbe 检查通过的 Pod
,才会出现在 Service
Endpoints
列表里。当某一个 Pod
出现问题时, Kubernetes
会自动把它从 Service
里摘除掉。

使用 kubectl get svc
可以查看到刚才看到的 Service
的信息和状态。

➜ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
app-service NodePort 10.108.26.155 <none> 80:30080/TCP 116m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 89d

nodePort 、port、targetPort都是啥

上面我们创建了一个 NodePort
类型的 Service
,在下面的端口映射 spec.ports
配置里,每个端口映射里出现了三种 port
:nodePort、port、targetPort。那这三种 port
都代表的什么意思呢?

  • port
    :指定在集群内部暴露 Service
    所使用的端口,集群内部使用 <ClusterIP>:<port>
    访问 Service
    EndPoints
    Service
    选中的Pod)。
  • nodePort
    :指定向集群外部暴露 Service
    所使用的端口,从集群外部使用 <NodeIp>:<NodePort>
    访问 Service
    EndPoints
    。如果你不显式地声明 nodePort
    字段,会随机分配可用端口来设置代理。这个端口的范围默认是 30000-32767。
  • targetPort
    targetPort
    是后面的 Pod
    监听的端口,容器里的应用也应该监听这个端口, Service
    会把请求发送到这个端口。

所以结合刚才我们创建的app-service这个 Service
的信息,在集群内部使用 10.108.26.155:80
访问 Pod
里的应用。因为我们试验使用的 minikube
是个单节点的集群, NodeIP
可以通过 minikube ip
命令获得。

➜ minikube ip

192.168.64.4

所以从集群外部,通过 192.168.64.4:30080
访问 Pod
里的应用。

➜ curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-mlqnh% ➜ curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-4x8p8% ➜ curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-vt7dx%

通过多次访问,我们可以看到请求会通过 Service
发给不同的应用 Pod
Pod
里的应用就是在原来的文章里一直使用的例子的基础上加了一行获取系统 Hostname
的代码:

...

func index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World")
hostname, _ := os.Hostname()
fmt.Fprintf(w, "Hostname: %s", hostname)
}

...

最后我们进到 Pod
里看一下 Service
创建后 kube-dns
组件在集群里为 app-service
这个 Service
对象创建的DNS A记录,因为 Service
定义里指定的名字是 app-service
,命名空间的话因为没有指定就是默认的 default
命名空间,所以我们使用 nslookup app-service.default.svc.cluster.local
查看一下这条 DNS
记录,进入到其中一个 Pod
里,执行上述查询的结果如下:

nslookup app-service.default.svc.cluster.local

Server: 10.96.0.10
Address: 10.96.0.10:53

Name: app-service.default.svc.cluster.local
Address: 10.108.26.155

对于 Service
EndPoints
也是有 DNS
记录的,因为不是 Headless Service
,所以需要用nslookup *.app-service.default.svc.cluster.local查询 DNS
记录。

nslookup *.app-service.default.svc.cluster.local

Server: 10.96.0.10
Address: 10.96.0.10:53

Name: *.app-service.default.svc.cluster.local
Address: 172.17.0.8
Name: *.app-service.default.svc.cluster.local
Address: 172.17.0.6
Name: *.app-service.default.svc.cluster.local
Address: 172.17.0.7

上面查询出来三条 DNS
记录,正好跟 Service
管控的 Pod
数量能够对上。

总结

今天的文章里我结合实例讲述了 Kubernetes
Service
对象的基本使用方法和对象本身的一些原理,其实需要计算机网络知识掌握的好才能从更深层次了解各种模式的 Service
的实现原理,这方面的内容推荐极客时间里的专栏文章 深入剖析Kubernetes Service
[2]

到这里如果你认真看了我写的关于Kubernetes的这几篇文章,再回看我之前的文章 Kubernetes入门实践–部署运行Go项目
,就会觉得文章里的例子很好理解了。 Kubernetes
的确是学习曲线比较陡峭,我也是在边学边练。希望我的这些入门文章能帮助到想学 Kubernetes
的后端程序员们,大家一起进步。

看到这里了,如果喜欢我的文章可以帮我点个赞,我会每周通过技术文章分享我的所学所见,感谢你的支持。微信搜索关注公众号「网管叨bi叨」第一时间获取我的文章推送。

参考资料

[1]

Service, DNS与服务发现: https://time.geekbang.org/column/article/68636

[2]

深入剖析Kubernetes Service: https://time.geekbang.org/column/article/68636

– END –

关注公众号,获取更多精选技术原创文章

微信扫一扫,分享到朋友圈

学练结合,快速掌握 Kubernetes Service

法官建议苹果Epic案交由陪审团审理 预计明年7月开庭

上一篇

iPhone12 Pro Max可能是今年唯一配备120Hz显示屏与LiDAR的机型

下一篇

你也可能喜欢

学练结合,快速掌握 Kubernetes Service

长按储存图像,分享给朋友