使用Docker-Compose,如何执行多个命令


499

我想做这样的事情,我可以依次运行多个命令。

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

Answers:


859

想通了,使用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
  "

6
@Pedram确保您使用的图像实际上已安装了bash。一些图像可能还需要直接进行bash的路径,例如/bin/bash
codemaven

5
如果没有安装bash,则可以尝试sh -c“您的命令”
Chaoste,2016年

确保传递给bash时将命令用引号引起来,并且我必须滑入“ sleep 5”以确保数据库启动,但是它对我有用。
2016年

74
基于高山的图像实际上似乎没有安装bash-像@Chaoste建议的那样使用,sh而是使用:[sh, -c, "cd /usr/src/app && npm start"]
Florian Loch

1
也可以只ash在高山上使用:)
Jonathan

160

我在单独的临时容器中运行诸如迁移之类的启动前内容(请注意,撰写文件的版本必须为“ 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. 您希望避免通过第一次使用构建和图像对其进行标记来实现多个构建;您可以在其他容器中引用图像


2
对我来说,这似乎是最好的选择,我想使用它。您能否详细说明标记设置,以避免构建多个版本?我宁愿避免采取额外的步骤,因此,如果需要一些步骤,我可以选择bash -c上面的步骤。
Stavros Korokithakis

3
在上面的yaml中,构建和标记发生在“迁移”部分。乍一看并不是很明显,但是当您指定构建和图像属性时,docker-compose会对其进行标记-图像属性会为该构建指定标记。然后可以在不触发新构建的情况下使用该属性(如果您查看Web,您会看到它没有构建,只有image属性)。这里的一些细节docs.docker.com/compose/compose-file
比约恩·Stiel

25
尽管我喜欢这个主意,但问题是,depends_on仅确保它们按该顺序启动,而不是确保它们按该顺序准备就绪。wait-for-it.sh可能是某些人需要的解决方案。
2016年

2
这是绝对正确的,并且让docker-compose不支持任何细粒度的控件(例如,等待容器退出或开始监听端口)是一种遗憾。但是,是的,自定义脚本可以解决此问题,这很重要!
Bjoern Stiel

1
该答案给出了有关depends_on如何工作的错误信息,并可能造成破坏性的信息。
antonagestam

96

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

这是一个例子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 -启动我的开发服务器

2
就个人而言,这是我最喜欢,最干净的解决方案。
BugHunterUK

1
我也是。正如@LondonAppDev指出的那样,默认情况下并非在所有容器中都可以使用bash来优化空间(例如,大多数容器是基于Alpine Linux构建的)
ewilan

2
我必须使用\来逃避多行&&
Andre Van Zuydam

@AndreVanZuydam hmmm,很奇怪,我不需要这样做。您是否用引号引起来?您正在运行哪种类型的Docker?
LondonAppDev

2
@oligofren的>用于启动多线输入(参见stackoverflow.com/a/3790497/2220370
LondonAppDev

39

这对我有用:

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”中“命令”选项的无效插值格式:


大家好 我遇到一个问题:```无法识别的参数:/ 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的任何外壳中寻找它们的值,这意味着它们仍然存在以供bash取消引用( docker处理完您的.env文件之后)。
MatrixManAtYrService

谢谢你的评论。我确实做到了你所说的。所以我在错误信息中得到了$(Client)。我更改了读取环境变量的方式,以便在python中使用os.getenv。不管怎么说,还是要谢谢你。
纽特

23

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

制作一个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 PS:不要忘记docker-entrypoint.sh与您的代码一起复制。


请注意,这也将在您执行时执行docker-compose run service-name ....
thisismydesign

18

另一个想法:

如果像在这种情况下那样构建容器,只需将启动脚本放入其中并使用命令运行它。或将启动脚本安装为卷。


是的,最后我创建了一个run.sh脚本:(#!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000丑陋的
一行

9

*更新*

我认为运行某些命令的最佳方法是编写一个自定义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

因此,这会分叉mongo,进行monogimport,然后杀死已分离的分叉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




0

我在尝试将我的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

这似乎是对其他问题的答案。
kenorb

-7

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

command: "sleep 20; echo 'a'"

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.