docker中单容器开启多服务时systemctl引发的血案及破案过程
suiw9 2024-10-31 16:03 30 浏览 0 评论
title: docker中单容器开启多服务时systemctl引发的血案及破案过程
date: 2022-02-27 19:09:52
tags:
- docker - systemd - systemctl
categories:
- 编程开发 - 运维管理
docker中单容器开启多服务时systemctl引发的血案及破案过程
问题的起源来源于,想要将一个运行在centos7上的项目,移植到docker上,实现快速部署。原项目,我们暂且称之为Myproject, 提供了install_centos7.sh和Vagrant的构建文件。Vagrant 文件工作的很好,但是笔者是在虚拟机中完成的vagrant构建的验证,也遇见了不少问题,例如嵌套虚拟化的问题
但,虚拟机里面搞总感觉不得劲,又加上vagrant比较小众,我就盯上了Docker。本来想着这不是很简单吗,pull一个centos7的image,然后run一个container,bash install一下就完了呗。
没想到这就开始了,痛苦的采坑之旅。
docker安装Myproject出现的的问题
由于docker的设计原则是一个container只运行一个服务,所以像Myproject这样的需要多个服务的(redis, httpd, psql, mongo, rabbitmq等),想要在一个容器中使用,存在很多限制。
问题包括,但不限于docker中执行systemctl命令问题记录和解决_WELTest的专栏-CSDN博客_docker systemctl
Not able to use systemd on ubuntu docker container - Stack Overflow
问题有:Failed to get D-Bus connection: Operation not permitted
解决这个问题的一个方法是使用--privileged和/usr/sbin/init
- privileged的作用是使得docker的环境以管理员角色运行
- /usr/sbin/init是
/usr/sbin/init 启动容器之后可以使用systemctl方法
-privileged=true 获取宿主机root权限(特殊权限-)docker -privileged和/usr/sbin/init - lvph - 博客园
但是这个方法使用后出现了以下问题:
~ docker run -itd --privileged --name centos7 centos:7 /usr/sbin/init
a2f71d42e0f4c5c13b06a607da719df40e428305188c1a3154889a1bc4991f6d
~ docker exec -it centos7 ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.4 0.1 42716 3888 ? Ss 10:44 0:00 /usr/sbin/init
root 8 0.0 0.1 51748 3400 pts/1 Rs+ 10:44 0:00 ps aux
~ docker exec -it centos7 /bin/bash
[root@a2f71d42e0f4 /]# systemctl
Failed to get D-Bus connection: No such file or directory
还有一种方法是:run docker image with docker run --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro <image> and systemctl works fine
经过试验,也解决不了我的问题
~ docker run --privileged -itd -v /sys/fs/cgroup:/sys/fs/cgroup:ro --name centos7 centos:7 /usr/sbin/init
7ff1b85e7c9fecf17c889e0c2a25ab73203131ecdcb629180798fa1c07b4ee22
~ docker exec -it centos7 ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 1.0 0.1 42716 3944 ? Ss 10:49 0:00 /usr/sbin/init
root 8 0.0 0.1 51748 3456 pts/1 Rs+ 10:49 0:00 ps aux
~ docker exec -it centos7 /bin/bash
[root@7ff1b85e7c9f /]# systemctl
Failed to get D-Bus connection: No such file or directory
~ docker run -it \
--volume /sys/fs/cgroup:/sys/fs/cgroup:ro \
--rm centos:7 /bin/bash
[root@9c3b2edf75ff /]# systemctl
Failed to get D-Bus connection: Operation not permitted
[root@9c3b2edf75ff /]#
Failed to connect to bus: No such file or directory
在搜索资料的过程中发现了这篇文章, 对这个问题的解释比较到位:如何在Docker里面使用systemctl - Ehds
容器里面是没有systemd进程的,而很多进程管理工具都需要和systemd通信,这里面就有我们这里的主角systemctl。docker只是提供了进程隔离,不是操作系统的虚拟。
That’s because “systemctl” talks to the systemd daemon by using the d-bus. In a container there is no systemd-daemon. Asking for a start will probably not quite do what you expect - the dev-mapping need to be a bit longer. This is by design. Docker should be running a process in the foreground in your container and it will be spawned as PID 1 within the container’s pid namespace. Docker is designed for process isolation, not for OS virtualization, so there are no other OS processes and daemons running inside the container (like systemd, cron, syslog, etc), only your entrypoint or command you run.
If they included systemd commands, you’d find a lot of things not working since your entrypoint replaces init. Systemd also makes use to cgroups which docker restricts inside of containers since the ability to change cgroups could allow a process to escape the container’s isolation. Without systemd running as init inside your container, there’s no daemon to process your start and stop commands.
给出解决方案,确实能work
解决方案
我们可以在启动容器的时候将在启动参数加上 /sbin/init 来让其生效。
以centos为例:
docker run -d -v /sys/fs/cgroup/:/sys/fs/cgroup:ro --cap-add SYS_ADMIN --name systemd_websrv centos /sbin/init
就可以正常使用systemd了
但是如果容器重启,那么就可能导致失效。替换systemctl
使用 docker-systemctl-replacement替换容器中的systemctl。
以ubuntu镜像为例:
1). 安装python2
sudo apt install python
2). 替换systemcl (注意路径,可以使用whereis systemctl查看当前默认路径)
wget https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl.py -O /bin/systemctl
3). 给定权限
sudo chmod a+x /bin/systemctl
这样接可以使用非systemd的systemctl,但是因为是非官方的systemcl所以可能存在一些未知问题。最好还是建议将docker作为进程隔离环境,single app single container, 但是遇到非常特殊的情况下,可以上述两个解决方案,如果有更好的方案,欢迎提出。
上面这些在单容器单服务下都工作的很好,但是在笔者的场景下呢?情况不是太好
事情还没有完
当我们想要像在虚拟机上一样,在一个容器中运行多个container,比如一般网站的配置,redis,httpd,mongo,rabbitmq等等,这该怎么搞?不要问我为什么不把容器拆分,要问就是配置起来太麻烦了。
笔者遇到一个问题就是,按照上述copy的方法对container中所有服务安装完毕之后,一切非常正常,但问题发生在,重启container,systemctl就再也启动不起来某些服务,例如httpd,psql等。
经过一番周折,以及和systemctl.py的作者进行沟通,最后发现:其实这个问题的根源还是在systemctl上.
在centos系统中,systemctl需要使用d-bus来与systemd通信,完成服务的启动。但是在docker的容器中并没有systemd-daemon的守护进程,所以上述通信是不会完成的。这就导致了前面所说的两个问题:D-Bus connection: Operation not permitted以及No such file or directory的问题
下面举个例子
一般我们启动容器的过程一般使用docker run -t -i centos:7 /bin/bash, 那么容器中一号进程就是/bin/bash, 而不是我们熟悉的systemd。
~ docker pull centos:7
7: Pulling from library/centos
2d473b07cdd5: Downloading
7: Pulling from library/centos
2d473b07cdd5: Pull complete
Digest: sha256:c73f515d06b0fa07bb18d8202035e739a494ce760aa73129f60f4bf2bd22b407
Status: Downloaded newer image for centos:7
docker.io/library/centos:7
~ docker run -itd --name centos7 centos:7
c625bedacd894f9db6ca065839e413bb02e46cf6b602822f26baebfab745982a
~ docker exec -it centos7 ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.4 0.1 11844 2944 pts/0 Ss+ 10:41 0:00 /bin/bash
root 14 0.0 0.1 51748 3448 pts/1 Rs+ 10:41 0:00 ps aux
对象都没有了,所以暴d-bus的错误,就很容易解释了。
上述描述是对这个问题的通俗解释,想要了解关于这个问题的根本原因可以看下面两个链接,进一步了解,基本都是在解释一号进程的问题
linux - how to solve Docker issue Failed to connect to bus: No such file or directory - Stack Overflow
Not able to use systemd on ubuntu docker container - Stack Overflow
解决方案
问题的根源找到了,解决这个问题的思路就非常的清晰了,就是使服务的管理不依赖于systemd。
Do not rely on systemd as a process manager but have the docker container run your desired application in the foreground.
有一款工具,就能够实现执行systemctl而不依赖于systemd,他就是GitHub - gdraheim/docker-systemctl-replacement: docker systemctl replacement - allows to deploy to systemd-controlled containers without starting an actual systemd daemon (e.g. centos7, ubuntu16), 这个github仓库专门在处理这个问题。同时,如果你不想写一些dockerfile来实现上述过程,你也可以使用该作者的另一个仓库,docker-systemctl-images/centos-httpd.dockerfile at master · gdraheim/docker-systemctl-images · GitHub
在这里,作者给出了很多版本的可以使用systemctl的镜像,非常的有用。
在使用systemctl的过程中,其实也踩了很多坑
第一个问题是,什么时候拷贝的问题?
最最朴素的想法是,拷贝一次就行了呗,当初我也是这样想的,结果,水很深。
Most os packages with a systemd service have declared a dependency on systemd. When you install an os package it updates systemd which overwrites /usr/bin/systemctl which in turn kills the replacement functionality. The next "systemctl" is again executed by systemd which does not work in a container easily as you know. So after a os package install it helps to just drop-in the replacement script again.
can not start the service when reboot? · Issue #137 · gdraheim/docker-systemctl-replacement · GitHub
这是与作者沟通时候,作者的解释,很容易懂,大致就是,安装软件时会更新systemctl,那么我们之前一次的拷贝不就失效了嘛。所以安装了软件之后,就拷贝一下吧!!!
时机:
After "yum install" and before the next "systemctl" execution.
can not start the service when reboot? · Issue #137 · gdraheim/docker-systemctl-replacement · GitHub
第二个问题是,启动点设为systemctl还是/bin/bash/还是/usr/sbin/init.
首先给出答案,需要设置成systemctl,因为它负责移除对systemd的依赖,所以要让它成为一号进程。
这个可以看看作者给出的dockerfile
FROM centos:7.7.1908
LABEL __copyright__="(C) Guido Draheim, licensed under the EUPL" \
__version__="1.4.4147"
EXPOSE 80
COPY files/docker/systemctl.py /usr/bin/systemctl
RUN yum install -y httpd httpd-tools
COPY files/docker/systemctl.py /usr/bin/systemctl
RUN echo TEST_OK > /var/www/html/index.html
RUN systemctl enable httpd
CMD /usr/bin/systemctl
总结
- **问题的根源在systemctl,它需要像docker容器中的systemd通信,但是却没有对象
- 解决问题的方法在于,解除systemctl与systemd的依赖关系,这里我们用到了一个systemctl.py的工具
- 使用systemctl.py需要注意拷贝的时机,以及设置systemctl作为容器一号进程
参考资料及备注
一号进程有什么作用?
Docker 的 stop 和 kill 命令都是用来向容器发送信号的。注意,只有容器中的 1 号进程能够收到信号,这一点非常关键!究竟谁是 1 号进程则主要由 EntryPoint, CMD, RUN 等指令的写法决定,所以这些指令的使用是很有讲究的。在 docker 容器中捕获信号 - sparkdev - 博客园
CMD 和 ENTRYPOINT 指令都支持 exec 模式和 shell 模式的写法,所以要理解 CMD 和 ENTRYPOINT 指令的用法,就得先区分 exec 模式和 shell 模式。这两种模式主要用来指定容器中的不同进程为 1 号进程。
使用 exec 模式时,容器中的任务进程就是容器内的 1 号进程,exec 模式是建议的使用模式
使用 shell 模式时,docker 会以 /bin/sh -c "task command" 的方式执行任务命令。也就是说容器中的 1 号进程不是任务进程而是 bash 进程
CMD 指令的目的是:为容器提供默认的执行命令。
CMD 指令有三种使用方式,其中的一种是为 ENTRYPOINT 提供默认的参数:
CMD ["param1","param2"]
另外两种使用方式分别是 exec 模式和 shell 模式:
CMD ["executable","param1","param2"] // 这是 exec 模式的写法,注意需要使用双引号。
CMD command param1 param2 // 这是 shell 模式的写法。
注意命令行参数可以覆盖 CMD 指令的设置,但是只能是重写,却不能给 CMD 中的命令通过命令行传递参数。
ENTRYPOINT 指令的目的也是为容器指定默认执行的任务。
ENTRYPOINT 指令有两种使用方式,就是我们前面介绍的 exec 模式和 shell 模式:
ENTRYPOINT ["executable", "param1", "param2"] // 这是 exec 模式的写法,注意需要使用双引号。
ENTRYPOINT command param1 param2 // 这是 shell 模式的写法。
指定 ENTRYPOINT 指令为 exec 模式时,命令行上指定的参数会作为参数添加到 ENTRYPOINT 指定命令的参数列表中
命令行参数被 ENTRYPOINT 指令的 shell 模式忽略了。
https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/
其他参考资料
Not able to use systemd on ubuntu docker container - Stack Overflow
相关推荐
- 俄罗斯的 HTTPS 也要被废了?(俄罗斯网站关闭)
-
发布该推文的ScottHelme是一名黑客,SecurityHeaders和ReportUri的创始人、Pluralsight作者、BBC常驻黑客。他表示,CAs现在似乎正在停止为俄罗斯域名颁发...
- 如何强制所有流量使用 HTTPS一网上用户
-
如何强制所有流量使用HTTPS一网上用户使用.htaccess强制流量到https的最常见方法可能是使用.htaccess重定向请求。.htaccess是一个简单的文本文件,简称为“.h...
- https和http的区别(https和http有何区别)
-
“HTTPS和HTTP都是数据传输的应用层协议,区别在于HTTPS比HTTP安全”。区别在哪里,我们接着往下看:...
- 快码住!带你十分钟搞懂HTTP与HTTPS协议及请求的区别
-
什么是协议?网络协议是计算机之间为了实现网络通信从而达成的一种“约定”或“规则”,正是因为这个“规则”的存在,不同厂商的生产设备、及不同操作系统组成的计算机之间,才可以实现通信。简单来说,计算机与网络...
- 简述HTTPS工作原理(简述https原理,以及与http的区别)
-
https是在http协议的基础上加了一层SSL(由网景公司开发),加密由ssl实现,它的目的是为用户提供对网站服务器的身份认证(需要CA),以至于保护交换数据的隐私和完整性,原理如图示。1、客户端发...
- 21、HTTPS 有几次握手和挥手?HTTPS 的原理什么是(高薪 常问)
-
HTTPS是3次握手和4次挥手,和HTTP是一样的。HTTPS的原理...
- 一次安全可靠的通信——HTTPS原理
-
为什么HTTPS协议就比HTTP安全呢?一次安全可靠的通信应该包含什么东西呢,这篇文章我会尝试讲清楚这些细节。Alice与Bob的通信...
- 为什么有的网站没有使用https(为什么有的网站点不开)
-
有的网站没有使用HTTPS的原因可能涉及多个方面,以下是.com、.top域名的一些见解:服务器性能限制:HTTPS使用公钥加密和私钥解密技术,这要求服务器具备足够的计算能力来处理加解密操作。如果服务...
- HTTPS是什么?加密原理和证书。SSL/TLS握手过程
-
秘钥的产生过程非对称加密...
- 图解HTTPS「转」(图解http 完整版 彩色版 pdf)
-
我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取。所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议。...
- HTTP 和 HTTPS 有何不同?一文带你全面了解
-
随着互联网时代的高速发展,Web服务器和客户端之间的安全通信需求也越来越高。HTTP和HTTPS是两种广泛使用的Web通信协议。本文将介绍HTTP和HTTPS的区别,并探讨为什么HTTPS已成为We...
- HTTP与HTTPS的区别,详细介绍(http与https有什么区别)
-
HTTP与HTTPS介绍超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的...
- 一文让你轻松掌握 HTTPS(https详解)
-
一文让你轻松掌握HTTPS原文作者:UC国际研发泽原写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。...
- 如何在Spring Boot应用程序上启用HTTPS?
-
HTTPS是HTTP的安全版本,旨在提供传输层安全性(TLS)[安全套接字层(SSL)的后继产品],这是地址栏中的挂锁图标,用于在Web服务器和浏览器之间建立加密连接。HTTPS加密每个数据包以安全方...
- 一文彻底搞明白Http以及Https(http0)
-
早期以信息发布为主的Web1.0时代,HTTP已可以满足绝大部分需要。证书费用、服务器的计算资源都比较昂贵,作为HTTP安全扩展的HTTPS,通常只应用在登录、交易等少数环境中。但随着越来越多的重要...
你 发表评论:
欢迎- 一周热门
-
-
Linux:Ubuntu22.04上安装python3.11,简单易上手
-
宝马阿布达比分公司推出独特M4升级套件,整套升级约在20万
-
MATLAB中图片保存的五种方法(一)(matlab中保存图片命令)
-
别再傻傻搞不清楚Workstation Player和Workstation Pro的区别了
-
Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器
-
如何提取、修改、强刷A卡bios a卡刷bios工具
-
Element Plus 的 Dialog 组件实现点击遮罩层不关闭对话框
-
日本组合“岚”将于2020年12月31日停止团体活动
-
SpringCloud OpenFeign 使用 okhttp 发送 HTTP 请求与 HTTP/2 探索
-
tinymce 号称富文本编辑器世界第一,大家同意么?
-
- 最近发表
- 标签列表
-
- dialog.js (57)
- importnew (44)
- windows93网页版 (44)
- yii2框架的优缺点 (45)
- tinyeditor (45)
- qt5.5 (60)
- windowsserver2016镜像下载 (52)
- okhttputils (51)
- android-gif-drawable (53)
- 时间轴插件 (56)
- docker systemd (65)
- slider.js (47)
- android webview缓存 (46)
- pagination.js (59)
- loadjs (62)
- openssl1.0.2 (48)
- velocity模板引擎 (48)
- pcre library (47)
- zabbix微信报警脚本 (63)
- jnetpcap (49)
- pdfrenderer (43)
- fastutil (48)
- uinavigationcontroller (53)
- bitbucket.org (44)
- python websocket-client (47)