หลายวันก่อน ผมเผยแพร่ long, thorough guide (คู่มือยาวและละเอียด) เกี่ยวกับ Docker เทคโนโลยีเสมือนภาคีระดับ OS based on LXC ซึ่งเสนอวิธีเร็ว เบา และปลอดภัยในการ provision (จัดเตรียม) containerized applications เยี่ยมมาก
ตอนนี้ หนึ่งในปัญหาที่เราเจอตอนทดสอบ services แรกๆ คือ SSH และ Apache คือการควบคุม services เหล่านี้ เราไม่มี init scripts หรือ systemd ภายใน containers และ老实说 เราอาจไม่อยากมีมัน แต่เราต้องการ mechanism (กลไก) ใดๆ เพื่อ start, stop และอื่นๆ services ของเรา นำเสนอ supervisord ดังนั้นจึงมี tutorial นี้ ติดตามมาเถอะ
Supervisord ในภาพรวม (Supervisord in a nutshell)
Supervisord คือ process control system ออกแบบมาเพื่อ monitor (เฝ้าติดตาม) และ control (ควบคุม) processes มันไม่ได้มุ่ง replace init แต่ encapsulate (ห่อหุ้ม) processes ภายใน framework ของตัวเอง และสามารถ start พวกมันที่ boot time ได้ ตรงที่เราอยากได้ ไม่มีเหตุผลที่จะลึกลงไปลึกเกินกว่านี้ในจุดนี้
การตั้งค่า Supervisord (Supervisord setup)
พื้นฐานคือ supervisord เป็น python module สามารถติดตั้งได้ด้วย easy_install ซึ่งเป็นส่วนหนึ่งของ setuptools ซึ่งตัวเองเป็น extension ของ Python distutils package ใช่ มัน complicated (ซับซ้อน) ค่อนข้างมากที่จุดนี้ โชคดี most Linux distributions ship with easy_install รวมถึง CentOS ซึ่งเป็น test platform สำหรับวันนี้
เพื่อเริ่มต้น เราต้องติดตั้ง supervisord ภายใน container จากนั้น commit image และใช้เป็น baseline สำหรับ builds ของเรา ซึ่งจะรวม services เช่น SSH และ Apache ที่กล่าวมา หากพยายามติดตั้ง supervisord บน host คุณควรสำเร็จโดยไม่มีปัญหา:
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
ภายใน container คุณจะได้ error เพราะ Python framework ที่เพิ่มเข้าไปใน container ไม่ complete และบาง modules หายไป
# 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
นี่หมายความว่าเราต้อง setup easy_install แบบ manual:
wget https://bitbucket.org/pypa/setuptools/raw/ ->
-> bootstrap/ez_setup.py -O - | python
การตั้งค่า Supervisord (Supervisord configuration)
ขั้นตอนต่อไปคือสร้าง configuration สำหรับ container เราสามารถสร้างไฟล์บน host แล้ว copy เข้า image ระหว่าง build process ด้วย COPY instruction ใน Dockerfile
COPY ./supervisord.conf /etc/supervisord.conf
Configuration เองจะเป็นแบบนี้:
[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
[program:httpd]
command=/bin/bash -c "exec /usr/sbin/httpd -DFOREGROUND"
เรามีอะไรบ้าง? แต่ละคู่ square bracket กำหนด section สำหรับ supervisord เอง เรากำหนดให้ start ใน foreground แทนที่จะ daemonize (กลายเป็น background service) สำหรับ program ชื่อ sshd เรา execute relevant command โดยพื้นฐานคือรัน SSHD ใน background สำหรับ program ชื่อ httpd เราเริ่ม server ใน foreground ใน shell แยก มี options อื่นๆมาก แต่ตอนนี้นี่คือ bare minimum (ขั้นต่ำสุด) ที่ต้องการเพื่อเริ่มต้นกับ supervisord
การรัน Container & Troubleshooting
หลังจาก build image และ run แล้ว supervisord ควร kick in (เริ่มทำงาน) และ start processes ของเรา แต่ก่อน มาทacle (จัดการ) common errors บางอย่าง error หนึ่งที่อาจเห็นคือ:
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
หากคุณระบุ wrong path สำหรับ supervisord.conf หรือใช้ wrong name service จะรันไม่ได้ คุณต้องแก้ COPY instruction, rebuild image และเริ่มใหม่ error ถัดไปที่อาจเจอคือ:
# 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
ที่นี่ Apache threads exiting บังคับให้ supervisord retry เริ่มใหม่ และสุดท้าย give up นำไปสู่ fatal state container ของเรา pretty much useless (เกือบไร้ประโยชน์) ณ จุดนี้
Resolution (วิธีแก้) คือแก้ไข supervisord.conf และเพิ่ม directives เพิ่มเติมใน httpd program section ซึ่งจะจัดการ process spawning อย่าง graceful มากขึ้น
[program:httpd]
startsecs = 0
autorestart = false
command=/bin/bash -c "exec /usr/sbin/httpd -DFOREGROUND"
เราเพิ่ม startsecs = 0 และ autorestart = false directive แรกบอกว่า:
จำนวนวินาทีทั้งหมดที่ program ต้อง stay running หลัง startup เพื่อถือว่า start สำเร็จ หาก program ไม่ stay up นานพอหลังจากเริ่มต้น แม้จะ exit ด้วย expected exit code (ดู exitcodes) startup จะถือว่า fail
Directive ที่สองหมายถึง supervisord จะไม่ handle Apache thread restarts และจะถูกจัดการโดย Web server เอง เมื่อมันจัดการ incoming HTTP requests
อาจเป็น false, unexpected, หรือ true หาก false process จะไม่ถูก autorestarted ครับ หาก unexpected process จะ restart เมื่อ program exits ด้วย exit code ที่ไม่ใช่หนึ่งใน exit codes ที่เกี่ยวข้องกับ process configuration นี้ (ดู exitcodes) หาก true process จะถูก unconditionally restarted เมื่อมัน exit โดยไม่สนใจ exit code
ตอนนี้ลองรัน container อีกครั้ง จำไว้ /run/httpd issue ที่เราเจอใน original guide? โดยไม่มี /run directory อยู่ คุณจะเห็น messages ดังนี้:
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)
ณ จุดนี้ คุณต้อง connect (attach หรือ exec BASH shell) ไปที่ container ตรวจสอบ logs และพยายามเข้าใจว่าทำไม service ไม่รันตามที่คาดหวัง
การทดสอบที่สำเร็จ (Successful test)
และแล้ว เมื่อเราแก้ทุกอย่างเสร็จสิ้น:
สรุป
นี่คือ tutorial ที่ nerdy (เฉพาะทาง/ซับซ้อน) ค่อนข้าง แต่ introduce (นำเสนอ) useful tool อีกตัวเข้า arsenal (คลังอาวุธ) ของเรา ซึ่งเราสามารถใช้ควบคุม services ภายใน containers อย่าง robust และ elegant โดยไม่ต้องติดตั้ง complex, expensive frameworks อย่าง systemd มันยังสอนเพิ่มเติมเกี่ยวกับ Docker วิธีทำงานและ troubleshoot ปัญหาและอย่างนั้น
Supervisord ไม่ได้ strictly necessary (จำเป็นอย่างเคร่งครัด) เพราะเรา manage sshd และ httpd processes ได้โดยไม่ต้องใช้มัน แต่บางคนอาจชอบวิธีนี้ โดยเฉพาะหากต้อง start และ restart services บ่อยๆ ไม่ว่าจะอย่างไร การคุ้นเคยกับ utility ช่วยสร้าง understanding และ confidence เข้าไปใน Docker mechanism ผมหวังว่าคุณจะพบว่า guide นี้ useful และโปรดส่ง requests ว่าเราควรสำรวจอะไรต่อไป
ขอแสดงความนับถือ