Spring Boot应用Docker部署下如何将Actuator暴露在根路径(保留原有应用上下文路径)
我之前也碰到过一模一样的需求——既要保留应用原有的上下文路径,又得把Actuator暴露在根路径下,结合Docker部署的场景,分享几个亲测有效的解决方案:
方案一:用Nginx在Docker容器内做反向代理(推荐生产环境使用)
这个方案的思路是在Docker镜像里同时运行Nginx和Spring Boot应用,让Nginx作为请求入口,把/actuator/**的请求转发到应用的/context-path/actuator/**,而应用本身的请求还是走原有的/context-path/**路径。
具体步骤:
- 编写Nginx配置文件(命名为
nginx.conf):
server { listen 80; # 转发Actuator请求到带上下文路径的地址 location /actuator/ { proxy_pass http://localhost:8080/context-path/actuator/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 透传应用本身的请求 location /context-path/ { proxy_pass http://localhost:8080/context-path/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
- 编写启动脚本(命名为
start.sh),用来同时启动Nginx和Spring Boot应用:
#!/bin/sh # 后台启动Spring Boot应用 java -jar /app/your-app.jar & # 前台启动Nginx(保证容器不退出) nginx -g "daemon off;"
记得给脚本加执行权限:chmod +x start.sh
- 编写Dockerfile:
# 阶段1:构建Spring Boot应用 FROM openjdk:17-jdk-slim as builder WORKDIR /app COPY pom.xml . COPY src ./src RUN ./mvnw clean package -DskipTests # 阶段2:构建最终镜像(包含Nginx和应用) FROM nginx:alpine # 复制编译好的jar包到镜像 COPY --from=builder /app/target/your-app.jar /app/your-app.jar # 复制Nginx配置 COPY nginx.conf /etc/nginx/conf.d/default.conf # 复制启动脚本 COPY start.sh /start.sh RUN chmod +x /start.sh # 容器启动时执行脚本 CMD ["/start.sh"]
这样构建镜像后,启动容器,访问http://server-address/actuator/health就会自动转发到http://server-address/context-path/actuator/health,应用本身的http://server-address/context-path/**也能正常访问。
方案二:在Spring Boot应用内添加请求转发Filter(轻量方案)
如果你不想额外引入Nginx,可以在应用里自定义一个Filter,捕获/actuator/**的请求并转发到带上下文路径的地址。
具体实现:
- 编写Filter类:
import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; public class ActuatorForwardFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String requestUri = req.getRequestURI(); // 如果是Actuator的请求,转发到带上下文路径的地址 if (requestUri.startsWith("/actuator/")) { String forwardUri = "/context-path" + requestUri; req.getRequestDispatcher(forwardUri).forward(request, response); } else { // 其他请求正常处理 chain.doFilter(request, response); } } }
- 注册Filter:
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<ActuatorForwardFilter> actuatorForwardFilter() { FilterRegistrationBean<ActuatorForwardFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new ActuatorForwardFilter()); // 只拦截Actuator路径的请求 registrationBean.addUrlPatterns("/actuator/*"); // 设置Filter优先级,确保比其他Filter先执行 registrationBean.setOrder(1); return registrationBean; } }
这个方案不需要修改Docker镜像结构,只需要在应用里加几行代码,就能实现需求。测试的时候直接启动应用,访问/actuator/**就会自动转发到带上下文路径的Actuator端点。
方案三:用Docker Compose搭配独立Nginx容器(多容器部署场景)
如果你的架构是多容器部署,可以单独部署一个Nginx容器作为反向代理,转发请求到Spring Boot应用容器。
编写docker-compose.yml:
version: '3.8' services: spring-app: build: ./your-app-directory expose: - "8080" environment: - SERVER_SERVLET_CONTEXT_PATH=/context-path nginx-proxy: image: nginx:alpine ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf depends_on: - spring-app
Nginx配置文件nginx.conf和方案一类似,只是把proxy_pass的地址改成Spring Boot容器的服务名:
server { listen 80; location /actuator/ { proxy_pass http://spring-app:8080/context-path/actuator/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /context-path/ { proxy_pass http://spring-app:8080/context-path/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
启动docker-compose up -d后,就能通过Nginx容器的80端口访问Actuator和应用了。
备注:内容来源于stack exchange,提问作者user28362297

