Skip to main content

moregeek program

docker与gosu_github.com/zq2599的博客-多极客编程

欢迎访问我的GitHub



这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos



容器中不要使用root账号




  • gosu是个工具,用来提升指定账号的权限,作用与sudo命令类似,而docker中使用gosu的起源来自安全问题;




  • docker容器中运行的进程,如果以root身份运行的会有安全隐患,该进程拥有容器内的全部权限,更可怕的是如果有数据卷映射到宿主机,那么通过该容器就能操作宿主机的文件夹了,一旦该容器的进程有漏洞被外部利用后果是很严重的。




  • 因此, 容器内使用非root账号运行进程才是安全的方式, 这也是我们在制作镜像时要注意的地方。




  • 这里有篇文章也推荐在容器中使用最小权限的账号:https://snyk.io/blog/10-docker-image-security-best-practices/ ,如下图:
    在这里插入图片描述




在镜像中创建非root账号




  • 既然不能用root账号,那就要创建其他账号来运行进程了,以redis官方镜像的Dockerfile为例,来看看如何创建账号,如下图,地址是:https://github.com/docker-library/redis/blob/6845f6d4940f94c50a9f1bf16e07058d0fe4bc4f/5.0/Dockerfile :
    在这里插入图片描述




  • 可见redis官方镜像使用groupadd和useradd创建了名为redis的组合账号,接下来就是用redis账号来启动服务了,理论上应该是以下套路:





  1. USER redis 将账号切换到redis;
  2. 在docker-entrypoint.sh执行的时候已经是redis身份了,如果遇到权限问题,例如一些文件只有root账号有读、写、执行权限,用sudo xxx命令来执行即可;



  • 但事实并非如此!




  • 在Dockerfile脚本中未发现 USER redis 命令,这意味着执行docker-entrypoint.sh文件的身份是root;




  • 其次,在docker-entrypoint.sh中没有发现 su - redis 命令,也没有 sudo 命令;




  • 这是怎么回事呢?难道容器内的redis服务是用root账号启动的?




确认redis服务的启动账号




  • 还是自己动手来证实一下吧,我的环境信息如下:




  • 操作系统:CentOS Linux release 7.6.1810




  • Docker: 1.13.1




  • 操作步骤如下:




  • 启动一个redis容器:




docker run --name myredis -idt redis


  • 进入容器:

docker exec -it myredis /bin/bash


  • 在容器内,先更新apt:

apt-get update


  • 安装ps命令:

apt-get install procps


  • 执行命令 ps -ef 查看redis服务,结果如下:

root@122c2df16bbb:/data# ps -ef
UID PID PPID C STIME TTY TIME CMD
redis 1 0 0 09:22 ? 00:00:01 redis-server *:6379
root 287 0 0 09:36 ? 00:00:00 /bin/bash
root 293 287 0 09:39 ? 00:00:00 ps -ef



  • 上面的结果展示了两个关键信息:




  • 第一,redis服务是redis账号启动的,并非root;




  • 第二,redis服务的PID等于1,这很重要,宿主机执行 docker stop 命令时,该进程可以收到SIGTERM信号量,于是redis应用可以做一些退出前的准备工作,例如保存变量、退出循环等,也就是优雅停机(Gracefully Stopping);




  • 现在我们已经证实了redis服务并非root账号启动,而且该服务进程在容器内还是一号进程,但是我们在Dockerfile和docker-entrypoint.sh脚本中都没有发现切换到redis账号的命令,也没有sudo和su,这是怎么回事呢?




答案是gosu



  • 再看一次redis的docker-entrypoint.sh文件,如下图,地址是:https://github.com/docker-library/redis/blob/6845f6d4940f94c50a9f1bf16e07058d0fe4bc4f/5.0/docker-entrypoint.sh :

在这里插入图片描述



  • 注意上图中的代码,我们来分析一下:
  • 假设启动容器的命令是 docker run --name myredis -idt redis redis-server /usr/local/etc/redis/redis.conf
  • 容器启动后会执行docker-entrypoint.sh脚本,此时的账号是root;
  • 当前账号是root,因此会执行上图红框中的逻辑;
  • 红框中的$0表示当前脚本的名称,即 docker-entrypoint.sh
  • 红框中的$@表示外部传入的所有参数,即 redis-server /usr/local/etc/redis/redis.conf
  • gosu redis "$0" "@",表示以redis账号的身份执行以下命令:

docker-entrypoint.sh redis-server /usr/local/etc/redis/redis.conf


  • gosu redis "$0" "@"前面加上个exec,表示以gosu redis "$0" "@"这个命令启动的进程替换正在执行的docker-entrypoint.sh进程,这样就保证了gosu redis "$0" "@"对应的进程ID为1;
  • gosu redis "$0" "@"导致docker-entrypoint.sh再执行一次,但是当前的账号已经不是root了,于是会执行兜底逻辑 exec "$@";
  • 此时的$@是redis-server /usr/local/etc/redis/redis.conf,因此redis服务会启动,而且账号是redis;
  • $@前面有个exec,会用redis-server命令启动的进程取代当前的docker-entrypoint.sh进程,所以,最终redis进程的PID等于1,而docker-entrypoint.sh这个脚本的进程已经被替代,因此就结束掉了;

关于gosu



  • 通过上面的分析,我们对gosu的作用有了基本了解:功能和sudo类似,提升指定账号的权限,用来执行指定的命令,其官网地址是:https://github.com/tianon/gosu ,如下图所示,官方的描述也是说su和sudo命令有一些问题,所以才有了gosu工具来作为替代品:

在这里插入图片描述



  • 在docker的官方文档中,也见到了gosu的使用示例,和前面的redis很像,如下图,地址是:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
    在这里插入图片描述
  • 注意上图中底部的那段话:使用exec XXX命令以确保XXX对应的进程的PID保持为1,这样该进程才能收到宿主机发送给容器的信号量;

为什么要用gosu取代sudo?



  • 前面主要讲gosu的用法,但是为什么要用gosu呢?接下来通过实战对比来看看sudo的问题在哪:
  • 执行以下命令创建一个容器:

docker run --rm gosu/alpine gosu root ps aux


  • 上述命令会启动一个安装了gosu的linux容器,并且启动后自动执行命令 gosu root ps aux ,作用是以root账号的身份执行 ps aux ,也就是将当前进程都打印出来,执行结果如下:

[root@centos7 ~]# docker run --rm gosu/alpine gosu root ps aux
PID USER TIME COMMAND
1 root 0:00 ps aux


  • 上述信息显示,我们执行docker run时的 gosu root ps aux 会执行ps命令,该命令成了容器内的唯一进程,这说明通过gosu启动的是符合我们要求的(PID为1),接下来再看看用sudo执行ps命令的效果;
  • 执行以下命令创建一个容器:

docker run --rm ubuntu:trusty sudo ps aux


  • 上述命令会用sudo启动ps命令,结果如下:

USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root 1 0.0 0.0 46012 1772 ? Rs 12:05 0:00 sudo ps aux
root 6 0.0 0.0 15568 1140 ? R 12:05 0:00 ps aux



  • 尽管我们只想启动ps进程,但是容器内出现了两个进程,sudo命令会创建第一个进程,然后该进程再创建了ps进程,而且ps进程的PID并不等于1,这是达不到我们要求的,此时在宿主机向该容器发送信号量,收到信号量的是sudo进程。




  • 通过上面对可以小结:




  • gosu启动命令时只有一个进程,所以docker容器启动时使用gosu,那么该进程可以做到PID等于1;




  • sudo启动命令时先创建sudo进程,然后该进程作为父进程去创建子进程,1号PID被sudo进程占据;




  • 综上所述,在docker的entrypoint中有如下建议:




  • 创建group和普通账号,不要使用root账号启动进程;




  • 如果普通账号权限不够用,建议使用gosu来提升权限,而不是sudo;




  • entrypoint.sh脚本在执行的时候也是个进程,启动业务进程的时候,在命令前面加上exec,这样新的进程就会取代entrypoint.sh的进程,得到1号PID;




  • exec "$@"是个保底的逻辑,如果entrypoint.sh的入参在整个脚本中都没有被执行,那么exec "$@"会把入参执行一遍,如果前面执行过了,这一行就不起作用,这个命令的细节在Stack Overflow上有详细的描述,如下图,地址是:https://stackoverflow.com/questions/39082768/what-does-set-e-and-exec-do-for-docker-entrypoint-scripts




在这里插入图片描述


如何在镜像中安装gosu



  • 前面的redis例子中,我们看到docker-entrypoint.sh中用到了gosu,那么是在哪里安装了gosu呢?自然是Dockerfile中,一起来看看redis的Dockerfile中是如何安装gosu的:

# grab gosu for easy step-down from root
# https://github.com/tianon/gosu/releases
ENV GOSU_VERSION 1.10
RUN set -ex; \
\
fetchDeps=" \
ca-certificates \
dirmngr \
gnupg \
wget \
"; \
apt-get update; \
apt-get install -y --no-install-recommends $fetchDeps; \
rm -rf /var/lib/apt/lists/*; \
\
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
export GNUPGHOME="$(mktemp -d)"; \
gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
gpgconf --kill all; \
rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \
chmod +x /usr/local/bin/gosu; \
gosu nobody true; \
\
apt-get purge -y --auto-remove $fetchDeps


  • 至此,gosu在docker中的作用已经分析完毕,希望在您编写自定义镜像的时候,本文能给您带来一些参考;

欢迎关注51CTO博客:程序员欣宸



学习路上,你不孤单,欣宸原创一路相伴...



©著作权归作者所有:来自51CTO博客作者程序员欣宸的原创作品,请联系作者获取转载授权,否则将追究法律责任

springcloud-01 rest学习环境搭建笔记_wx63311348dcab6的博客-多极客编程

目录​​写在前面​​​​一、新建一个项目​​​​二、服务提供者​​​​1、编写父项目​​​​2、编写子项目​​​​新建一个module​​​​3、连接Mysql​​​​使用idea创建数据库表​​​​4、编写实体类​​​​5、新建一个子项目​​​​创建yaml文件​​​​配置application.yaml​​​​编写Mapper​​​​编写Controller​​​​创建SpringBoot启动

【js】js的历史:认识js的来龙去脉_wx63311348dcab6的博客-多极客编程

文章目录​​1 JavaScript 历史​​​​2 JavaScript是什么?​​​​3 JavaScript的作用​​​​4 Html/Css/Js的关系​​​​4.1Html/CSS 标记语言---描述类语言​​​​4.2 Js脚本语言 -- 编程类语言​​​​4.3 执行的区别​​​​5 JS的组成​​​​5.1 ECMAScript​​​​5.2 Dom​​​​5.3 Bom​​​​6

【tomcat】壹 - tomcat的快速入门:你也许会用tomcat,但你真的了解它吗?本文章收录了tomcat的简介、tomcat各文件夹的作用、tomcat的安装卸载使用,手动部署tomcat_wx63311348dcab6的博客-多极客编程

文章目录​​Tomcat简介​​​​Tomcat的安装和删除​​​​Tomcat的安装​​​​Tomcat文件夹下各文件的作用​​​​解决Tomcat运行时cmd命令乱码的问题​​​​Tomcat的删除​​​​TomCat的使用​​​​Tomcat端口号的修改​​​​Tomcat启动失败​​​​一是端口号被占用了​​​​二是命令行窗口一闪而过​​​​Web项目的手动部署​​Tomcat简介Tomc

存储资源盘活系统,“盘活”物联网架构难题(下)_天翼云开发者社区的博客-多极客编程

高效应对物联网架构难题,是进一步释放物联网技术动能的关键。从​​《物联网架构设计难?天翼云存储资源盘活系统有妙招(上)》​​可以了解到,​​天翼云​​存储资源盘活系统在解决物联网架构基本设计模式难题中拥有众多优势。那么在面对复合设计模式时,它又有哪些价值?复合设计模式一:多系统多系统模式是指多个物联网系统彼此独立运行的模式。每个系统都有唯一的端点、网络和平台。通常,不同的物联网系统彼此独立地发展一

存储资源盘活系统,“盘活”物联网架构难题(上)_天翼云开发者社区的博客-多极客编程

物联网 Internet of things(IoT),是一种与物有关的互联网,通过射频识别、红外感应器、全球定位系统、激光扫描器等信息传感设备,按约定的协议把任何物品与互联网相连接,进行信息交换和通信,以实现对物品的智能化识别、定位、跟踪、监控和管理。物联网技术伴随着计算机技术和互联网发展而快速发展,逐渐成为我国经济转型升级的一大突破点。物联网技术促进万物互联,然而其技术多样性和集成复杂性让物联

kubernetes小技巧关于节点pod ip node数量规划_对你无可奈何的博客-多极客编程

背景: 最近就想体验各种多集群互联(基于wireguard),然后就深感网络划分的重要性,开始网络设计的杂七乱八的。想互联了都各种问题了,网络重叠了怎么办?集群扩容IP资源不够了杂整?还有就是默认的每个node节点的subset都默认是24?我一台机器上面也跑不了那么多Pod阿...... 恩 默认的 SUBNET都是24,举个例子: 我的kubernetes集群初始化配置文件networking

docker的/var/run/docker.sock参数_github.com/zq2599的博客-多极客编程

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于/var/run/docker.sock参数 在创建docker容器时,有时会用到 /var/run/docker.sock 这样的数据卷参数,例如以下docker-compose.yml,可以看到kafka容器的数据卷参数带有 /var/ru

docker镜像列表中的none:none是什么_github.com/zq2599的博客-多极客编程

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 在构建过Docker镜像的电脑上查看本地镜像列表,有可能看到下图红框中的镜像,在列表中展示为**<none>:<none>** : 这种镜像在Docker官方文档中被称作dangling images ,指

从 0 到 1 构建一个自己的 docker 应用_imooc慕课君的博客-多极客编程

Docker,可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。Docker“真香定律”:不会用的时候觉得复杂,用顺手了就再也放不下了。今天我们就带大家从 0 到 1 构建一个自己的 Docker 应用。使用的语言和应用的版本如下:Python 3.8.1,Flask库 1.1.1,Redis库 3.4.1。1. web

openjdk镜像的tag说明_github.com/zq2599的博客-多极客编程

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 先来聊聊为什么会用到openjdk镜像。 关于java应用的docker镜像 将java应用作成docker镜像时,需要镜像中带有jdk或者jre环境,通常有三种情况: 在Dockerfile中加入安装jdk环境的脚本; 镜像中只有应用ja

git-secret:在 git 存储库中加密和存储密钥(上)_seal安全的博客-多极客编程

当涉及处理机密信息(如密码、令牌、密钥文件等)等,以下问题值得考虑: 安全性十分重要,但高安全性往往伴随着高度的不便。 在团队中,共享某些密钥有时无法避免(因此现在我们需要考虑在多人之间分发和更新密钥的安全方法)。 具体的密钥通常取决于环境。 目前市面上已经存在许多较为成熟的密钥管理产品,比如 HashiCorp Vault,AWS Secrets Manager 以及 GCP S

免费申请和使用intellij idea商业版license指南_github.com/zq2599的博客-多极客编程

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos IntelliJ IDEA是广受Java开发者喜爱的工具,其商业版的价格十分昂贵,如下图: 现在有机会免费获取IntelliJ IDEA的正版License,您是否心动呢?我把自己成功申请License的步骤记录下来,咱们一起行动吧。