一般来说一个软件的镜像的最后由ENTRYPOINT
和CMD
来构成,有的镜像可能没有ENTRYPOINT
,ENTRYPOINT
和CMD
的主要区别在于即使你在运行镜像时指定了“命令”
(e.g. docker run nginx echo "test"
的echo "test"
部分就是“命令”),ENTRYPOINT
中
指定的命令(e.g. ENTRYPOINT entrypoint.sh
)也不会被忽略。
总的来说,可以把以下几条规则当做创作Dockerfile的thumb rules:
RUN
: 用来安装软件或添加依赖,因为RUN
会创建一层layer
。ENTRYPOINT
: 用来指定该镜像为可执行程序,并且指定一个一个脚本用来初始化基础环境(
e.g. 创建镜像运行时所使用的用户、加载程序的依赖、初始化环境变量等。)。CMD
: 用来运行软件。ENTRYPOINT
与CMD
结合使用:在
ENTRYPOINT entrypoint.sh
中初始化环境,最后执行exec "$@"
,以此来继续执行CMD
中的内容。
Dockbix的Dockerfile的结构
大致上来说,和我们构建一个物理Zabbix服务器的结构是一样的。
都是先装OS,再安装Zabbix软件,以及准备一系列我们需要用来达到“自省”的脚本和工具软件。
以下是dockbix的Dockerfile,这个镜像构建出来的可以算是一个“一体化”软件。包括了Zabbix Server、
Zabbix Web Interface、Nginx作为web服务器。
用图片来表示这个Dockerfile做了什么:
从Dockbix镜像学习到实用的工具
Dockbix镜像运行起来之后,其程序树是这样的:
[root@8a11e51b8ddb /]# ps axjf
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 615 615 615 pts/0 2343 Ss 0 0:00 /bin/bash
615 2343 2343 615 pts/0 2343 R+ 0 0:00 \_ ps axjf
0 1 1 1 ? -1 Ss 0 0:01 /usr/bin/python /usr/bin/supervisord -n -c /etc/supervisord.conf
1 379 379 1 ? -1 S 0 0:00 nginx: master process /usr/sbin/nginx
379 389 379 1 ? -1 S 80 0:00 \_ nginx: worker process
379 390 379 1 ? -1 S 80 0:00 \_ nginx: worker process
1 380 380 1 ? -1 S 0 0:01 php-fpm: master process (/etc/php-fpm.conf)
1 381 381 1 ? -1 S 0 0:00 /bin/sh /config/ds.sh /tmp/zabbix_server.pid sudo -u zabbix /usr/local/sbin/zabbix_server --foreground -c /usr/local/etc/zab
381 383 381 1 ? -1 S 0 0:00 \_ sudo -u zabbix /usr/local/sbin/zabbix_server --foreground -c /usr/local/etc/zabbix_server.conf
383 392 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server --foreground -c /usr/local/etc/zabbix_server.conf
392 399 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: configuration syncer [synced configuration in 0.016130 sec, idle 60 sec]
392 400 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: alerter #1 started
392 401 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: alerter #2 started
392 402 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: alerter #3 started
392 403 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: housekeeper [deleted 50373 hist/trends, 0 items/triggers, 85 events, 0 sessions,
392 404 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: timer #1 [updated 0 hosts, suppressed 0 events in 0.000859 sec, idle 59 sec]
392 405 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: http poller #1 [got 0 values in 0.000503 sec, idle 5 sec]
392 406 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: discoverer #1 [processed 0 rules in 0.000670 sec, idle 60 sec]
392 407 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: history syncer #1 [processed 0 values, 1 triggers in 0.000493 sec, idle 1 sec]
392 408 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: history syncer #2 [processed 0 values, 0 triggers in 0.000010 sec, idle 1 sec]
392 409 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: history syncer #3 [processed 1 values, 0 triggers in 0.001232 sec, idle 1 sec]
392 410 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: history syncer #4 [processed 0 values, 0 triggers in 0.000015 sec, idle 1 sec]
392 411 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: escalator #1 [processed 0 escalations in 0.000964 sec, idle 3 sec]
392 412 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: proxy poller #1 [exchanged data with 0 proxies in 0.000045 sec, idle 5 sec]
392 413 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: self-monitoring [processed data in 0.000026 sec, idle 1 sec]
392 414 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: task manager [processed 0 task(s) in 0.000324 sec, idle 5 sec]
392 415 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: poller #1 [got 0 values in 0.000013 sec, idle 1 sec]
392 416 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: poller #2 [got 0 values in 0.000007 sec, idle 1 sec]
392 417 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: poller #3 [got 0 values in 0.000007 sec, idle 1 sec]
392 418 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: poller #4 [got 0 values in 0.000009 sec, idle 1 sec]
392 419 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: poller #5 [got 1 values in 0.000538 sec, idle 1 sec]
392 420 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: unreachable poller #1 [got 0 values in 0.000095 sec, idle 1 sec]
392 421 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: trapper #1 [processed data in 0.000000 sec, waiting for connection]
392 422 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: trapper #2 [processed data in 0.000000 sec, waiting for connection]
392 423 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: trapper #3 [processed data in 0.000000 sec, waiting for connection]
392 424 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: trapper #4 [processed data in 0.001913 sec, waiting for connection]
392 425 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: trapper #5 [processed data in 0.011211 sec, waiting for connection]
392 426 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: icmp pinger #1 [got 0 values in 0.000022 sec, idle 5 sec]
392 427 381 1 ? -1 S 1000 0:01 \_ /usr/local/sbin/zabbix_server: alert manager #1 [sent 0, failed 0 alerts, idle 5.010558 sec during 5.010662 sec]
392 428 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: preprocessing manager #1 [queued 0, processed 4 values, idle 5.002023 sec during
392 429 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: preprocessing worker #1 started
392 430 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: preprocessing worker #2 started
392 431 381 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_server: preprocessing worker #3 started
1 382 382 1 ? -1 S 0 0:00 /usr/bin/bash -c while true; do sleep 3600; /usr/bin/bash /config/init/12-xxl-ping.sh; done
382 1761 382 1 ? -1 S 0 0:00 \_ sleep 3600
1 384 384 1 ? -1 S 0 0:00 bash -c while inotifywait -q -r -e create,delete,modify,move,attrib --exclude "/\." /etc/nginx/ /data/conf/nginx/; do nginx
384 388 384 1 ? -1 S 0 0:00 \_ inotifywait -q -r -e create,delete,modify,move,attrib --exclude /\. /etc/nginx/ /data/conf/nginx/
1 385 385 1 ? -1 S 0 0:00 /bin/sh /config/ds.sh /tmp/zabbix_agentd.pid sudo -u zabbix /usr/local/sbin/zabbix_agentd --foreground -c /usr/local/etc/zab
385 387 385 1 ? -1 S 0 0:00 \_ sudo -u zabbix /usr/local/sbin/zabbix_agentd --foreground -c /usr/local/etc/zabbix_agentd.conf
387 391 385 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_agentd --foreground -c /usr/local/etc/zabbix_agentd.conf
391 393 385 1 ? -1 S 1000 0:02 \_ /usr/local/sbin/zabbix_agentd: collector [idle 1 sec]
391 394 385 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_agentd: listener #1 [waiting for connection]
391 396 385 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_agentd: listener #2 [waiting for connection]
391 397 385 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_agentd: listener #3 [waiting for connection]
391 398 385 1 ? -1 S 1000 0:00 \_ /usr/local/sbin/zabbix_agentd: active checks #1 [idle 1 sec]
利用supervisor和inotify-util来做程序的自省和动态重载配置文件
Supervisor是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。它可以很方便的监听、启动、停止、重启一个或多个进程。用Supervisor管理的进程,当一个进程意外被杀死,supervisor监听到进程死后,会自动将它重新拉起,很方便的做到进程自动恢复的功能,不再需要自己写shell脚本来控制。
Dockbix中也实用supervisor来做Nginx和php-fpm的自动重启以及配置文件动态更新。
Dockbix镜像最终启动过程是大概如下:
Dockbix的入口为
config/bootstrap.sh
,由Dockerfile
最后的CMD
定义。执行
config/bootstrap.sh
过程中调用/config/init/bootstrap.sh
,由传入的环境变量(环境变量在docker run --env=...
来指定),来决定过滤掉哪些配置文件,也就是是否启动某些软件(功能)。比如如果不启动Nginx(i.e. Zabbix Web Interface),就传入
ZW_enabled=false
。过滤不需要要启动的软件后启动,也就是删除掉不需要启动的软件的supervisor配置文件。
supervisor的配置文件内容:
[include] files = /etc/supervisor.d/*.conf
supervisor.d/nginx.conf的内容:
[program:nginx] command = /usr/sbin/nginx autorestart = true stderr_logfile = NONE stdout_logfile = NONE # Watch for changes in Nginx conf directories and restart Nginx when a config change occured [program:nginx-reload] command=bash -c 'while inotifywait -q -r -e create,delete,modify,move,attrib --exclude "/\." /etc/nginx/ /data/conf/nginx/; do nginx -t && nginx -s reload; done'
这里可以看到,
[program:nginx-reload]
中定义了检查nginx的配置文件状态是否发生改变,如果发生改变,就调用nginx -s reload
。最后继续执行
config/bootstrap.sh
,调用supervisor,来启动这个镜像需要执行的程序。
题外话
虽然使用supervisor可以解决Docker容器同时运行多个业务程序,就算业务程序挂了也无感知的问题,
能不能完美地做到单个容器中监控多个程序是否运行,应该也要看supervisor的配置文件中命令是否合理。