学练结合,快速掌握 Kubernetes Service
source link: https://mp.weixin.qq.com/s/VJIwipm5lR62uAPVkiUXWQ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
今天这篇文章里我们来讲一下 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叨」第一时间获取我的文章推送。
参考资料
Service, DNS与服务发现: https://time.geekbang.org/column/article/68636
深入剖析Kubernetes Service: https://time.geekbang.org/column/article/68636
- END -
关注公众号,获取更多精选技术原创文章
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK