服务顺序启动,解决 docker-compose 启动顺序 mysql启动慢导致服务失败

问题的产生

我使用docker-compose 编写了一个脚本,包括了mysqlredisspring boot server。但是由于mysql 数据量大了后启动会变慢,导致sprong boot server 启动失败

即使使用了 depends_on 这个配置也无效,因为这个配置只能保证容器的启动顺序,但是在mysql容器启动了但是服务还未启动,因此出现了这个问题。

解决问题

你赶时间的话请直接采用我的最终解,将你的 docker-compose,中的镜像命令修改一下

  • image:一个包含了监听其他端口脚本的镜像
  • entrypoint: 等待mysqlredis启动后 执行 -c 后的命令
my_server:
    image: sunbrightness/jdk-wait_run:8
    container_name: my_server
    ports:
      - 8080:8080
    environment:
      - LANG=C.UTF-8
      - TZ=Asia/Shanghai
    volumes:
      # 配置文件
      - ./server/:/home/java/
    privileged: true
    entrypoint: 'wait_run -d my_mysql:3306,my_redis:6379 -c "java -jar admin.jar"'
    restart: always
    depends_on:
      - km_mysql
      - km_redis

问题细节

编写一个sh 脚本

利用 linux 中的 netcat 工具监听端口,在端口启动后运行spring boot server

touch wait_run
chmod +x wait_run
vim wait_run
#!/bin/bash
#set -x

: ${SLEEP_SECOND:=2}

wait_for() {
    echo Waiting for $1 to listen on $2...
    while ! nc -z $1 $2; do echo waiting...; sleep $SLEEP_SECOND; done
}

declare DEPENDS
declare CMD

while getopts "d:c:" arg
do
    case $arg in
        d)
            DEPENDS=$OPTARG
            ;;
        c)
            CMD=$OPTARG
            ;;
        ?)
            echo "unkonw argument"
            exit 1
            ;;
    esac
done

for var in ${DEPENDS//,/ }
do
    host=${var%:*}
    port=${var#*:}
    wait_for $host $port
done

eval $CMD

这样你就可以通过如下命令,等待多个端口启动后再执行你的命令

/bin/bash wait_run -d 127.0.0.1:3306,127.0.0.1:6379 -c "java -jar admin.jar"

注意 jdk的docker容器不包含这个命令 nc

因此我们需要自定义一个Dockerfile 文件,让其安装好 netcat

# 基础镜像
FROM openjdk:8
# author
MAINTAINER sunbrightness

# 挂载目录
VOLUME /home/java
# 创建目录
RUN mkdir -p /home/java
# 指定路径
WORKDIR /home/java

# fix apt update error
RUN echo "deb https://mirrors.cernet.edu.cn/debian/ bullseye main contrib non-free" > /etc/apt/sources.list
RUN apt-get update
# 安装netcat用于监听端口启动
RUN apt-get -y install netcat

# 将刚刚创建的nc命令移动到镜像中
COPY ./wait_run /bin
RUN chmod +x /bin/wait_run

这个便是我在最上文提到的sunbrightness/jdk-wait_run:8 镜像了。

花了好长时间才搞好的。也知道了java8的docker镜像是基于Debian 11 系统的

### Docker Compose 启动时 `docker-db-1` 容器依赖问题解决方案 当使用 Docker Compose 部署服务时,如果某个容器(如 `docker-db-1`)因为其依赖的服务未成功启动而进入不健康状态,则可能是由于以下几个原因造成的: #### 1. **依赖顺序问题** Docker Compose 默认不会等待一个服务完全启动后再启动下一个服务。因此,即使定义了依赖关系,目标服务可能尚未准备好接受连接。可以通过设置 `healthcheck` 和 `depends_on` 来解决此问题。 ```yaml version: '3.8' services: app: image: my-app-image depends_on: db: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s timeout: 10s retries: 3 db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: example healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "--silent"] interval: 10s timeout: 5s retries: 5 ``` 通过上述配置,可以确保 `app` 只有在 `db` 健康运行后才会继续初始化[^1]。 #### 2. **网络延迟或数据库初始化时间过长** 某些情况下,尽管设置了 `depends_on` 或 `service_healthy`,但由于数据库初始化较或其他因素,应用仍然会尝试提前连接到数据库并失败。可以在应用程序代码中加入重试逻辑来处理这种情况。 以下是 Python 应用程序中的示例实现: ```python import time from sqlalchemy import create_engine, exc def wait_for_db(): engine = create_engine("mysql+pymysql://root:example@db:3306/mydatabase") while True: try: connection = engine.connect() connection.close() break except (exc.SQLAlchemyError, Exception) as e: print(f"Database not ready yet... Retrying in 5 seconds ({e})") time.sleep(5) if __name__ == "__main__": wait_for_db() # 继续执行其他业务逻辑... ``` 这段脚本会在每次尝试连接失败后休眠一段时间再重新尝试,直到成功为止[^2]。 #### 3. **环境变量缺失或错误** 检查 `docker-compose.yml` 文件中是否正确传递了必要的环境变量给 `docker-db-1` 容器及其关联的应用程序容器。例如 MySQL 的用户名、密码以及主机地址等参数都需准确无误。 #### 4. **日志分析** 查看具体的错误消息可以帮助定位根本原因。运行命令获取更详细的日志信息: ```bash docker logs docker-db-1 ``` 或者实时跟踪日志变化: ```bash docker-compose logs -f ``` 这些日志通常包含了关于为什么该服务未能按预期工作的线索[^3]。 --- ### 总结 综合以上几点建议,在调整好服务间的依赖条件之后,还需确认所有的外部资源都已经就绪,并且适当增加一些健壮性的设计比如自动重连机制等措施以应对不可预见的情况发生。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SUNbrightness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值