Kubernetes技术详解-从理论到实践-(6)-服务-Service
Kubernetes Service为Pod提供统一访问入口,支持负载均衡和服务发现。本文介绍了四种Service类型:ClusterIP(集群内部访问)、NodePort(外部直接访问)、LoadBalancer(云负载均衡)和ExternalName(指向外部服务)。重点演示了ClusterIP Service的创建过程,通过Deployment管理3个Nginx Pod副本,然后定义Serv
1 Service简介
Kubernetes Service将一组Pod封装成一个虚拟服务,提供一个统一入口,并支持负载均衡、服务发现等功能。
2 四种类型的Service
2.1 四种类型的Service概述
| 类型 | 暴露范围 | 典型用途 |
|---|---|---|
| ClusterIP(默认) | 仅集群内部 | 微服务之间调用 |
| NodePort | 集群外可访问 | 开发/测试直接访问 |
| LoadBalancer | 集群外+云负载均衡 | 生产对外暴露 |
| ExternalName | DNS 转发到外部域名 | 指向集群外服务 |
2.2 创建Service前的准备工作
2.2.1 Service与Deployment/ReplicaSet/Pod的关系
Deployment负责回滚/升级,它创建一个ReplicaSet。
ReplicaSet负责管理Pod,控制Pod的数量,扩缩容等。
Pod提供实际的服务。
Service为Pod提供稳定的访问入口。
Deployment和Service之间没有直接耦合关系,它们两个的共同点就是作用对象都是Pod,一个实现副本管理,一个提供稳定访问。
Deployment,ReplicaSet,Pod,Service之间的关系可以通过下图表达。
┌────────────┐ 1. 声明副本数 ┌────────────┐
│ Deployment │ ───────────────────► │ ReplicaSet │
└────────────┘ └─────┬──────┘
│ │ 2. 创建/删除 Pod
│ ▼
┌────────────┐ 3. 标签匹配 ┌────────────┐
│ Service │ ◄────────────────────│ Pod │
└────────────┘ └────────────┘
2.2.2 创建3个Pod副本
虽然Service和Deployment没有直接关系,直接创建Pod也可以实现Service效果,但为了后续方便管理,我们将创建一个Deployment,其标签为nginx-dm,Pod副本数为3,供后续Service使用。
[root@master 6-service]# cat nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-dm
spec:
selector:
matchLabels:
app: nginx-dm
replicas: 3
template:
metadata:
labels:
app: nginx-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
创建名字为nginx-deploy的Deployment
[root@master 6-service]# kubectl get pod
No resources found in default namespace.
[root@master 6-service]# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx-deploy created
# 3个pod副本创建成功,其IP地址分别为10.42.0.57、58和59
[root@master 6-service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-2zh97 1/1 Running 0 4s 10.42.0.57 master <none> <none>
nginx-deploy-7c7bbf4cc9-fkms6 1/1 Running 0 4s 10.42.0.59 master <none> <none>
nginx-deploy-7c7bbf4cc9-px5r8 1/1 Running 0 4s 10.42.0.58 master <none> <none>
下面将基于这三个Pod创建Service。
2.3 ClusterIP类型Service
2.3.1 一个典型的ClusterIP Service资源清单文件
[root@master 6-service]# cat nginx-clusterip-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-clusterip-svc
spec:
type: ClusterIP
selector:
app: nginx-dm
ports:
- port: 8080
targetPort: 80
这个清单文件的含义是,创建一个ClusterIP类型的Service,把所有标签为app=nginx-dm的Pod统一暴露到一个集群内部虚拟IP上,端口映射为8080->80。
各字段的具体含义如下:
| 字段 | 含义 |
|---|---|
| apiVersion: v1 | 使用核心APIv1版本。 |
| kind: Service | 资源类型是Service。 |
| metadata.name: nginx-clusterip-svc | Service名字。 |
| spec.type: ClusterIP | 默认为ClusterIP,只在集群内部可访问。 |
| spec.selector.app: nginx-dm | 选择所有标签为app=nginx-dm的Pod。 |
| ports.port: 8080 | Service VIP端口(集群内部访问时用)。 |
| ports.targetPort: 80 | Pod内容器实际端口。 |
2.3.2 ClusterIP Service常见操作
2.3.2.1 创建clusterip类型的service
先查看一下目前default命名空间是否有service。有一个叫kubernetes的service已经存在,它是在集群部署时默认创建的,我们不用管它。
# 查看当前集群的service
[root@master 6-service]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 11h
# 创建service
[root@master 6-service]# kubectl apply -f nginx-clusterip-svc.yaml
service/nginx-clusterip-svc created
# 可以看到nginx-clusterip-svcy已经创建成功,其CLUSTER-IP为10.43.142.182,端为8080,可以在集群内部访问。
# svc为service的缩写。-o wide可以查看更详细的servcie信息,这里可以看到多了SELECTOR一列,我们创建的service,将选择app=nginx-dm的pod。
[root@master 6-service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 37h
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 3s
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 37h <none>
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 6s app=nginx-dm
2.3.2.2 ClusterIP Service访问
创建Service后,仍可以通过Pod IP直接访问Pod中的服务。
[root@master 6-service]# curl 10.42.0.57
...(略)<h1>Welcome to nginx!</h1>(略)...
使用cluster ip访问nginx服务,注意端口映射8080->80
[root@master 6-service]# curl 10.43.57.157:8080
...(略)<h1>Welcome to nginx!</h1>(略)...
扩缩容pod,使其ip发生变化,查看是否可以通过service端口访问nginx
[root@master 6-service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-2zh97 1/1 Running 0 12m 10.42.0.57 master <none> <none>
nginx-deploy-7c7bbf4cc9-fkms6 1/1 Running 0 12m 10.42.0.59 master <none> <none>
nginx-deploy-7c7bbf4cc9-px5r8 1/1 Running 0 12m 10.42.0.58 master <none> <none>
#副本变为0个
[root@master 6-service]# kubectl scale --replicas=0 deploy nginx-deploy
deployment.apps/nginx-deploy scaled
#没有pod
[root@master 6-service]# kubectl get pod -o wide
No resources found in default namespace.
#副本变为3个
[root@master 6-service]# kubectl scale --replicas=3 deploy nginx-deploy
deployment.apps/nginx-deploy scaled
#重新创建了pod,IP地址已经发生了变化
[root@master 6-service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-cxrbn 1/1 Running 0 4s 10.42.0.61 master <none> <none>
nginx-deploy-7c7bbf4cc9-gs9rp 1/1 Running 0 4s 10.42.0.62 master <none> <none>
nginx-deploy-7c7bbf4cc9-jrlcj 1/1 Running 0 4s 10.42.0.60 master <none> <none>
#ClusterIP Service的IP地址没有变化
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 38h <none>
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 4m56s app=nginx-dm
#仍然可以通过service的ip访问网络。
#pod的ip地址是很容易发生变化的,但service的ip地址不容易发生变化,在集群内部,其他pod可以通过访问service地址与nginx pod通信。
#然而service删除重新创建,ip地址也会发生变化,为了解决这个问题,后面将介绍使用DNS方式访问。
[root@master 6-service]# curl 10.43.57.157:8080
...(略)<h1>Welcome to nginx!</h1>(略)...
2.3.2.3 删除ClusterIP Service
[root@master ~]# kubectl delete svc nginx-clusterip-svc
service "nginx-clusterip-svc" deleted
2.4 NodePort类型Service
2.4.1 一个典型的NodePort Service资源清单文件
[root@master 6-service]# cat nginx-nodeport-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport-svc
spec:
type: NodePort
selector:
app: nginx-dm
ports:
- port: 80
targetPort: 80
nodePort: 30010
这个清单文件的含义是,创建一个NodePort类型的Service,把所有标签为app=nginx-dm的Pod统一暴露到一个集群内部虚拟IP和集群外部端口上。
在集群内部,可以通过curl cluster-ip:80访问pod。
在集群外部,可以通过任意节点IP:30010访问这些nginx pod,如,curl http://<节点IP>:30010
各字段的具体含义如下:
| 字段 | 含义 |
|---|---|
| apiVersion: v1 | 使用核心API v1。 |
| kind: Service | 资源类型是Service。 |
| metadata.name: nginx-nodeport-svc | Service名字。 |
| spec.type: NodePort | 公开方式:在每个节点上开放一个固定端口。 |
| selector.app: nginx-dm | 把流量转发给所有标签为app=nginx-dm的Pod。 |
| ports.port: 80 | Service内部端口(集群内访问)。 |
| ports.targetPort: 80 | Pod容器实际端口。 |
| ports.nodePort: 30010 | 节点外部端口,范围 30000-32767;若省略则由系统随机分配。 |
2.4.2 NodePort Service常见操作
2.4.2.1 创建NodePort Service
[root@master 6-service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 38h
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 12m
[root@master 6-service]# kubectl apply -f nginx-nodeport-svc.yaml
service/nginx-nodeport-svc created
# 创建成功,Selector为app=nginx-dm,具有这些标签的Pod将被Service选择。
# NodePort的内部Cluster-IP为10.43.129.77,集群内可以使用此IP访问服务,集群外无法访问
# 可以看到,NodePort Service与ClusterIP Service不同之处在于PORT(S)字段,多了一个对集群外暴漏的端口30010
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 38h <none>
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 12m app=nginx-dm
nginx-nodeport-svc NodePort 10.43.129.77 <none> 80:30010/TCP 5s app=nginx-dm
2.4.2.2 访问NodePort Service服务
# 集群内部可以使用Cluster-IP访问服务
[root@master 6-service]# curl 10.43.129.77
...(略)<h1>Welcome to nginx!</h1>(略)...
# 集群内端口为80,30010是对外暴露的端口,集群内无法访问
[root@master 6-service]# curl 10.43.129.77:30010
curl: (7) Failed to connect to 10.43.129.77 port 30010 after 21005 ms: Connection refused
# 使用节点IP+对外端口访问Service
[root@master 6-service]# curl 192.168.88.132:30010
...(略)<h1>Welcome to nginx!</h1>(略)...
[root@master 6-service]#
2.4.2.3 删除NodePort Service
[root@master ~]# kubectl delete svc nginx-nodeport-svc
service "nginx-nodeport-svc" deleted
2.5 LoadBalancer类型Service
LoadBalancer Service一般用在公有云环境,在云环境会由云厂商自动创建外部负载均衡器;在非公有云(裸金属、自建K8s、本地集群、vSphere、OpenStack等)环境需要额外插件(如 MetalLB、ServiceLB)来分配一个可路由的EXTERNAL-IP。在我们此次搭建的K3s环境中,已经默认启动了ServiceLB插件。
2.5.1 一个典型的LoadBalancer Service资源清单文件
[root@master 6-service]# cat nginx-loadbalancer-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-loadbalancer-svc
spec:
type: LoadBalancer
selector:
app: nginx-dm
ports:
- port: 6666
targetPort: 80
protocol: TCP
这个清单文件的含义是,创建一个LoadBalancer类型的Service,把集群外部访问端口6666的流量负载均衡到集群内标签为app=nginx-dm的所有Pod的80端口。
各字段的具体含义如下:
| 字段 | 含义 |
|---|---|
| apiVersion: v1 | 使用 Kubernetes v1 API。 |
| kind: Service | 资源类型是 Service。 |
| metadata.name: nginx-loadbalancer-svc | Service 在集群内的名字。 |
| spec.type: LoadBalancer | 声明这是一个LoadBalancer服务。 |
| spec.selector.app: nginx-dm | Service会把流量只转发给标签app=nginx-dm的 Pod。 |
| spec.ports[*] | 端口映射规则: port: 6666:Service 对外暴露的端口。 targetPort: 80:后端 Pod 容器实际监听的端口。 protocol: TCP:四层协议。 |
2.5.2 LoadBalancer Service常见操作
2.5.2.1 创建LoadBalancer Service
# 目前环境中,有一个ClusterIP类型Service和一个NodePort类型Service
[root@master 6-service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 38h
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 29m
nginx-nodeport-svc NodePort 10.43.129.77 <none> 80:30010/TCP 16m
# 创建LoadBalancer类型的Service,可以看到创建成功,6666为Service对外暴露的端口,192.168.88.132和端口5423为k3s自带负载均衡器自动分配的IP和端口
[root@master 6-service]# kubectl apply -f nginx-loadbalancer-svc.yaml
service/nginx-loadbalancer-svc created
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 38h <none>
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 29m app=nginx-dm
nginx-loadbalancer-svc LoadBalancer 10.43.91.177 192.168.88.132 6666:5423/TCP 5s app=nginx-dm
nginx-nodeport-svc NodePort 10.43.129.77 <none> 80:30010/TCP 16m app=nginx-dm
2.5.2.2 访问LoadBalancer Service
[root@master 6-service]#
# 通过cluster-ip + port方式访问,只在集群内部可达。
[root@master 6-service]# curl 10.43.91.177:6666
...(略)<h1>Welcome to nginx!</h1>(略)...
# 通过node-ip + node-port方式访问。这个5432与NodePort的30010类似,任意一个节点IP+5432均可访问服务。如果仅用这种方式访问服务,那它将等价于一个NodePort Service。
[root@master 6-service]# curl 192.168.88.132:5423
...(略)<h1>Welcome to nginx!</h1>(略)...
# 通过external-ip+port方式访问。
# k3s自带的ServiceLB把external-ip绑定到当前所有节点的本地回环网卡上,并在节点上监听6666端口。任何可以路由到192.168.88.132这个external-ip的主机都可以直接访问192.168.88.132:6666,流量再被转给Pod。
[root@master 6-service]# curl 192.168.88.132:6666
...(略)<h1>Welcome to nginx!</h1>(略)...
2.5.2.3 删除LoadBalancer Service
[root@master ~]# kubectl delete svc nginx-loadbalancer-svc
service "nginx-loadbalancer-svc" deleted
2.5.3 LoadBalancer Service的负载均衡器
LoadBalancer类型Service一般用在公有云环境上,云厂商会自动创建外部负载均衡器。如果是内部搭建的私有云集群,并且没有配置过如MetalLB、ServiceLB等额外插件,将无法分配EXTERNAL-IP,该列的IP分配将一直处于状态。
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx-loadbalancer-svc LoadBalancer 10.43.91.177 <pending> 6666:5423/TCP 5s app=nginx-dm
我们搭建的这套K3s环境,默认启用了ServiceLB插件(如果在K3s启动时没有加--disable=servicelb选项,则会内置ServiceLB controller)。
ServiceLB通过创建一个DaemonSet控制器进行工作,可以通过如下命令查看ServiceLB创建的DaemonSet控制器。
[root@master 6-service]# kubectl get DaemonSet -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
svclb-nginx-loadbalancer-svc-dfb32e36 1 1 1 1 1 <none> 90s
2.6 ExternalName类型Service
ExternalName Service和前面三种Service有所不同,它主要用于访问集群外部的服务。它没有选择器Selector,也无法定义任何端口Port,它的作用是返回集群外服务的域名,在集群内通过该域名访问外部服务。
2.6.1 一个典型的ExternalName Service资源清单文件
[root@master 6-service]# cat nginx-externalname-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-externalname-svc
spec:
type: ExternalName
externalName: google.com # 注意这里只能是域名,不能是IP地址。在这里,K8s组件只会校验是否为空字符串,所以填IP也可以,但后续DNS解析会失败。
这个清单文件的含义是,创建一个ExternalName类型的Service,在集群内部通过一个域名别名(nginx-externalname-svc.default.svc.cluster.local)访问外部域名(google.com),Service和域名的相关知识将在本章后面的服务发现章节详细讲解。
各字段的具体含义如下:
| 字段 | 含义 |
|---|---|
| metadata.name: nginx-externalname-svc | 在集群内部生成的DNS名称前缀,完整形式为: nginx-externalname-svc.default.svc.cluster.local |
| spec.type: ExternalName | 声明这是一个ExternalName服务,不会分配ClusterIP、NodePort或LoadBalancer IP。 |
| spec.externalName: google.com | 当集群DNS收到对nginx-externalname-svc.default.svc.cluster.local的查询时,直接返回一条CNAME记录指向google.com;后续解析由上游DNS完成。 |
2.6.2 ExternalName Service常见操作
2.6.2.1 创建ExternalName Service
# 目前环境中,有一个ClusterIP类型Service、一个NodePort类型Service和一个LoadBalancer类型Service。
[root@master 6-service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 39h
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 100m
nginx-loadbalancer-svc LoadBalancer 10.43.91.177 192.168.88.132 6666:5423/TCP 70m
nginx-nodeport-svc NodePort 10.43.129.77 <none> 80:30010/TCP 87m
# 创建ExternalName类型Service
[root@master 6-service]# kubectl apply -f nginx-externalname-svc.yaml
service/nginx-externalname-svc created
# ExternalName Service创建成功,可以看到其External-IP是一个域名。这个Service没有端口,没有选择器。
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 39h <none>
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 100m app=nginx-dm
nginx-externalname-svc ExternalName <none> google.com <none> 4s <none>
nginx-loadbalancer-svc LoadBalancer 10.43.91.177 192.168.88.132 6666:5423/TCP 70m app=nginx-dm
nginx-nodeport-svc NodePort 10.43.129.77 <none> 80:30010/TCP 87m app=nginx-dm
[root@master 6-service]#
2.6.2.2 访问ExternalName Service
由于ExternalName类型Service需要域名相关知识,这里暂不演示,待本章后面服务发现章节再说。
2.6.2.3 删除LoadBalancer Service
[root@master ~]# kubectl delete svc nginx-externalname-svc
service "nginx-externalname-svc" deleted
2.7 四种Service特点及差异
-
ClusterIP Service
- 只在内网可见;kube-proxy/iptables 做四层负载均衡。
- 生命周期与Service一致,删除Service即消失。
- 可配合sessionAffinity做会话保持。
-
NodePort Service
- 在ClusterIP之上额外打开每个节点的固定端口(30000-32767)。实际上,NodePort在内部实现依赖ClusterIP。
- 节点IP变动、端口范围有限,适合临时或测试环境。
- 云厂商场景下常被 LoadBalancer 取代。
-
LoadBalancer Service
- 需要负载均衡器:依赖“LoadBalancer Controller”(云厂商或 MetalLB/kube-vip/OpenELB)。
- 会分配一个公网或内网VIP,并自动把流量转到NodePort/ClusterIP。
- 真正生产暴露80/443或TCP业务的首选。
-
ExternalName Service
- 无IP、无端口、无负载均衡,仅一条DNS CNAME。
- 只能指向域名,不能写IP;解析结果随外部DNS变化而实时生效。
- 常用于把内部域名映射到SaaS、数据库外部地址、CDN等。
下面是对比表格。
| 类型 | 是否分配集群 IP | 集群外可达 | 是否可关联Pod | 典型访问方式 | 适用场景 |
|---|---|---|---|---|---|
| ClusterIP | 10.x.x.x | 仅集群内 | 可以 | clusterIP:port | 东西向流量、微服务内部调用 |
| NodePort | (额外)NodePort 30000-32767 | 节点IP:NodePort | 可以 | <任意节点IP>:NodePort | 快速把服务暴露到物理机/局域网,无云LB时 |
| LoadBalancer | (额外)云 ELB/MetalLB IP | 公网或内网 VIP | 可以 | :port | 云厂商ELB/SLB或裸机MetalLB,生产对外入口 |
| ExternalName | 无 | 不涉及 | 不可以 | DNS CNAME | 把集群内部域名直接指到外部域名,纯DNS重定向 |
3 Service与Endpoints
一个Service关联一组Pod,通过Selector选择符合要求的Pod。Pod会发生变化,删除,创建,扩缩容,Service是怎么管理这些Pod的呢,这里就引入了一个概念:Endpoints。
Endpoints是Kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址,其格式为:
ip1:port1,ip2:port2[ipN:portN]。
一个Service关联一组Pod,这些Pod通过Endpoints暴露出来,Endpoints就是Service的事实路由表,Service通过它知道应该把流量转发到哪些Pod的IP:Port。打个比方,Service就像前台接待,提供统一入口,Endpoint就像后台花名册,实时记录着今天有哪些Pod在上班。
Service与Endpoints关系
| Service(逻辑) | Endpoint(数据) |
|---|---|
| 提供 稳定虚拟IP和DNS | 提供动态Pod IP列表 |
| spec.selector 决定选哪些 Pod | subsets.addresses 列出这些Pod IP |
| 不随Pod生命周期变化 | 随Pod生命周期实时变化** |
Endpoints一般在Service创建时自动创建,但有些场景也可以手动创建,下面将介绍这两种方式。
3.1 自动生成Endpoints
我们创建一组pod,再创建一个service,service内通过selector选择pod,此时会自动生成endpoints资源对象,下面是演示操作。
创建一个service
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 15h
nginx-clusterip-svc ClusterIP 10.43.142.182 <none> 8080/TCP 3h26m
使用kubectl get endpoints命令查看Endpoints,注意通过这个命令看到的NAME字段,是Endpoints的名字,不是Service的名字,它只是和Service同名。
可以看到nginx-clusterip-svc这个Endpoints下面有三组IP和端口。
[root@master ~]# kubectl get endpoints nginx-clusterip-svc
NAME ENDPOINTS AGE
nginx-clusterip-svc 10.42.0.33:80,10.42.0.34:80,10.42.0.35:80 3h27m
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-5prvh 1/1 Running 0 3h25m 10.42.0.34 master <none> <none>
nginx-deploy-7c7bbf4cc9-hzwvj 1/1 Running 0 3h25m 10.42.0.33 master <none> <none>
nginx-deploy-7c7bbf4cc9-v2ffp 1/1 Running 0 3h25m 10.42.0.35 master <none> <none>
把Deploy的Pod缩容为0,可以看到Service的Endpoints变为
[root@master ~]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 3/3 3 3 3h28m
[root@master ~]# kubectl scale --replicas=0 deploy nginx-deploy
deployment.apps/nginx-deploy scaled
[root@master ~]# kubectl get endpoints nginx-clusterip-svc
NAME ENDPOINTS AGE
nginx-clusterip-svc <none> 3h28m
把Deploy的Pod扩容为2,可以看到这两个Pod自动被添加到Service的Endpoints列。
[root@master ~]# kubectl scale --replicas=2 deploy nginx-deploy
deployment.apps/nginx-deploy scaled
[root@master ~]# kubectl get endpoints nginx-clusterip-svc
NAME ENDPOINTS AGE
nginx-clusterip-svc 10.42.0.36:80,10.42.0.37:80 3h28m
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-7psbk 1/1 Running 0 8s 10.42.0.37 master <none> <none>
nginx-deploy-7c7bbf4cc9-gktrc 1/1 Running 0 8s 10.42.0.36 master <none> <none>
3.2 手动创建Endpoints
下面演示手动创建Endpoints在副本扩缩容时的效果。
(1) 创建Deployment,Pod副本为3
[root@master 6-service]# kubectl get pod
No resources found in default namespace.
[root@master 6-service]# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx-deploy created
[root@master 6-service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-4fpr5 1/1 Running 0 6s 10.42.0.40 master <none> <none>
nginx-deploy-7c7bbf4cc9-cdpfl 1/1 Running 0 6s 10.42.0.39 master <none> <none>
nginx-deploy-7c7bbf4cc9-v8blh 1/1 Running 0 6s 10.42.0.41 master <none> <none>
(2) 创建用于对比的ClusterIP Service
[root@master 6-service]# kubectl apply -f nginx-clusterip-svc.yaml
service/nginx-clusterip-svc created
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 20h <none>
nginx-clusterip-svc ClusterIP 10.43.238.57 <none> 8080/TCP 5s app=nginx-dm
(3) 查看Endpoints
可以看到,名字为nginx-cluster-svc的Endpoints已经被自动创建出来,其IP列表有三个,对应三个Pod。ep是endpoints的缩写。名字为kubernetes的Endpoints是K3s系统部署时自动创建的,对应着kubernetes服务,这里不用关注。
[root@master 6-service]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.88.132:6443 20h
nginx-clusterip-svc 10.42.0.39:80,10.42.0.40:80,10.42.0.41:80 16s
[root@master 6-service]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.88.132:6443 20h
nginx-clusterip-svc 10.42.0.39:80,10.42.0.40:80,10.42.0.41:80 18s
(4) 编写nginx-endpoints.yaml文件
该文件用于手动创建Endpoints对象。注意这里面的ip address,是前面创建的三个pod中的前两个。
[root@master 6-service]# cat nginx-endpoints.yaml
apiVersion: v1
kind: Endpoints
metadata:
name: nginx-endpoints-svc # 注意这里Endpoints的命名,这个和后面要创建的Service是同名的,因为两者同名将会自动绑定
subsets:
- addresses:
- ip: 10.42.0.39
- ip: 10.42.0.40
ports:
- port: 80
(5) 手动创建Endpoints对象
[root@master 6-service]# kubectl apply -f nginx-endpoints.yaml
endpoints/nginx-endpoints created
(6) 查看Endpoints对象
可以看到其列表中有两个ip地址,比nginx-clusterip-svc少1个。
[root@master 6-service]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.88.132:6443 20h
nginx-clusterip-svc 10.42.0.39:80,10.42.0.40:80,10.42.0.41:80 81s
nginx-endpoints-svc 10.42.0.39:80,10.42.0.40:80 12s
# 此时仍是原来那些service,还没有nginx-endpoints-svc
[root@master 6-service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 20h
nginx-clusterip-svc ClusterIP 10.43.238.57 <none> 8080/TCP 77s
(7) 编写nginx-endpoints-svc.yaml文件
本文件用于创建Service对象,对象名为nginx-endpoints-svc,如果有同名的Endpoints,两者将会自动绑定。
[root@master 6-service]# cat nginx-endpoints-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-endpoints-svc
spec:
type: ClusterIP
ports:
- port: 8010
targetPort: 80
(8) 创建Service对象
注意:Endpoints与Service是通过同名自动绑定的,请确保两者名字相同
该service对象没有selector选择器,SELECTOR字段为,但是有同名的endpoints(前面已创建),两者将自动绑定。
[root@master 6-service]# kubectl apply -f nginx-endpoints-svc.yaml
service/nginx-endpoints-svc created
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 20h <none>
nginx-clusterip-svc ClusterIP 10.43.238.57 <none> 8080/TCP 1m12s app=nginx-dm
nginx-endpoints-svc ClusterIP 10.43.10.248 <none> 8010/TCP 7s <none>
(9) 查看Endpoints
创建nginx-endpoints-svc service对象后,Endpoints没有变化
[root@master 6-service]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.88.132:6443 20h
nginx-clusterip-svc 10.42.0.39:80,10.42.0.40:80,10.42.0.41:80 1m43s
nginx-endpoints-svc 10.42.0.39:80,10.42.0.40:80 34s
(10) Pod扩容为4个
[root@master 6-service]# kubectl scale --replicas=4 deploy nginx-deploy
deployment.apps/nginx-deploy scaled
[root@master 6-service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-4fpr5 1/1 Running 0 2m13s 10.42.0.40 master <none> <none>
nginx-deploy-7c7bbf4cc9-cdpfl 1/1 Running 0 2m13s 10.42.0.39 master <none> <none>
nginx-deploy-7c7bbf4cc9-jc4jb 1/1 Running 0 5s 10.42.0.42 master <none> <none>
nginx-deploy-7c7bbf4cc9-v8blh 1/1 Running 0 2m13s 10.42.0.41 master <none> <none>
(11) 查看Endpoints
nginx-clusterip-svc为自动创建的Endpoints对象,当Pod新增时,Pod IP也会自动添加到这个“路由表”。由于列表已经显示不下,因此用+1 more ...显示,使用kubectl describe ep nginx-clusterip-svc命令可以看到完整的IP列表。
nginx-endpoints-svc为手动创建的Endpoints对象,Pod新增时,Pod IP不会自动添加。
[root@master 6-service]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.88.132:6443 20h
nginx-clusterip-svc 10.42.0.39:80,10.42.0.40:80,10.42.0.41:80 + 1 more... 2m3s
nginx-endpoints-svc 10.42.0.39:80,10.42.0.40:80 54s
(12) Pod缩容为1个
Pod缩容时,自动创建的Endpoints会自动删除Pod IP,手动创建的Endpoints不会有任何修改。
[root@master 6-service]# kubectl scale --replicas=1 deploy nginx-deploy
deployment.apps/nginx-deploy scaled
[root@master 6-service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-cdpfl 1/1 Running 0 3m4s 10.42.0.39 master <none> <none>
[root@master 6-service]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.88.132:6443 20h
nginx-clusterip-svc 10.42.0.39:80 2m48s
nginx-endpoints-svc 10.42.0.39:80,10.42.0.40:80 99s
(13) 访问service
由于nginx-endpoints-svc的IP列表有两个,但实际上pod只有一个,此时如果再访问,根据轮询调度算法,如果调度到10.42.0.40上,将会无法访问。而访问nginx-clusterip-svc则每次都能访问成功。
# 8080端口的nginx-clusterip-svc每次都可以访问成功
[root@master 6-service]# curl 10.43.238.57:8080
...(略)<h1>Welcome to nginx!</h1>(略)...
[root@master 6-service]# curl 10.43.238.57:8080
...(略)<h1>Welcome to nginx!</h1>(略)...
[root@master 6-service]# curl 10.43.238.57:8080
...(略)<h1>Welcome to nginx!</h1>(略)...
# 8010端口的nginx-endpoints-svc有时会失败
[root@master 6-service]# curl 10.43.10.248:8010
...(略)<h1>Welcome to nginx!</h1>(略)...
[root@master 6-service]# curl 10.43.10.248:8010
curl: (7) Failed to connect to 10.43.10.248 port 8010 after 3052 ms: No route to host
3.3 手动创建的Endpoints应用场景
Endpoints既可以由系统自动生成(通过Service的selector),也可以手动创建。手动场景通常用于:
| 场景 | 手动创建Endpoints用途 |
|---|---|
| 数据库集群在集群外 | 把外部数据库主从IP写进 Endpoints,K8s内部Pod通过Service访问。 |
| 迁移旧系统 | 灰度阶段先手动填外部IP,完全迁移后再改回selector自动维护。 |
| 金丝雀 | 在Endpoint里只放金丝雀Pod IP,Service无selector,流量全部打到金丝雀。 |
| 无标签Pod | Pod没有合适label,手工写IP列表即可让Service生效。 |
4 Service三大作用
Service有三大作用,统一入口,负载均衡和服务发现,下面分别介绍。
4.1 统一入口
我们创建了一个Deployment,包含三个Pod副本,K8s为每个Pod分配了一个IP地址,在集群内部,可以通过IP地址访问Pod。但由于IP地址是动态分配的,当Pod被删除或弹性扩缩容时,IP地址会发生变化,且IP地址仅限于集群内部,集群外部无法访问,因此IP地址访问的这种方式有很大局限性。Service为一组Pod提供了一个稳定的IP地址和DNS名称,使得应用程序可以通过统一入口访问Pod。
K8s为Service分配的统一入口IP也叫VIP(Virtual IP),它是集群对外暴露的单一且高可用的IP地址,所有外部流量先进入这个IP,再由集群的kube-proxy组件把流量按域名、路径或端口转发到Service/Pod。
4.2 负载均衡
我们创建了一个Deployment,包含三个Pod副本,在Service中使用Selector选择了这三个Pod,那么当访问Service这个VIP地址时,究竟是哪个Pod提供的nginx服务呢?
一种方法是查看每一个Pod的日志,我们通过如下命令查看每个nginx的log:kubectl logs -f <pod-name>
当访问nginx时,对应pod日志会输出一行,没有被访问到的不会输出日志。
我们访问nginx时,具体访问到哪个pod,实际上是由背后的负载均衡决定的。
Kubernetes的负载均衡由kube-proxy负责,如何调度pod,取决于kube-proxy的配置。
kube-proxy支持iptables和ipvs两种模式。iptables我们比较熟悉,ipvs是一种基于Linux内核的四层负载均衡技术,主要用于处理大规模网络服务的流量分发。
ipvs需要手动开启和配置,如果没有配置过ipvs,在OpenEular22.03上,是iptables模式。
这两种模式的算法如下:
iptables:轮询(round-robin),只有这一种算法。
ipvs:rr、lc、wlc、sh、dh、sed、nq等多种算法,默认是轮询(rr, round-robin)。
由于我们没有配置过ipvs,因此我们访问service时,kube-proxy会轮询各个pod,轮询到哪个,就由哪个提供服务。
如何查看本机是否开启ipvs
k8s集群和k3s集群查看方式不同。
(1) k8s集群查看方法
如果使用k8s而非k3s搭建Kubernetes集群,则可以通过以下三种方法快速确认kube-proxy是否使用ipvs:
- 方法1
查看kube-proxy配置kubectl -n kube-system get configmap kube-proxy -o yaml | grep -i mode输出mode: "ipvs"表示已开启,输出mode: "iptables"表示未开启ipvs- 方法2
查看kube-proxy日志kubectl -n kube-system logs -l k8s-app=kube-proxy | grep -i "Using ipvs"若看到Using ipvs字样,则确认已启用ipvs。- 方法3
检查kube-proxy启动参数 登录任意节点执行:ps -ef | grep kube-proxy | grep -i ipvs
若包含–proxy-mode=ipvs,则启用。(2) k3s集群查看方法
如果集群的搭建使用k3s而不是k8s,因为k3s把所有控制平面组件(kube-apiserver、kube-controller-manager、kube-scheduler)以及kube-proxy统统打包进k3s这一个进程,所以查看方法有所不同。
- 方法1
查看k3s启动参数cat /etc/systemd/system/k3s.service | grep -oP 'kube-proxy-arg.*?proxy-mode=\K[^ ]+'
输出ipvs表示已启用ipvs,输出iptables或者没有任何参数则表示未开启ipvs- 方法2
检查内核规则 看ipvs表是否存在sudo ipvsadm -Ln出现 TCP 10.96.x.x:80 rr等条目代表ipvs已生效,提示ipvsadm not
found表示ipvs未启用或未安装ipvsadm- 方法3
动态查看k3s日志journalctl -u k3s -g 'proxy-mode'日志中出现–proxy-mode=ipvs即确认启用了ipvs。
4.3 服务发现
K8s服务发现主要解决下面这两个问题:
- 服务访问者如何找到服务提供者?
- 服务提供者变动时,服务访问者如何无感更新?
K8s的服务发现有DNS名字发现和环境变量发现两种。
- DNS名字发现:K8s使用域名解决服务发现问题。kube-dns/CoreDNS是K8s集群的内部DNS服务,负责把Service、Pod、ExternalName等名字解析成对应IP地址,让Pod用域名就可以找到彼此。kube-dns是k8s早期版本的DNS服务,现在高版本一般都使用CoreDNS服务。
- 环境变量发现:由于环境变量这种服务发现方式要求Pod启动前Service就已经存在,所以现在基本已经被DNS名字发现取代。
4.3.1 DNS名字发现
4.3.1.1 A记录和CNAME记录
DNS是域名解析系统(Domain Name Service)的缩写,它可以将域名转换成机器可读的IP地址,使得普通用户能够通过易于记忆的名称来访问网站。常见的DNS域名记录类型有A记录、AAAA记录、CNAME记录、MX记录、NS记录及TXT记录等。K8s系统中,主要涉及的是A记录(Address Record)和CNAME记录(Canonical Name Record)。
- A记录
A记录是最常见的DNS记录类型,用于将域名直接指向一个特定的IP地址。这种直接映射的关系使得我们在使用中只需要记忆网站的域名,不需要关心网站的IP地址。例如,如果有一个网站www.test.com,它部署在IP为192.168.88.188的服务器上,这时我们可以在DNS配置中添加一条A记录,将www.test.com指向192.168.88.188即可。后续访问该网站,只需要输入域名www.test.com。 - CNAME记录
CNAME记录也称为别名记录,它可以为一个域名创建别名。举个例子,如果我们有一个域名www.test.com,为其创建域名别名www.t.com,当www.test.com域名发生变化时,只需要修改CNAME记录,用户仍访问www.t.com,没有任何影响。如果域名较长,比如www.test-a-long-long-url.com,也可以为其创建一个www.t-short.com的域名,方便访问,还可以隐藏后端真实长域名。如果多个业务域名共用同一个后端域名(或集群内部统一网关),也可以通过CNAME把流量集中到同一处,实现入口统一、配置简单、切换迅速等高级运维需求。
总结一下,A记录把域名解析成IP,CNAME记录把域名解析成另一个域名。
4.3.1.2 Pod FQDN:基于IP的Pod DNS记录
在Kubernetes官方文档中讲到,kube-dns/CoreDNS会为每个已就绪的Pod自动生成一个DNS域名(Pod-level DNS),官方名称为Pod FQDN(Fully Qualified Domain Name),一般称作基于IP的Pod DNS记录。这个域名解析靠的是Pod DNS A记录,它属于“Per-Pod DNS”条目,格式为:<pod-ip-with-dashes>.<namespace>.pod.cluster.local
pod-ip-with-dashes意思是用短横线-代替ip中的.。如果一个在default命名空间的pod ip为10.42.0.34,则其DNS域名为:10-42-0-23.default.pod.cluster.local
kube-dns/CoreDNS会保存一个对应的DNS A记录,用于将该域名与Pod的IP地址10.42.0.23对应。
如果这个Pod提供了一个nginx服务,端口为80,则在集群中其他pod可以使用如下命令访问该服务。后面的:80由于是http默认端口,也可以省略。curl http://10-42-0-23.default.pod.cluster.local:80
但我们知道Pod IP会经常变化,因此这个域名并不可靠。
4.3.1.3 Service FQDN:Service域名
当我们创建一个Service,Service关联一组Pod,此时只需要使用Service统一入口访问Pod,Pod地址是否变化就变得不重要了。
实际上,当Service创建完成后,kube-dns/CoreDNS会为这个Service创建一个DNS域名,Service FQDN(Fully Qualified Domain Name),格式如下:<service-name>.<namespace>.svc.cluster.local
Service在资源重建后,统一入口VIP会发生变化,但service-name不会发生变化,因此这个域名在集群内部是稳定的。
kube-dns/CoreDNS为这个DNS域名保存一个对应的A记录,用于将该域名与Service的IP地址对应,且Service IP地址变化后,该记录也会更新。
对于ExternalName类型的Service,虽然没有Cluster-ip,没有Endpoints,也没有负载均衡,但kube-dns/CoreDNS仍会为其创建一个域名<service-name>.<namespace>.svc.cluster.local
这和普通的Service域名相同,但由于该域名不是指向Service IP,而是指向一个外部的域名,因此这里会用CNAME记录保存这种对应关系。
4.3.1.4 Pod DNS Subdomain:Pod DNS子域
在StatefulSet控制器一章,我们还将介绍一种Headless Service,这种服务的特点是其他都和ClusterIP Service相同,type类型也是ClusterIP,但它没有ClusterIP,内部外部都无法访问该Service,只能通过DNS域名这种方式访问。
如果Headless Service选择的后端Pod是由StatefulSet控制器管理的,那每个Pod将会有自己固定的名字。如果这个Pod有自己固定的名字,那kube-dns/CoreDNS将为这个具有固定名字的Pod生成一个DNS域名。举例说明:
下面的Pod,最后三个nginx-statefulset-*,它们是由StatefulSet控制器管理,销毁和重新创建后,名字不会发生变化,nginx-deploy-7c7bbf4cc9-*这几个由Deployment控制器管理的Pod销毁和重新创建后名字将会变化。
[root@master 6-service]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deploy-7c7bbf4cc9-cxrbn 1/1 Running 0 4h42m
nginx-deploy-7c7bbf4cc9-gs9rp 1/1 Running 0 4h42m
nginx-deploy-7c7bbf4cc9-jrlcj 1/1 Running 0 4h42m
nginx-statefulset-0 1/1 Running 0 4s
nginx-statefulset-1 1/1 Running 0 3s
nginx-statefulset-2 1/1 Running 0 2s
如果后面为这三个Pod创建一个Headless Service,那将会有如下域名:<pod-ip-with-dashes>.<namespace>.pod.cluster.local<service-name>.<namespace>.svc.cluster.local<pod-name><service-name>.<namespace>.svc.cluster.local
第一个是Pod FQDN(Fully Qualified Domain Name),一般称为基于IP的Pod DNS记录。
第二个是Service FQDN,一般称作Service域名
第三个是Pod DNS Subdomain,一般称作Pod DNS子域或Stateful稳定域名
4.3.2 环境变量服务发现
如果pod启动时service已经存在,Kubernetes会将环境变量注入到pod,通过环境变量也可以访问到service。
如果一个service的名称为nginx-cluster-svc,则其ip和端口分别为:NGINX_CLUSTERIP_SVC_SERVICE_HOSTNGINX_CLUSTERIP_SVC_SERVICE_PORT
在Pod里可以使用echo查看环境变量值,使用curl访问到service。echo $NGINX_CLUSTERIP_SVC_SERVICE_HOSTecho $NGINX_CLUSTERIP_SVC_SERVICE_PORTcurl http://$NGINX_CLUSTERIP_SVC_SERVICE_HOST:$NGINX_CLUSTERIP_SVC_SERVICE_PORT
如果想验证环境变量方式访问service,有如下两种方式可以保证pod启动时service已经存在:
- 先创建service,再创建deploy,pod自然比service启动晚
- 先创建deploy,再创建service,然后deploy先缩容到0再扩容到3
下面演示通过环境变量访问service
(1) 进入一个Pod(需要先保证这个Pod比Service启动得晚,否则无Service的环境变量)
[root@master 6-service]# kubectl exec -it nginx-deploy-7c7bbf4cc9-5prvh -- /bin/bash
root@nginx-deploy-7c7bbf4cc9-5prvh:/# env
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=nginx-deploy-7c7bbf4cc9-5prvh
NGINX_CLUSTERIP_SVC_PORT_8080_TCP=tcp://10.43.142.182:8080
PWD=/
PKG_RELEASE=1~bookworm
HOME=/root
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
DYNPKG_RELEASE=1~bookworm
NGINX_CLUSTERIP_SVC_PORT_8080_TCP_PORT=8080
NGINX_CLUSTERIP_SVC_PORT=tcp://10.43.142.182:8080
NJS_VERSION=0.8.7
TERM=xterm
SHLVL=1
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
NGINX_CLUSTERIP_SVC_PORT_8080_TCP_PROTO=tcp
KUBERNETES_SERVICE_HOST=10.43.0.1
KUBERNETES_PORT=tcp://10.43.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
NGINX_CLUSTERIP_SVC_SERVICE_PORT=8080 # nginx-clusterip-svc Service的端口
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NGINX_CLUSTERIP_SVC_SERVICE_HOST=10.43.142.182 # nginx-clusterip-svc Service的IP地址
NGINX_VERSION=1.27.3
NJS_RELEASE=1~bookworm
NGINX_CLUSTERIP_SVC_PORT_8080_TCP_ADDR=10.43.142.182
_=/usr/bin/env
(2) 访问nginx服务
root@nginx-deploy-7c7bbf4cc9-5prvh:/# curl http://$NGINX_CLUSTERIP_SVC_SERVICE_HOST:$NGINX_CLUSTERIP_SVC_SERVICE_PORT
...(略)<h1>Welcome to nginx!</h1>(略)...
(3) 退出Pod,回到宿主机
root@nginx-deploy-7c7bbf4cc9-5prvh:/# exit
exit
5 访问Service及Pod
5.1 Service及Pod访问方式汇总
可以通过IP或DNS域名两种方式访问Service及Pod,前面创建四种类型Service时已经有了初步介绍,这里做一个汇总。
5.1.1 ClusterIP Service访问方式
如果创建的是ClusterIP Service,则有如下访问方式:
IP:
Pod IP 仅限集群内部访问 (每种Service都可以用这种方式访问)
Cluster IP 仅限集群内部访问
DNS:<pod-ip-with-dashes>.<namespace>.pod.cluster.local 仅限集群内部访问<service-name>.<namespace>.svc.cluster.local 仅限集群内部访问
5.1.2 NodePort Service访问方式
如果创建的是NodePort Service,则有如下访问方式:
IP:
Pod IP 仅限集群内部访问 (每种Service都可以用这种方式访问)
Cluster IP 仅限集群内部访问 (与ClusterIP方式相同)
Node IP + Node Port 可以在集群外部访问
DNS:<pod-ip-with-dashes>.<namespace>.pod.cluster.local 仅限集群内部访问<service-name>.<namespace>.svc.cluster.local 仅限集群内部访问
5.1.3 LoadBalancer Service访问方式
如果创建的是LoadBalancer Service,则有如下访问方式(与NodePort类似):
IP:
Pod IP 仅限集群内部访问 (每种Service都可以用这种方式访问)
Cluster IP 仅限集群内部访问 (与ClusterIP方式相同)
Node IP + Node Port 可以在集群外部访问 (与NodePort方式相同)
Exteral IP 可以在集群外部访问
DNS:<pod-ip-with-dashes>.<namespace>.pod.cluster.local 仅限集群内部访问<service-name>.<namespace>.svc.cluster.local 仅限集群内部访问
5.1.4 ExternalName Service访问方式
如果创建的是ExternalName Service,则有如下访问方式:
DNS:<service-name>.<namespace>.svc.cluster.local 仅限集群内部访问
5.1.5 Headless Service访问方式
如果创建的是Headless Service(这种Service在后面StatefulSet控制器一章会细说),则有如下访问方式:
DNS:<pod-ip-with-dashes>.<namespace>.pod.cluster.local 仅限集群内部访问<service-name>.<namespace>.svc.cluster.local 仅限集群内部访问<pod-name><service-name>.<namespace>.svc.cluster.local 仅限集群内部访问
5.2 访问Service及Pod前的准备工作
(1)创建nginx和busybox Pod
在集群内创建两个独立于各Service的Pod:nginx和busybox
[root@master 6-service]# cat nginx-busybox-pods.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
namespace: default
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: busybox-pod
namespace: default
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:1.34.1
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- |
while :; do
echo "$(date '+%F %T') - hello from busybox"
sleep 10
done
[root@master 6-service]# kubectl apply -f nginx-busybox-pods.yaml
pod/nginx-pod created
pod/busybox-pod created
[root@master 6-service]# kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox-pod 1/1 Running 0 2s
nginx-deploy-7c7bbf4cc9-cxrbn 1/1 Running 0 5h34m
nginx-deploy-7c7bbf4cc9-gs9rp 1/1 Running 0 5h34m
nginx-deploy-7c7bbf4cc9-jrlcj 1/1 Running 0 5h34m
nginx-pod 1/1 Running 0 2s
(2) 查看集群Service及Pod等信息
集群内Service及Pod信息如下:
[root@master 6-service]# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.88.132:6443 43h
nginx-clusterip-svc 10.42.0.60:80,10.42.0.61:80,10.42.0.62:80 5h42m
nginx-loadbalancer-svc 10.42.0.60:80,10.42.0.61:80,10.42.0.62:80 5h12m
nginx-nodeport-svc 10.42.0.60:80,10.42.0.61:80,10.42.0.62:80 5h29m
[root@master 6-service]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 43h <none>
nginx-clusterip-svc ClusterIP 10.43.57.157 <none> 8080/TCP 5h42m app=nginx-dm
nginx-externalname-svc ExternalName <none> google.com <none> 4h2m <none>
nginx-loadbalancer-svc LoadBalancer 10.43.91.177 192.168.88.132 6666:5423/TCP 5h12m app=nginx-dm
nginx-nodeport-svc NodePort 10.43.129.77 <none> 80:30010/TCP 5h29m app=nginx-dm
[root@master 6-service]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-pod 1/1 Running 0 3m8s 10.42.0.68 master <none> <none>
nginx-deploy-7c7bbf4cc9-cxrbn 1/1 Running 0 5h38m 10.42.0.61 master <none> <none>
nginx-deploy-7c7bbf4cc9-gs9rp 1/1 Running 0 5h38m 10.42.0.62 master <none> <none>
nginx-deploy-7c7bbf4cc9-jrlcj 1/1 Running 0 5h38m 10.42.0.60 master <none> <none>
nginx-pod 1/1 Running 0 3m8s 10.42.0.67 master <none> <none>
5.3 访问ClusterIP Service
5.3.1 IP方式访问
10.42.0.60是其中一个Pod的IP地址
[root@master 6-service]# curl 10.42.0.60
...(略)<h1>Welcome to nginx!</h1>(略)...
如果想使用ClusterIP访问,必须注意端口映射,8080->80
[root@master 6-service]# curl 10.43.57.157
curl: (7) Failed to connect to 10.43.57.157 port 80 after 21008 ms: Connection refused
使用ClusterIP+Port方式访问
[root@master 6-service]# curl 10.43.57.157:8080
...(略)<h1>Welcome to nginx!</h1>(略)...
[root@master 6-service]#
5.3.2 域名方式访问
[root@master 6-service]# kubectl exec -it nginx-pod -- /bin/bash
root@nginx-pod:/# curl 10-42-0-61.default.pod.cluster.local
...(略)<h1>Welcome to nginx!</h1>(略)...
root@nginx-pod:/# curl nginx-clusterip-svc.default.svc.cluster.local:8080
...(略)<h1>Welcome to nginx!</h1>(略)...
root@nginx-pod:/#
5.4 访问NodePort Service
5.4.1 IP方式访问
[root@master 6-service]# curl 10.42.0.60
...(略)<h1>Welcome to nginx!</h1>(略)...
[root@master 6-service]# curl 10.43.129.77
...(略)<h1>Welcome to nginx!</h1>(略)...
[root@master 6-service]# curl 192.168.88.132:30010
...(略)<h1>Welcome to nginx!</h1>(略)...
5.4.2 域名方式访问
[root@master 6-service]# kubectl exec -it nginx-pod -- /bin/bash
root@nginx-pod:/# curl 10-42-0-61.default.pod.cluster.local
...(略)<h1>Welcome to nginx!</h1>(略)...
root@nginx-pod:/# curl nginx-nodeport-svc.default.svc.cluster.local
...(略)<h1>Welcome to nginx!</h1>(略)...
# 30010是Service对外暴露的端口,域名方式访问时不能使用此端口
root@nginx-pod:/# curl nginx-nodeport-svc.default.svc.cluster.local:30010
curl: (7) Failed to connect to nginx-nodeport-svc.default.svc.cluster.local port 30010 after 21023 ms: Couldn't connect to server
root@nginx-pod:/#
5.5 访问LoadBalancer Service
5.5.1 IP方式访问
直接通过Pod IP访问
[root@master 6-service]# curl 10.42.0.60
...(略)<h1>Welcome to nginx!</h1>(略)...
通过cluster-ip方式访问,只能在集群内部使用这种方式
[root@master 6-service]# curl 10.43.91.177:6666
...(略)<h1>Welcome to nginx!</h1>(略)...
通过node-ip + node-port访问,与NodePort方式类似。5423是Kubernetes在每个节点上额外打开的NodePort类型端口
[root@master 6-service]# curl 192.168.88.132:5423
...(略)<h1>Welcome to nginx!</h1>(略)...
通过外部负载均衡器配置的IP地址+外部负载均衡器监听的端口(这里的192.168.88.132和6666是外部负载均衡器分配的端口,只不过分配的192.168.88.132这个IP与节点IP相同了)
[root@master 6-service]# curl 192.168.88.132:6666
...(略)<h1>Welcome to nginx!</h1>(略)...
5.5.2 域名方式访问
[root@master 6-service]# kubectl exec -it nginx-pod -- /bin/bash
root@nginx-pod:/# curl 10-42-0-61.default.pod.cluster.local
...(略)<h1>Welcome to nginx!</h1>(略)...
root@nginx-pod:/# curl nginx-loadbalancer-svc.default.svc.cluster.local:6666
...(略)<h1>Welcome to nginx!</h1>(略)...
# LoadBalancer内部端口为6666,外部端口为5432,但外部端口的IP和内部端口的IP不同,服务没有开放80端口,因此无法访问
root@nginx-pod:/# curl nginx-loadbalancer-svc.default.svc.cluster.local
curl: (7) Failed to connect to nginx-loadbalancer-svc.default.svc.cluster.local port 80 after 21017 ms: Could not connect to server
# 5432是外部端口,相当于NodePort端口,内部无法访问,所以无法通过域名方式访问该端口
root@nginx-pod:/# curl nginx-loadbalancer-svc.default.svc.cluster.local:5423
curl: (7) Failed to connect to nginx-loadbalancer-svc.default.svc.cluster.local port 5423 after 21014 ms: Couldn't connect to server
root@nginx-pod:/#
5.6 访问ExternalName Service
5.6.1 IP方式访问
ExternalName Service无法使用IP方式访问
5.6.2 域名方式访问
在nginx Pod使用curl工具访问域名,kube-dns/CoreDNS会将其解析为外部域名,外部域名进一步解析得到IP地址,然后访问到网站服务。
这里需要有访问google的权限,否则在创建Service时应将该地址改为集群所在宿主机可以访问的一个域名,如内部网站或国内网站。
[root@master 6-service]# kubectl exec -it nginx-pod -- /bin/bash
root@nginx-pod:/# curl nginx-externalname2-svc.default.svc.cluster.local
<!DOCTYPE html>
<html>
goooooooooooooooooooogle一下!
<html>
root@nginx-pod:/#
进入busybox Pod
使用nslookup解析DNS域名,可以看到它是一个CNAME记录,指向google.com。
如果我们没有访问google这个站点的访问权限,也可以将其替换成其他任意路由可达的网站,或使用自己通过dnsmsq搭建的域名地址。
[root@master 6-service]# kubectl exec -it busybox-pod -- /bin/sh
/ # nslookup nginx-externalname-svc.default.svc.cluster.local
Server: 10.43.0.10
Address: 10.43.0.10:53
nginx-externalname-svc.default.svc.cluster.local canonical name = google.com
Name: google.com
Address: 2404:6800:4012:8::200e
*** Can't find nginx-externalname-svc.default.svc.cluster.local: No answer
# 最后这一行`Can't find ...`并不是报错,只是nslookup自己查询有没有额外记录时未得到响应的提示信息。
更多推荐

所有评论(0)