Sử dụng Docker-Compose, cách thực hiện nhiều lệnh


500

Tôi muốn làm một cái gì đó như thế này nơi tôi có thể chạy nhiều lệnh theo thứ tự.

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

Câu trả lời:


861

Tìm ra nó, sử dụng bash -c.

Thí dụ:

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

Ví dụ tương tự trong multilines:

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

Hoặc là:

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

6
@Pedram Hãy chắc chắn rằng bạn đang sử dụng một hình ảnh thực sự đã cài đặt bash. Một số hình ảnh cũng có thể yêu cầu một đường dẫn trực tiếp đến bash ví dụ/bin/bash
codemaven

5
Nếu không có bash nào được cài đặt, bạn có thể thử sh -c "lệnh của bạn"
Chaoste

Hãy chắc chắn rằng bạn gói các lệnh của bạn trong dấu ngoặc kép khi chuyển sang bash và tôi phải trượt "ngủ 5" để đảm bảo db đã hoạt động, nhưng nó hoạt động với tôi.
ngày

74
Các hình ảnh dựa trên núi cao thực sự dường như không có bash nào được cài đặt - hãy làm như @Chaoste khuyến nghị và sử dụng shthay thế:[sh, -c, "cd /usr/src/app && npm start"]
Florian Loch

1
Cũng có thể sử dụng chỉ ashtrên alpine :)
Jonathan

160

Tôi chạy các công cụ tiền khởi động như di chuyển trong một thùng chứa phù du riêng biệt, như vậy (lưu ý, tệp soạn thảo phải thuộc loại phiên bả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

Điều này giúp mọi thứ giữ sạch sẽ và riêng biệt. Hai điều cần xem xét:

  1. Bạn phải đảm bảo trình tự khởi động chính xác (sử dụng phụ thuộc)

  2. bạn muốn tránh nhiều bản dựng đạt được bằng cách gắn thẻ lần đầu tiên bằng cách sử dụng bản dựng và hình ảnh; bạn có thể tham khảo hình ảnh trong các container khác sau đó


2
Đây có vẻ là lựa chọn tốt nhất đối với tôi và tôi muốn sử dụng nó. Bạn có thể giải thích về thiết lập gắn thẻ của mình để tránh nhiều bản dựng không? Tôi muốn tránh các bước thêm, vì vậy nếu điều này cần một số, tôi có thể đi với bash -cở trên.
Stavros Korokithakis

3
Trong yaml ở trên, việc xây dựng và gắn thẻ xảy ra trong phần di chuyển. Nó không thực sự rõ ràng ngay từ cái nhìn đầu tiên, nhưng docker-compose các thẻ khi bạn chỉ định các thuộc tính hình ảnh AND xây dựng - theo đó thuộc tính hình ảnh chỉ định thẻ cho bản dựng đó. Điều đó sau đó có thể được sử dụng mà không kích hoạt bản dựng mới (nếu bạn nhìn vào web, bạn sẽ thấy nó không có bản dựng mà chỉ có thuộc tính hình ảnh). Dưới đây là một số chi tiết khác docs.docker.com/compose/compose-file )
Bjoern Stiel

26
Mặc dù tôi thích ý tưởng này, nhưng vấn đề là phụ thuộc vào việc chỉ đảm bảo họ bắt đầu theo thứ tự đó chứ không phải họ đã sẵn sàng theo thứ tự đó. Wait-for-it.sh có thể là giải pháp mà một số người cần.
ngày

2
Điều đó hoàn toàn chính xác và một chút xấu hổ khi docker-compose không hỗ trợ bất kỳ kiểm soát chi tiết tốt nào như chờ đợi một container thoát ra hoặc bắt đầu nghe trên một cổng. Nhưng có, một kịch bản tùy chỉnh không giải quyết điều này, điểm tốt!
Bjoern Stiel

1
Câu trả lời này cung cấp thông tin không chính xác và có khả năng phá hủy về cách thức hoạt động của phụ thuộc.
antonagestam

96

Tôi khuyên bạn nên sử dụng shtrái ngược với bashvì nó có sẵn trên hầu hết các hình ảnh dựa trên unix (alpine, v.v.).

Đây là một ví dụ 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"

Điều này sẽ gọi các lệnh sau theo thứ tự:

  • python manage.py wait_for_db - đợi db sẵn sàng
  • python manage.py migrate - chạy bất kỳ di chuyển
  • python manage.py runserver 0.0.0.0:8000 - khởi động máy chủ phát triển của tôi

2
Cá nhân đây là giải pháp yêu thích và sạch sẽ nhất của tôi.
BugHunterUK

1
Của tôi cũng thế. Như @LondonAppDev chỉ ra, bash không có sẵn theo mặc định trong tất cả các container để tối ưu hóa không gian (ví dụ: hầu hết các container được xây dựng trên đỉnh của Linux Linux)
ewilan 20/11/18

2
Tôi đã phải thoát khỏi multiline && với một \
Andre Van Zuydam

@AndreVanZuydam hmmm thật lạ, tôi không cần phải làm điều đó. Bạn đã bao quanh với dấu ngoặc kép? Hương vị nào của docker bạn đang chạy?
LondonAppDev

2
@oligofren the >được sử dụng để bắt đầu nhập nhiều dòng (xem stackoverflow.com/a/3790497/2220370 )
LondonAppDev 22/03/19

40

Điều này làm việc cho tôi:

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 cố gắng biến các biến số trước khi chạy lệnh, vì vậy nếu bạn muốn bash xử lý các biến, bạn cần thoát khỏi các ký hiệu đô la bằng cách nhân đôi chúng ...

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

... Nếu không, bạn sẽ gặp lỗi:

Định dạng nội suy không hợp lệ cho tùy chọn "lệnh" trong dịch vụ "web":


Chào bạn. Tôi đã gặp một vấn đề: `` `đối số không được nhận dạng: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows` `` lệnh trong docker-compose.yml của tôi là: lệnh: - / thùng / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Client} -T $ {Các loại} Bạn có biết cách khắc phục điều đó không? Tôi thêm Client và Type trong tệp .env của mình.
Newt

Đây là tài liệu dành cho bạn: docs.docker.com/compose/compose-file/#variable-substlation Tôi nghĩ rằng điều đang xảy ra là tệp .env của bạn đặt các biến đó trong môi trường vùng chứa, nhưng docker-compose đang tìm kiếm trong môi trường shell của bạn . Hãy thử thay thế $${Types}$${Client}. Tôi nghĩ rằng điều này sẽ ngăn docker compose diễn giải các biến đó và tìm kiếm các giá trị của chúng trong bất kỳ shell nào mà bạn gọi docker-compose từ đó, điều đó có nghĩa là chúng vẫn ở xung quanh để bash để xử lý chúng ( sau khi docker đã xử lý .envtệp của bạn ).
MatrixManAtYrService

Cám ơn bạn đã góp ý. Tôi đã làm những gì bạn nói trong thực tế. Vì vậy, tôi đã nhận được $ (Client) trong thông tin lỗi. Tôi đã thay đổi cách đọc các biến môi trường để sử dụng os.getenv trong python, điều này dễ dàng hơn. Dù sao cũng cảm ơn bạn.
Newt

23

Bạn có thể sử dụng điểm vào đây. entrypoint trong docker được thực thi trước lệnh while trong khi lệnh là lệnh mặc định sẽ được chạy khi container bắt đầu. Vì vậy, hầu hết các ứng dụng thường thực hiện quy trình thiết lập trong tệp entrypoint và cuối cùng chúng cho phép lệnh chạy.

tạo một tập lệnh shell có thể là docker-entrypoint.sh(tên không quan trọng) với nội dung sau trong đó.

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

trong tập tin docker-compose.yml sử dụng nó với entrypoint: /docker-entrypoint.shvà đăng ký lệnh như command: python manage.py runserver 0.0.0.0:8000 PS: đừng quên sao chép docker-entrypoint.shcùng với mã của bạn.


Lưu ý rằng điều này cũng sẽ thực thi khi bạn thực hiệndocker-compose run service-name ....
thisismydesign

18

Một ý tưởng khác:

Nếu, như trong trường hợp này, bạn xây dựng vùng chứa chỉ cần đặt một tập lệnh khởi động vào nó và chạy lệnh này bằng lệnh. Hoặc gắn tập lệnh khởi động dưới dạng âm lượng.


Vâng, cuối cùng tôi đã tạo một kịch bản #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000run.sh : (oneline xấu xí)
fero

9

* CẬP NHẬT *

Tôi đã tìm ra cách tốt nhất để chạy một số lệnh là viết Dockerfile tùy chỉnh thực hiện mọi thứ tôi muốn trước khi CMD chính thức được chạy từ hình ảnh.

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)

Đây có lẽ là cách sạch nhất để làm điều đó.

* CÁCH CŨ *

Tôi đã tạo một kịch bản shell với các lệnh của tôi. Trong trường hợp này, tôi muốn bắt đầu mongodvà chạy mongoimportnhưng gọi mongodbạn sẽ không chạy phần còn lại.

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

Vì vậy, điều này tạo ra mongo, thực hiện monogimport và sau đó giết chết mongo rẽ nhánh được tách ra, và khởi động lại mà không cần tháo ra. Không chắc chắn nếu có một cách để gắn vào một quá trình rẽ nhánh nhưng điều này không hoạt động.

LƯU Ý: Nếu bạn thực sự muốn tải một số dữ liệu db ban đầu thì đây là cách để làm điều đó:

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

Các tập tin mongo_fixenses / *. json được tạo thông qua lệnh 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

Nếu bạn cần chạy nhiều hơn một quy trình trình nền, có một gợi ý trong tài liệu Docker để sử dụng Giám sát trong chế độ không tách rời để tất cả các trình nền phụ sẽ xuất ra thiết bị xuất chuẩn.

Từ một câu hỏi SO khác, tôi phát hiện ra bạn có thể chuyển hướng đầu ra của các tiến trình con đến thiết bị xuất chuẩn. Bằng cách đó bạn có thể thấy tất cả đầu ra!


Nhìn vào điều này một lần nữa, câu trả lời này có vẻ phù hợp hơn để chạy song song nhiều lệnh thay vì ser seri.
Tim Tonomall


1

Sử dụng một công cụ như chờ đợi cho nó hoặc dockerize . Đây là các tập lệnh bao bọc nhỏ mà bạn có thể đưa vào hình ảnh của ứng dụng. Hoặc viết tập lệnh trình bao bọc của riêng bạn để thực hiện các lệnh cụ thể hơn cho ứng dụng. theo: https://docs.docker.com/compose/startup-order/


0

Tôi đã gặp phải điều này trong khi cố gắng để thiết bị chứa jenkins của mình được thiết lập để xây dựng các container docker với tư cách là người dùng jenkins.

Tôi cần phải chạm vào tệp docker.sock trong Dockerfile khi tôi liên kết nó sau này trong tệp soạn thảo docker. Trừ khi tôi chạm vào nó trước, nó vẫn chưa tồn tại. Điều này làm việc cho tôi.

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

Đây dường như là câu trả lời cho câu hỏi khác nhau.
kenorb

-7

thử sử dụng ";" để tách các lệnh nếu bạn ở trong câu hai, vd

command: "sleep 20; echo 'a'"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.