k8s网络与应用
-
- [1. 先看网络位置关系](#1. 先看网络位置关系)
- [2. 容器端口只是声明](#2. 容器端口只是声明)
- [3. 用 Service 关联 Pod](#3. 用 Service 关联 Pod)
- [4. 外部访问方式一:NodePort](#4. 外部访问方式一:NodePort)
- [5. 外部访问方式二:LoadBalancer](#5. 外部访问方式二:LoadBalancer)
- [6. 外部访问方式三:Ingress](#6. 外部访问方式三:Ingress)
- [7. 调试时可以用 port-forward](#7. 调试时可以用 port-forward)
- [8. 常见踩坑点](#8. 常见踩坑点)
- [9. 推荐生产方案](#9. 推荐生产方案)
-
- [完整 YAML:Deployment + Service + Ingress](#完整 YAML:Deployment + Service + Ingress)
- 关键字段对照
- 访问链路
- 使用前需要改这几项
- 验证命令
- DNS解析配置
-
- [1. 先拿到 Ingress Controller 的外部 IP](#1. 先拿到 Ingress Controller 的外部 IP)
- [2. 配置 DNS 解析(A 记录)](#2. 配置 DNS 解析(A 记录))
- [3. 不同场景的解析方式](#3. 不同场景的解析方式)
-
- 场景一:公网域名(生产环境)
- [场景二:内网环境 / 自建集群(没有公网域名)](#场景二:内网环境 / 自建集群(没有公网域名))
- [场景三:云环境用 LoadBalancer 类型 Service](#场景三:云环境用 LoadBalancer 类型 Service)
- [4. 验证 DNS 是否生效](#4. 验证 DNS 是否生效)
- [5. 常见问题排查](#5. 常见问题排查)
- [6. 完整流程回顾](#6. 完整流程回顾)
场景:container里启动了java服务,端口是30201,来理解k8s的部署网络链路。
可以这样理解:容器里的端口只是"应用自己监听的端口",它不会自动对外暴露。要让外部访问,必须经过 Pod → Service → 外部入口这一层链路。
下面用 Java 服务 containerPort 30201 串一遍。
1. 先看网络位置关系
text
外部用户 / 浏览器 / 其他系统
↓
负载均衡器 / Ingress Controller / Node 端口
↓
Service(ClusterIP)
↓
Endpoints:一组 Pod 的 IP
↓
Pod
↓
Container
↓
Java 进程监听 0.0.0.0:30201
| 层级 | 作用 | 端口含义 |
|---|---|---|
| Container | 真正跑 Java 服务 | 30201 是应用监听端口 |
| Pod | 容器网络载体 | 与容器共享网络命名空间 |
| Service | 稳定访问入口 | port 是 Service 端口 |
| Ingress / LB | 外部流量入口 | 对外暴露域名或公网 IP |
2. 容器端口只是声明
Deployment 里这样写:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 3
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: java
image: java-app:latest
ports:
- containerPort: 30201
这里 containerPort: 30201 只是声明:这个容器会在 30201 端口提供服务。
它不会自动让外部访问。
3. 用 Service 关联 Pod
Service 通过 label 找到 Deployment 创建的 Pod,并给它们一个稳定入口:
yaml
apiVersion: v1
kind: Service
metadata:
name: java-app-svc
spec:
selector:
app: java-app
ports:
- name: http
port: 80
targetPort: 30201
protocol: TCP
type: ClusterIP
这里三个端口要分清:
| 字段 | 值 | 含义 |
|---|---|---|
containerPort |
30201 | 容器里 Java 进程监听的端口 |
targetPort |
30201 | Service 转发到 Pod 的端口 |
port |
80 | Service 在集群内的访问端口 |
所以集群内部其他 Pod 访问它时,用:
text
http://java-app-svc:80
流量会由 kube-proxy 转发到某个 Pod 的 30201 端口。
4. 外部访问方式一:NodePort
如果集群没有云负载均衡器,可以用 NodePort:
yaml
apiVersion: v1
kind: Service
metadata:
name: java-app-svc
spec:
selector:
app: java-app
ports:
- port: 80
targetPort: 30201
nodePort: 30201
protocol: TCP
type: NodePort
外部访问地址就是:
text
http://<任意节点IP>:30201
NodePort 默认范围是 30000-32767,所以这里刚好可以用 30201。
完整链路:
text
外部访问 NodeIP:30201
→ kube-proxy 转发到 Service ClusterIP:80
→ 转发到某个 Pod IP:30201
→ 进入容器,到达 Java 进程
5. 外部访问方式二:LoadBalancer
如果是阿里云、AWS、华为云这类云环境,可以直接用:
yaml
type: LoadBalancer
云平台会自动分配一个外部 IP。
访问方式:
text
http://<负载均衡器外部IP>:80
它底层通常也是转发到 NodePort,再交给 kube-proxy 路由到 Pod。
6. 外部访问方式三:Ingress
如果对外提供 HTTP 服务,更推荐用 Ingress:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: java-app-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: java-app-svc
port:
number: 80
链路是:
text
外部访问 api.example.com
→ DNS 解析到 Ingress Controller IP
→ Ingress Controller 按域名/路径转发
→ Service java-app-svc:80
→ Pod IP:30201
→ Container 30201
Ingress 适合多服务共用 80/443 端口,也方便做域名、HTTPS、灰度发布。
7. 调试时可以用 port-forward
如果只是临时验证 Java 服务是否正常,不需要暴露给外网:
bash
kubectl port-forward deploy/java-app 30201:30201 -n cubex-base
然后本地访问:
bash
curl http://localhost:30201
这只是调试用,不是生产暴露方式。
8. 常见踩坑点
- Java 进程必须监听
0.0.0.0:30201,不能只监听127.0.0.1,否则其他 Pod 和 Service 都访问不到。 - targetPort 必须等于 containerPort,除非容器里另有端口映射。
- NodePort 端口必须在 30000-32767 之间,且节点防火墙要放开。
- Pod IP 会变,所以外部不要直接访问 Pod IP,要通过 Service。
- Deployment replicas=3 时,Service 会自动把流量负载均衡到三个 Pod。
9. 推荐生产方案
| 场景 | 推荐方式 |
|---|---|
| 本地测试 | kubectl port-forward |
| 内网/测试集群 | NodePort |
| 云上生产 HTTP 服务 | Ingress + ClusterIP Service |
| 云上生产 TCP 服务 | LoadBalancer |
| 自建机房生产 | Ingress + MetalLB / 外部 Nginx |
Java 服务如果是 HTTP 接口,最推荐:ClusterIP Service + Ingress ;如果只是临时访问,用 NodePort 最快。
下面是一份可直接套用的模板,按你之前的场景设计:命名空间 cubex-base,容器里 Java 服务监听 30201。
完整 YAML:Deployment + Service + Ingress
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
namespace: cubex-base
labels:
app: java-app
spec:
replicas: 3
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: java
image: java-app:latest
ports:
- containerPort: 30201
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
livenessProbe:
httpGet:
path: /actuator/health
port: 30201
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 30201
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: java-app-svc
namespace: cubex-base
spec:
selector:
app: java-app
ports:
- name: http
protocol: TCP
port: 80
targetPort: 30201
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: java-app-ingress
namespace: cubex-base
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
ingressClassName: nginx
rules:
- host: java-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: java-app-svc
port:
number: 80
保存为 java-app.yaml 后执行:
bash
kubectl apply -f java-app.yaml
关键字段对照
| 位置 | 字段 | 值 | 含义 |
|---|---|---|---|
| Container | containerPort |
30201 | Java 进程实际监听端口 |
| Service | targetPort |
30201 | 转发到 Pod 的端口 |
| Service | port |
80 | 集群内访问 Service 的端口 |
| Ingress | backend.service.port.number |
80 | Ingress 转发到 Service 的端口 |
| Ingress | pathType |
Prefix | 路径前缀匹配 |
访问链路
text
外部访问 http://java-app.example.com
→ Ingress Controller
→ Service java-app-svc:80
→ Pod IP:30201
→ Container 30201
→ Java 服务
使用前需要改这几项
image: java-app:latest换成实际镜像。host: java-app.example.com换成真实域名。/actuator/health如果 Java 服务没有这个健康检查路径,换成/health、/或实际接口。ingressClassName: nginx要和你集群里实际安装的 Ingress Controller 一致,可以用kubectl get ingressclass确认。- 如果要用 HTTPS,需要在 Ingress 里加
tls字段,并提前创建 TLS Secret。
验证命令
bash
kubectl get deploy,svc,ingress -n cubex-base
kubectl get endpoints java-app-svc -n cubex-base
kubectl get ingress java-app-ingress -n cubex-base
如果集群还没有 Ingress Controller,需要先安装 Nginx Ingress Controller;如果是在云上,也可以直接用 LoadBalancer 类型 Service 暴露。
模板中的域名 java-app.example.com 需要解析到 Ingress Controller 的公网 IP 才能访问。
DNS解析配置
下面讲一下如何让域名解析到 Ingress Controller,让外部能通过域名访问服务。
1. 先拿到 Ingress Controller 的外部 IP
bash
# 查看 Ingress Controller 的 Service
kubectl get svc -n ingress-nginx
# 或者查看所有 ingressclass
kubectl get ingressclass
# 查看 ingress 资源
kubectl get ingress -A
你会看到类似这样的输出:
text
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx LoadBalancer 10.96.100.50 203.0.113.10 80:30080/TCP,443:30443/TCP 5d
关键看 EXTERNAL-IP 那一列:
- 如果有公网 IP(如
203.0.113.10),直接用这个 IP 做 DNS 解析。 - 如果是
<pending>,说明云平台还没分配好,等一会儿再查。 - 如果是内网 IP(如
10.x.x.x),那这个 IP 只有集群内部网络能访问。
2. 配置 DNS 解析(A 记录)
拿到 IP 后,在域名服务商(阿里云、腾讯云、CloudFlare 等)的 DNS 控制台添加一条 A 记录:
| 记录类型 | 主机记录 | 记录值 | TTL |
|---|---|---|---|
| A | java-app |
203.0.113.10 |
默认或 600 |
这样 java-app.example.com 就会解析到 203.0.113.10。
如果 Ingress 里写的是
host: java-app.example.com,那主机记录填java-app即可。如果想用通配符覆盖所有子域名,可以填*。
3. 不同场景的解析方式
场景一:公网域名(生产环境)
在域名服务商控制台添加 A 记录,解析到 Ingress Controller 的公网 IP。
text
java-app.example.com → A 记录 → 203.0.113.10
api.example.com → A 记录 → 203.0.113.10
DNS 生效后,外部就能通过 http://java-app.example.com 访问服务。
场景二:内网环境 / 自建集群(没有公网域名)
方式 A:修改客户端 /etc/hosts(临时测试)
在你要访问的机器上编辑 /etc/hosts:
203.0.113.10 java-app.example.com
Windows 路径:
C:\Windows\System32\drivers\etc\hosts
方式 B:集群内部 DNS(CoreDNS)
如果你希望集群内的其他 Pod 也能通过域名访问,确保 Ingress Controller 的域名能在集群内部解析。通常 CoreDNS 会自动处理 ClusterIP 的 DNS,但 Ingress 的 host 字段需要你在集群内部也配置 DNS 指向 Ingress Controller 的 IP。
可以在 kube-system 命名空间下创建一个 ConfigMap 来添加自定义 DNS 规则,或者直接用 Service 的短域名:
bash
# 集群内 Pod 访问 Java 服务,直接用 Service 域名
curl http://java-app-svc.cubex-base.svc.cluster.local:80
# 或者简写(同命名空间下)
curl http://java-app-svc:80
场景三:云环境用 LoadBalancer 类型 Service
如果你用的是云提供商(阿里云 ACK、AWS EKS 等),创建 type: LoadBalancer 的 Service 后,云平台会自动给你一个公网 IP 或域名。
bash
kubectl get svc -n cubex-base
输出可能是一个公网 IP,也可能是一个云厂商分配的域名(如 a1234567890.cn-north-1.elb.amazonaws.com.cn)。
- 如果是 IP:按上面的方式加 A 记录。
- 如果是域名 :加一条 CNAME 记录 指向它(注意 CNAME 不能和 A 记录混用)。
4. 验证 DNS 是否生效
bash
# Linux / Mac
nslookup java-app.example.com
# 或
dig java-app.example.com
# Windows
nslookup java-app.example.com
期望输出:
text
Name: java-app.example.com
Address: 203.0.113.10
如果解析到的 IP 和 Ingress Controller 的 EXTERNAL-IP 一致,说明 DNS 配置正确。
5. 常见问题排查
| 问题 | 可能原因 | 解决 |
|---|---|---|
nslookup 超时 |
DNS 未生效或记录写错 | 检查域名服务商控制台记录是否正确,DNS 生效通常需要几分钟 |
| 能 ping 通但浏览器打不开 | 防火墙/安全组没放行 80/443 | 检查云平台安全组规则,放行 80 和 443 端口 |
| 解析到内网 IP 外网打不开 | 用了内网 IP 做 A 记录 | 公网域名必须解析到公网 IP |
| 多个域名只解析到一个 | 所有域名 A 记录指向同一个 IP 是正常的 | Ingress 根据 host 字段区分不同服务,不需要多个 IP |
| HTTPS 证书不匹配 | TLS 证书没配或域名不对 | 在 Ingress 的 tls 字段中指定正确的 secret 和域名 |
6. 完整流程回顾
text
1. kubectl get svc -n ingress-nginx → 拿到 EXTERNAL-IP
2. 域名服务商控制台 → 添加 A 记录:java-app.example.com → IP
3. 等待 DNS 生效(几分钟)
4. nslookup java-app.example.com → 验证解析正确
5. 浏览器访问 http://java-app.example.com → 成功
如果 Ingress Controller 是通过 NodePort 方式部署的(没有 LoadBalancer IP),那还需要把节点 IP 的 80/443 端口在安全组里放行,然后 DNS 解析到节点 IP 即可。