Usando Docker-Compose, cómo ejecutar múltiples comandos


500

Quiero hacer algo como esto donde puedo ejecutar varios comandos en orden.

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

Respuestas:


861

Lo descubrí, úsalo bash -c.

Ejemplo:

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

Mismo ejemplo en multilíneas:

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

O:

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

66
@Pedram Asegúrese de estar usando una imagen que realmente tenga instalado bash. Algunas imágenes también pueden requerir una ruta directa a bash, por ejemplo/bin/bash
codemaven

66
Si no hay una bash instalada, puede probar sh -c "your command"
Chaoste

Asegúrese de ajustar sus comandos entre comillas al pasar a bash y tuve que deslizar un "sleep 5" para asegurarme de que el db estaba activo, pero funcionó para mí.
día

74
Las imágenes basadas en alpinos en realidad parecen no tener instalado bash - haga lo que @Chaoste recomienda y use shen su lugar:[sh, -c, "cd /usr/src/app && npm start"]
Florian Loch

1
También se puede usar solo ashen alpino :)
Jonathan

160

Ejecuto cosas previas al inicio como migraciones en un contenedor efímero separado, así (nota, componer archivo debe ser del tipo de versión '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

Esto ayuda a mantener las cosas limpias y separadas. Dos cosas a tener en cuenta:

  1. Debe asegurarse de la secuencia de inicio correcta (usando depende_en)

  2. desea evitar múltiples compilaciones, lo cual se logra etiquetándolo la primera vez usando compilación e imagen; puede referirse a la imagen en otros contenedores y luego


2
Esta parece ser la mejor opción para mí, y me gustaría usarla. ¿Puede elaborar su configuración de etiquetado para evitar múltiples compilaciones? Preferiría evitar pasos adicionales, así que si esto necesita algunos, podría seguir con lo bash -canterior.
Stavros Korokithakis

3
En el yaml anterior, la compilación y el etiquetado ocurren en la sección de migración. No es realmente obvio a primera vista, pero Docker-compose lo etiqueta cuando especifica las propiedades de compilación Y de imagen, por lo que la propiedad de imagen especifica la etiqueta para esa compilación. Eso se puede usar posteriormente sin activar una nueva compilación (si mira web, verá que no tiene compilación sino solo una propiedad de imagen). Aquí hay más detalles docs.docker.com/compose/compose-file )
Bjoern Stiel

26
Si bien me gusta la idea de esto, el problema es que depende de que solo se asegure de que comiencen en ese orden, no de que estén listos en ese orden. wait-for-it.sh puede ser la solución que algunas personas necesitan.
día

2
Eso es absolutamente correcto y es una pena que Docker-compose no admita ningún control de grano fino como esperar a que un contenedor salga o comience a escuchar en un puerto. Pero sí, un script personalizado resuelve esto, ¡buen punto!
Bjoern Stiel

1
Esta respuesta proporciona información incorrecta y potencialmente destructiva sobre cómo depende del trabajo.
antonagestam

96

Recomiendo usar shen lugar de bashporque está más disponible en la mayoría de las imágenes basadas en Unix (alpina, etc.).

Aquí hay un ejemplo 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"

Esto llamará a los siguientes comandos en orden:

  • python manage.py wait_for_db - espera a que el db esté listo
  • python manage.py migrate - ejecutar cualquier migración
  • python manage.py runserver 0.0.0.0:8000 - iniciar mi servidor de desarrollo

2
Personalmente, esta es mi solución favorita y más limpia.
BugHunterUK

1
Mío también. Como señala @LondonAppDev, bash no está disponible por defecto en todos los contenedores para optimizar el espacio (por ejemplo, la mayoría de los contenedores construidos sobre Alpine Linux)
ewilan

2
Tuve que escapar del multilínea && con un \
Andre Van Zuydam el

@AndreVanZuydam hmmm eso es extraño, no necesitaba hacer eso. ¿Te rodeó de citas? ¿Qué sabor de docker estás ejecutando?
LondonAppDev

2
@oligofren >se usa para iniciar una entrada de varias líneas (consulte stackoverflow.com/a/3790497/2220370 )
LondonAppDev

40

Esto funciona para mi:

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 intenta desreferenciar las variables antes de ejecutar el comando, por lo que si desea que bash maneje las variables, deberá escapar de los signos de dólar al duplicarlas ...

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

... de lo contrario obtendrá un error:

Formato de interpolación no válido para la opción "comando" en el servicio "web":


Hola colega. Me encontré con un problema: `` `` argumentos no reconocidos: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows '' `` el comando en mi docker-compose.yml es: comando: - / bin / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Cliente} -T $ {Tipos} ¿Sabes cómo solucionarlo? Agrego Cliente y Tipos en mi archivo .env.
Newt

Aquí hay un documento para usted: docs.docker.com/compose/compose-file/#variable-substitution Creo que lo que está sucediendo es que su archivo .env coloca esas variables en el entorno del contenedor, pero docker-compose está buscando en su entorno de shell . Intenta en su lugar $${Types}y $${Client}. Creo que esto evitará que docker compose interprete esas variables y busque sus valores en cualquier shell desde el que invoque docker-compose, lo que significará que todavía están disponibles para que bash los desreferencia ( después de que docker haya procesado su .envarchivo).
MatrixManAtYrService

Gracias por tu comentario. Hice lo que dijiste de hecho. Entonces obtuve el $ (Cliente) en la información de error. Cambié la forma de leer las variables de entorno para usar os.getenv en python, lo cual es más fácil. Gracias de cualquier manera.
Newt

23

Puede usar el punto de entrada aquí. El punto de entrada en la ventana acoplable se ejecuta antes del comando, mientras que el comando es el comando predeterminado que se debe ejecutar cuando se inicia el contenedor. Por lo tanto, la mayoría de las aplicaciones generalmente llevan el procedimiento de configuración en el archivo de punto de entrada y en el último permiten que se ejecute el comando.

hacer un archivo de script de shell puede ser como docker-entrypoint.sh(el nombre no importa) con los siguientes contenidos.

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

en el archivo docker-compose.yml utilícelo con entrypoint: /docker-entrypoint.shy registre el comando como command: python manage.py runserver 0.0.0.0:8000 PS: no olvide copiar docker-entrypoint.shjunto con su código.


Tenga en cuenta que esto también se ejecutará cuando lo hagadocker-compose run service-name ....
thisismydesign

18

Otra idea:

Si, como en este caso, crea el contenedor, simplemente coloque un script de inicio y ejecútelo con el comando. O monte el script de inicio como volumen.


Sí, al final creé un script run.sh: #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(feo en línea)
fero

9

* ACTUALIZACIÓN *

Pensé que la mejor manera de ejecutar algunos comandos es escribir un Dockerfile personalizado que haga todo lo que quiera antes de que el CMD oficial se ejecute desde la imagen.

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)

Esta es probablemente la forma más limpia de hacerlo.

* VIEJA FORMA *

Creé un script de shell con mis comandos. En este caso, quería comenzar mongody ejecutar, mongoimportpero llamandomongod te impide ejecutar el resto.

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

Entonces esto bifurca a mongo, monogimporta y luego mata al mongo bifurcado que se separa, y lo inicia nuevamente sin separarse. No estoy seguro de si hay una manera de adjuntar a un proceso bifurcado, pero esto funciona.

NOTA: Si desea cargar estrictamente algunos datos de db iniciales, esta es la forma de hacerlo:

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

Los archivos mongo_fixtures / *. json se crearon mediante el comando 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

5

Si necesita ejecutar más de un proceso de demonio, hay una sugerencia en la documentación de Docker para usar Supervisord en un modo no separado para que todos los sub-demonios salgan al stdout.

De otra pregunta de SO, descubrí que puede redirigir los procesos secundarios a la salida estándar. ¡De esa manera puedes ver toda la salida!


Mirando esto nuevamente, esta respuesta parece más adecuada para ejecutar múltiples comandos en paralelo en lugar de en serie.
Tim Tisdall



0

Me topé con esto mientras intentaba configurar mi contenedor jenkins para construir contenedores docker como usuario jenkins.

Necesitaba tocar el archivo docker.sock en el Dockerfile mientras lo enlazo más adelante en el archivo docker-compose. A menos que lo haya tocado primero, todavía no existe. Esto funcionó para mí.

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

Esta parece ser la respuesta a una pregunta diferente.
kenorb

-7

intente usar ";" para separar los comandos si está en la versión dos, por ejemplo

command: "sleep 20; echo 'a'"

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.