目录
[一. 认识Service](#一. 认识Service)
[二.集群内部IP------Cluster IP](#二.集群内部IP——Cluster IP)
[2.2创建Cluster IP](#2.2创建Cluster IP)
[2.3通过ClusterIP 访问后端数据库](#2.3通过ClusterIP 访问后端数据库)
一. 认识Service
1.1什么是Service
1.概念 :Service主要负责K8s集群中的流量通讯管理 ,称之为东西流量(具有Namespaces隔离性)。他是实现k8s集群微服务的核心架构,通过创建Service,可以为一组具有相同功能的Pod提供统一的流量入口地址,并将请求的流量负载分发到后端应用当中。Service 用于为一组提供服务的 Pod 抽象一个稳定的网络访问地址
简单来说,Service类似分发流量的中转站,客户端通过访问中转站,可以将流量负载分发到具有相同功能的多个Pod,Service为客户端提供统一访问他们的接口 。同时,当Pod发生节点切换时(PodIP改变时),只要Pod的标签没有改变,通过Service依旧能够访问到该Pod。后端切片会自动更新这个PodIP。

2.Service对外暴露的方式主要分为三类:
1.ClusterIP:默认 ,给 Service 分配一个集群内部的虚拟 IP,只能在集群内访问
2.NodePort: 在 ClusterIP 的基础上,在每个节点上开一个端口 ,外部可以通过节点IP:端口访问, 通过暴露端口,供集群外部访问**。**
3.LoadBalancer: 在 NodePort 的基础上,向云厂商申请一个外部负载均衡器 ,分配一个公网 IP。外部访问这个公网IP,可以访问到后端Pod。

1.2标签------Label
1.2.1简单介绍
概念 :我们之前简单介绍过Label,它类似资源的一个身份证。一个 Label 是一个 key=value 键值对,其中的 key 与 value 由用户自己指定。Label可以被附加到各种资源对象上,例如 Node、 Pod、Service、Deployment 等,一个资****源对象可以定义任意数量的 Label,同一个 Label 也可以被添加到任意数量的资源对 象上。
Label 通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除。可以通过给指定的资源对象捆绑一个或多个不同的 Label 来实现多维度的资源分组管理功能,以便灵活、方便地进行资源分配、调度、配置、 部署等管理工作。
1.2.2定义标签
说明 :标签一般在文件中定义,且一个资源可以拥有多个标签 。如果创建资源时,没有定义资源,K8s集群会自动为该资源分配一个标签使用。注意,同一个资源中,不能定义两个相同的key。
bash
labels:
app: "web"
web: "test"

定义多个标签:


1.2.3查看标签
bash
kubectl get 资源类型 资源名称 --show-labels

1.3筛选器------Selector
1.3.1简单介绍
概念 :Label Selector(标签选择器)查询和筛选拥有某些 Label 的资源对象,Kubernetes 通过这种方式实现了类似 SQL 的简单又通用的对象查询机制。在Service中,通过筛选这个Label,来确定EndpointSlice列表(后端Pod组列表)。将所有具备这个Label标签的Pod全部加入到EndPointSlice列表当中,即进入负载调度当中。
当前有两种 Label Selector 表达式:**基于等式的(**Equality-based)Selector 表达式和基于集合的(Set-based)Selector 表达式。
等式表达式:
1.name=redis-slave:匹配所有具有 name=redis-slave 标签的资源对象。
2.env!= prod :匹配所有不具有 env=prod 标签的资源对象,比如"env=test"就是满足此条件的标签之一。
集合操作类表达式:
1.name in (redis-master,redis-slave):匹配所有具有 name=redis-master 标签或者name=redis-slave 标签的资源对象。
**2.name not in (php-frontend):**匹配所有不具有 name=php-frontend 标签的资源对象。
1.3.2定义筛选项
说明 :筛选项用来匹配对应资源的标签,如果匹配成功就会加入到EndpointSlice(后端Pod地址列表)。同样地,筛选项也可以定义多个。
bash
selector:
app: "db"

1.3.3查看筛选项
bash
kubectl get svc 资源名称 -o wide #仅查看service的筛选项,看不到后端PodIP
kubectl describe svc 资源名称 #查看详细信息


1.4后端切片------EndpointSlice
1.4.1简单介绍
概念 :Endpoints 和 EndpointSlice 都是 K8s 里用来记录 Service 后端 Pod 实际地址 的对象。Service 通过 selector 选出 Pod 后,真正记录这些 Pod 的 IP:Port 的,是 Endpoints 对象。
你可以把它们理解成 Service 的"后端清单"------Service 是"门面",Endpoints/EndpointSlice 是"门面背后真实干活的 Pod 列表"。

1.4.2查看后端切片的Pod列表
bash
kubectl get endpointslices.discovery.k8s.io
kubectl get endpoints #将被弃用

bash
kubectl describe svc 资源名称

1.5软件型定义网络------kube-proxy
1.5.1简单介绍
1.概述 : K8s 里负责实现 Service 转发规则 的组件,它在每个节点上都会存在,负责配置转发规则 。他是软件型定义网络,一经创建,所有节点上生效。存在两种工作模式。
2.工作机制 :它以deployment形式存在,持续监听API-server上的Service和EndpointSlice。一旦 Service 或 Endpoints 变化,kube-proxy 立刻在本节点更新转发规则(iptables 或 IPVS),把发往 ClusterIP 的流量转发到后端 Pod。

2. 负载均衡可以通过两种模式实现**:Linux IPVS和Linux IPTABLES。**
1.IPVS :基于IPVS规则 做转发,支持多种负载均衡算法(推荐)
2.IPTABLES :用 iptables 规则 做转发,因为他是基于多条规则做的转发,当存在大量规则时,转发负担变重,因此不适合大规模集群
1.5.2查看当前的kube-proxy模式
1.通过命令查看
**说明:**筛选出yaml文件中的流量转发模式
bash
kubectl get cm -n kube-system kube-proxy -o yaml | grep mode

2.访问本地的10249端口
bash
curl localhost:10249/proxyMode

3.查看流量转发规则:
bash
ipvsadm -Ln

1.5.3修改流量转发规则
修改kube-proxy配置:
说明:一经修改,所有节点上生效
bash
kubectl edit configmap -n kube-system kube-proxy
kubectl rollout restart daemonset -n kube-system kube-proxy
curl localhost:10249/proxyMode




二.集群内部IP------Cluster IP
2.1简单介绍
1.概念 :默认类型,为k8s集群内部Pod提供统一的流量入口 ,处于集群内部的node和Pod都能够访问该入口,集群外部无法访问。相比于一个一个访问PodIP,通过访问ClusterIP能够实现将流量负载均衡到每一个后端Pod上,且不用担心Pod的IP变化。Service通过标签,自动检测到新Pod的IP,并更新到EndpointSlice后端切片中。

2.2创建Cluster IP
1.编写yaml文件
bash
cd yml/service/
vim service-db.yml

添加以下内容:
bash
apiVersion: "v1"
kind: "Service" #Service资源类型
metadata:
name: "database-service"
spec:
type: "ClusterIP" #模式
ports:
- protocol: "TCP" #使用TCP协议
port: 3306 #自身对外暴露端口
selector: #筛选标签
app: "db"
sessionAffinity: ClientIP #表示根据客户端的 IP 做会话保持,同一个客户端 IP 的请求会一直被转发到同一个 Pod
sessionAffinity:会话亲和性,决定同一个客户端的请求是否总是转发到同一个后端 Pod

运行容器:
bash
kubectl apply -f service-db.yml
kubectl describe svc database-service


2.3通过ClusterIP 访问后端数据库
说明:通过之前的标签筛选,访问Service的IP或者Service的资源名称,即可访问到匹配app=db的Pod。
bash
kubectl apply -f pod-web.yml #运行前端容器
kubectl apply -f pod-db.yml #运行后端容器
pod-web.yml:
bash
apiVersion: "v1"
kind: "Pod"
metadata:
name: "webserver"
labels:
app: "web"
web: "test"
spec:
containers:
- name: "apache-frontend"
image: "reg.westos.org/library/httpd:dns"
ports:
- containerPort: 80
前端容器内部的/var/www/cgi-bin/action文件:
bash
#!/usr/bin/python
# -*- coding: utf-8 -*-
import MySQLdb as mdb
import os
con = mdb.connect('database-service', 'root', 'redhat', 'mysql') #定义的后端数据库的IP是Service的域名
with con:
cur = con.cursor()
cur.execute("SELECT User FROM user")
rows = cur.fetchall()
print 'Content-type:text/html\r\n\r\n'
print '<html>'
print '<head>'
print '<title>My Application</title>'
print '</head>'
print '<body>'
print '<h1>' + 'Here comes the list of database users:' + '</h1>'
for row in rows:
print '<h2>' + row[0] + '</h2>'
print '</body>'
print '</html>'
前端的环境变量:
bash
env |grep DATABASE_SERVICE #当前的后端数据库服务IP是当前的Service资源


pod-db.yml:
bash
apiVersion: "v1"
kind: "Pod"
metadata:
name: "database"
labels:
app: "db" #标签app=db,用来匹配Service
spec:
containers:
- name: "db-backend"
image: "reg.westos.org/library/mysql"
env:
- name: MYSQL_ROOT_PASSWORD
value: redhat
ports:
- containerPort: 3306
测试前端能否通过Service连接到后端数据库:
bash
kubectl get pod webserver -o wide
curl 10.244.109.107/cgi-bin/action

重建后端数据库Pod,观察还能否成功访问:
说明:重建Pod后,Pod的IP地址更新,观察Service还能否连接到后端数据库
bash
kubectl get pod database -o wide
kubectl replace -f pod-db.yml --force
kubectl get pod database -o wide

成功连接:
bash
curl 10.244.109.107/cgi-bin/action

2.4集群外无法访问ClusterIP
集群外部访问:
bash
yum install -y telnet #下载端口检测软件
telnet 10.107.237.133 3306

集群内部访问:
bash
telnet 10.107.237.133 3306

三.集群内部的DNS服务------CoreDNS
3.1简单介绍
1.概念 :CoreDNS主要负责集群内部的DNS解析服务 ,负责把 Service 名称解析成 ClusterIP,是集群服务发现的核心组件。
2.查看ClusterIP
每个 Pod 的 /etc/resolv.conf 都指向 CoreDNS 的 ClusterIP。Pod内部查看
bash
cat /etc/resolv.conf

3.2工作机制
服务发现 :每当创建一个Service资源以后,通过APIserver将资源写入到etcd当中存。CoreDNS通过APIserver监听Service资源和对应的后端切片,动态生成或更新DNS记录,并自动创建A资源记录解析地址,该过程是自动完成的。后面的Pod访问这个Service域名后,会自动解析对应的ClusterIP,通过kube-proxy将流量转发到后端的EndpointSlice列表中。

3.3核心功能
1.定义环境变量 :每个Pod中都会加入DNS解析,如(SVCNAME_SERVICE_HOST ,SVCNAME_SERVICE_PORT)
2.该组件通过kubernetes监控服务和端点
3.管理集群的DNS资源
3.1创建默认集群DNS名称,如cluster.local
3.2将DNS名称分发给命名空间,如default.svc.cluster.local
3.3为服务分配DNS名称,如svcname.default.svc.cluster.local
创建A类资源记录:ClusterIP,解析的是svc地址
访问方式 解析结果 database-service同命名空间内直接访问 database-service.default跨命名空间,指定 namespace database-service.default.svc加上 svc 标识 database-service.default.svc.cluster.local完整 FQDN pod-ip-dashes.default.pod.cluster.localPod 的 DNS 记录(如 10-244-1-5.default.pod.cluster.local)
3.4核心作用
1.服务发现(核心) :Kubernetes 中,Pod 的 IP 是动态的,会随着重建、扩缩容而变化。如果应用直接写死 IP,几乎无法维护 。CoreDNS 让应用可以用固定的名字访问后端服务
2.为 Pod 提供 DNS 解析能力:每个 Pod 启动时,kubelet 会自动配置它的 /etc/resolv.conf,把 DNS 指向 CoreDNS 的 ClusterIP。
3. 解析集群内部域名: CoreDNS 通过 kubernetes 插件,监听 K8s API,动态感知 Service 和 Pod 的变化,并生成对应的 DNS 记录。
4.转发集群外部域名 :对于集群外的域名(如 www.baidu.com),CoreDNS 通过 forward 插件把请求转发给上游 DNS,也就是把外部域名交给节点本身的 DNS 配置去解析。
5.缓存 DNS 结果: CoreDNS 内置 cache 插件,默认缓存 30 秒,减少重复查询,提升解析效率
四.节点端口------NodePort
4.1简单介绍
1.概念 :NodePort 是 Kubernetes Service 的一种类型,它通过在每个节点上开放一个端口 ,让集群外部的客户端可以访问集群内的 Service。
2.开放端口 :默认为30000-32767,如果需要扩展,需要自行打开限制
3.工作机制:
外部集群通过NodePort暴露的外部端口,访问集群内部的任意节点IP:Port,进入内部集群,访问集群内部Pod。

4.2创建NodePort
1.编写yaml文件
bash
vim service-web-np.yml

添加以下内容:
bash
apiVersion: "v1"
kind: "Service"
metadata:
name: "web-service"
spec:
type: "NodePort" #资源类型
ports:
- protocol: "TCP"
port: 80
nodePort: 30001 #暴露端口
selector:
app: "web"

运行NodePort:
bash
kubectl apply -f service-web-np.yml
kubectl get svc web-service

4.3通过NodePort访问Pod
1.集群内部访问Pod
bash
curl 10.96.223.251 #访问NodePort的Cluster IP

2.集群外访问Pod
说明 :集群外客户端访问k8s集群中的任意一个节点IP加暴露端口(Port),访问NodePort暴露的集群内部Pod。
bash
curl 192.168.7.163:30001
curl 192.168.7.164:30001
curl 192.168.7.165:30001

4.4修改NodePort的端口限制
说明:默认情况下,NodePort端口只允许开放30000到32767,超出限制的端口即使定义了也不会生效。需要我们修改APi-Server.yml的NodePort的端口限制。
1.设置暴露端口为33333
bash
apiVersion: "v1"
kind: "Service"
metadata:
name: "web-service"
spec:
type: "NodePort"
ports:
- protocol: "TCP"
port: 80
nodePort: 33333 #修改端口号为33333
selector:
app: "web"

bash
kubectl apply -f service-web-bp.yml

2.修改API-Server中设置的限制范围
编写kube-apiserver.yaml静态pod文件
bash
vim /etc/kubernetes/manifests/kube-apiserver.yaml #修改apiserver的静态yaml文件
添加以下内容:
bash
- --service-node-port-range=30000-50000 #修改限制范围为30000~50000

3.运行NodePort文件
bash
kubectl apply -f service-web-bp.yml
kubectl get svc web-server


报错:api-server文件修改后,会自动重启,在api-server重启成功前,运行service会报错。
解决:等待api-server重启完毕后,再运行service文件
4.验证端口能否访问
bash
curl 192.168.7.163:33333

浏览器访问:

五.外部访问IP------LoadBalancer
5.1简单介绍
1.LoadBalancer概念 :LoadBalancer 是 Kubernetes 中 Service 的一种类型,它的核心目标是:为集群内的 Service 提供一个外部可访问的、稳定的 IP 地址,并实现流量的负载均衡。 Load Balancer 模式适用云平台,裸金属环境需要安装 metallb 提供支持
2. 裸金属(Bare Metal): 指的是直接运行在物理硬件上的服务器,没有经过虚拟化层(如 VMware、KVM、Xen 等)的封装
3.MetalLB: 是一个纯软件的负载均衡器实现 ,专门为裸金属 K8s 集群 提供 LoadBalancer 类型的 Service 支持,主要负责分配EXTERNAL-IP。公有云的环境下,由云厂商负责分发公网IP。如果是自建机房,需要裸金属和MetalLB为LoadBalancer分发网络IP。
2.工作机制:
首先kubectl监测到存在LoadBalancer,需要分发外部访问IP。但是环境中没有云厂商分发IP,使用裸金属,通过MetalLB从IP网络池中分发一个IP(EXTERNAL-IP)给LoadBalancer。客户端通过访问这个LoadBalancer暴露的IP地址,访问集群内部的后端Pod。

5.2部署MetalLB
1.官网下载MetalLB
Installation :: MetalLB, bare metal load-balancer for Kubernetes
https://metallb.universe.tf/installation/2.启用kube-proxy的严格ARP 模式
说明 :kube-proxy 在 IPVS 模式 下,会默认把 Service 的 IP 绑定到一个名为 kube-ipvs0 的虚拟网卡上。这会导致每个节点都认为"自己拥有这个 IP",并会响应针对该 IP 的 ARP 请求。但 MetalLB 在 Layer 2 模式下的工作方式,恰恰是只让一个被选中的节点(Leader 节点)来响应 ARP 请求 。如果所有节点都来抢答,流量就会随机发往各个节点,而不是发往 MetalLB 指定的那个节点,导致连接不稳定或负载均衡失效。
bash
kubectl edit configmap -n kube-system kube-proxy

修改以下内容:
bash
strictARP: true

bash
kubectl -n kube-system get pod|grep kube-proxy | awk '{system("kubectl -n kube-system delete pod "$1"")}' #重启服务

3.下载yaml文件
bash
wget https://raw.githubusercontent.com/metallb/metallb/v0.16.1/config/manifests/metallb-native.yaml
sed -i 's#quay.io/#reg.westos.org/#g' metallb-native.yaml #修改镜像文件地址


4.上传Metlab镜像
4.1下载所需镜像
bash
docker pull quay.io/metallb/speaker:v0.16.1
docker pull quay.io/metallb/controller:v0.16.1


4.2新建项目

4.3修改镜像名并上传镜像
bash
docker images |grep metallb | awk '{print $1":"$2}' | awk -F/ '{system("docker tag"$0" reg.westos.org/metallb/"$3"")}'
docker images |grep 'reg.westos.org/metallb' | awk '{system("docker push "$1":"$2"")}'


5.部署MetalLB
bash
kubectl apply -f metallb-native.yaml
kubectl -n metallb-system get pod #查看服务状态


6.配置IP地址池和Layer 2模式
6.1编写IP地址池
bash
vim ip-address-pool.yml

添加以下内容:
bash
apiVersion: metallb.io/v1beta1
kind: IPAddressPool #定义IP地址池
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses:
- 192.168.7.100-192.168.7.150 #分配的网络IP范围(需与集群网络互通)
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement #设置Metallb为Layer 2 模式,该模式无需额外网络设备配置,通过 ARP/NDP 广播宣告 IP 所有权,适合单集群或简单网络环境
metadata:
name: default-l2-advert
namespace: metallb-system
spec:
ipAddressPools:
- default-pool

6.2运行yaml文件
bash
kubectl apply -f ip-address-pool.yml #应用
kubectl api-resources | grep metallb


5.3创建LoadBalancer
1.编写yaml文件
bash
vim service-web-lb.yml

添加以下内容:
bash
apiVersion: "v1"
kind: "Service"
metadata:
name: "web-service-lb"
spec:
allocateLoadBalancerNodePorts: false
type: "LoadBalancer" #Service种类
ports:
- name: "http"
protocol: "TCP"
port: 80
targetPort: 80
selector:
app: "web"

2.运行并查看LoadBalancer
bash
kubectl apply -f service-web-lb.yml
kubectl get svc web-service-lb

5.4通过LoadBalancer访问Pod
1.集群内部访问Pod
bash
curl 10.102.42.137 #集群IP
curl 192.168.7.100 #对外IP

2.集群外访问
bash
curl 10.102.42.137 #集群IP
curl 192.168.7.100 #对外IP

六.外部服务域名映射------externalname
6.1简单介绍
1.概念: 在 Kubernetes 中,ExternalName 是一种特殊类型的 Service,它不通过选择器(selector)关联任何 Pod,而是直接将****Service 映射到一个外部域名(FQDN ),用于将集群内部的服务请求转发到集群外部的域名。ExternalName 没有 ClusterIP,没有 EndpointSlice,不经过 kube-proxy。
2.工作机制 :将外部服务以域名的方式暴露在容器内部,CoreDNS 为这个 Service 生成了一条 CNAME 记录,相当于注册了一个CoreDNS域名解析 ,Pod内部的容器通过解析该域名,访问外部服务。

3.核心特点:
**1.无选择器,端点切片和端口映射:**externalname没有选择器,ClusterIP,EndpointSlice和端口映射,只有单纯的域名映射
2.域名映射:将外部服务以域名的方式暴露在Pod容器中。
4.适用场景
1.访问集群外的服务:将外部服务(如第三方API、监控等)以域名的方式暴露在集群内部,统一访问方式。
2.服务器迁移:将外部服务迁移到内部服务中,先通过Externalname指向外部地址,迁移完成后,修改Service,切换成Cluster模式。
3.简化配置:Pod内部容器只需配置域名,即可访问外部服务
6.2创建externalname
1.编写yaml文件
说明:将百度服务映射到Pod内
bash
vim service-externalname.yml

添加以下内容:
bash
apiVersion: v1
kind: Service
metadata:
name: my-service #内部域名
spec:
type: ExternalName #资源类型
externalName: www.baidu.com #外部服务

2.运行服务
bash
kubectl apply -f service-externalname.yml
kubectl get svc my-service

6.3进入容器内部,访问外部服务
bash
kubectl exec -it webserver -- bash
ping my-service

七.无头服务------Headless
7.1简单介绍
1.概念 :在 Kubernetes 中,Headless Service(无头服务)是一种特殊的 ClusterIP 类型 Service,核心特点是不分配集群虚拟****IP(ClusterIP ),而是直接通过 DNS 解析返回其关联的所有 Pod IP 列表,让客户端自主选择 Pod 进行通信。
2.核心特点:
1.不分配 ClusterIP :与普通 ClusterIP Service 最大区别,DNS 解析时不会返回单一虚拟 IP。
2.DNS 直接返回 Pod IP :客户端解析 Headless Service 名称时,DNS 会返回所有健康 Pod 的 IP 列表(A/AAAA 记录)。
3.支持自定义域名:结合 externalName 可实现更灵活的 DNS 配置(较少用)。
4.依赖 Endpoints/EndpointSlice :需通过 selector 关联 Pod,或手动创建 Endpoints 绑定 IP。
3.区别:
|------------|-----------------------|--------------|
| 维度 | Headless | Cluster IP类型 |
| Cluster IP | 无 | 分配集群内的虚拟IP |
| DNS解析 | 返回关联的所有Pod的IP列表 | 返回Cluster IP |
| 负载均衡 | 不提供,由客户端自行选择要访问的后端Pod | 内置负载均衡 |
| 适用场景 | 有状态服务、客户端自行选择Pod | 无状态服务,流量负载 |
7.2创建headless
1.编写yaml文件
bash
vim service-headless.yml

添加以下内容:
bash
apiVersion: "v1"
kind: "Service"
metadata:
name: "headless"
spec:
clusterIP: None #设定无集群IP,即确定类型为Headless
ports:
- protocol: "TCP"
port: 3306
selector:
app: "db"

2.运行服务
bash
kubectl apply -f service-headless.yml
kubectl get svc headless
kubectl get endpointslice | grep headless #此时后端Pod存在两个


7.3通过headless访问Pod
bash
kubectl exec -it webserver -- bash
ping headless


八.无尾服务------Tailness
8.1简单介绍
1.概念: headless是没有ClusterIP,tailness是对应的没有EndpointSlice。它的核心作用是:在没有选择器的情况下,手动创建后端endpointslice, 通过创建"Tailless"服务连接到现有基础设施。
8.2创建Tailness
1.编写yaml文件
bash
vim service-tailless.yml

添加以下内容:
bash
apiVersion: "v1"
kind: "Service"
metadata:
name: "tailless"
spec:
clusterIP: None #这里同时使用了无头服务
ports:
- name: dns
protocol: UDP
port: 53
#这里没有添加选择器

2.运行tailness服务
bash
kubectl apply -f service-tailless.yml
kubectl get svc tailless


8.3创建endpoint
1.编写yaml文件
bash
vim endpointslices-internet.yml

添加以下内容:
bash
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: tailless-1
labels:
kubernetes.io/service-name: tailless #通过服务资源名称,添加后端endpoint
addressType: IPv4
ports:
- name: dns
protocol: UDP
port: 53
endpoints:
- addresses:
- "114.114.114.114"

2.运行endpoint,查看是否成功添加Tailness服务中
bash
kubectl apply -f endpointslices-internet.yml
kubectl describe svc tailless
kubectl get endpointslices tailless-1



