Docker Compose启动失败:0.0.0.0:3307端口已被占用求助
我有一个连接MySQL数据库的Spring Boot应用,尝试将其容器化,并编写docker-compose.yml来启动MySQL容器和可与其通信的Spring Boot容器。
项目结构
myapp/ src/ main/ java/ <All the Java code here> resources/ application.yml build/ libs/ myapp.jar Dockerfile .env docker-compose.yml
执行./gradlew clean bootJar可构建出build/libs/myapp.jar。
.env文件内容
MAIN_DB_HOST=127.0.0.1 MAIN_DB_PORT=3307 MAIN_DB_DOCKER_PORT=3306 MAIN_DB_USERNAME=root MAIN_DB_PASSWORD=123456 MAIN_DB_ROOT_PASSWORD=123456 MAIN_DB_DATABASE=myapp_db SERVER_PORT=9200 WS_PORT=9200 WS_DOCKER_PORT=9201
Dockerfile内容
FROM eclipse-temurin:11 RUN mkdir /opt/app COPY app/build/libs/myapp.jar myapp ENTRYPOINT ["java","-jar","myapp"]
docker-compose.yml内容
version: "3.8" services: myapp_db: image: mysql:8.0.32 restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD=$MAIN_DB_ROOT_PASSWORD - MYSQL_DATABASE=$MAIN_DB_DATABASE - MYSQL_CHARSET=utf8mb4 - MYSQL_COLLATION=utf8mb4_0900_ai_ci - MYSQL_ENGINE=InnoDB ports: - $MAIN_DB_PORT:$MAIN_DB_DOCKER_PORT volumes: - db:/var/lib/mysql myapp: depends_on: - myapp_db build: . restart: on-failure ports: - $WS_PORT:$WS_DOCKER_PORT - $MAIN_DB_PORT:$MAIN_DB_DOCKER_PORT volumes: db:
执行命令与错误信息
执行./gradlew clean bootJar && docker-compose --env-file .env up -d后,镜像构建成功,但启动时出现以下错误:
WARNING: Image for service myapp was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating myapp_myapp_db_1 ... done Creating myapp_myapp_db_1 ... Creating myapp_myapp_db_1 ... error ERROR: for myapp_myapp_db_1 Cannot start service myapp: driver failed programming external connectivity on endpoint myapp_myapp_db_1 (33899c0810d03db26ef7b48e67328ea9eb4e0f75cebc0201e667db72ce21d7ba): Bind for 0.0.0.0:3307 failed: port is already allocated ERROR: Encountered errors while bringing up the project.
问题分析与解决
核心问题1:端口冲突
错误提示显示无法启动myapp服务,原因是宿主机的3307端口已被占用——你在docker-compose.yml里同时给myapp_db和myapp两个服务配置了相同的端口映射$MAIN_DB_PORT:$MAIN_DB_DOCKER_PORT。myapp_db先启动占用了3307端口,后续myapp服务尝试绑定同一个端口就会失败。
而且Spring Boot容器完全不需要映射MySQL的端口,容器之间通过Docker内部网络直接通信,只需要用服务名(比如myapp_db)就能访问MySQL,不需要把MySQL端口暴露给Spring Boot容器。
核心问题2:数据库连接地址错误
.env里的MAIN_DB_HOST=127.0.0.1是错误的:在Docker容器中,127.0.0.1指向容器自身,而非宿主机的MySQL服务。容器间通信应该使用Docker Compose中定义的服务名myapp_db作为数据库主机地址。
核心问题3:Dockerfile路径错误
你的Dockerfile中写的COPY app/build/libs/myapp.jar myapp路径有误,项目结构里myapp.jar实际在build/libs/下,且未指定正确的容器内存储路径,会导致启动时找不到jar包。
修正后的文件内容
1. 修正后的.env
# 用Docker服务名作为数据库主机 MAIN_DB_HOST=myapp_db # MySQL容器内部端口固定为3306 MAIN_DB_PORT=3306 # 宿主机映射的MySQL端口(供外部工具连接用,可选) MAIN_DB_DOCKER_HOST_PORT=3307 MAIN_DB_USERNAME=root MAIN_DB_PASSWORD=123456 MAIN_DB_ROOT_PASSWORD=123456 MAIN_DB_DATABASE=myapp_db SERVER_PORT=9200 WS_PORT=9200 WS_DOCKER_PORT=9201
2. 修正后的Dockerfile
FROM eclipse-temurin:11 RUN mkdir -p /opt/app # 复制正确路径的jar包到容器目录 COPY build/libs/myapp.jar /opt/app/myapp.jar # 切换工作目录到jar包所在路径 WORKDIR /opt/app ENTRYPOINT ["java","-jar","myapp.jar"]
3. 修正后的docker-compose.yml
version: "3.8" services: myapp_db: image: mysql:8.0.32 restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD=${MAIN_DB_ROOT_PASSWORD} - MYSQL_DATABASE=${MAIN_DB_DATABASE} - MYSQL_USER=${MAIN_DB_USERNAME} - MYSQL_PASSWORD=${MAIN_DB_PASSWORD} - MYSQL_CHARSET=utf8mb4 - MYSQL_COLLATION=utf8mb4_0900_ai_ci ports: # 仅映射MySQL端口到宿主机(如果需要外部工具连接,不需要可删除) - ${MAIN_DB_DOCKER_HOST_PORT}:3306 volumes: - db:/var/lib/mysql # 可选:解决MySQL 8.0的认证插件及时区问题 command: --default-authentication-plugin=mysql_native_password --init-connect='SET NAMES utf8mb4;' myapp: depends_on: - myapp_db build: . restart: on-failure ports: # 仅映射Spring Boot的服务端口 - ${WS_PORT}:${WS_DOCKER_PORT} environment: # 传递数据库连接信息给Spring Boot容器 - SPRING_DATASOURCE_URL=jdbc:mysql://${MAIN_DB_HOST}:${MAIN_DB_PORT}/${MAIN_DB_DATABASE}?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true - SPRING_DATASOURCE_USERNAME=${MAIN_DB_USERNAME} - SPRING_DATASOURCE_PASSWORD=${MAIN_DB_PASSWORD} volumes: db:
额外注意事项
depends_on仅保证容器启动顺序,不保证MySQL服务完全就绪。如果Spring Boot启动时MySQL还未初始化完成,会导致连接失败,可添加wait-for-it脚本或在Spring Boot中配置数据库连接重试机制。- 确保
application.yml中的数据库配置支持环境变量覆盖,示例配置:spring: datasource: url: ${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3306/myapp_db} username: ${SPRING_DATASOURCE_USERNAME:root} password: ${SPRING_DATASOURCE_PASSWORD:123456}
内容的提问来源于stack exchange,提问作者hotmeatballsoup

