如何科学的在Docker Container中运行多个服务

栏目: 数据库 · Mysql · 发布时间: 6年前

内容简介:在一个Docker Container中运行多个服务?打扰了。Docker,或说任何基于在实际中,使用

在一个Docker Container中运行多个服务?打扰了。

0x00 前言

Docker,或说任何基于 内核namespace 的轻量级进程隔离技术,在设计之初,都不是为了当作虚拟机使用的。也就是说,其中运行的并不是一个完整的操作系统。包括 Docker 官方,也是推荐在一个Container内仅运行一个服务。如果需要运行多个服务,应通过 docker run --link 或者 docker-compose 来关联多个容器。但是在实际的应用中,我们经常希望将一个完整的可运行环境打包成一个 docker image ,不再依赖其他的容器。比如在CTF比赛中,将多个服务打包成一个Image,可以有效地提高在环境受损后恢复的效率。在经历了多场比赛,看过各种大师傅用各种奇怪的姿势完成这个任务后,觉得应该好好的讨论一下这个问题。

0x01 错误的姿势

  1. 使用upstart的启动方式
# Dockerfile
From ubuntu:14.04
RUN apt-get update && apt-get upgrade -y && apt-get install mysql apache2  php 7.0 
ADD web /var/www/html
RUN service mysql start && /var/www/html/init_sql.sh && service mysql stop
CMD service mysql start && service apache2 start && while true; do sleep 10;done
  1. 使用systemd的启动方式
# Dockerfile
From ubuntu:16.04
RUN apt-get update && apt-get upgrade -y && apt-get install mysql apache2 php7.0 
ADD web /var/www/html
RUN systemctl start mysql && /var/www/html/init_sql.sh && systemctl stop mysql
CMD systemctl start mysql && systemctl start apache2 && while true; do sleep 10;done
  1. 使用启动脚本启动多个服务
# Dockerfile
From ubuntu:16.04
RUN apt-get update && apt-get upgrade -y && apt-get install mysql apache2 php7.0 
ADD web /var/www/html
ADD entrypoint.sh /sbin/
RUN chmod +x /sbin/entrypoint.sh /var/www/html/init_sql.sh&& \
     /etc/init.d/mysql start && /var/www/html/init_sql.sh && /etc/init.d/mysql stop
CMD /sbin/entrypoint.sh
#!/bin/bash 

# entrypoint.sh
/usr/bin/mysqld start &
/usr/bin/httpd &
while true
do
sleep 100
done

在实际中,使用 方法1 或者 方法2 很大几率无法完成将多个服务跑在同一个container中的任务。 方法3 虽然可以,但仍然存在一些问题:

SIGTERM

方法1方法2 不能成功,是因为docker只是一个进程隔离的沙箱环境,并不是真正的虚拟机。而 service xxx startsystemctl start xxx 分别是 upstartsystemd 这两个 /sbin/init 进程的替代者的服务管理命令。而 upstartsystemd 都要求系统必须是物理机或虚拟机,并不支持作为container的 init 进程。方法3存在问题是因为,在正常的系统中, init 进程永远占用 PID=1 的位置,回收僵尸进程、处理未处理的信号等都是由 init 进程帮我们完成的,一个子进程如果失去了父进程,也会由 init 进程接管。但是在container中, init 进程并不存在, PID=1 的进程是我们在 Dockerfile 中定义的 Entrypoint 或最后一个 CMD 指定的命令。

root@vpscn:/var/lib/docker# docker exec -it hackmd sh
/hackmd # ps -ef
PID   USER     TIME   COMMAND
    1 hackmd     1:03 node app.js
   42 hackmd     0:00 /usr/local/bin/node ./lib/workers/dmpWorker.js
   62 root       0:00 sh
   69 root       0:00 ps -ef

因此,对于启动方法3的container,我们应该在启动时加上 --init 参数,来强制使用 tini 作为 init 进程。但是就算这样,在服务多了之后,进行重启等操作仍然很繁琐。

0x02 推荐的姿势

作为一个~~金牌运维~~打杂的,简单谈谈我常用的方法。

首先推荐一个超级好用的基础镜像 phusion/baseimage 。截至SUCTF2018环境准备完成时,该镜像的最新版本是 0.10.1 ,基于 ubuntu 16.04 。我们常用的 apt-get 等命令都可以无缝兼容。 phusion/baseimage 采用了作者自己开发的一个基于 pythoninit 进程作为Container的 Entrypoint ,采用 runit 作为服务管理器。这个基础镜像还是在Coding打杂的时候知道的, Coding ~~WebIDE~~ Studio 的Web Terminal也是基于这个镜像做的。NUAACTF/SUCTF的PWN题基础镜像 ctf-xinetd 也是基于这个镜像做的。

然后直接贴个在SUCTF2018运维期间写的 Dockerfile

#Dockerfile
FROM phusion/baseimage:0.10.1
MAINTAINER Yibai Zhang <xm1994@outlook.com>

RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list &&\
    sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list &&\
    apt-get update && apt-get install -y apache2 libapache2-mod-php php-mysql mariadb-server &&\
    apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/www/html/*

RUN mkdir -p /etc/service/apache2/ && \
    printf "#!/bin/sh\n\ntrap \"apachectl -k graceful-stop\" 1 2 3 6 15\n\nexec /usr/sbin/apachectl -D FOREGROUND\n" > /etc/service/apache2/run &&\
    chmod +x /etc/service/apache2/run && mkdir -p /etc/service/mysql/ &&\
    printf "#!/bin/sh\n\ntrap \"mysqladmin -uroot -psuCTF_P1us_1s shutdown\" 1 2 3 6 15\n\nexec /usr/bin/mysqld_safe" > /etc/service/mysql/run &&\
    mkdir -p /var/run/mysqld/ && chown mysql:mysql /var/run/mysqld &&\
    chmod 700 /etc/service/mysql/run /etc/service/apache2/run

COPY web /var/www/html
COPY flag /flag
RUN echo "secure-file-priv=/var/www/" >>/etc/mysql/mariadb.conf.d/50-server.cnf && chmod -R 777 /var/www/html/favicon
COPY init_sql.sh /tmp/init_sql.sh
RUN chmod +x /tmp/init_sql.sh && bash -c "/tmp/init_sql.sh" && rm /tmp/init_sql.sh
EXPOSE 80
#!/usr/bin/env bash
#init_sql.sh

mysqld_safe &   
echo -n "Waiting for mysql startup"
while ! mysqladmin --host="localhost" --silent ping ; do
    echo -n "."
    sleep 1
done
echo

mysql -uroot <<EOF
UPDATE mysql.user SET Password=PASSWORD('XXXXXX'), plugin = '' WHERE User='root';
create database calc;
use calc;
create table user(
id INT NOT NULL AUTO_INCREMENT primary key,
username varchar(32) NOT NULL,
password varchar(32) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user values(1,'admin','aa67095d8e65d624548cb6b50bd4778e');
create table file(
id INT NOT NULL AUTO_INCREMENT primary key,
filename varchar(32) NOT NULL,
filehash varchar(32) NOT NULL,
sig varchar(120) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table flag(
flag varchar(120) primary key
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into flag values('SUCTF{a_very_long_long_long_long_long_fake_flag_d}');
grant SELECT, INSERT on calc.user to 'suctf'@localhost identified by 'suctf';
grant SELECT, INSERT, UPDATE on calc.file to 'suctf'@localhost ;
grant SELECT on calc.flag to 'suctf'@localhost ;
FLUSH PRIVILEGES;
EOF

mysqladmin -uroot -pXXXXXX shutdown

这里着重看一下 printf "#!/bin/sh\n\ntrap \"apachectl -k graceful-stop\" 1 2 3 6 15\n\nexec /usr/sbin/apachectl -D FOREGROUND\n" > /etc/service/apache2/run ,这个命令就是在创建runit启动脚本。具体的说明可以去看 phusion/baseimage 或者 runit 的手册。执行完成后会在 /etc/service/apache2/run 下面生成如下内容的脚本

#!/bin/sh

trap "apachectl -k graceful-stop" 1 2 3 6 15

exec /usr/sbin/apachectl -D FOREGROUND

这个脚本会作为runit的子进程运行,并将Apache2保持在前台运行。在接收到 1 2 3 6 15 这几个信号的时候友好的(graceful)结束Apache2。如果在运行中需要重启Apache服务,只需要运行 docker exec container_name sv restart apache2 即可。通过这种方式,在Container停止的时候也可以通知相关的进程,而不是直接全部杀死,更可以保证服务的完整性。~~虽然在比赛中基本挂了就要恢复环境根本不需要保证完整性。~~


以上所述就是小编给大家介绍的《如何科学的在Docker Container中运行多个服务》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法设计与分析基础

算法设计与分析基础

Anany Levitin / 潘彦 / 清华大学出版社 / 2015-2-1 / 69.00元

作者基于丰富的教学经验,开发了一套全新的算法分类方法。该分类法站在通用问题求解策略的高度,对现有大多数算法准确分类,从而引领读者沿着一条清晰、一致、连贯的思路来探索算法设计与分析这一迷人领域。《算法设计与分析基础(第3版)》作为第3版,相对前版调整了多个章节的内容和顺序,同时增加了一些算法,并扩展了算法的应用,使得具体算法和通用算法设计技术的对应更加清晰有序;各章累计增加了70道习题,其中包括一些......一起来看看 《算法设计与分析基础》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器