在CentOS 7的Docker容器中使用systemd来启动服务
首先
由于 CentOS 7 中 init 守护程序已更改为 systemd,因此使用 centos:centos7 的 Docker 镜像创建容器后,如果在容器中正常执行 systemctl 命令,只会输出错误消息 “Failed to get D-Bus connection: No connection to service manager.”。
$ sudo docker run -it centos:centos7 /bin/bash
# systemctl
Failed to get D-Bus connection: No connection to service manager.
因此,为了启动服务,必须以不同的方式启动容器。
在以下的验证中使用了httpd。
也许在其他服务中可能需要进行不同的操作。
准备工作
如果主机端使用CentOS 7,按照以下方式进行。
由于本次主机操作是通过非root管理员用户进行的,所以提示符为“$”。而在容器内部,提示符为“#”。希望能够通过提示符来区分是在操作主机还是容器。
安装Docker。出于这个原因,docker-io软件包已从EPEL 7 beta中删除,因为它在extras仓库中可用。尽管我在8月15日看到它还存在。
$ sudo yum install docker
启动Docker服务。
$ sudo systemctl enable docker
$ sudo systemctl start docker
请安装nsenter。
这是nsenter的安装命令。为了安全起见。
2014-08-27补充:只有在启用SELinux时才需要–privileged选项。
$ sudo docker run --privileged --rm -v /usr/local/bin:/target jpetazzo/nsenter
如果您使用CoreOS作为主机,它已经包含了docker和nsenter,因此无需进行任何准备。
操作集装箱
经过试错,最简单的方式是使用以下命令启动docker run。
-
- 特権モードにする(–privileged)
- コンテナで起動されるコマンドは/sbin/initにする
因此,下面是启动容器的步骤。
这次,如果想要通过ID来操作,则不需要使用–name选项进行命名。
$ sudo docker run --privileged -d -p 80:80 --name httpd1 centos:centos7 /sbin/init
追記: 如果SELinux是禁用的,据实际情况来看,在本页面所做的处理程度上似乎仅需要提供CAP_SYS_ADMIN的能力就足够了。因此,如果使用的是Docker 1.2.0及更高版本,则可以通过添加–cap-add选项来替换–privileged选项,以便在安全性方面降低容器所具备的权限。
$ sudo docker run --cap-add=SYS_ADMIN -d -p 80:80 --name httpd1 centos:centos7 /sbin/init
如果SELinux被启用,就无法突破SELinux壁垒来授予能力,因此使用–privileged选项。
2015年05月10日追加:根据@ngyuki的信息进行了验证,从2015年04月04日开始的centos:centos7映像上,已经安装了systemd-container而不是fakesystemd,因此不需要像之前安装fakesystemd那样进行包的更换,现在服务可以正常启动。(systemd-container里也包含/sbin/init)
$ sudo docker run --privileged -d -p 80:80 --name httpd centos:centos7 /sbin/init
$ sudo docker exec -it httpd /bin/bash
# yum install httpd -y
# systemctl enable httpd.service
# systemctl start httpd.service
# exit
$ curl -s http://localhost/ | head -n 1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html><head>
为了参考,以下是在fakesystemd安装之前留下的附加信息。
2014年9月16日更新:最近的CentOS 7镜像中,安装了fakesystemd而不是systemd(从大约2014年9月5日开始?)。
由于需要使用systemctl来启动服务,并且镜像中没有/sbin/init,因此无法使用上述命令来启动容器,因此需要卸载fakesystemd并安装systemd,然后使用docker commit等方式创建镜像。
$ sudo docker run -it --name temp centos:centos7 /bin/bash
# yum swap fakesystemd systemd
# exit
$ sudo docker commit temp yunano/centos7-systemd
$ sudo docker run --privileged -d -p 80:80 --name httpd1 yunano/centos7-systemd /sbin/init
使用nsenter命令进入容器。
可以使用先前设置的容器名称指定,也可以使用ID来指定。
如果nsenter没有指定命令,则会启动宿主机登录用户的默认Shell。但是在centos:centos7镜像中只有sh或bash,因此如果将其他Shell作为默认Shell,需要在命令中指定/bin/sh或/bin/bash。如果登录用户的默认Shell为sh或bash,或者已经在容器中安装了要启动的Shell,则不需要指定命令。
$ sudo nsenter -t $(sudo docker inspect --format '{{.State.Pid}}' httpd1) -m -u -i -n -p /bin/sh
因为如果保持原样,在使用yum时可能出现日语乱码问题,所以请执行以下操作。
# localedef -vc -i ja_JP -f UTF-8 ja_JP.UTF-8
安装并启动httpd服务。
无错误地启动。
# yum install httpd
# systemctl enable httpd
# systemctl start httpd
随意创建/var/www/html/index.html并离开容器。
# echo 12345 > /var/www/html/index.html
# exit
确认可以通过curl进行访问。
这里是在主机上执行的,但也可以从其他计算机向主机执行。
$ curl http://localhost
12345
一旦停止并重新启动容器后,仍然可以通过curl无问题地访问。
$ sudo docker stop httpd1
$ sudo docker start httpd1
$ curl http://localhost
如果主机是CentOS 6操作系统的话
2014年8月27日补充
如果主机使用的是CentOS 6,按照该页面的说明运行docker run命令时,在容器内执行systemctl命令会出现“Failed to get D-Bus connection: Failed to connect to socket /run/systemd/private: Connection refused”错误消息。
由于内核版本过旧(systemd的运行要求是Linux内核3.0以上-2015年5月10日注意:现在看来已经改为3.7以上),如果能在主机端升级内核,就可以正常运行。例如,可以从ELRepo-kernel进行安装(仅安装并不能更改默认内核,需要修改/etc/grub.conf中的default并重新启动,或者在grub的内核选择界面选择安装的内核)。需要注意的是,确认仅使用命令yum install http://elrepo.org/linux/kernel/el6/x86_64/RPMS/kernel-lt-3.10.53-1.el6.elrepo.x86_64.rpm进行。
唯一选择:参考文献
-
- http://rhatdan.wordpress.com/2014/04/30/running-systemd-within-a-docker-container/
- http://maci0.wordpress.com/2014/07/23/run-systemd-in-an-unprivileged-docker-container/