Docker多阶段构建:如何运行不同镜像中的CMD与ENTRYPOINT脚本?
解决Docker多阶段构建中ENTRYPOINT与CMD共存的问题
核心问题分析
你的问题根源在于多阶段构建的每个FROM都会生成全新镜像,第一个Go镜像里的CMD不会被第二个Java镜像继承,而且第二个镜像缺少air等Go工具的二进制文件,直接把air写进ENTRYPOINT肯定跑不起来。要解决这个问题,关键是把第一个阶段编译好的air等工具复制到第二个镜像,再让entrypoint.sh转发CMD的命令。
具体解决方案
1. 修改Dockerfile,复制Go工具到运行镜像
把第一个阶段标记为构建镜像,然后复制air和dlv的二进制文件到第二个镜像,同时重新设置ENTRYPOINT和CMD:
# 第一阶段:构建Go工具 FROM golang:1.17.2 AS builder COPY . /go/src/sample WORKDIR /go/src/sample RUN go install github.com/go-delve/delve/cmd/dlv@latest RUN go install github.com/cosmtrek/air@latest # 第二阶段:运行镜像 FROM eclipse-temurin:17-focal # 从构建阶段复制air和dlv二进制到系统可执行路径 COPY --from=builder /go/bin/air /usr/local/bin/ COPY --from=builder /go/bin/dlv /usr/local/bin/ # 复制自定义entrypoint脚本 COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # 设置ENTRYPOINT,CMD指定air启动命令 ENTRYPOINT ["/entrypoint.sh"] CMD ["air", "-c", ".air.toml"]
2. 调整entrypoint.sh,让它转发CMD命令
你的entrypoint.sh需要在执行完自有配置后,运行CMD传入的命令。修改脚本内容,最后加上exec "$@"来转发参数:
#!/bin/sh # 这里写你的自有配置逻辑,比如环境变量初始化、前置检查等 # 示例:设置环境变量 # export APP_ENV=development # 执行CMD指定的命令(这里就是air的启动命令) exec "$@"
3. 验证docker-compose配置
你的docker-compose.yml里已经设置了working_dir: /go/src/backend,和挂载路径./backend:/go/src/backend匹配,无需额外修改。
原理说明
- 多阶段构建中,
COPY --from=builder把第一个镜像里go install生成的二进制文件(默认在/go/bin)复制到第二个镜像的/usr/local/bin,让第二个镜像能直接运行air命令。 ENTRYPOINT脚本最后用exec "$@"会把CMD的参数作为命令执行,既保留了entrypoint的自定义配置逻辑,又能运行原来的热重载命令。
内容的提问来源于stack exchange,提问作者Alex Nikolsky
相关产品推荐
相关产品推荐

