You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Docker容器启动所有服务后自动执行指定Bash脚本?

问题描述

我在Docker容器中部署了一个Flask API,项目用来爬取学校系统的全部课程表,为学生生成所有可行的课程表组合,目前运行正常。但有一个需要手动调用的爬取学校网页的接口/FetchGroupDataUPSite,这是后续功能运行的核心前提。我写了一个Bash脚本通过GET请求调用该接口,现在想实现每次构建Docker容器并启动所有服务后自动运行这个脚本,同时希望得到其他更优的解决思路。

以下是现有脚本、docker-compose.yml和Dockerfile:

调用接口的Bash脚本

#!/bin/bash

URL="http://localhost:5555/FetchGroupDataUPSite"
max_retries=3
retries=0
delay=5

echo "Sending GET request to scrapper endpoint..."

while [ $retries -lt $max_retries ]
do
  response=$(curl -s -o /dev/null -w "%{http_code}" -X GET $URL)
  if [ $response -eq 200 ]
  then
    echo "Scrapper script completed."
    break
  else
    echo "Request failed with status code $response. Retrying in $delay seconds..."
    retries=$((retries+1))
    sleep $delay
  fi
done

if [ $retries -eq $max_retries ]
then
  echo "Maximum number of retries reached. Scrapper script failed."
fi

docker-compose.yml

version: '3.8'
networks:
  app-tier:
    driver: bridge
services:
  db:
    image: mysql:5.7
    container_name: UP_DB
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ***
      MYSQL_PASSWORD: ***
      MYSQL_DATABASE: ***
      MYSQL_USER: ***
    command: --default-authentication-plugin=mysql_native_password --bind-address=0.0.0.0 --explicit_defaults_for_timestamp
    volumes:
      - ./sql:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306
    networks:
      - app-tier
  adminer:
    image: adminer
    container_name: UP_Adminer
    restart: always
    ports:
      - 8080:8080
    networks:
      - app-tier
  api:
    container_name: UP_API
    stdin_open: true
    tty: true
    restart: always
    build: .
    ports:
      - 5555:5555
    volumes:
      - ./App:/SmartUP/App
    networks:
      - app-tier

Dockerfile

FROM python:3.10.7-buster
WORKDIR /SmartUP
COPY . .
ENV FLASK_ENV development
ENV DEBUG true
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get install -y wget
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
RUN apt-get -y update
RUN apt-get install -y google-chrome-stable
RUN apt-get install -yqq unzip
RUN wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip
# Unzip the Chrome Driver into /usr/local/bin directory
RUN unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/
# Set display port as an environment variable
ENV DISPLAY=:99

RUN pip install --upgrade pip && pip3 install -r requirements.txt
CMD ["python", "App/run.py"]

# Makes the file executable
EXPOSE 5555

解决方案

方法一:在API容器启动后自动执行脚本

可以通过修改Dockerfile或docker-compose配置,让Flask服务启动后自动触发爬取脚本。

方式1:用ENTRYPOINT+启动脚本组合

创建一个start.sh启动脚本,后台启动Flask服务后,执行带重试的爬取调用:

#!/bin/bash

# 后台启动Flask服务
python App/run.py &
FLASK_PID=$!

# 调用爬取接口(改用容器网络内的服务地址,更可靠)
URL="http://localhost:5555/FetchGroupDataUPSite"
max_retries=3
retries=0
delay=5

echo "Waiting for Flask API to be ready..."
while [ $retries -lt $max_retries ]
do
  response=$(curl -s -o /dev/null -w "%{http_code}" -X GET $URL)
  if [ $response -eq 200 ]
  then
    echo "Scrapper script completed."
    break
  else
    echo "Request failed with status code $response. Retrying in $delay seconds..."
    retries=$((retries+1))
    sleep $delay
  fi
done

if [ $retries -eq $max_retries ]
then
  echo "Maximum number of retries reached. Scrapper script failed."
fi

# 等待Flask进程,避免容器提前退出
wait $FLASK_PID

修改Dockerfile,将启动脚本复制到容器并设置为入口:

# ... 保留原有所有配置 ...

# 复制启动脚本并赋予执行权限
COPY start.sh /SmartUP/start.sh
RUN chmod +x /SmartUP/start.sh

# 替换原有CMD为ENTRYPOINT
ENTRYPOINT ["/SmartUP/start.sh"]

方式2:直接在docker-compose中指定命令

如果不想修改Dockerfile,可以在api服务的command字段中串联启动命令:

api:
  # ... 保留原有配置 ...
  command: bash -c "python App/run.py & sleep 8 && /SmartUP/your-script.sh && wait"

注意:需要将本地脚本挂载到容器内(比如通过volume),或者在Dockerfile中复制进去;sleep 8是简单等待,不如自带重试的脚本可靠,优先推荐方式1。

方法二:修改Flask应用,启动时自动执行爬取逻辑

这是最直接的优化方案——跳过HTTP请求,直接在Flask启动时调用爬取函数:

from flask import Flask
# 导入你的爬取核心函数,比如fetch_group_data_up_site

app = Flask(__name__)

# ... 其他路由、配置代码 ...

if __name__ == '__main__':
    # 先执行爬取逻辑
    print("Starting course data scrapping...")
    try:
        fetch_group_data_up_site()
        print("Scrapping completed successfully.")
    except Exception as e:
        print(f"Scrapping failed: {str(e)}")
    # 启动Flask服务
    app.run(host='0.0.0.0', port=5555)

这种方式省去了额外的HTTP请求,减少了依赖点,效率和可靠性都更高。

方法三:新增独立的初始化服务到docker-compose

在docker-compose中添加一个专门执行爬取调用的服务,依赖API服务就绪后再运行:

  1. 给api服务添加健康检查,确保服务真正可用:
api:
  # ... 保留原有配置 ...
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:5555/FetchGroupDataUPSite"]
    interval: 5s
    timeout: 5s
    retries: 3
  1. 添加初始化服务:
version: '3.8'
networks:
  app-tier:
    driver: bridge
services:
  # ... db、adminer、api服务保留原有配置 ...
  init-scrapper:
    image: curlimages/curl:latest
    volumes:
      - ./your-script.sh:/script.sh
    command: bash /script.sh
    depends_on:
      api:
        condition: service_healthy
    networks:
      - app-tier

注意:需要把脚本中的URL改成http://api:5555/FetchGroupDataUPSite,因为在docker-compose网络中,服务名称可直接作为域名使用。


内容的提问来源于stack exchange,提问作者Luis Cedillo Maldonado

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:07:01