今天给大家介绍下k8s的service的几种访问模式。
概述
我们知道pod的ip不是固定的,是根据所在宿主机的docker0网卡生成的,每次重启,更新,调度等情况IP都会变,那pod与pod之间需要互相调用,肯定不能用ip的,因为地址不是固定的, 如何能保障pod之前访问的可靠性,由此就衍生出Service的概念。
在实际生产环境中,一般有两种访问 对集群内部的访问, 集群外部的访问。service现在分为以下类型
ClusterIP
集群内部容器访问地址,会生成一个虚拟IP 与pod不在一个网段。
NodePort
会在宿主机上映射一个端口,供外部应用访问模式。
Headless CluserIP
无头模式,无serviceip,即把spec.clusterip设置为None 。
LoadBalancer
使用外部负载均衡。
Port类型
我们先理解Service Port的几种类型。
NodePort
指定暴露到宿主机的端口,不指定的话会随机分配个,分配的IP在apiserver的配置文件中指定了--service-node-port-range=30000-50000,表示只允许分配30000-50000之间的端口。
比如一个nginx应用需要能被外部访问,就需要配置类型为type=NodePort,并且需要配置下nodePort: 30002(指定固定端口),这样的话外部使用http://ip:30002就可以访问这个应用了。
也有一些内部服务是需要外部访问的,那就不需要到使用NodePort模式了。
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx01
name: nginx
spec:
ports:
port: 80 服务端口
protocol: TCP
targetPort: 80 容器暴露端口
nodePort: 30002 nodeport开放端口
selector:
app: nginx01
type: NodePort 类型为NodePort ,外部可以访问这个应用
Port
集群内部服务之间访问的端口。
比如一个nginx容器暴露了80端口,但是其他容器需要通过nginx:80访问,就需要配置port:80 ,外部是没法访问这个端口的,因为没有对外开放端口。
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx01
name: nginx
spec:
ports:
- port: 80 服务端口
protocol: TCP 服务支持tcp和UDP,但是默认的是TCP
targetPort: 80 容器暴露端口
selector:
app: nginx01
type: ClusterIP 类型为ClusterIP ,集群类别访问
targetPort
容器本身暴露的端口,和dockerfile中的expose意思一样
例子说明
接下来我们通过几个例子来理解说明
创建NodePort类型Service
如果选择了“NodePort”,那么 Kubernetes master 会分配一个区域范围内,(默认是30000-32767),并且,每一个node,都会代理(proxy)这个端口到你的服务中,我们可以在spec.ports[*].nodePort 找到具体的值
如果我们指定一个端口,我们可以直接写在nodePort上,系统就会给你指派指定端口,但是这个值必须是指定范围内的。
Cluster service 的 IP 地址是虚拟的,因此,只能从node节点上使用该IP 地址访问应用。为了从集群外访问应用,K8S 提供了使用 node 节点的IP 地址访问应用的方式。
基本上,NodePort 服务与普通的 “ClusterIP” 服务 YAML 定义有两点区别。 首先,type 是 “NodePort”。还有一个称为 nodePort 的附加端口,指定在节点上打开哪个端口。 如果你不指定这个端口,它会选择一个随机端口。
该端口号的范围是 kube-apiserver 的启动参数 –service-node-port-range指定的,在当前测试环境中其值是 30000-50000。表示只允许分配30000-50000之间的端口。
ps:一般使用NodePort 都会在外部搭建负载均衡来代理多个node节点。
创建一个Deployment
[root@master-01 ~]# cat nginx-deploy.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
null :
:
nginx-deploy :
nginx-deploy :
spec:
2 :
:
:
nginx-pod :
:
:
:
nginx-pod :
:
:
nginx :
Always :
nginx :
Always :
[root@master-01 ~]# kubectl apply -f nginx-deployment.yaml
created
创建service
[root@master-01 ~]# cat ng-svc.yaml
apiVersion: v1
kind: Service
metadata:
:
nginx :
nginx :
spec:
:
80 :
TCP :
80 :
30002 :
:
nginx-pod :
NodePort :
#创建
[root@master-01 ~]# kubectl create -f ng-svc.yaml
created
查看详情
[root@master-01 ~]# kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: app=nginx
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx","namespace":"default"},"spec":{"p...
Selector: app=nginx-pod
Type: NodePort 访问类型
IP: 10.254.192.90 ClusterIP
Port: <unset> 80/TCP ClusterIP 端口
TargetPort: 80/TCP 容器暴露的端口
NodePort: <unset> 30002/TCP NodePort附加端口
Endpoints: 172.17.37.3:80,172.17.64.2:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
查看状态
[root@master-01 ~]# kubectl get po,ep,svc
NAME READY STATUS RESTARTS AGE
pod/nginx-deploy-b47575595-khxsp 1/1 Running 0 96s
pod/nginx-deploy-b47575595-lwnk6 1/1 Running 0 96s
NAME ENDPOINTS AGE
80,172.17.64.2:80 4m49s :
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
NodePort 10.254.192.90 <none> 80:30002/TCP 4m49s
访问测试
# 通过endpoint 访问
[root@master-01 ~]# curl 172.17.64.2:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
。。。
# 通过service访问
[root@master-01 ~]# curl 10.254.192.90
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
。。。
# 通过nodePort访问
[root@master-01 ~]# curl 192.168.209.130:30002
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
。。。
在pod中也可以通过service的名称访问(一般都这样使用)
创建ClusterIP类型Service
会生成一个集群内部的虚拟IP(网段和pod不同)只是给集群内部和pod之间访问的,外部无法访问,网段通过配置文件指定。
ClusterIP也是Kubernetes service的默认类型。
原理
用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求以后将数据存储到etcd中。
每个节点中都有一个叫做kube-proxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables中。
iptables 使用NAT等技术将virtualIP的流量转至endpoint中。
创建pod
~]# cat nginx-deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
labels:
run: nginx-deploy
name: nginx-deploy
spec:
replicas: 2
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
image: nginx
imagePullPolicy: Always
name: nginx
Always :
创建Service
[root@master-01 ~]# cat ng-svc-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx01
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-pod
type: ClusterIP
[root@master-01 ~]# kubectl create -f ng-svc-clusterip.yaml
service/nginx01 created
[root@master-01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx01 ClusterIP 10.254.218.218 <none> 80/TCP 13s
[root@master-01 ~]# curl 10.254.218.218
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
。。。
创建HeadlessClusterIP类型Service
有时候我们可能不需要一个固定的IP和分发,这个时候我们只需要将spec.clusterIP的值设置为none就可以了,通过设置标签绑定到pod的方式完成,对于这样的服务来说,集群IP没有分配,这个时候当你查询服务的名称的时候,DNS会返回多个A记录,这些记录都是指向后端Pod的。Kube-proxy代理不会处理这个服务,在服务的前端也没有负载均衡器。但是endpoints controller还是会创建Endpoints,在访问服务的时候返回后端的全部的Pod IP地址。
创建pod
[root@master-01 ~]# cat busybox-deploy.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
null :
:
busybox-deploy :
busybox-deploy :
spec:
1 :
:
:
busybox-pod :
:
:
:
busybox-pod :
:
:
infoblox/dnstools :
Always :
busybox :
Always :
[root@master-01 ~]# kubectl apply -f busybox-deploy.yaml
创建service
[root@master-01 ~]# cat busybox-svc-headless.yaml
apiVersion: v1
kind: Service
metadata:
:
busybox-svc :
busybox-svc :
spec:
:
80 :
TCP :
80 :
:
busybox-pod :
None 设置为None :
ClusterIP 类型 :
[root@master-01 ~]# kubectl apply -f busybox-svc-headless.yaml
查看状态
可以看到CLUSTER-IP 为None了,
[root@master-01 ~]# kubectl get svc,ep,po
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/busybox-svc ClusterIP None <none> 80/TCP 28m
NAME ENDPOINTS AGE
endpoints/busybox-svc 172.17.96.3:80 28m
NAME READY STATUS RESTARTS AGE
pod/busybox-deploy-fb6b4d77f-65cr8 1/1 Running 0 2m28s
[root@master-01 ~]# kubectl describe svc busybox-svc
Name: busybox-svc
Namespace: default
Labels: app=busybox-svc
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"busybox-svc"},"name":"busybox-svc","namespace":"default"...
Selector: app=busybox-pod
Type: ClusterIP
IP: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 172.17.96.3:80 容器ip
Session Affinity: None
Events: <none>
以上可以看出后端的pod列表已经加到该svc。
我们进容器中通过service名字是否能够解析访问到pod
查看dns域
root@busybox-deploy-b47575595-khxsp:/# cat /etc/resolv.conf
nameserver 10.254.0.10
search default.svc.cluster.local. svc.cluster.local. cluster.local.
options dots:5
root@busybox-deploy-b47575595-khxsp:/# nslookup busybox-svc
Server: 10.254.0.10
Address: 10.254.0.10#53
Name: busybox-svc.default.svc.cluster.local
Address: 172.17.96.3
指定dns服务器并查询该域名:`busybox-svc.default.svc.cluster.local`
root@busybox-deploy-b47575595-khxsp:/# dig @10.254.0.10 busybox-svc.default.svc.cluster.local
; <<9.10.3-P4-Debian <<>> @10.254.0.10 busybox-svc.default.svc.cluster.local DiG
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -opcode: QUERY, status: NOERROR, id: 32431 HEADER<<-
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;busybox-svc.default.svc.cluster.local. IN A
;; ANSWER SECTION:
busybox-svc.default.svc.cluster.local. 5 IN A 172.17.96.3
;; Query time: 3 msec
;; SERVER: 10.254.0.10#53(10.254.0.10)
;; WHEN: Sat Mar 23 07:39:06 UTC 2019
;; MSG SIZE rcvd: 119
可以看到以上的A记录,解析出的是pod的ip地址
1.普通 Service:解析成 ClusterIP
2.Headless Service:解析为指定 Pod的IP列表,Serivce域名也起到了通过dns做负载的能力。
往期文章一览
2、Kubernetes集群搭建之企业级环境中基于Harbor搭建自己的私有仓库
4、Kubernetes集群搭建之CNI-Flanneld部署篇
如果您觉得不错,请别忘了转发、分享、点赞让更多的人去学习, 您的举手之劳,就是对小编最好的支持,非常感谢!