Usando o Docker-Compose, como executar vários comandos


500

Eu quero fazer algo assim, onde eu possa executar vários comandos em ordem.

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

Respostas:


861

Descobri, use bash -c.

Exemplo:

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

O mesmo exemplo em multilinhas:

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

Ou:

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

6
@Pedram Verifique se você está usando uma imagem que realmente possui o bash instalado. Algumas imagens também podem exigir um caminho direto para o bash por exemplo/bin/bash
codemaven

6
Se não houver nenhuma festa instalado, você poderia tentar sh -c "o comando"
Chaoste

Certifique-se de colocar seus comandos entre aspas ao passar para o bash e eu tive que inserir um "sono 5" para garantir que o banco de dados estivesse ativo, mas funcionou para mim.
traday 26/10/16

74
Imagens baseadas nos Alpes parecem não ter o bash instalado - faça como o @Chaoste recomenda e use sh:[sh, -c, "cd /usr/src/app && npm start"]
Florian Loch

1
Também é possível utilizar apenas ashem alpino :)
Jonathan

160

Eu corro coisas pré-inicialização, como migrações em um contêiner efêmero separado, assim (observe, o arquivo de composição deve ser do tipo versão 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

Isso ajuda as coisas a se manterem limpas e separadas. Duas coisas a considerar:

  1. Você precisa garantir a sequência de inicialização correta (usando o Depend_on)

  2. você deseja evitar várias compilações, o que é conseguido marcando-a na primeira vez usando compilação e imagem; você pode consultar a imagem em outros recipientes


2
Essa parece ser a melhor opção para mim e eu gostaria de usá-la. Você pode elaborar sua configuração de marcação para evitar várias compilações? Eu preferiria evitar etapas extras, por isso, se isso precisar de alguma coisa, eu posso ir com o bash -cacima.
Stavros Korokithakis

3
No yaml acima, a construção e a marcação acontecem na seção de migração. Não é realmente óbvio à primeira vista, mas o docker-componha marcá-lo quando você especifica as propriedades build AND image - em que a propriedade image especifica o tag para esse build. Isso pode ser usado posteriormente sem acionar uma nova compilação (se você observar a web, verá que ela não tem compilação, mas apenas uma propriedade de imagem). Aqui estão mais alguns detalhes docs.docker.com/compose/compose-file )
Bjoern Stiel

26
Embora eu goste da idéia, o problema é que depende_on apenas garante que eles iniciem nessa ordem, e não que estejam prontos nessa ordem. wait-for-it.sh pode ser a solução que algumas pessoas precisam.
traday 26/10/16

2
Isso está absolutamente correto e é uma pena que a composição do docker não suporte nenhum controle refinado, como esperar que um contêiner saia ou comece a ouvir em uma porta. Mas sim, um script personalizado resolve isso, bom ponto!
Bjoern Stiel

1
Esta resposta fornece informações incorretas e potencialmente destrutivas sobre como depende o trabalho.
antonagestam 7/08/19

96

Eu recomendo usar sho contrário, bashporque está mais prontamente disponível na maioria das imagens baseadas em unix (alpino, etc.).

Aqui está um exemplo 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"

Isso chamará os seguintes comandos em ordem:

  • python manage.py wait_for_db - aguarde o db estar pronto
  • python manage.py migrate - execute qualquer migração
  • python manage.py runserver 0.0.0.0:8000 - iniciar meu servidor de desenvolvimento

2
Pessoalmente, essa é minha solução favorita e mais limpa.
BugHunterUK 10/10

1
Meu também. Como pontos-out @LondonAppDev, o bash não está disponível por padrão em todas as embalagens para otimizar no espaço (por exemplo, a maioria dos recipientes construídos sobre Alpine Linux)
ewilan

2
Eu tive que escapar da multilinha && com um \
Andre Van Zuydam

@AndreVanZuydam hmmm isso é estranho, eu não precisava fazer isso. Você cercou entre aspas? Qual o sabor do docker que você está executando?
LondonAppDev

2
@oligofren o >é utilizado para iniciar uma entrada multi-linha (ver stackoverflow.com/a/3790497/2220370 )
LondonAppDev

40

Isso funciona para mim:

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 tenta desreferenciar variáveis antes de executar o comando, portanto, se você deseja que o bash manipule variáveis, precisará escapar dos cifrões dobrando-os ...

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

... caso contrário, você receberá um erro:

Formato de interpolação inválido para a opção "comando" no serviço "web":


E aí, cara. Encontrei um problema: `` argumentos não reconhecidos: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows `` `o comando no meu docker-compose.yml é: command: - / bin / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Client} -T $ {Types} Você sabe como consertar isso? Eu adiciono Client e Types no meu arquivo .env.
Newt

Aqui está um documento para você: docs.docker.com/compose/compose-file/#variable-substitution Acho que o que está acontecendo é que o arquivo .env coloca essas variáveis ​​no ambiente do contêiner, mas o docker-compose está procurando no seu ambiente de shell . Tente em vez disso $${Types}e $${Client}. Eu acho que isso impedirá que o docker compose interprete essas variáveis ​​e procure seus valores em qualquer shell do qual você invoque o docker-composit, o que significa que eles ainda estão por aí para que o bash os desreferencie ( depois que o docker processou seu .envarquivo).
MatrixManAtYrService

Obrigado por seu comentário. Eu fiz o que você disse de fato. Então, recebi o $ (Client) nas informações de erro. Mudei a maneira de ler variáveis ​​de ambiente para usar os.getenv em python, o que é mais fácil. Obrigado mesmo assim.
Newt

23

Você pode usar o ponto de entrada aqui. O ponto de entrada na janela de encaixe é executado antes do comando while command é o comando padrão que deve ser executado quando o contêiner é iniciado. Portanto, a maioria dos aplicativos geralmente realiza o procedimento de instalação no arquivo de ponto de entrada e, no último, eles permitem a execução do comando.

make um arquivo de script de shell pode ser o seguinte docker-entrypoint.sh(o nome não importa) com o seguinte conteúdo.

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

no arquivo docker-compose.yml, use-o com entrypoint: /docker-entrypoint.she o comando register como command: python manage.py runserver 0.0.0.0:8000 PS: não esqueça de copiar docker-entrypoint.shjunto com o seu código.


Observe que isso também será executado quando você fizer issodocker-compose run service-name ....
thisismydesign

18

Outra ideia:

Se, como neste caso, você criar o contêiner, basta colocar um script de inicialização nele e executá-lo com o comando Ou monte o script de inicialização como volume.


Sim, no final eu criei um script run.sh: #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(oneline feio)
fero

9

* ATUALIZAÇÃO *

Achei que a melhor maneira de executar alguns comandos é escrever um Dockerfile personalizado que faça tudo o que eu quero antes que o CMD oficial seja executado a partir da imagem.

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 é provavelmente a maneira mais limpa de fazer isso.

* À MODA ANTIGA *

Eu criei um script de shell com meus comandos. Nesse caso, eu queria começar mongode executar, mongoimportmas chamar o mongodimpede de executar o 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

Então, esse garfo mongo, monitora a importação e, em seguida, mata o mongo bifurcado que é desanexado e o inicia novamente sem desconectar. Não tenho certeza se existe uma maneira de conectar-se a um processo bifurcado, mas isso funciona.

NOTA: Se você deseja carregar estritamente alguns dados db iniciais, é assim que se faz:

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

Os arquivos mongo_fixtures / *. json foram criados por meio do 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

Se você precisar executar mais de um processo de daemon, há uma sugestão na documentação do Docker para usar o Supervisord em um modo desanexado, para que todos os sub-daemons sejam exibidos no stdout.

De outra pergunta do SO, descobri que você pode redirecionar a saída dos processos filho para o stdout. Dessa forma, você pode ver toda a saída!


Olhando para isso novamente, esta resposta parece mais adequada para executar vários comandos em paralelo, em vez de serialmente.
precisa saber é o seguinte



0

Eu me deparei com isso enquanto tentava configurar meu contêiner jenkins para criar contêineres docker como o usuário jenkins.

Eu precisava tocar no arquivo docker.sock no Dockerfile, como eu o vinculo mais tarde no arquivo docker-compose. A menos que eu toquei primeiro, ele ainda não existia. Isso funcionou para mim.

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 a resposta para uma pergunta diferente.
kenorb

-7

tente usar ";" para separar os comandos se você estiver na versão dois, por exemplo

command: "sleep 20; echo 'a'"

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.