Java 部署:K8s Service 与 Ingress 配置(外部访问)
👋 大家好,欢迎来到我的技术博客!
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕Java部署这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!
文章目录
- Java 部署:K8s Service 与 Ingress 配置(外部访问) 🚀
Java 部署:K8s Service 与 Ingress 配置(外部访问) 🚀
在现代云原生应用架构中,将 Java 应用部署到 Kubernetes(简称 K8s)集群已成为行业标准。然而,仅仅将应用容器化并运行在 Pod 中远远不够——如何让外部用户或系统安全、高效地访问这些服务,是每个开发者必须掌握的核心技能。本文将深入探讨 Kubernetes 中 Service 和 Ingress 的原理、配置方式及其在 Java 应用外部访问中的实际应用,并通过完整的代码示例和架构图,帮助你构建一个可生产级的部署方案。
无论你是刚接触 K8s 的 Java 开发者,还是已有一定经验但希望系统梳理网络暴露机制的工程师,这篇文章都将为你提供清晰、实用的指导。让我们从基础开始,逐步深入,最终实现一个可通过公网访问的 Spring Boot 应用。🌐
为什么需要 Service 和 Ingress?🤔
在 Kubernetes 中,Pod 是最小的调度单元,但它们具有动态性:IP 地址会随着 Pod 的创建和销毁而变化。如果直接依赖 Pod IP 进行通信,系统将极其脆弱。因此,Kubernetes 引入了 Service 资源,为一组 Pod 提供稳定的网络端点。
然而,Service 默认仅在集群内部可达。若要让外部用户(如浏览器、移动端或其他外部服务)访问你的 Java 应用,就需要进一步的网络暴露机制。这就是 Ingress 发挥作用的地方。
简而言之:
- Service:解决集群内部服务发现与负载均衡。
- Ingress:解决集群外部流量路由与 HTTPS 终止等高级功能。
💡 小贴士:Ingress 并不是 Service 的替代品,而是其上层抽象。Ingress 通常通过 Ingress Controller(如 Nginx、Traefik)将外部请求转发到后端的 Service。
准备工作:一个简单的 Java Web 应用 🧪
在深入 K8s 配置之前,我们先准备一个轻量级的 Spring Boot 应用作为示例。该应用将暴露两个 HTTP 接口,用于验证部署是否成功。
项目结构
java-k8s-demo/ ├── pom.xml └── src/ └── main/ ├── java/ │ └── com/example/demo/ │ ├── DemoApplication.java │ └── ApiController.java └日晚间 resources/ └── application.properties Maven 依赖(pom.xml)
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>java-k8s-demo</artifactId><version>1.0.0</version><packaging>jar</packaging><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>主启动类(DemoApplication.java)
packagecom.example.demo;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublicclassDemoApplication{publicstaticvoidmain(String[] args){SpringApplication.run(DemoApplication.class, args);}}控制器(ApiController.java)
packagecom.example.demo;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importjava.net.InetAddress;importjava.net.UnknownHostException;@RestControllerpublicclassApiController{@GetMapping("/health")publicStringhealth(){return"OK";}@GetMapping("/info")publicStringinfo(){try{String hostname =InetAddress.getLocalHost().getHostName();return"Hello from Java app! Host: "+ hostname;}catch(UnknownHostException e){return"Hello from Java app! Host: unknown";}}}配置文件(application.properties)
server.port=8080 构建与测试本地运行
执行以下命令打包应用:
mvn clean package -DskipTests 生成的 JAR 文件位于 target/java-k8s-demo-1.0.0.jar。本地运行验证:
java -jar target/java-k8s-demo-1.0.0.jar 访问 http://localhost:8080/health 应返回 OK,http://localhost:8080/info 返回包含主机名的信息。✅
容器化 Java 应用 🐳
Kubernetes 运行的是容器,因此我们需要将 Java 应用打包成 Docker 镜像。
编写 Dockerfile
# 使用官方 OpenJDK 17 镜像作为基础 FROM openjdk:17-jdk-slim # 设置工作目录 WORKDIR /app # 将 JAR 文件复制到容器中 COPY target/java-k8s-demo-1.0.0.jar app.jar # 暴露端口 EXPOSE 8080 # 启动应用 ENTRYPOINT ["java", "-jar", "app.jar"] 构建镜像
docker build -t java-k8s-demo:1.0.0 .🔒 安全建议:生产环境中应使用多阶段构建以减小镜像体积,并避免将构建工具留在最终镜像中。
推送镜像到镜像仓库
为了在 K8s 集群中拉取镜像,需将其推送到可访问的镜像仓库(如 Docker Hub、阿里云 ACR、AWS ECR 等)。假设你已登录 Docker Hub:
docker tag java-k8s-demo:1.0.0 your-dockerhub-username/java-k8s-demo:1.0.0 docker push your-dockerhub-username/java-k8s-demo:1.0.0 ⚠️ 注意:后续 YAML 配置中需使用你实际推送的镜像名称。
Kubernetes Deployment:运行 Java 应用 📦
Deployment 是管理 Pod 生命周期的核心资源。它确保指定数量的 Pod 副本始终运行,并支持滚动更新、回滚等操作。
deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata:name: java-app-deployment labels:app: java-app spec:replicas:3selector:matchLabels:app: java-app template:metadata:labels:app: java-app spec:containers:-name: java-app image: your-dockerhub-username/java-k8s-demo:1.0.0 ports:-containerPort:8080resources:requests:memory:"256Mi"cpu:"200m"limits:memory:"512Mi"cpu:"500m"livenessProbe:httpGet:path: /health port:8080initialDelaySeconds:10periodSeconds:15readinessProbe:httpGet:path: /health port:8080initialDelaySeconds:5periodSeconds:10应用 Deployment
kubectl apply -f deployment.yaml 验证 Pod 是否正常运行:
kubectl get pods -l app=java-app 你应该看到 3 个处于 Running 状态的 Pod。
Service:集群内部的服务发现 🔍
现在 Java 应用已在集群中运行,但外部无法访问。首先,我们创建一个 ClusterIP 类型的 Service,这是默认类型,仅在集群内部可达。
service.yaml
apiVersion: v1 kind: Service metadata:name: java-app-service labels:app: java-app spec:type: ClusterIP selector:app: java-app ports:-protocol: TCP port:80targetPort:8080关键字段说明:
selector: 匹配带有app=java-app标签的 Pod。port: Service 对外暴露的端口(集群内其他服务通过此端口访问)。targetPort: Pod 中容器监听的端口(即 Java 应用的 8080)。
应用 Service:
kubectl apply -f service.yaml 验证 Service:
kubectl get svc java-app-service 输出类似:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE java-app-service ClusterIP 10.96.123.45 <none> 80/TCP 10s 此时,集群内的其他 Pod 可通过 http://java-app-service:80/health 访问该服务。
外部访问方案一:NodePort 🌐
如果你的 K8s 集群运行在裸机或 VM 上(非托管云服务),NodePort 是最简单的外部访问方式。它会在每个节点的特定端口(默认 30000-32767)上开放服务。
修改 Service 类型为 NodePort
apiVersion: v1 kind: Service metadata:name: java-app-service spec:type: NodePort selector:app: java-app ports:-protocol: TCP port:80targetPort:8080nodePort:30080# 可选,若不指定则自动分配应用后,获取节点 IP(假设为 192.168.1.100),即可通过 http://192.168.1.100:30080/health 访问应用。
⚠️ 局限性:NodePort 不适合生产环境,原因包括:端口号不友好(非 80/443)无法基于域名路由缺乏 TLS 终止等高级功能
外部访问方案二:LoadBalancer(云环境)☁️
在 AWS、GCP、Azure 等云平台上,将 Service 类型设为 LoadBalancer 会自动创建一个云负载均衡器,并分配公网 IP。
apiVersion: v1 kind: Service metadata:name: java-app-service spec:type: LoadBalancer selector:app: java-app ports:-protocol: TCP port:80targetPort:8080应用后,执行:
kubectl get svc java-app-service 等待几分钟,EXTERNAL-IP 字段将从 <pending> 变为实际 IP。通过该 IP 即可访问应用。
💡 优势:简单、自动集成云平台。
❌ 缺点:每个 Service 对应一个 LB,成本高;不支持基于路径或主机名的路由。
外部访问方案三:Ingress(推荐)🔥
Ingress 是 Kubernetes 官方推荐的外部流量入口方案。它通过一个统一的入口(通常由 Ingress Controller 实现,如 Nginx Ingress Controller)将外部请求路由到不同的后端 Service,支持:
- 基于主机名(Host)的路由
- 基于路径(Path)的路由
- TLS/SSL 终止
- 负载均衡策略
- 速率限制等高级功能
Ingress 工作原理
HTTP Request
Routes to
Routes to
Internet User
Ingress Controller
Service A
Service B
Pod A1
Pod A2
Pod B1
Ingress Controller 本身是一个 Deployment + Service(通常是 LoadBalancer 或 NodePort),它监听外部流量,并根据 Ingress 规则转发请求。
安装 Nginx Ingress Controller
大多数云厂商提供托管 Ingress Controller,但你也可以手动安装。以 Helm 为例(需先安装 Helm):
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helm install ingress-nginx ingress-nginx/ingress-nginx 📚 更多安装方式请参考 官方文档。
安装完成后,获取 Ingress Controller 的外部 IP:
kubectl get svc -n ingress-nginx 你会看到一个名为 ingress-nginx-controller 的 Service,其 EXTERNAL-IP 即为公网入口。
配置 Ingress 资源 🛠️
现在,我们创建一个 Ingress 资源,将流量路由到 java-app-service。
ingress.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata:name: java-app-ingress annotations:nginx.ingress.kubernetes.io/rewrite-target: / spec:ingressClassName: nginx # 指定 Ingress Classrules:-host: java-app.example.com # 替换为你的域名http:paths:-path: / pathType: Prefix backend:service:name: java-app-service port:number:80关键点:
host: 必须是你拥有的域名,并已解析到 Ingress Controller 的 IP。pathType: Prefix: 表示路径前缀匹配(K8s 1.18+ 推荐)。ingressClassName: 指定使用的 Ingress Controller 类型(需与安装时一致)。
应用 Ingress:
kubectl apply -f ingress.yaml 验证 Ingress
kubectl get ingress java-app-ingress 输出应包含 HOSTS 和 ADDRESS 字段。
本地测试(无真实域名)
若无真实域名,可通过修改本地 /etc/hosts 文件模拟:
<INGRESS_CONTROLLER_IP> java-app.example.com 然后访问 http://java-app.example.com/health。
支持 HTTPS:TLS 终止 🔒
生产环境中,HTTPS 是必须的。Ingress 支持通过 TLS Secret 配置证书。
步骤 1:准备 TLS 证书
你可以使用 Let’s Encrypt 免费获取证书,或使用自签名证书测试。
生成自签名证书(仅用于测试):
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout tls.key -out tls.crt -subj "/CN=java-app.example.com"步骤 2:创建 Kubernetes Secret
kubectl create secret tls java-app-tls-secret \ --key tls.key \ --cert tls.crt 步骤 3:更新 Ingress 配置
apiVersion: networking.k8s.io/v1 kind: Ingress metadata:name: java-app-ingress spec:ingressClassName: nginx tls:-hosts:- java-app.example.com secretName: java-app-tls-secret rules:-host: java-app.example.com http:paths:-path: / pathType: Prefix backend:service:name: java-app-service port:number:80应用后,即可通过 https://java-app.example.com/health 安全访问应用。
✅ 最佳实践:在生产环境中,建议使用 cert-manager 自动管理 Let’s Encrypt 证书。
高级 Ingress 配置示例 🧩
多服务路由(基于路径)
假设你有两个 Java 服务:user-service 和 order-service。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata:name: multi-service-ingress spec:ingressClassName: nginx rules:-host: api.example.com http:paths:-path: /users pathType: Prefix backend:service:name: user-service port:number:80-path: /orders pathType: Prefix backend:service:name: order-service port:number:80访问 https://api.example.com/users → user-service
访问 https://api.example.com/orders → order-service
基于主机名的多租户
rules:-host: tenant1.example.com http:paths:-path: / backend:...-host: tenant2.example.com http:paths:-path: / backend:...启用 gzip 压缩
通过 annotations 启用 Nginx 的 gzip:
metadata:annotations:nginx.ingress.kubernetes.io/gzip:"true"限流(Rate Limiting)
metadata:annotations:nginx.ingress.kubernetes.io/rate-limit-connections:"10"nginx.ingress.kubernetes.io/rate-limit-rpm:"300"故障排查技巧 🛠️
部署过程中可能遇到问题,以下是常见排查步骤:
1. 检查 Pod 状态
kubectl get pods -l app=java-app kubectl logs <pod-name>确保应用正常启动,无异常日志。
2. 验证 Service 是否关联正确 Pod
kubectl describe svc java-app-service 查看 Endpoints 字段,应列出 Pod IP。若为空,说明 selector 不匹配。
3. 测试集群内部访问
进入一个临时 Pod:
kubectl run -it --rm debug --image=busybox --restart=Never -- sh在容器内执行:
wget -qO- http://java-app-service/health 若失败,说明 Service 配置有问题。
4. 检查 Ingress 状态
kubectl describe ingress java-app-ingress 查看 Events 部分是否有错误。
5. 查看 Ingress Controller 日志
kubectl logs -n ingress-nginx <ingress-controller-pod-name>观察是否有 404、502 等错误。
生产环境最佳实践 🏆
1. 使用健康检查
已在 Deployment 中配置 livenessProbe 和 readinessProbe,确保流量只发送到就绪的 Pod。
2. 资源限制
为容器设置合理的 requests 和 limits,避免资源争抢。
3. 自动扩缩容(HPA)
根据 CPU 或自定义指标自动调整副本数:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata:name: java-app-hpa spec:scaleTargetRef:apiVersion: apps/v1 kind: Deployment name: java-app-deployment minReplicas:2maxReplicas:10metrics:-type: Resource resource:name: cpu target:type: Utilization averageUtilization:704. 使用 NetworkPolicy 限制流量
默认情况下,Pod 可被任意访问。通过 NetworkPolicy 限制来源:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata:name: allow-from-ingress spec:podSelector:matchLabels:app: java-app policyTypes:- Ingress ingress:-from:-namespaceSelector:matchLabels:kubernetes.io/metadata.name: ingress-nginx 5. 监控与日志
集成 Prometheus + Grafana 监控应用指标,使用 EFK(Elasticsearch, Fluentd, Kibana)收集日志。
总结:选择合适的外部访问方式 🎯
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| NodePort | 开发/测试环境 | 简单、无需额外组件 | 端口不友好、无高级路由 |
| LoadBalancer | 云环境、单一服务 | 自动集成云 LB | 成本高、不支持多路由 |
| Ingress | 生产环境、多服务 | 统一入口、支持 HTTPS、灵活路由 | 需部署 Ingress Controller |
对于绝大多数 Java 应用,Ingress 是首选方案。它不仅满足基本访问需求,还为未来的微服务架构打下基础。
扩展阅读 📚
通过本文,你应该已经掌握了如何将 Java 应用部署到 Kubernetes,并通过 Service 和 Ingress 实现安全、高效的外部访问。从本地开发到生产上线,每一步都至关重要。希望这些知识能助你在云原生之旅中走得更远!💪
Happy coding! 💻✨
🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨