智能
助手
最大化  清空记录 停止  历史记录
翻译选中文本
选中一段文本后进行翻译
名词解释
选中一段文本后进行名词解释
知识图谱生成
通过图谱展示知识信息
登录用户在知识浏览页面可用
答案生成
AI自动回答一个问答功能中的问题
登录用户在问答浏览页面,且问题开放回答中可用
知识摘要
自动为当前知识生成摘要
知识浏览页面可用
知识问答
针对当前知识进行智能问答
知识浏览面可用
2026-01-21 20:46:57 版本 : 6.8.2 svc(Service)服务发现与负载均衡(ClusterIP/NodePort/ExternalName/LoadBalancer)案例
作者: 文艺范儿 于 2026年01月03日 发布在分类 / Docker & K8S / K8S基础 下,并于 2026年01月21日 编辑
 历史版本

备注 修改日期 修改人
内容更新 2026-01-21 20:47:21[当前版本] 文艺范儿
内容更新 2026-01-21 20:46:57 文艺范儿
创建版本 2026-01-03 22:54:01 文艺范儿

6.8.2 svc(Service)服务发现与负载均衡(ClusterIP/NodePort/ExternalName/LoadBalancer)案例

简介

Service 是 Kubernetes 中暴露 Pod 稳定访问入口的资源对象,核心作用:

  • 服务发现:通过固定 DNS 名称(如 my-svc.default.svc.cluster.local)或 ClusterIP 访问后端 Pod,避免直接使用易变的 Pod IP。
  • 负载均衡:将请求均匀分发到匹配的 Pod 副本(默认轮询算法)。
  • 解耦访问与 Pod 生命周期:Pod 重建/扩缩容后,Service 自动更新 Endpoints(后端 Pod 列表)。
  • 可通过 iptables -t nat -L -n 查看 kube-proxy 规则

核心类型(本文聚焦):

类型 作用 访问范围 典型场景

ClusterIP

分配集群内虚拟 IP

仅集群内 Pod/节点可访问

内部服务通信(如前端 → 后端 API)

NodePort

在每个节点开放静态端口(30000-32767)

集群外可通过 节点IP:NodePort访问

测试环境外部访问、无 Ingress 时的临时暴露

LoadBalancer

将集群内的 Service 暴露到集群外部(公网或内网)

集群外可见:外部客户端(浏览器、手机、其他系统)可通过负载均衡器的 IP 或 DNS 访问集群内的服务。

对外提供 Web 服务<br />暴露 Ingress Controller<br />内网高可用访问<br />云环境中使用

ExternalName

为集群内的应用提供一种 稳定、可声明的方式来访问集群外的服务

集群内可见:Service 名称和 DNS 记录只在 Kubernetes 集群内部有效

访问云托管数据库<br />跨集群服务访问

案例1:ClusterIP 内部服务发现与负载均衡

场景:创建一个 Deployment 运行 3 个 Nginx Pod(标签 app=nginx-svc),再通过 ClusterIP 类型的 Service 暴露这些 Pod,实现:

  • 集群内其他 Pod 通过 Service 名称或 ClusterIP 访问 Nginx;
  • Service 自动负载均衡到 3 个 Pod。
# 1. 前置准备:创建 Deployment(Pod 后端)
[root@k8s-master-01 ~]# vim nginx-deploy-for-svc.yaml
[root@k8s-master-01 ~]# cat .
cat: .: Is a directory
[root@k8s-master-01 ~]# cat nginx-deploy-for-svc.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy-for-svc
  namespace: default
spec:
  replicas: 3  # 3 个 Pod 副本
  selector:
    matchLabels:
      app: nginx-svc  # 标签选择器,匹配 Pod
  template:
    metadata:
      labels:
        app: nginx-svc  # Pod 标签(需与 Service selector 一致)
    spec:
      containers:
      - name: nginx
        image: harbor.wyasw.com/library/nginx:1.21
        ports:
        - containerPort: 80  # 容器内暴露的端口(Service targetPort 需匹配)
# 部署 Deployment
[root@k8s-master-01 ~]# kubectl apply -f nginx-deploy-for-svc.yaml
[root@k8s-master-01 ~]# kubectl get pods -l app=nginx-svc  # 确认 3 个 Pod 均为 Running

# 2. 创建 ClusterIP 类型 Service
[root@k8s-master-01 ~]# vim nginx-svc-clusterip.yaml
[root@k8s-master-01 ~]# cat nginx-svc-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc-clusterip  # Service 名称(DNS 域名的一部分)
  namespace: default
  labels:
    app: nginx-svc
spec:
  type: ClusterIP  # 类型:集群内虚拟 IP
  selector:
    app: nginx-svc  # 标签选择器:匹配带有 app=nginx-svc 的 Pod(与 Deployment 的 Pod 标签一致)
  ports:
  - name: http  # 端口名称(可选,多端口时必须指定)
    port: 80    # Service 暴露的端口(集群内访问时使用,如 ClusterIP:80)
    targetPort: 80  # 后端 Pod 的容器端口(需与 Pod 内 containerPort 一致)
    protocol: TCP   # 协议(TCP/UDP/SCTP,默认 TCP)

# 创建Service
[root@k8s-master-01 ~]# kubectl apply -f nginx-svc-clusterip.yaml

# 3.验证service基本状态
# 查看 Service 详情(确认 ClusterIP、PORT(S))
[root@k8s-master-01 ~]# kubectl get svc nginx-svc-clusterip -o wide
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
nginx-svc-clusterip   ClusterIP   10.200.90.196   <none>        80/TCP    71s   app=nginx-svc


# 查看 Endpoints(Service 后端 Pod 的 IP:Port 列表,自动同步 Pod 变化)
[root@k8s-master-01 ~]# kubectl get endpoints nginx-svc-clusterip
NAME                  ENDPOINTS                                      AGE
nginx-svc-clusterip   10.100.1.27:80,10.100.2.37:80,10.100.2.38:80   90s

# 4. 创建测试数据
[root@k8s-master-01 ~]# kubectl get pods -o wide
NAME                                    READY   STATUS    RESTARTS   AGE     IP            NODE            NOMINATED NODE   READINESS GATES
nginx-deploy-for-svc-855cd8d787-9zktp   1/1     Running   0          11m     10.100.2.38   k8s-worker-02   <none>           <none>
nginx-deploy-for-svc-855cd8d787-p94l4   1/1     Running   0          11m     10.100.1.27   k8s-worker-01   <none>           <none>
nginx-deploy-for-svc-855cd8d787-s49dv   1/1     Running   0          11m     10.100.2.37   k8s-worker-02   <none>           <none>
test-pod                                1/1     Running   0          2m13s   10.100.2.39   k8s-worker-02   <none>           <none>
[root@k8s-master-01 ~]# 
[root@k8s-master-01 ~]# kubectl exec -it nginx-deploy-for-svc-855cd8d787-9zktp -- sh
# echo 1111111111111 > /usr/share/nginx/html/index.html
# exit
[root@k8s-master-01 ~]# kubectl exec -it nginx-deploy-for-svc-855cd8d787-p94l4 -- sh
# echo 22222222222 > /usr/share/nginx/html/index.html
# exit
[root@k8s-master-01 ~]# kubectl exec -it nginx-deploy-for-svc-855cd8d787-s49dv -- sh
# echo 33333333333 > /usr/share/nginx/html/index.html
# exit


# 5. 验证服务发现与负载均衡(集群内访问)
# Kubernetes 内置 DNS 服务(CoreDNS)会自动为 Service 创建 DNS 记录:
# 格式:<service-name>.<namespace>.svc.cluster.local(集群内全局唯一)
# 同一命名空间可简化为 <service-name>

# 创建临时测试 Pod 并访问 Service:
# 创建 busybox 测试 Pod
[root@k8s-master-01 ~]# kubectl run test-pod --image=harbor.wyasw.com/library/busybox:1.36 --rm -it -- sh

# # 在 Pod 内访问 Service(通过名称或 ClusterIP)
[root@k8s-master-01 ~]# kubectl run test-pod --image=harbor.wyasw.com/library/busybox:1.36 --rm -it -- sh
...
/ # wget -qO- http://nginx-svc-clusterip:80  # 通过 Service 名称访问
1111111111111
/ # wget -qO- http://nginx-svc-clusterip:80 
33333333333
/ # wget -qO- http://nginx-svc-clusterip:80 
22222222222
/ # wget -qO- http://10.200.90.196:80  # 通过 ClusterIP 访问
33333333333
/ # wget -qO- http://10.200.90.196:80 
22222222222
/ # wget -qO- http://10.200.90.196:80 
1111111111111
/ #

案例2:NodePort 外部访问(集群外流量接入)

场景描述:基于案例1的 Nginx Deployment,创建 NodePort 类型的 Service,允许集群外通过 节点IP:NodePort访问 Nginx(适用于测试环境或无 Ingress 控制器的场景)。

# 1. 配置yaml
[root@k8s-master-01 ~]# vim nginx-svc-nodeport.yaml
[root@k8s-master-01 ~]# cat nginx-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc-nodeport
  namespace: default
spec:
  type: NodePort  # 类型:NodePort(在每个节点开放静态端口)
  selector:
    app: nginx-svc  # 复用案例1的 Pod 标签(匹配 3 个 Nginx Pod)
  ports:
  - name: http
    port: 80        # Service 集群内端口(同 ClusterIP)
    targetPort: 80  # 后端 Pod 容器端口
    nodePort: 30080  # 节点开放的静态端口(可选,范围 30000-32767,不指定则随机分配)
    protocol: TCP

# 创建 NodePort Service
[root@k8s-master-01 ~]# kubectl apply -f nginx-svc-nodeport.yaml

# 2. 验证 NodePort 端口分配
[root@k8s-master-01 ~]# kubectl get svc nginx-svc-nodeport -o wide
NAME                 TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE   SELECTOR
nginx-svc-nodeport   NodePort   10.200.214.60   <none>        80:30080/TCP   36s   app=nginx-svc

# 3. 集群外访问验证(通过节点 IP)
# 通过 Master 节点 IP:NodePort 访问
[root@k8s-master-01 ~]# curl http://192.168.1.231:30080
22222222222
[root@k8s-master-01 ~]# curl http://192.168.1.231:30080
1111111111111
[root@k8s-master-01 ~]# curl http://192.168.1.231:30080
33333333333

# 通过 Worker 节点 IP:NodePort 访问(效果相同,流量会被转发到后端 Pod)
[root@k8s-master-01 ~]# curl http://192.168.1.233:30080
1111111111111
[root@k8s-master-01 ~]# curl http://192.168.1.233:30080
33333333333
[root@k8s-master-01 ~]# curl http://192.168.1.233:30080
22222222222

#多次执行 curl http://节点IP:30080,会看到返回的 Pod-内容轮询变化,证明 NodePort 负载均衡生效。

案例3:多端口 Service(区分不同服务)

场景描述:创建一个后端 Pod 同时暴露 HTTP(80)和 HTTPS(443)端口,通过 Service 的多端口配置分别映射,实现外部通过不同端口访问不同协议服务。

# 1. yaml配置
[root@k8s-master-01 ~]# vim multi-port-svc.yaml
[root@k8s-master-01 ~]# cat multi-port-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: multi-port-svc
spec:
  type: NodePort
  selector:
    app: multi-port-app  # 匹配后端 Pod 标签
  ports:
  - name: http  # 端口名称必须唯一(多端口时必填)
    port: 8080  # Service 集群内 HTTP 端口
    targetPort: 80   # 后端 Pod HTTP 容器端口
    nodePort: 30081  # 节点 HTTP 端口
    protocol: TCP
  - name: https
    port: 8443  # Service 集群内 HTTPS 端口
    targetPort: 443  # 后端 Pod HTTPS 容器端口
    nodePort: 30443  # 节点 HTTPS 端口
    protocol: TCP
---
# 后端 Pod(同时暴露 80 和 443 端口)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: multi-port-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: multi-port-app
  template:
    metadata:
      labels:
        app: multi-port-app
    spec:
      containers:
      - name: app
        image: harbor.wyasw.com/library/nginx:1.21  # 简化示例,实际需配置 HTTPS 证书
        ports:
        - containerPort: 80
        - containerPort: 443
# 部署
[root@k8s-master-01 ~]# kubectl apply -f multi-port-svc.yaml 

# 2. 部署后,通过不同 NodePort 访问不同端口:
# 访问 HTTP 服务
curl http://192.168.1.231:30081

# 访问 HTTPS 服务(忽略证书验证)
curl -k https://节点IP:30443

案例4:修改kube-proxy的工作模式为ipvs(生产推荐)

步骤1:在所有节点加载 IPVS 内核模块

IPVS 依赖内核模块,需在所有 Master 和 Worker 节点执行:

临时加载(重启失效)

# 加载核心模块
modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
modprobe nf_conntrack_ipv4  # ubuntu不需要 CentOS 7系统需要

# 验证模块是否加载成功
lsmod | grep ip_vs

输出应包含 ip_vsip_vs_rr等模块。

永久加载(开机自启)

为避免节点重启后模块丢失,需将模块加入开机加载配置:

CentOS/RHEL

# 创建模块加载配置文件
cat > /etc/modules-load.d/ipvs.conf << EOF
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack_ipv4
EOF

# 立即加载(无需重启)
systemctl restart systemd-modules-load.service

Ubuntu/Debian

# 编辑 /etc/modules 文件,追加模块名
echo -e "ip_vs\nip_vs_rr\nip_vs_wrr\nip_vs_sh\nip_conntrack" >> /etc/modules

# 立即加载
modprobe -a ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh ip_conntrack
步骤2:确认 kube-proxy 以 DaemonSet 方式运行

kube-proxy 通常以 DaemonSet 部署在集群中,需先找到其配置:

kubectl get daemonset kube-proxy -n kube-system -o yaml > kube-proxy-daemonset.yaml
步骤3:修改 kube-proxy 配置为 IPVS 模式

kube-proxy 模式通过 ConfigMap命令行参数 控制,推荐通过 ConfigMap 统一管理(适合 kubeadm 部署集群)。

方法1:通过 ConfigMap 修改(kubeadm 集群推荐)

# 1. 查看现有 kube-proxy ConfigMap
kubectl get configmap kube-proxy -n kube-system -o yaml > kube-proxy-configmap.yaml

# 2.编辑 ConfigMap,设置 mode: "ipvs" ,strictARP 为 true
sed -i "s/strictARP: false/strictARP: true/" kube-proxy-configmap.yaml
sed -i 's/mode: ""/mode: "ipvs"/' kube-proxy-configmap.yaml

# 3.应用 ConfigMap 更新
kubectl apply -f kube-proxy-configmap.yaml

# 4. 验证
kubectl -n kube-system describe cm kube-proxy | egrep "mode|strictARP"

方法2:直接修改 kube-proxy DaemonSet 启动参数(非 kubeadm 集群)

若集群非 kubeadm 部署(如二进制部署),可直接修改 kube-proxy 的启动参数:

kubectl edit daemonset kube-proxy -n kube-system

spec.template.spec.containers[0].command中添加 --proxy-mode=ipvs,例如:

containers:
- command:
  - /usr/local/bin/kube-proxy
  - --config=/var/lib/kube-proxy/config.conf
  - --proxy-mode=ipvs  # 直接指定模式(优先级高于 config.conf)
步骤4:重启 kube-proxy Pod 使配置生效

修改 ConfigMap/DaemonSet 后,需重启 kube-proxy Pod 加载新配置:

# 删除现有 kube-proxy Pod(DaemonSet 会自动重建)
kubectl delete pod -l k8s-app=kube-proxy -n kube-system

# 等待 Pod 重建并就绪
kubectl get pods -n kube-system -l k8s-app=kube-proxy -w
步骤5:验证 IPVS 模式是否生效
  1. 检查 kube-proxy Pod 日志

查看任意 kube-proxy Pod 日志,确认模式切换为 IPVS:

[root@k8s-master-01 ~]# kubectl get pods -n kube-system  -l  k8s-app=kube-proxy 
NAME               READY   STATUS    RESTARTS   AGE
kube-proxy-chkbg   1/1     Running   0          2m30s
kube-proxy-fngz8   1/1     Running   0          2m30s
kube-proxy-l9bnz   1/1     Running   0          2m30s
[root@k8s-master-01 ~]# 
[root@k8s-master-01 ~]# kubectl  -n kube-system logs kube-proxy-chkbg | grep  -A 2 "Using ipvs Proxier" 
I0115 14:47:20.562563       1 server_linux.go:189] "Using ipvs Proxier"
I0115 14:47:20.563439       1 proxier.go:353] "IPVS scheduler not specified, use rr by default" ipFamily="IPv4"
I0115 14:47:20.563858       1 proxier.go:353] "IPVS scheduler not specified, use rr by default" ipFamily="IPv6"
  1. 检查节点 IPVS 规则

在任意节点执行以下命令,查看 IPVS 虚拟服务(对应 Service):

# 安装 ipvsadm 工具(若未安装)
yum install -y ipvsadm  # CentOS/RHEL
apt-get install -y ipvsadm  # Ubuntu/Debian

# 查看 IPVS 规则
ipvsadm -Ln

正常输出示例(假设存在名为 kubernetes的 Service,ClusterIP: 10.100.0.1):

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.17.0.1:30094 rr
  -> 10.100.2.53:443              Masq    1      0          0         
TCP  172.17.0.1:32583 rr
  -> 10.100.2.53:80               Masq    1      0          0         
TCP  192.168.1.231:30094 rr
  -> 10.100.2.53:443              Masq    1      0          0         
TCP  192.168.1.231:32583 rr
  -> 10.100.2.53:80               Masq    1      0          0         
TCP  192.168.31.231:30094 rr
  -> 10.100.2.53:443              Masq    1      0          0         
TCP  192.168.31.231:32583 rr
  -> 10.100.2.53:80               Masq    1      0          0         
TCP  10.100.0.0:30094 rr
  -> 10.100.2.53:443              Masq    1      0          0         
TCP  10.100.0.0:32583 rr
  -> 10.100.2.53:80               Masq    1      0          0         
TCP  10.200.0.1:443 rr
  -> 192.168.1.231:6443           Masq    1      0          0         
TCP  10.200.0.10:53 rr
  -> 10.100.1.30:53               Masq    1      0          0         
  -> 10.100.1.31:53               Masq    1      0          0
  • Scheduler显示配置的算法(如 rr轮询)。
  • ForwardMasq(伪装模式,即 SNAT),符合 kube-proxy IPVS 默认行为。
  1. 验证 Service 流量转发

创建一个测试 Service,通过 ipvsadm观察连接是否正常转发:

# 部署测试 Deployment 和 Service
kubectl create deployment nginx-test --image=harbor.wyasw.com/test/nginx:v1 --replicas=2 -n default
kubectl expose deployment nginx-test --port=80 --target-port=80 -n default

# 查看 Service ClusterIP
kubectl get svc nginx-test -n default  # 假设 ClusterIP: 10.200.164.81

# 在节点访问 Service,触发连接
curl http://10.200.164.81

# 再次查看 IPVS 规则,ActiveConn/InActConn 会增加
ipvsadm -Ln | grep 10.200.164.81

# 删除测试环境
kubectl delete deployment nginx-test  -n default

案例5:MetalLB 部署及简介

简介

MetalLB 是 Kubernetes 的 开源负载均衡器实现,专为 Bare-Metal(裸金属)、虚拟机、私有云 等非云环境设计。

  • 核心作用:当在集群中创建 type: LoadBalancer的 Service 时,MetalLB 会自动为该 Service 分配一个 外部可访问的 IP 地址(从预先配置的 IP 池中),并配置相应的网络路由,使外部流量能够到达集群内的 Pod。
  • 背景:Kubernetes 原生的 LoadBalancer类型依赖云厂商的 cloud-controller-manager自动创建云负载均衡器(如 AWS ELB、阿里云 SLB)。在没有云厂商的环境中,这类 Service 会一直处于 <pending>状态,无法获取外部 IP。MetalLB 解决了这一痛点。
部署流程
# 1. 安装 MetalLB
# 使用官方 manifest 安装(包含 CRD、Controller、Speaker)
# 先在三个节点拉取镜像
docker pull quay.io/metallb/controller:v0.14.8
docker pull quay.io/metallb/speaker:v0.14.8
# 主节点部署
wget https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
kubectl apply -f metallb-native.yaml


# 2. 验证安装
kubectl get pods -n metallb-system
# 预期输出:
# NAME                          READY   STATUS    RESTARTS   AGE
# controller-55b679bf65-abcde    1/1     Running   0          2m
# speaker-xyz12                  1/1     Running   0          2m
# speaker-xyz34                  1/1     Running   0          2m
# speaker-jw2sh                  1/1     Running   0          2m

# 3. 配置 IP 地址池(AddressPool CRD)和 L2Advertisement(Layer 2 模式)
# 创建 IPAddressPool定义可用的外部 IP 范围:
vim metallb-ippool-l2adv.yaml
cat metallb-ippool-l2adv.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: prod-public-ips
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.210-192.168.1.220  # 按需修改为你的 IP 段
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2-adv
  namespace: metallb-system
spec:
  ipAddressPools:
  - prod-public-ips

# 部署
kubectl apply -f metallb-ippool-l2adv.yaml
测试
# 1.创建测试service
vim nginx-lb-test.yaml
cat nginx-lb-test.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-metallb-test
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
  type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: harbor.wyasw.com/test/nginx:v1
        ports:
        - containerPort: 80


# 2. 部署
kubectl apply -f nginx-lb-test.yaml

# 3.验证外部IP
kubectl get svc nginx-metallb-test
# 输出
# NAME                 TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
# nginx-metallb-test   LoadBalancer   10.200.197.241   192.168.1.211   80:30444/TCP   6s

# 基于metallb提供的地址池访问
curl http://192.168.1.211
# 基于NodePort访问
curl http://192.168.1.231:30444

案例6:基于loadBalancerIP方式传递配置

MetalLB 是 Bare-Metal Kubernetes 集群实现 LoadBalancer 服务的关键组件,通过 Layer 2(ARP/NDP)BGP 模式将外部流量引入集群,弥补了非云环境缺失云厂商负载均衡器的短板。

# 1. metallb自定义EXTERNAL-IP及NodePort端口
vim nginx-LoadBalancerIP-test.yaml
cat nginx-LoadBalancerIP-test.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-v2-metallb-test
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.1.212
  selector:
    app: nginx-v2
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30090
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test-v2
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-v2
  template:
    metadata:
      labels:
        app: nginx-v2
    spec:
      containers:
      - name: nginx
        image: harbor.wyasw.com/test/nginx:v2
        ports:
        - containerPort: 80

# 3.验证外部IP
kubectl get svc nginx-v2-metallb-test
# 输出
# NAME                 TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
# nginx-metallb-test   LoadBalancer   10.200.150.177   192.168.1.212   80:30090/TCP   6s

# 基于metallb提供的地址池访问
curl http://192.168.1.212
# 基于NodePort访问
curl http://192.168.1.231:30090

案例7:使用ExternalName映射K8S集群外部的服务

作用ExternalNameService 不代理或转发任何网络流量。它仅仅是在 Kubernetes 集群的 DNS 系统中,为集群内部的应用创建一个 别名(CNAME 记录),将这个别名指向一个外部的、完全限定域名(FQDN)。

工作原理:当集群内的应用尝试通过 ExternalNameService 的名称访问时,CoreDNS 会将其解析为 externalName字段中指定的外部地址。之后的所有网络通信都直接从应用 Pod 发往那个外部地址,完全绕过 Kubernetes 的网络组件(如 kube-proxy)

步骤 1:创建 ExternalName Service

我们定义一个 Service,它不包含任何选择器(selector),因为它不指向任何集群内的 Pod。它的全部意义在于 spec.externalName字段。

创建一个名为 external-postgres-service.yaml的文件:

apiVersion: v1
kind: Service
metadata:
  name: external-postgres
  namespace: default  # 建议与你的应用在同一个 Namespace
  labels:
    app: external-services # 用于管理和识别
spec:
  type: ExternalName
  externalName: pgm-xxxxxxxxx.pg.rds.aliyuncs.com # 【重要】替换为你的真实 RDS 地址
  ports:
  - port: 5432        # Service 端口,主要起文档化作用
    protocol: TCP
    targetPort: 5432  # 外部服务的端口,主要起文档化作用

应用这个配置

kubectl apply -f external-postgres-service.yaml
步骤 2:验证 DNS 解析

现在,我们可以创建一个临时的调试 Pod 来验证 Kubernetes DNS 是否正确地将我们的 Service 名称解析为了外部的 RDS 地址。

kubectl run -it --rm --image=busybox:1.36 dns-checker --restart=Never -- sh

在 busybox 容器内部,使用 nslookupping进行测试:

# 使用 nslookup 查询 Service 的完整 DNS 名称
nslookup external-postgres.default.svc.cluster.local

预期的命令输出

Name:   external-postgres.default.svc.cluster.local
Address: pgm-xxxxxxxxx.pg.rds.aliyuncs.com

请注意,返回的地址正是我们在 externalName中定义的 RDS 地址。这说明 DNS 别名已经成功创建。

你也可以用 ping来验证(尽管 PostgreSQL 不响应 ICMP 请求,但 DNS 解析会发生):

ping external-postgres

输出会类似于:

PING external-postgres.default.svc.cluster.local (pgm-xxxxxxxxx.pg.rds.aliyuncs.com): 56 data bytes

测试完成后,输入 exit退出并删除临时 Pod。

步骤 3:在应用程序中使用 Service 名称

这是最关键的一步。现在,你的 Django 应用(或其他任何应用)不再需要关心 RDS 的具体地址。它只需要使用标准的 Kubernetes 内部 DNS 名称来连接数据库。

Django 的 settings.py配置示例

旧的写法(硬编码外部地址,不推荐):

# settings.py - 不推荐的写法
import os

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydjango_db',
        'USER': 'django_user',
        'PASSWORD': os.environ.get('DB_PASSWORD'), # 密码仍然应该用 Secret 管理
        'HOST': 'pgm-xxxxxxxxx.pg.rds.aliyuncs.com', # 硬编码!难以维护。
        'PORT': '5432',
    }
}

新的写法(使用 Service 名称,推荐):

# settings.py - 推荐的写法
import os

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydjango_db',
        'USER': 'django_user',
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        # 核心改动:使用 Kubernetes Service 名称作为 HOST
        # K8s DNS 会自动将其解析为 RDS 的真实地址
        'HOST': 'external-postgres', 
        'PORT': '5432',
    }
}

注意:数据库密码等敏感信息依然应该使用 Kubernetes Secret 来存储和管理,并通过环境变量(如 DB_PASSWORD)注入到 Pod 中,这与是否使用 ExternalName无关。

步骤 4:生产环境的最佳实践与考量
  1. 管理变更:如果未来 RDS 实例发生迁移或更换,导致其地址(FQDN)发生变化,你只需要在 external-postgres-service.yaml文件中修改 externalName字段,然后重新执行 kubectl apply -f external-postgres-service.yaml即可。所有引用该 Service 的应用程序都无需进行任何代码或配置修改,实现了配置与代码的解耦。
  2. 处理网络连通性:确保你的 Kubernetes 集群节点和 Pod 能够访问外部 RDS 的地址和端口。这通常意味着:
    • 集群节点需要有通往 RDS 所在 VPC 或公网的路由。
    • 如果 RDS 在 VPC 内,需要确保集群和 VPC 之间有专线或 VPN 连接,或者 RDS 实例配置了公网访问(需注意安全)。
    • 确保安全组(Security Group)或防火墙规则允许从集群的 CIDR 块访问 RDS 的端口(如 5432)。
  3. 无健康检查ExternalNameService 不提供任何健康检查。Kubernetes 无法知道 pgm-xxxxxxxxx.pg.rds.aliyuncs.com这个外部服务是否真的在线。因此,应用程序本身必须具备处理数据库连接失败的能力(例如,通过重试、熔断等机制)。
  4. TLS/SSL 连接:如果外部服务(如 RDS)要求使用 TLS/SSL 加密连接,那么应用程序 Pod 内部需要能够验证 RDS 服务器的证书。你可能需要在 Pod 内部安装 CA 根证书,或者在应用配置中禁用证书验证(不推荐,会降低安全性)。

案例8:修改svc的NodePort的端口范围

默认情况下,这个范围是 30000-32767。出于安全策略(例如,为了避免与服务器上已有的服务端口冲突)或特定业务需求(例如,需要使用小于 30000 的端口以便于记忆和访问),我们可能需要修改这个范围。

核心原理:修改此范围不会改变已存在的 Service 的端口,它只会影响之后新创建的 NodePort Service。已存在的 Service 会一直保持它们原有的端口。

# 1. 定位 kube-apiserver 静态 Pod 清单文件

# 在主流的 Kubernetes 发行版(如 kubeadm)中,kube-apiserver是以 静态 Pod​ 的形式运行的。其配置文件通常位于 /etc/kubernetes/manifests/目录下。
# 进入 manifests 目录
cd /etc/kubernetes/manifests/

# 查看目录下的文件,寻找 apiserver 的清单文件
ls -l kube-apiserver.yaml

# 2. 备份原始配置文件

cp kube-apiserver.yaml kube-apiserver.yaml.bak_$(date +%F-%T)

# 3.编辑 kube-apiserver.yaml 文件

vim kube-apiserver.yaml

# 在文件中找到 spec.containers[0].command部分,这里定义了 kube-apiserver容器的所有启动参数。你需要在这个列表中查找或添加 --service-node-port-range参数。

# 情况 A:如果参数已存在
# 如果已经存在一行类似 - --service-node-port-range=30000-32767的配置,直接修改其值即可。

# 情况 B:如果参数不存在
# 如果列表中没有这个参数,你需要在其他 --开头的参数附近添加一行新的。

# 修改示例:
# 假设我们想将端口范围修改为 10000-20000。
 ...
 spec:
  containers:
  - command:
    - kube-apiserver
    - --service-node-port-range=10000-20000  # 进行添加这一行即可
 ...


# 4.应用配置并重启 kubelet
# 当你保存对 kube-apiserver.yaml文件的修改后,Kubernetes 的静态 Pod 管理机制(由 kubelet负责)会检测到文件的变化。它会自动终止当前的 kube-apiserverPod,并根据新的清单文件重新创建一个新的 Pod。
# 你可以通过以下命令观察这个过程:
# 在一个终端窗口监控 kube-apiserver pod 的重启
watch kubectl get pods -n kube-system -l component=kube-apiserver

# 在另一个终端窗口,你可以看到 kubelet 正在重新创建 Pod
# 通常几秒钟内就会完成
journalctl -u kubelet -f

# 等待片刻,直到 kube-apiserverPod 的状态再次变为 Running。

# 5. 创建测试应用
历史版本-目录  [回到顶端]
    文艺知识分享平台 -V 5.2.5 -wcp