Làm thế nào để giữ Docker container chạy sau khi bắt đầu dịch vụ?


155

Tôi đã thấy một loạt các hướng dẫn dường như làm điều tương tự mà tôi đang cố gắng thực hiện, nhưng vì một số lý do, các container Docker của tôi thoát ra. Về cơ bản, tôi đang thiết lập một máy chủ web và một vài trình tiện ích bên trong một container Docker. Tôi thực hiện các phần cuối cùng của điều này thông qua một tập lệnh bash được gọi là run-all.shtôi chạy qua CMD trong Dockerfile của tôi. run-all.shtrông như thế này:

service supervisor start
service nginx start

Và tôi khởi động nó bên trong Dockerfile của mình như sau:

CMD ["sh", "/root/credentialize_and_run.sh"]

Tôi có thể thấy rằng tất cả các dịch vụ đều khởi động chính xác khi tôi chạy mọi thứ một cách thủ công (tức là truy cập vào hình ảnh với -i -t / bin / bash) và mọi thứ trông giống như nó chạy chính xác khi tôi chạy hình ảnh, nhưng nó đã thoát một lần nó kết thúc bắt đầu quá trình của tôi. Tôi muốn các quá trình chạy vô thời hạn, và theo như tôi hiểu, container phải tiếp tục chạy để điều này xảy ra. Tuy nhiên, khi tôi chạy docker ps -a, tôi thấy:

➜  docker_test  docker ps -a
CONTAINER ID        IMAGE                            COMMAND                CREATED             STATUS                      PORTS               NAMES
c7706edc4189        some_name/some_repo:blah   "sh /root/run-all.sh   8 minutes ago       Exited (0) 8 minutes ago                        grave_jones

Đưa cái gì? Tại sao nó thoát? Tôi biết tôi chỉ có thể đặt một vòng lặp while vào cuối tập lệnh bash của mình để giữ cho nó tiếp tục, nhưng cách đúng đắn để giữ cho nó không thoát ra là gì?


1
bạn có để lộ các cổng của dịch vụ ra bên ngoài không (tùy chọn -p để chạy docker)? (tất nhiên điều này sẽ không ngăn họ thoát ra)
ribamar

1
Tôi đã sử dụng ENTRYPOINT trong Dockerfile của mình và sau khi tập lệnh được xác định trong ENTRYPOINT (tập lệnh init của tôi) chạy, nó xuất hiện trong nhật ký nhưng dường như container của tôi đã thoát. Vì vậy, thay vì ENTRYPOINT, tôi đã sử dụng lệnh RUN để chạy tập lệnh và container vẫn đang chạy trong nền.
ypahalajani

Câu trả lời:


49

Đây không thực sự là cách bạn nên thiết kế các container Docker của mình.

Khi thiết kế bộ chứa Docker, bạn phải xây dựng nó để chỉ có một quy trình đang chạy (nghĩa là bạn nên có một bộ chứa cho Nginx và một bộ chứa cho người giám sát hoặc ứng dụng đang chạy); Ngoài ra, quá trình đó sẽ chạy ở nền trước.

Container sẽ "thoát" khi quá trình tự thoát (trong trường hợp của bạn, quá trình đó là tập lệnh bash của bạn).


Tuy nhiên, nếu bạn thực sự cần (hoặc muốn) chạy nhiều dịch vụ trong bộ chứa Docker của mình, hãy xem xét bắt đầu từ "Docker Base Image" , sử dụng runitnhư một quy trình giả ( runitsẽ trực tuyến trong khi Nginx và Giám sát chạy), sẽ vẫn ở lại ở phía trước trong khi các quá trình khác của bạn làm việc của họ.

Họ có tài liệu đáng kể, vì vậy bạn sẽ có thể đạt được những gì bạn đang cố gắng thực hiện một cách hợp lý dễ dàng.


1
Bạn có thể giải thích tại sao tôi chỉ nên có một dịch vụ đang chạy không? Tôi có thể thêm nginx cho người giám sát nếu cần thiết, nhưng không chắc tại sao điều này lại cần thiết.
Eli

3
@Eli Câu trả lời ngắn gọn là đây là cách Docker hoạt động. Docker sẽ chỉ chạy một tiến trình (và con của nó) trên mỗi container. Chúng tôi khuyên rằng quy trình này là một quy trình ứng dụng thực tế (để nếu nó thoát, Docker biết), nhưng bạn thực sự có thể sử dụng trình giám sát như quy trình đó. Lưu ý rằng bạn sẽ phải định cấu hình trình giám sát để chạy ở nền trước (nghĩa là không phải trình nền), được thực hiện thông qua --nodaemontùy chọn.
Thomas Orozco

1
@Eli Bài đăng trên blog Docker này làm cho trường hợp chạy nhiều quy trình (và nói rộng ra, xem một container dưới dạng "VPS nhỏ") là không tối ưu. Trong trường hợp của bạn, chủ đề bình luận có thể sẽ phù hợp hơn bài viết trên blog thực tế.
Thomas Orozco

1
Hình ảnh cơ sở Docker là một giải pháp khủng khiếp cho rất nhiều vấn đề của doanh nghiệp vì rất ít công ty nghiêm túc sử dụng ubfox, thay vào đó thích sử dụng cây RHEL / Centos.
Kỹ sư phần mềm

9
"Vài công ty nghiêm túc" dường như không thể bảo vệ được. Sự lựa chọn của hệ điều hành dường như hoàn toàn dựa trên trường hợp sử dụng. Bất kỳ công ty nào cũng có nhiều môi trường khác nhau bao gồm sử dụng nhà phát triển nội bộ, sử dụng nhân viên nội bộ, hỗ trợ bán hàng, dàn dựng, POC và cuối cùng là sản xuất (và thậm chí đó là một thuật ngữ mơ hồ). Tôi không tin OP đã đề cập đến trường hợp sử dụng của họ vì vậy, (xin lỗi vì quá đáng) nhưng loại bình luận này dường như là loại phổ biến thông tin có ý kiến ​​cao mà không có lý do tại sao.
John Carrell

154

Nếu bạn đang sử dụng Dockerfile, hãy thử:

ENTRYPOINT ["tail", "-f", "/dev/null"]

(Rõ ràng điều này chỉ dành cho mục đích phát triển, bạn không cần phải giữ một container tồn tại trừ khi nó đang chạy một quy trình, ví dụ: nginx ...)


5
Tôi đã sử dụng CMD["sleep", "1d"]nhưng giải pháp của bạn có vẻ tốt hơn
George Pligoropoulos

@GeorgiosPligoropoulos cái này sẽ bị kẹt trong dòng đó; có thể chạy trong nền sẽ hoạt động
Prashanth Sams

5
Cũng có thể sử dụng CMD["sleep", "infinity"].
Romain

5
hoặc 'mèo' nhưng mọi người có thể nói đó là hành vi ngược đãi động vật. xD
lawph photo

Bạn có thể hoàn thành tập lệnh điểm vào của mình exec tail -f /dev/nullnhưng sử dụng taillàm điểm nhập cảnh là một câu trả lời sai.
Torsten Bronger

86

Tôi chỉ gặp vấn đề tương tự và tôi phát hiện ra rằng nếu bạn đang chạy container của bạn với cờ -t-d, nó sẽ tiếp tục chạy.

docker run -td <image>

Đây là những gì các cờ làm (theo docker run --help):

-d, --detach=false         Run container in background and print container ID
-t, --tty=false            Allocate a pseudo-TTY

Điều quan trọng nhất là -tcờ. -dchỉ cho phép bạn chạy container trong nền.


3
Tôi không thể tái tạo điều này. Bạn vui lòng cung cấp một ví dụ? Có bất cứ điều gì cụ thể (ví dụ: CMD) về Dockerfile mà chúng ta cần để làm việc này không?
Matheus Santana

2
Điều này đã không làm việc cho tôi. Tôi đã sử dụng lệnh docker logs <image>để chắc chắn rằng đó là một lỗi khiến container docker của tôi thoát ra. Trạng thái thoát là 0và đầu ra cuối cùng là xác nhận rằng lighttpdmáy chủ của tôi đang chạy:[ ok ] Starting web server: lighttpd.
ob1

Tôi đã không làm việc với Docker một thời gian. Vì vậy, có thể giao diện dòng lệnh đã thay đổi và lệnh này không hoạt động nữa.
arne.z

4
Tôi có thể xác nhận rằng điều này thực sự hoạt động với phiên bản docker mới nhất. Nếu sau này bạn muốn đính kèm vào phiên này, sử dụng -dit cũng sẽ hoạt động.
John Hamilton

1
@Long một tập lệnh sẽ không chấp nhận một tty, thêm exec bashhoặc exec shnếu bash không được cài đặt, đến cuối start.sh. Sau đó, bạn có thể sử dụng cờ -t
123

43

Lý do nó thoát là vì tập lệnh shell được chạy đầu tiên là PID 1 và khi hoàn thành, PID 1 không còn nữa và docker chỉ chạy trong khi PID 1.

Bạn có thể sử dụng trình giám sát để thực hiện mọi thứ, nếu chạy với cờ "-n", nó được bảo là không tạo daemon, vì vậy nó sẽ giữ nguyên quy trình đầu tiên:

CMD ["/usr/bin/supervisord", "-n"]

Và giám sát viên của bạn:

[supervisord]
nodaemon=true

[program:startup]
priority=1
command=/root/credentialize_and_run.sh
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=false
startsecs=0

[program:nginx]
priority=10
command=nginx -g "daemon off;"
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.log
autorestart=true

Sau đó, bạn có thể có nhiều quy trình khác như bạn muốn và người giám sát sẽ xử lý việc khởi động lại chúng nếu cần.

Bằng cách đó, bạn có thể sử dụng giám sát trong trường hợp bạn có thể cần nginx và php5-fpm và sẽ không có ý nghĩa gì khi tách chúng ra.


Nó nói ở đâu trong tài liệu nếu PID 1 kết thúc container docker dừng chạy?
8oh8

@ 8oh8 Đó thực chất là cách các không gian tên hoạt động; nó không đặc trưng cho Docker nhiều như "thứ nằm dưới tất cả các container". Từ man7.org/linux/man-pages/man7/pid_namespaces.7.html :If the "init" process of a PID namespace terminates, the kernel terminates all of the processes in the namespace via a SIGKILL signal. This behavior reflects the fact that the "init" process is essential for the correct operation of a PID namespace.
dannysauer

40

bạn có thể chạy đơn giản catmà không có bất kỳ đối số nào như bro @ Sa'ad đã đề cập để đơn giản giữ cho container hoạt động [thực sự không làm gì ngoài việc chờ người dùng nhập vào] (plugin Docker của Jenkins cũng làm điều tương tự)


ngoài câu trả lời của tôi: nhưng hãy hiểu rằng docker-compose (không được trình bày) được sử dụng để hiển thị cho bạn quy trình làm việc của container của bạn, vì vậy có thể thuận tiện để theo dõi các tệp nhật ký của các dịch vụ đã bắt đầu của bạn. chúc mừng
Serge Velikanov

1
hoặc cat. Plugin docker của jenkin làm như vậy.
Sa'ad

12

Đảm bảo rằng bạn thêm daemon off;cho bạn nginx.conf hoặc chạy nó CMD ["nginx", "-g", "daemon off;"]theo hình ảnh nginx chính thức

Sau đó, sử dụng cách sau để chạy cả trình giám sát là dịch vụ và nginx làm tiến trình tiền cảnh sẽ ngăn container thoát ra

service supervisor start && nginx

Trong một số trường hợp, bạn sẽ cần có nhiều hơn một quy trình trong thùng chứa của mình, do đó, buộc container phải có chính xác một quy trình sẽ không hoạt động và có thể tạo ra nhiều vấn đề hơn khi triển khai.

Vì vậy, bạn cần phải hiểu sự đánh đổi và đưa ra quyết định phù hợp.


7

Động lực:

Không có gì sai khi chạy nhiều tiến trình bên trong một container docker . Nếu một người thích sử dụng docker như một VM trọng lượng nhẹ - thì hãy là nó. Những người khác muốn chia ứng dụng của họ thành các dịch vụ vi mô. Tôi nghĩ: Một chồng LAMP trong một container? Tuyệt vời

Câu trả lời:

Dán với một hình ảnh cơ sở tốt như hình ảnh cơ sở phusion . Có thể có người khác. Hãy bình luận.

Và đây chỉ là một lời biện hộ khác cho người giám sát. Bởi vì hình ảnh cơ sở phusion đang cung cấp người giám sát bên cạnh một số thứ khác như thiết lập cron và locale. Thứ bạn muốn thiết lập khi chạy một máy ảo trọng lượng nhẹ như vậy. Đối với những gì nó có giá trị, nó cũng cung cấp các kết nối ssh vào container.

Hình ảnh phusion sẽ chỉ bắt đầu và tiếp tục chạy nếu bạn đưa ra câu lệnh chạy docker cơ bản này:

moin@stretchDEV:~$ docker run -d phusion/baseimage
521e8a12f6ff844fb142d0e2587ed33cdc82b70aa64cce07ed6c0226d857b367
moin@stretchDEV:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS
521e8a12f6ff        phusion/baseimage   "/sbin/my_init"     12 seconds ago      Up 11 seconds

Hoặc chết đơn giản:

Nếu một hình ảnh cơ sở không dành cho bạn ... Để CMD nhanh chóng duy trì hoạt động, tôi sẽ giả sử một cái gì đó như thế này cho bash:

CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"

Hoặc cái này cho busybox:

CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"

Điều này là tốt, bởi vì nó sẽ thoát ngay lập tức trên a docker stop. Chỉ cần đơn giản sleephoặc catsẽ mất một vài giây trước khi container thoát ra.


Tôi đã tùy chỉnh hình ảnh cơ sở centos7 để tải PostgreSQL 11. Bạn bắt đầu điều đó bằng một cuộc gọi đến / usr / pssql-11 / bin / pg_ctl nhưng pg_ctl sẽ thoát khi máy chủ đang chạy. Đề nghị của bạn để sử dụng bẫy làm việc tuyệt vời; đó là dòng cuối cùng của kịch bản của tôi pgstartwait.sh
Nhà giả kim

6

Nắm bắt quy trình PID của quá trình ngnix trong một biến (ví dụ $ NGNIX_PID) và ở cuối tệp nhập điểm

wait $NGNIX_PID 

Theo cách đó, container của bạn sẽ chạy cho đến khi ngnix còn sống, khi ngnix dừng, container cũng dừng lại


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.