前言
这里基于whoami
示范服务,部署3个实例,分别一一验证各种类型的k8s service服务范畴。
大致逐一从下面列表逐一验证每种类型的service访问方式:
service name
- 域名解析结果等
cluster-ip
external-ip
一些设定如下:
- 测试环境k8s版本号为
v1.27.3
- k8s集群node节点ip地址段范围:
10.0.1.0/24
- k8s集群自动生成pod网段为
10.43.0.0/24
- 本书所列代码皆可拷贝直接粘贴到终端界面直接运行
首先,部署whoami服务
先部署包含3个实例的whoami
:
# cat << 'eof' | kubectl apply -f -
apiversion: apps/v1
kind: deployment
metadata:
name: whoami
labels:
app: whoami
spec:
replicas: 3
selector:
matchlabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: containous/whoami
ports:
- containerport: 80
name: web
eof
查看一下:
# kubectl get all
name ready status restarts age
pod/whoami-767d459f67-qffqw 1/1 running 0 23m
pod/whoami-767d459f67-xdv9p 1/1 running 0 23m
pod/whoami-767d459f67-gwpgx 1/1 running 0 23m
name ready up-to-date available age
deployment.apps/whoami 3/3 3 3 23m
name desired current ready age
replicaset.apps/whoami-767d459f67 3 3 3 23m
其次,安装busybox进行调试
安装一个包含有curl
的busybox方便后续调试:
kubectl run busybox-curl --image=yauritux/busybox-curl --command -- sleep 3600
另起一个终端,输入下面命令进入:
kubectl exec -ti busybox-curl -n default -- sh
环境准备好之后,下面逐一测试各种类型:
默认cluster ip模式
k8s默认service为cluster ip
模式,面向内部pod以及通过ingress对外提供服务。
下面一张图很清晰解释清楚了port
和targetport
适用情景,port
为service对外输出的端口,targetport
为服务后端pod的端口,两者之间有一个转换:port -> targetport -> containerport
。
创建一个service:
cat << 'eof' | kubectl apply -f -
apiversion: v1
kind: service
metadata:
labels:
name: whoami-clusterip
name: whoami-clusterip
spec:
ports:
- port: 80
targetport: 80
protocol: tcp
selector:
app: whoami
eof
部署后可以查看一下:
name type cluster-ip external-ip port(s) age
service/whoami-clusterip clusterip 10.43.247.74 80/tcp 57s
下面就需要逐一测试了。
域名形式:
# curl whoami-clusterip
hostname: whoami-767d459f67-gwpgx
ip: 127.0.0.1
ip: 10.42.8.35
remoteaddr: 10.42.9.32:35968
get / http/1.1
host: whoami-clusterip
user-agent: curl/7.81.0
accept: */*
cluster ip形式:
# curl 10.43.247.74
hostname: whoami-767d459f67-qffqw
ip: 127.0.0.1
ip: 10.42.3.73
remoteaddr: 10.42.9.32:42398
get / http/1.1
host: 10.43.247.74
user-agent: curl/7.81.0
accept: */*
域名解析,只解析到cluster ip上:
# nslookup whoami-clusterip
server: 10.43.0.10
address: 10.43.0.10:53
name: whoami-clusterip.default.svc.cluster.local
address: 10.43.247.74
external ip模式
原理同cluster ip模式,为指定服务绑定一个额外的一个ip地址。当终端访问该ip地址,将流量一样转发到service。
当访问external ip
,其端口转换过程:port -> targetport -> containerport
。
与默认service相比,端口转换流程没有增加,但好处对外暴露了一个可访问的ip地址,不过可能需要在交换机/路由器层面提供动静态路由支持。
cat << 'eof' | kubectl apply -f -
apiversion: v1
kind: service
metadata:
labels:
name: whoami-externalip
name: whoami-externalip
spec:
ports:
- port: 80
targetport: 80
protocol: tcp
selector:
app: whoami
externalips:
- 10.10.10.10
eof
服务显示如下,绑定了指定的扩展ip地址10.10.10.10
。
# name type cluster-ip external-ip port(s) age
service/whoami-externalip clusterip 10.43.192.118 10.10.10.10 80/tcp 57s
kube-proxy
将在每一个node节点为10.10.10.10
上建立一个转发规则,该ip地址的80
端口将直接转发到对应的后端三个whoami
pod 上。
-a kube-services -d 10.10.10.10/32 -p tcp -m comment --comment "default/whoami-externalip external ip" -m tcp --dport 80 -j kube-ext-qn5hievyupdp6unk
......
-a kube-ext-qn5hievyupdp6unk -j kube-svc-qn5hievyupdp6unk
......
-a kube-svc-qn5hievyupdp6unk ! -s 10.42.0.0/16 -d 10.43.192.118/32 -p tcp -m comment --comment "default/whoami-externalip cluster ip" -m tcp --dport 80 -j kube-mark-masq
-a kube-svc-qn5hievyupdp6unk -m comment --comment "default/whoami-externalip -> 10.42.2.79:80" -m statistic --mode random --probability 0.33333333349 -j kube-sep-jsat6d2kfcsf4ylf
-a kube-svc-qn5hievyupdp6unk -m comment --comment "default/whoami-externalip -> 10.42.3.77:80" -m statistic --mode random --probability 0.50000000000 -j kube-sep-2r66ui3g2ay2imnm
-a kube-svc-qn5hievyupdp6unk -m comment --comment "default/whoami-externalip -> 10.42.8.42:80" -j kube-sep-zhhil2san2g37gcm
访问域名:
# curl whoami-externalip
hostname: whoami-767d459f67-gwpgx
ip: 127.0.0.1
ip: 10.42.8.35
remoteaddr: 10.42.9.32:46746
get / http/1.1
host: whoami-externalip
user-agent: curl/7.81.0
accept: */*
访问clusterip形式:
# curl 10.43.192.118
hostname: whoami-767d459f67-qffqw
ip: 127.0.0.1
ip: 10.42.3.73
remoteaddr: 10.42.9.32:47516
get / http/1.1
host: 10.43.192.118
user-agent: curl/7.81.0
accept: */*
访问暴露的external ip:
# curl 10.10.10.10
hostname: whoami-767d459f67-gwpgx
ip: 127.0.0.1
ip: 10.42.8.35
remoteaddr: 10.42.9.0:38477
get / http/1.1
host: 10.10.10.10
user-agent: curl/7.81.0
accept: */*
域名解析结果只解析到其对应的cluster ip:
# nslookup whoami-externalip
server: 10.43.0.10
address: 10.43.0.10:53
name: whoami-externalip.default.svc.cluster.local
address: 10.43.192.118
nodeport 模式
与cluster ip
相比,多了一个nodeport
,这个nodeport会在k8s所有node节点上都会开放。
这里有一个端口转换过程:nodeport -> port -> targetport -> containerport
,多了一层数据转换过程。
服务定义如下:
cat << 'eof' | kubectl apply -f -
apiversion: v1
kind: service
metadata:
labels:
name: whoami-nodeport
name: whoami-nodeport
spec:
type: nodeport
ports:
- port: 80
targetport: 80
nodeport: 30080
protocol: tcp
selector:
app: whoami
eof
查看一下服务分配地址:
name type cluster-ip external-ip port(s) age
service/whoami-nodeport nodeport 10.43.215.233 80:30080/tcp 57s
访问域名:
# curl whoami-nodeport
hostname: whoami-767d459f67-xdv9p
ip: 127.0.0.1
ip: 10.42.2.75
remoteaddr: 10.42.9.32:36878
get / http/1.1
host: whoami-nodeport
user-agent: curl/7.81.0
accept: */*
测试 cluster ip :
# curl 10.43.215.233
hostname: whoami-767d459f67-qffqw
ip: 127.0.0.1
ip: 10.42.3.73
remoteaddr: 10.42.9.32:40552
get / http/1.1
host: 10.43.215.233
user-agent: curl/7.81.0
accept: */*
因为是在每一个k8s node节点上都会开放一个30080
端口,因此可以这样访问 {node ip}:{nodeport}
,如下node ip地址为10.0.1.11
# curl 10.0.1.11:30080
hostname: whoami-767d459f67-qffqw
ip: 127.0.0.1
ip: 10.42.3.73
remoteaddr: 10.42.1.0:1880
get / http/1.1
host: 10.0.1.11:30080
user-agent: curl/7.81.0
accept: */*
域名还是只解析到对应cluster ip:
# nslookup whoami-nodeport
server: 10.43.0.10
address: 10.43.0.10:53
name: whoami-nodeport.default.svc.cluster.local
address: 10.43.215.233
loadbalancer 模式
loadbalancer
模式,会强制k8s service自动开启nodeport
。
这里有一张图,详细解析数据流向。
服务数据端口转换过程:port -> nodeport -> port -> targetport -> containerport
:
- 与默认
cluster ip
相比,多了两层数据转换过程
- 与
nodeport
相比,对了一层数据转换过程
- 与
externalip
相比,在小流量场景下就没有什么优势了
具体服务定义:
cat << 'eof' | kubectl apply -f -
apiversion: v1
kind: service
metadata:
labels:
name: whoami-clusterip-none
name: whoami-clusterip-none
spec:
clusterip: none
ports:
- port: 80
targetport: 80
protocol: tcp
selector:
app: whoami
eof
查看一下部署结果:
name type cluster-ip external-ip port(s) age
service/whoami-loadbalancer loadbalancer 10.43.63.92 80:30906/tcp 57s
服务域名形式:
# curl whoami-loadbalancer
hostname: whoami-767d459f67-qffqw
ip: 127.0.0.1
ip: 10.42.3.73
remoteaddr: 10.42.9.32:57844
get / http/1.1
host: whoami-loadbalancer
user-agent: curl/7.81.0
accept: */*
测试 cluster-ip
# curl 10.43.63.92
hostname: whoami-767d459f67-xdv9p
ip: 127.0.0.1
ip: 10.42.2.75
remoteaddr: 10.42.9.32:42400
get / http/1.1
host: 10.43.63.92
user-agent: curl/7.81.0
accept: */*
域名解析到cluster ip:
# nslookup whoami-loadbalancer
server: 10.43.0.10
address: 10.43.0.10:53
name: whoami-loadbalancer.default.svc.cluster.local
address: 10.43.63.92
安装loadbalancer
此时whoami-loadbalancer
服务对应的external-ip
为
,我们需要安装一个负载均衡器,可以选择metallb
作为负载均衡器。
# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.11/config/manifests/metallb-native.yaml
稍后分配可用的loadbalaner可分配的地址池:
cat << 'eof' | kubectl apply -f -
apiversion: metallb.io/v1beta1
kind: ipaddresspool
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses:
- 10.0.1.100-10.0.1.200
---
apiversion: metallb.io/v1beta1
kind: l2advertisement
metadata:
name: default
namespace: metallb-system
spec:
ipaddresspools:
- default-pool
eof
等安装完成之后,可以看到服务whoami-loadbalancer
分配的ip地址为 10.0.1.101
:
name type cluster-ip external-ip port(s) age
......
service/whoami-loadbalancer loadbalancer 10.43.63.92 10.0.1.101 80:30906/tcp 27h
......
测试负载均衡ip地址
测试一下:
# curl 10.0.1.101
hostname: whoami-767d459f67-xdv9p
ip: 127.0.0.1
ip: 10.42.2.78
remoteaddr: 10.42.8.0:33658
get / http/1.1
host: 10.0.1.101
user-agent: curl/7.79.1
accept: */*
我们看到该服务分配的端口为80:30906/tcp
,30906
为k8s为该服务自动生成的nodeport类型端口。
可以找任一k8s node节点ip地址测试一下:
# curl 10.0.1.12:30906
hostname: whoami-767d459f67-qffqw
ip: 127.0.0.1
ip: 10.42.3.77
remoteaddr: 10.42.2.0:9717
get / http/1.1
host: 10.0.1.12:30906
user-agent: curl/7.81.0
accept: */*
分析一下路由表,可以分析到该负载均衡的external_ip:80
的打流量到nodeport:30906
上,然后走service对应{pod:80}
流量分发逻辑。
-a kube-nodeports -p tcp -m comment --comment "default/whoami-loadbalancer" -m tcp --dport 30906 -j kube-ext-nbtybeexaczi7dpc
......
-a kube-services -d 10.0.1.101/32 -p tcp -m comment --comment "default/whoami-loadbalancer loadbalancer ip" -m tcp --dport 80 -j kube-ext-nbtybeexaczi7dpc
......
-a kube-ext-nbtybeexaczi7dpc -m comment --comment "masquerade traffic for default/whoami-loadbalancer external destinations" -j kube-mark-masq
-a kube-ext-nbtybeexaczi7dpc -j kube-svc-nbtybeexaczi7dpc
......
-a kube-svc-nbtybeexaczi7dpc ! -s 10.42.0.0/16 -d 10.43.63.92/32 -p tcp -m comment --comment "default/whoami-loadbalancer cluster ip" -m tcp --dport 80 -j kube-mark-masq
-a kube-svc-nbtybeexaczi7dpc -m comment --comment "default/whoami-loadbalancer -> 10.42.2.79:80" -m statistic --mode random --probability 0.33333333349 -j kube-sep-e3k3suynfwt2vice
-a kube-svc-nbtybeexaczi7dpc -m comment --comment "default/whoami-loadbalancer -> 10.42.3.77:80" -m statistic --mode random --probability 0.50000000000 -j kube-sep-hg5myvvid7gjoza7
-a kube-svc-nbtybeexaczi7dpc -m comment --comment "default/whoami-loadbalancer -> 10.42.8.42:80" -j kube-sep-gfjh72ycbkbfb6og
headless 无头模式
一般应用在有状态的服务,或需要终端调用者自己实现负载均衡,等一些特定场景。
通过调用者从端口角度分析,数据转换流程:targetport -> containerport
。
在意服务性能的场景,不妨试试无头模式。
服务定义:
cat << 'eof' | kubectl apply -f -
apiversion: v1
kind: service
metadata:
labels:
name: whoami-clusterip-none
name: whoami-clusterip-none
spec:
clusterip: none
ports:
- port: 80
targetport: 80
protocol: tcp
selector:
app: whoami
eof
查看服务部署情况:
name type cluster-ip external-ip port(s) age
service/whoami-clusterip-none clusterip none 80/tcp 9h
通过service域名访问,k8s会自动根据服务域名whoami-clusterip-none
进行pick后端对应pod ip地址。
# curl whoami-clusterip-none
hostname: whoami-767d459f67-xdv9p
ip: 127.0.0.1
ip: 10.42.2.75
remoteaddr: 10.42.9.32:34998
get / http/1.1
host: whoami-clusterip-none
user-agent: curl/7.81.0
accept: */*
查询dns会把所有节点都列出来。
# nslookup whoami-clusterip-none
server: 10.43.0.10
address: 10.43.0.10:53
name: whoami-clusterip-none.default.svc.cluster.local
address: 10.42.3.73
name: whoami-clusterip-none.default.svc.cluster.local
address: 10.42.2.75
name: whoami-clusterip-none.default.svc.cluster.local
address: 10.42.8.35
external name模式
用于引进带域名的外部服务,这里引入内部服务作为测试。
多了一层域名解析过程,端口转换流程依赖于所引入服务的服务设定。
服务定义:
cat << 'eof' | kubectl apply -f -
apiversion: v1
kind: service
metadata:
labels:
name: whoami-externalname
name: whoami-externalname
spec:
type: externalname
externalname: whoami-clusterip.default.svc.cluster.local
eof
这里外联的是whoami-clusterip
服务的完整访问域名。
查看服务部署情况:
name type cluster-ip external-ip port(s) age
service/whoami-externalname externalname whoami-clusterip.default 9h
根据域名访问测试:
# curl whoami-externalname
hostname: whoami-767d459f67-qffqw
ip: 127.0.0.1
ip: 10.42.3.77
remoteaddr: 10.42.9.35:36756
get / http/1.1
host: whoami-externalname
user-agent: curl/7.81.0
accept: */*
dns解析结果:
# nslookup whoami-externalname
server: 10.43.0.10
address: 10.43.0.10:53
whoami-externalname.default.svc.cluster.local canonical name = whoami-clusterip.default.svc.cluster.local
name: whoami-clusterip.default.svc.cluster.local
address: 10.43.247.74
小结
简要分析了各种类型service定义、服务引用场景以及测试流程等,整理清楚了,也方便在具体业务场景中进行抉择选择具体服务类型。