Spring Boot微服务在Docker容器中启用HTTPS的配置问题咨询
我来给你梳理下把这些Spring Boot微服务(Config Service、Discovery Service、App Service)部署到Docker并启用HTTPS的核心步骤和常见坑点,毕竟我之前也折腾过类似的场景😉
一、镜像构建:把Keystore带入容器
首先要解决的是自签名证书的keystore文件如何在Docker容器中被Spring Boot识别,有两种常用方式:
1. 直接打包进镜像(适合固定证书的场景)
在每个服务的Dockerfile里添加COPY命令,把本地的keystore复制到容器内的指定目录,比如:
# 基础镜像用官方的OpenJDK FROM openjdk:17-jdk-slim WORKDIR /app # 复制jar包和keystore COPY target/your-service.jar app.jar COPY ./your-service-keystore.jks keystore.jks # 启动命令,指定Spring Boot的SSL配置路径 ENTRYPOINT ["java","-jar","app.jar","--server.ssl.key-store=/app/keystore.jks","--server.ssl.key-store-password=your-keystore-pass"]
2. 用Docker Volume挂载(适合需要频繁更换证书的场景)
这种方式不用修改Dockerfile,启动容器时通过-v参数把本地的keystore挂载到容器内的路径:
docker run -d \ -p 8443:8443 \ -v /本地路径/your-service-keystore.jks:/app/keystore.jks \ --name your-service \ your-service-image:latest
同时在Spring Boot配置里指定容器内的keystore路径:server.ssl.key-store=/app/keystore.jks
二、解决服务间的证书信任问题
本地环境能正常访问是因为你的本地JVM已经信任了自签名证书,但Docker容器里的JVM默认不认识这些自签名证书,这是最容易踩坑的地方!
1. 让Discovery/App Service信任Config Service的证书
因为Discovery和App启动时要从Config Service拉取配置,必须让它们信任Config的自签名证书,有两种方法:
方法一:把Config的证书导入容器的JVM信任存储
先从Config的keystore里导出证书:
keytool -export -alias config-alias -file config-cert.cer -keystore config-keystore.jks -storepass your-config-keystore-pass
然后在Discovery和App的Dockerfile里添加导入命令(以OpenJDK镜像为例):
# 导入Config Service的证书到JVM信任存储 COPY config-cert.cer /tmp/config-cert.cer RUN keytool -importcert -file /tmp/config-cert.cer -alias config-cert -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -noprompt
注意:
changeit是JVM默认信任存储的密码,大部分OpenJDK镜像都是这个值,如果你的镜像不一样需要调整。
方法二:自定义信任存储并通过JVM参数指定
创建一个包含Config证书的信任存储:
keytool -importcert -file config-cert.cer -alias config-cert -keystore truststore.jks -storepass your-truststore-pass -noprompt
然后启动容器时添加JVM参数:
docker run -d \ -p 8443:8443 \ -v /本地路径/truststore.jks:/app/truststore.jks \ --name discovery-service \ discovery-service-image:latest \ java -jar app.jar \ -Djavax.net.ssl.trustStore=/app/truststore.jks \ -Djavax.net.ssl.trustStorePassword=your-truststore-pass
2. 配置服务间的HTTPS通信
Discovery Service(比如Eureka)的配置
要让Eureka用HTTPS注册和通信,在application.yml里添加:
eureka: client: serviceUrl: defaultZone: https://config-service:8443/eureka/ # 用Docker网络里的容器名访问Config Service instance: secure-port-enabled: true non-secure-port-enabled: false secure-port: ${server.port} status-page-url: https://${eureka.instance.hostname}:${server.port}/actuator/info health-check-url: https://${eureka.instance.hostname}:${server.port}/actuator/health
App Service的配置
确保从Config Service加载配置的地址是HTTPS:
spring: config: import: configserver:https://config-service:8443
三、Docker网络配置:让服务互相访问
为了让三个服务在容器内可以通过名称互相访问,建议创建一个自定义Docker网络:
# 创建自定义网络 docker network create spring-microservices-net # 启动每个服务时加入这个网络 docker run -d \ --network spring-microservices-net \ --name config-service \ config-service-image:latest docker run -d \ --network spring-microservices-net \ --name discovery-service \ discovery-service-image:latest docker run -d \ --network spring-microservices-net \ --name app-service \ app-service-image:latest
这样每个服务就可以用容器名(比如config-service)作为域名访问,不用依赖主机IP。
四、测试与排坑
- 测试容器内的HTTPS访问:进入容器内部用
curl测试(-k跳过证书验证,仅测试用):
docker exec -it app-service curl -k https://config-service:8443
- 证书信任问题排查:如果出现
SSLHandshakeException,检查证书是否正确导入,或者JVM参数是否指定了正确的信任存储。 - 服务间通信失败:检查是否加入了同一个Docker网络,容器名是否正确,端口是否在容器内开放(Spring Boot的server.port配置是否正确)。
注意:以上方案适合测试环境,生产环境绝对不要用自签名证书,建议使用CA签发的证书(比如Let's Encrypt的免费证书),并做好证书的自动轮换和管理。
内容的提问来源于stack exchange,提问作者Bad_Pan

