Vài ngày trước, tôi đã xuất bản hướng dẫn dài, kỹ lưỡng về Docker, một công nghệ ảo hóa cấp hệ điều hành dựa trên LXC, cung cấp phương pháp nhanh, nhẹ và an toàn để cung cấp ứng dụng containerized. Tuyệt vời.
Bây giờ, một trong những vấn đề chúng ta gặp khi test dịch vụ đầu tiên, SSH và Apache, là kiểm soát các dịch vụ này. Chúng ta không có init scripts hay systemd bên trong container, và thực sự, chúng ta có thể không muốn chúng. Nhưng chúng ta muốn một cơ chế nào đó để start, stop và những thứ khác cho dịch vụ của mình. Giới thiệu supervisord, do đó có hướng dẫn này. Xin mời theo dõi.
Supervisord trong vỏ hạt dẻ
Supervisord là một hệ thống điều khiển tiến trình, được thiết kế để giám sát và kiểm soát các tiến trình. Nó không nhằm thay thế init, thay vào đó nó đóng gói các tiến trình bên trong framework riêng của nó, và có thể khởi động chúng tại boot time, giống như chúng ta muốn. Không có lý do để đi sâu hơn về phần mềm tại thời điểm này.
Thiết lập Supervisord
Về cơ bản, supervisord là một module python. Nó có thể được cài đặt bằng easy_install, là một phần của setuptools, mà chính nó là một extension của gói Python distutils. Có, nó trở nên phức tạp ngay tại thời điểm này. May mắn thay, hầu hết các bản phân phối Linux đều đi kèm easy_install, bao gồm CentOS, là nền tảng test của chúng ta hôm nay.
Để bắt đầu, chúng ta sẽ cần cài đặt supervisord bên trong một container. Sau đó, chúng ta sẽ commit image và dùng nó làm baseline cho các build của chúng ta, sẽ bao gồm các dịch vụ như SSH và Apache đã nêu. Nếu bạn thử cài supervisord trên host, bạn nên thành công mà không gặp vấn đề:
easy_install supervisor
Searching for supervisor
Reading https://pypi.python.org/simple/supervisor/
Best match: supervisor 3.1.3
Downloading https://pypi.python.org/packages/source/s/supervisor/
supervisor-3.1.3.tar.gz#md5=aad263c4fbc070de63dd354864d5e552
Processing supervisor-3.1.3.tar.gz
Writing /tmp/easy_install-vbOcMG/supervisor-3.1.3/setup.cfg
Running supervisor-3.1.3/setup.py -q bdist_egg --dist-dir /tmp/easy_install-vbOcMG/supervisor-3.1.3/egg-dist-tmp-i96mIs
warning: no previously-included files matching '*' found under directory 'docs/.build'
Adding supervisor 3.1.3 to easy-install.pth file
Installing echo_supervisord_conf script to /usr/bin
Installing pidproxy script to /usr/bin
Installing supervisorctl script to /usr/bin
Installing supervisord script to /usr/bin
Installed
/usr/lib/python2.7/site-packages/supervisor-3.1.3-py2.7.egg
Processing dependencies for supervisor
Searching for meld3>=0.6.5
Reading https://pypi.python.org/simple/meld3/
Best match: meld3 1.0.2
Downloading https://pypi.python.org/packages/source/m/meld3/
meld3-1.0.2.tar.gz#md5=3ccc78cd79cffd63a751ad7684c02c91
Processing meld3-1.0.2.tar.gz
Writing /tmp/easy_install-wnhLVS/meld3-1.0.2/setup.cfg
Running meld3-1.0.2/setup.py -q bdist_egg --dist-dir /tmp/easy_install-wnhLVS/meld3-1.0.2/egg-dist-tmp-Lp88cX
zip_safe flag not set; analyzing archive contents...
Adding meld3 1.0.2 to easy-install.pth file
Installed /usr/lib/python2.7/site-packages/meld3-1.0.2-py2.7.egg
Finished processing dependencies for supervisor
Bên trong container, bạn sẽ gặp lỗi, vì khung Python được thêm vào container không hoàn chỉnh, và một số module bị thiếu.
# easy_install supervisor
Traceback (most recent call last):
File "/usr/bin/easy_install", line 5, in <module>
from pkg_resources import load_entry_point
ImportError: No module named pkg_resources
Điều này có nghĩa là chúng ta sẽ cần thiết lập easy_install thủ công:
wget https://bitbucket.org/pypa/setuptools/raw/ ->
-> bootstrap/ez_setup.py -O - | python
Cấu hình Supervisord
Bước tiếp theo là tạo cấu hình cho container. Chúng ta có thể tạo file trên host, và sau đó copy nó vào image của chúng ta trong quá trình build bằng cách dùng instruction COPY trong Dockerfile.
COPY ./supervisord.conf /etc/supervisord.conf
Cấu hình bản thân sẽ giống như:
[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
[program:httpd]
command=/bin/bash -c "exec /usr/sbin/httpd -DFOREGROUND"
Chúng ta có gì ở đây? Mỗi cặp ngoặc vuông định nghĩa một section. Cho supervisord bản thân, chúng ta định nghĩa nó nên chạy ở foreground thay vì daemonize (trở thành background service).
Cho program tên sshd, chúng ta thực thi lệnh liên quan, thực chất chạy SSHD ở background. Cho program tên httpd, chúng ta khởi động server ở foreground, trong một shell riêng. Có nhiều tùy chọn khác, nhưng tại thời điểm này, đây là tối thiểu chúng ta cần để bắt đầu với supervisord.
Chạy container & Troubleshooting
Sau khi chúng ta đã build image và chạy nó, supervisord nên kick in (kích hoạt) và khởi động các tiến trình của chúng ta. Nhưng trước hết, hãy giải quyết một số lỗi thường gặp. Một lỗi bạn có thể thấy là:
docker run -ti -p 22 -p 80 image-3:latest
/usr/lib/python2.7/site-packages/supervisor-3.1.3-py2.7.egg/
supervisor/options.py:296: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
'Supervisord is running as root and it is searching '
Error: No config file found at default paths (/usr/etc/supervisord.conf, /usr/supervisord.conf, supervisord.conf, etc/supervisord.conf, /etc/supervisord.conf); use the -c option to specify a config file at a different path
For help, use /usr/bin/supervisord -h
Nếu bạn chỉ định sai đường dẫn cho file supervisord.conf, hoặc dùng sai tên, dịch vụ sẽ không thể chạy. Bạn sẽ phải chỉnh instruction COPY, rebuild image và bắt đầu lại. Sau đó, lỗi tiếp theo bạn có thể gặp sẽ là:
# docker run -ti -p 22 -p 80 image-3:latest
/usr/lib/python2.7/site-packages/supervisor-3.1.3-py2.7.egg/
supervisor/options.py:296: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
'Supervisord is running as root and it is searching '
372 CRIT Supervisor running as root (no user in config file)
375 INFO supervisord started with pid 1
378 INFO spawned: 'httpd' with pid 9
381 INFO spawned: 'sshd' with pid 10
602 INFO exited: httpd (exit status 1; not expected)
606 INFO spawned: 'httpd' with pid 11
606 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
671 INFO exited: httpd (exit status 1; not expected)
676 INFO spawned: 'httpd' with pid 12
742 INFO exited: httpd (exit status 1; not expected)
749 INFO spawned: 'httpd' with pid 13
825 INFO exited: httpd (exit status 1; not expected)
826 INFO gave up: httpd entered FATAL state, too many start retries too quickly
gave up: httpd entered FATAL state, too many start retries too quickly
Chuyện gì xảy ra ở đây là các thread Apache thoát, buộc supervisord retry (thử lại) khởi động chúng lại, và cuối cùng bỏ cuộc, dẫn đến trạng thái fatal. Container của chúng ta khá vô dụng tại thời điểm này.
Ở đây, giải pháp là chỉnh sửa file supervisord.conf và thêm các directive bổ sung vào section program httpd, sẽ xử lý việc spawn tiến trình theo cách graceful hơn.
[program:httpd]
startsecs = 0
autorestart = false
command=/bin/bash -c "exec /usr/sbin/httpd -DFOREGROUND"
Chúng ta đã thêm startsecs = 0 và autorestart = false. Directive đầu tiên nói:
Tổng số giây mà chương trình cần tiếp tục chạy sau khi khởi động để coi việc khởi động là thành công. Nếu chương trình không tiếp tục chạy được bao nhiêu giây đó sau khi đã bắt đầu, ngay cả khi nó thoát với exit code mong đợi (xem exitcodes), việc khởi động sẽ được coi là thất bại.
Thứ hai có nghĩa là supervisord sẽ không xử lý việc restart thread Apache, và những thứ này sẽ được xử lý bởi chính Web server, khi nó xử lý các request HTTP đến.
Có thể là false, unexpected, hoặc true. Nếu false, tiến trình sẽ không bao giờ được autorestart. Nếu unexpected, tiến trình sẽ được restart khi chương trình thoát với exit code không phải là một trong các exit code liên quan đến cấu hình tiến trình này (xem exitcodes). Nếu true, tiến trình sẽ được restart unconditionally (vô điều kiện) khi nó thoát, bất kể exit code của nó.
Bây giờ, chúng ta có thể thử chạy container lại. Nhớ vấn đề /run/httpd mà chúng ta gặp trong hướng dẫn gốc? Nếu không có thư mục /run, bạn sẽ thấy các thông báo sau:
docker run -ti -p 22 -p 80 image-3:latest
/usr/lib/python2.7/site-packages/supervisor-3.1.3-py2.7.egg/
supervisor/options.py:296: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
'Supervisord is running as root and it is searching '
913 CRIT Supervisor running as root (no user in config file)
916 INFO supervisord started with pid 1
919 INFO spawned: 'httpd' with pid 8
921 INFO spawned: 'sshd' with pid 9
079 INFO success: httpd entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)
105 INFO exited: httpd (exit status 0; expected)
120 INFO reaped unknown pid 10
122 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
Tại thời điểm này, bạn phải kết nối (attach hoặc exec một BASH shell) vào container, kiểm tra log, và cố gắng hiểu tại sao dịch vụ không chạy như mong đợi.
Test thành công
Và rồi, khi chúng ta cuối cùng sắp xếp mọi thứ:
Kết luận
Đây là một hướng dẫn khá nerdy. Nhưng nó giới thiệu một công cụ hữu ích khác vào kho vũ khí của chúng ta, mà chúng ta bây giờ có thể dùng để kiểm soát dịch vụ bên trong container theo cách robust và thanh lịch, mà không phải cài đặt các framework phức tạp, đắt tiền như systemd. Nó cũng dạy thêm về Docker, cách làm việc và troubleshoot vấn đề và những thứ đó.
Supervisord không bắt buộc, vì chúng ta đã quản lý chạy sshd và httpd tiến trình mà không cần nó, nhưng một số người có thể thích phương pháp này, đặc biệt nếu họ phải start và restart dịch vụ của họ khá thường xuyên. Dù sao, việc quen thuộc với tiện ích này giúp xây dựng sự hiểu lòng và tự tin vào cơ chế Docker. Tôi hy vọng bạn thấy hướng dẫn này hữu ích, và vui lòng gửi yêu cầu của bạn về điều chúng ta nên khám phá tiếp theo.
Cheers.