【问题标题】:Using Docker-Compose, how to execute multiple commands使用 Docker-Compose,如何执行多个命令
【发布时间】:2015-07-15 19:58:54
【问题描述】:

我想做这样的事情,我可以按顺序运行多个命令。

db:
  image: postgres
web:
  build: .
  command: python manage.py migrate
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - .:/code
  ports:
    - "8000:8000"
  links:
    - db

【问题讨论】:

    标签: docker docker-compose


    【解决方案1】:

    想通了,使用 bash -c

    例子:

    command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
    

    多行中的相同示例:

    command: >
        bash -c "python manage.py migrate
        && python manage.py runserver 0.0.0.0:8000"
    

    或者:

    command: bash -c "
        python manage.py migrate
        && python manage.py runserver 0.0.0.0:8000
      "
    

    【讨论】:

    • @Pedram 确保您使用的是实际安装了 bash 的图像。某些图像可能还需要 bash 的直接路径,例如/bin/bash
    • 如果没有安装 bash 你可以试试 sh -c "your command"
    • 基于 Alpine 的图像实际上似乎没有安装 bash - 像 @Chaoste 推荐的那样使用 sh 代替:[sh, -c, "cd /usr/src/app && npm start"]
    • 也可以在 alpine 上使用 ash :)
    • 我使用sh -cx,所以我也可以看到正在运行的命令。对调试很有用。
    【解决方案2】:

    我在一个单独的临时容器中运行启动前的东西,比如迁移,就像这样(注意,撰写文件必须是版本“2”类型):

    db:
      image: postgres
    web:
      image: app
      command: python manage.py runserver 0.0.0.0:8000
      volumes:
        - .:/code
      ports:
        - "8000:8000"
      links:
        - db
      depends_on:
        - migration
    migration:
      build: .
      image: app
      command: python manage.py migrate
      volumes:
        - .:/code
      links:
        - db
      depends_on:
        - db
    

    这有助于保持清洁和分开。需要考虑的两件事:

    1. 您必须确保正确的启动顺序(使用depends_on)。

    2. 您想避免多次构建,这是通过使用构建和映像在第一次标记它来实现的;然后你可以参考其他容器中的图像。

    【讨论】:

    • 这对我来说似乎是最好的选择,我想使用它。您能否详细说明您的标记设置以避免多次构建?我宁愿避免额外的步骤,所以如果这需要一些,我可能会选择上面的bash -c
    • 在上面的 yaml 中,构建和标记发生在迁移部分。乍一看并不是很明显,但是 docker-compose 在您指定构建和图像属性时对其进行标记 - 其中图像属性指定该构建的标记。然后可以在不触发新构建的情况下随后使用它(如果您查看网络,您会发现它没有构建,只有一个图像属性)。这里有更多细节docs.docker.com/compose/compose-file)
    • 虽然我喜欢这个想法,但问题在于,depends_on 仅确保它们按该顺序开始,而不是确保它们按该顺序准备好。 wait-for-it.sh 可能是一些人需要的解决方案。
    • 这是绝对正确的,有点遗憾的是 docker-compose 不支持任何细粒度的控制,比如等待容器退出或开始监听端口。但是,是的,自定义脚本确实解决了这个问题,好点!
    • 此答案提供了有关depends_on 工作方式的不正确且可能具有破坏性的信息。
    【解决方案3】:

    我建议使用sh 而不是bash,因为它更容易在大多数基于 Unix 的映像(alpine 等)上使用。

    这是一个例子docker-compose.yml

    version: '3'
    
    services:
      app:
        build:
          context: .
        command: >
          sh -c "python manage.py wait_for_db &&
                 python manage.py migrate &&
                 python manage.py runserver 0.0.0.0:8000"
    

    这将按顺序调用以下命令:

    • python manage.py wait_for_db - 等待数据库准备好
    • python manage.py migrate - 运行任何迁移
    • python manage.py runserver 0.0.0.0:8000 - 启动我的开发服务器

    【讨论】:

    • 就我个人而言,这是我最喜欢、最干净的解决方案。
    • 我的也是。正如@LondonAppDev 指出的那样,默认情况下,并非所有容器都可以使用 bash 来优化空间(例如,大多数构建在 Alpine Linux 之上的容器)
    • 我不得不用 \ 转义多行 &&
    • @AndreVanZuydam 嗯,这很奇怪,我不需要这样做。你用引号包围了吗?你在运行什么风格的 docker?
    • @oligofren > 用于启动多行输入(见stackoverflow.com/a/3790497/2220370
    【解决方案4】:

    这对我有用:

    version: '3.1'
    services:
      db:
        image: postgres
      web:
        build: .
        command:
          - /bin/bash
          - -c
          - |
            python manage.py migrate
            python manage.py runserver 0.0.0.0:8000
    
        volumes:
          - .:/code
        ports:
          - "8000:8000"
        links:
          - db
    

    docker-compose 尝试在运行命令之前取消引用变量,因此如果您希望 bash 处理变量,您需要通过加倍来转义美元符号...

        command:
          - /bin/bash
          - -c
          - |
            var=$$(echo 'foo')
            echo $$var # prints foo
    

    ...否则会报错:

    服务“web”中“command”选项的插值格式无效:

    【讨论】:

    • 嗨,伙计。我遇到了一个问题:```无法识别的参数:/bin/bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows ```我的docker-compose.yml中的命令是:命令: - /bin/bash - -c - | python3 /usr/local/airflow/__init__.py -C ${Client} -T ${Types} 你知道怎么解决吗?我在 .env 文件中添加了客户端和类型。
    • 这是给您的文档:docs.docker.com/compose/compose-file/#variable-substitution 我认为您的 .env 文件将这些变量放在容器环境中,但 docker-compose 正在查看您的 shell 环境。改用$${Types}$${Client}。我认为这将阻止 docker compose 解释这些变量并在您调用 docker-compose 的任何 shell 中查找它们的值,这意味着它们仍然存在让 bash 取消引用它们(after docker已处理您的.env 文件)。
    • 感谢您的评论。我确实按照你说的做了。所以我在错误信息中得到了 $(Client) 。我将读取环境变量的方式改为在python中使用os.getenv,这样更容易。还是谢谢。
    【解决方案5】:

    最干净?

    ---
    version: "2"
    services:
      test:
        image: alpine
        entrypoint: ["/bin/sh","-c"]
        command:
        - |
           echo a
           echo b
           echo c
    

    【讨论】:

    • 当我的图像的入口点不是 sh/bash 时,这非常有效。
    【解决方案6】:

    您可以在此处使用入口点。 docker 中的入口点在命令之前执行,而命令是容器启动时应该运行的默认命令。 所以大多数应用程序通常在入口点文件中携带设置过程,最后它们允许命令运行。

    制作一个shell脚本文件可以是docker-entrypoint.sh(名字无关紧要),里面有以下内容。

    #!/bin/bash
    python manage.py migrate
    exec "$@"
    

    在 docker-compose.yml 文件中使用 entrypoint: /docker-entrypoint.sh 并将命令注册为 command: python manage.py runserver 0.0.0.0:8000 P.S : 不要忘记将docker-entrypoint.sh 连同您的代码一起复制。

    【讨论】:

    • 请注意,这也会在您执行docker-compose run service-name ....时执行
    【解决方案7】:

    另一个想法:

    如果在这种情况下构建容器,只需在其中放置一个启动脚本并使用命令运行它。或者将启动脚本挂载为卷。

    【讨论】:

    • 是的,最后我创建了一个run.sh脚本:#!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(丑陋的单行)
    【解决方案8】:

    * 更新 *

    我认为运行某些命令的最佳方法是编写一个自定义 Dockerfile,它在从映像运行官方 CMD 之前执行我想要的所有操作。

    docker-compose.yaml:

    version: '3'
    
    # Can be used as an alternative to VBox/Vagrant
    services:
    
      mongo:
        container_name: mongo
        image: mongo
        build:
          context: .
          dockerfile: deploy/local/Dockerfile.mongo
        ports:
          - "27017:27017"
        volumes:
          - ../.data/mongodb:/data/db
    

    Dockerfile.mongo:

    FROM mongo:3.2.12
    
    RUN mkdir -p /fixtures
    
    COPY ./fixtures /fixtures
    
    RUN (mongod --fork --syslog && \
         mongoimport --db wcm-local --collection clients --file /fixtures/clients.json && \
         mongoimport --db wcm-local --collection configs --file /fixtures/configs.json && \
         mongoimport --db wcm-local --collection content --file /fixtures/content.json && \
         mongoimport --db wcm-local --collection licenses --file /fixtures/licenses.json && \
         mongoimport --db wcm-local --collection lists --file /fixtures/lists.json && \
         mongoimport --db wcm-local --collection properties --file /fixtures/properties.json && \
         mongoimport --db wcm-local --collection videos --file /fixtures/videos.json)
    

    这可能是最干净的方法。

    * 旧方式 *

    我用我的命令创建了一个 shell 脚本。在这种情况下,我想启动 mongod,然后运行 ​​mongoimport,但调用 mongod 会阻止您运行其余的程序。

    docker-compose.yaml

    version: '3'
    
    services:
      mongo:
        container_name: mongo
        image: mongo:3.2.12
        ports:
          - "27017:27017"
        volumes:
          - ./fixtures:/fixtures
          - ./deploy:/deploy
          - ../.data/mongodb:/data/db
        command: sh /deploy/local/start_mongod.sh
    

    start_mongod.sh

    mongod --fork --syslog && \
    mongoimport --db wcm-local --collection clients --file /fixtures/clients.json && \
    mongoimport --db wcm-local --collection configs --file /fixtures/configs.json && \
    mongoimport --db wcm-local --collection content --file /fixtures/content.json && \
    mongoimport --db wcm-local --collection licenses --file /fixtures/licenses.json && \
    mongoimport --db wcm-local --collection lists --file /fixtures/lists.json && \
    mongoimport --db wcm-local --collection properties --file /fixtures/properties.json && \
    mongoimport --db wcm-local --collection videos --file /fixtures/videos.json && \
    pkill -f mongod && \
    sleep 2 && \
    mongod
    

    所以这个fork mongo,执行monogimport,然后杀死分离的fork mongo,然后再次启动它而不分离。不确定是否有办法附加到分叉的进程,但这确实有效。

    注意:如果您确实想加载一些初始数据库数据,这是这样做的方法:

    mongo_import.sh

    #!/bin/bash
    # Import from fixtures
    
    # Used in build and docker-compose mongo (different dirs)
    DIRECTORY=../deploy/local/mongo_fixtures
    if [[ -d "/fixtures" ]]; then
        DIRECTORY=/fixtures
    fi
    echo ${DIRECTORY}
    
    mongoimport --db wcm-local --collection clients --file ${DIRECTORY}/clients.json && \
    mongoimport --db wcm-local --collection configs --file ${DIRECTORY}/configs.json && \
    mongoimport --db wcm-local --collection content --file ${DIRECTORY}/content.json && \
    mongoimport --db wcm-local --collection licenses --file ${DIRECTORY}/licenses.json && \
    mongoimport --db wcm-local --collection lists --file ${DIRECTORY}/lists.json && \
    mongoimport --db wcm-local --collection properties --file ${DIRECTORY}/properties.json && \
    mongoimport --db wcm-local --collection videos --file ${DIRECTORY}/videos.json
    

    mongo_fixtures/*.json 文件是通过 mongoexport 命令创建的。

    docker-compose.yaml

    version: '3'
    
    services:
      mongo:
        container_name: mongo
        image: mongo:3.2.12
        ports:
          - "27017:27017"
        volumes:
          - mongo-data:/data/db:cached
          - ./deploy/local/mongo_fixtures:/fixtures
          - ./deploy/local/mongo_import.sh:/docker-entrypoint-initdb.d/mongo_import.sh
    
    
    volumes:
      mongo-data:
        driver: local
    

    【讨论】:

      【解决方案9】:

      使用 bash -c 在 docker-compose 文件中运行多个命令。

      command: >
          bash -c "python manage.py makemigrations
          && python manage.py migrate
          && python manage.py runserver 0.0.0.0:8000"
      

      来源:https://intellipaat.com/community/19590/docker-run-multiple-commands-using-docker-compose-at-once?show=19597#a19597

      【讨论】:

        【解决方案10】:

        这个帖子中已经有很多很好的答案,但是,我发现其中一些的组合似乎效果最好,尤其是对于基于 Debian 的用户。

        services:
          db:
            . . . 
          web:
            . . .
            depends_on:
               - "db"
            command: >      
              bash -c "./wait-for-it.sh db:5432 -- python manage.py makemigrations
              && python manage.py migrate
              && python manage.py runserver 0.0.0.0:8000"
        

        先决条件:将wait-for-it.sh 添加到您的项目目录中。

        来自the docs 的警告:“(在生产中使用 wait-for-it.sh 时),您的数据库可能随时变得不可用或移动主机......(此解决方案适用于不需要的人)这种弹性水平。”

        编辑:

        这是一个很酷的短期修复,但对于长期解决方案,您应该尝试在 Dockerfile 中为每个映像使用入口点。

        【讨论】:

          【解决方案11】:

          基于 Alpine 的图像实际上似乎没有安装 bash,但您可以使用链接到 /bin/busyboxshash

          例如docker-compose.yml:

          version: "3"
          services:
          
            api:
              restart: unless-stopped
              command: ash -c "flask models init && flask run"
          

          【讨论】:

            【解决方案12】:

            如果您需要运行多个守护进程,suggestion in the Docker documentation to use Supervisord 处于非分离模式,因此所有子守护进程都将输出到标准输出。

            From another SO question, I discovered you can redirect the child processes output to the stdout. 这样你就可以看到所有的输出了!

            【讨论】:

            • 再看一遍,这个答案似乎更适合并行而不是串行运行多个命令。
            【解决方案13】:

            使用诸如 wait-for-it 或 dockerize 之类的工具。这些是可以包含在应用程序映像中的小型包装脚本。或者编写自己的包装脚本来执行更多特定于应用程序的命令。根据:https://docs.docker.com/compose/startup-order/

            【讨论】:

            • 链接到wait-for-it.sh。应该注意(正如您链接到的文档中那样)“等待数据库(例如)准备就绪的问题实际上只是分布式系统更大问题的一个子集。在生产中,您的数据库可以随时变得不可用或移动主机。您的应用程序需要对这些类型的故障具有弹性。”此解决方案适用于“不需要这种级别的弹性”的人
            【解决方案14】:

            这是我对这个问题的解决方案:

            services:
              mongo1:
                container_name: mongo1
                image: mongo:4.4.4
                restart: always
            #    OPTION 01:
            #    command: >
            #      bash -c "chmod +x /scripts/rs-init.sh
            #      && sh /scripts/rs-init.sh"
            #    OPTION 02:
                entrypoint: [ "bash", "-c", "chmod +x /scripts/rs-init.sh && sh /scripts/rs-init.sh"]
                ports:
                  - "9042:9042"
                networks:
                  - mongo-cluster
                volumes:
                  - ~/mongors/data1:/data/db
                  - ./rs-init.sh:/scripts/rs-init.sh
                  - api_vol:/data/db
                environment:
                  *env-vars
                depends_on:
                  - mongo2
                  - mongo3
            
            

            【讨论】:

              【解决方案15】:

              我在尝试设置我的 jenkins 容器以作为 jenkins 用户构建 docker 容器时遇到了这个问题。

              我需要触摸 Dockerfile 中的 docker.sock 文件,因为我稍后会在 docker-compose 文件中链接它。除非我先触摸它,否则它还不存在。这对我有用。

              Dockerfile:

              USER root
              RUN apt-get update && \
              apt-get -y install apt-transport-https \
              ca-certificates \
              curl \
              software-properties-common && \
              curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; 
              echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
              add-apt-repository \
              "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
              $(lsb_release -cs) \
              stable" && \
              apt-get update && \
              apt-get -y install docker-ce
              RUN groupmod -g 492 docker && \
              usermod -aG docker jenkins  && \
              touch /var/run/docker.sock && \
              chmod 777 /var/run/docker.sock
              
              USER Jenkins
              

              docker-compose.yml:

              version: '3.3'
              services:
              jenkins_pipeline:
                  build: .
                  ports:
                    - "8083:8083"
                    - "50083:50080"
                  volumes:
                      - /root/pipeline/jenkins/mount_point_home:/var/jenkins_home
                      - /var/run/docker.sock:/var/run/docker.sock
              

              【讨论】:

              • 这似乎是不同问题的答案。
              【解决方案16】:

              我遇到了同样的问题,我想在端口 3000 上运行我的 react 应用程序,在端口 6006 上运行我的故事书都在同一个容器中。

              我尝试从 Dockerfile 启动入口点命令以及使用 docker-compose command 选项。

              在花了一些时间之后,决定将这些服务分离到单独的容器中,它就像魅力一样工作

              【讨论】:

                【解决方案17】:

                在@Bjorn 回答的基础上,docker 有recently introduced 特殊的依赖规则,允许您等到“init 容器”成功退出,这给出了

                db:
                  image: postgres
                web:
                  image: app
                  command: python manage.py runserver 0.0.0.0:8000
                  depends_on:
                    db:
                    migration:
                      condition: service_completed_successfully
                migration:
                  build: .
                  image: app
                  command: python manage.py migrate
                  depends_on:
                    - db
                

                我不确定你是否还需要 buildkit,但在我这边它可以使用

                DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker-compose up
                

                【讨论】:

                  【解决方案18】:

                  如果其他人试图通过基于 Windows 的容器找出多个命令,则以下格式有效:
                  command: "cmd.exe /c call C:/Temp/script1.bat && dir && C:/Temp/script2.bat && ..."

                  包含 'call' 指令是为我修复它的原因。

                  或者,如果每个命令都可以在前面的命令不成功的情况下执行,只需用分号分隔每个命令:
                  command: "cmd.exe /c call C:/Temp/script1.bat; dir; C:/Temp/script2.bat; ... "

                  【讨论】:

                    【解决方案19】:

                    尝试使用“;”如果您使用的是两个版本,则将命令分开 例如

                    command: "sleep 20; echo 'a'"

                    【讨论】:

                      猜你喜欢
                      • 2019-10-22
                      • 2020-10-08
                      • 2021-03-27
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2023-01-28
                      • 2020-03-23
                      相关资源
                      最近更新 更多