欢迎访问我的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  COMMAND1 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中的作用已经分析完毕,希望在您编写自定义镜像的时候,本文能给您带来一些参考;

欢迎关注阿里云开发者社区博客:程序员欣宸

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

docker与gosu-阿里云开发者社区相关推荐

  1. pg数据库生成随机时间_postgresql 时区与时间函数-阿里云开发者社区

    postgresql 时区与时间函数 rudygao 2016-02-03 1951浏览量 简介: --把时间戳转成epoch值 postgres=# select extract(epoch fro ...

  2. 博客同步至阿里云开发者社区,快来帮我涨人气吧

    博客同步至阿里云开发者社区,快来帮我涨人气吧! 直达链接! 直达链接! 直达链接! 直达链接! 直达链接! https://developer.aliyun.com/profile/sijaicxpx ...

  3. freebsd mysql 安装_Freebsd中mysql安装及使用笔记-阿里云开发者社区

    Freebsd中mysql安装及使用笔记 x3d 2009-07-31 662浏览量 简介: 1.安装 一开始连mysql的软件包在freebsd中叫什么都不知道: 依稀属于databases类,先到 ...

  4. 杭州内推 | 阿里云开发者社区招聘计算机视觉算法工程师(2022年毕业生)

    合适的工作难找?最新的招聘信息也不知道? AI 求职为大家精选人工智能领域最新鲜的招聘信息,助你先人一步投递,快人一步入职! 阿里云 阿里巴巴集团拥有海量的图像/视频数据,强大的计算能力和巨大的市场空 ...

  5. mysql join 索引 无效_ORACLE MYSQL中join 字段类型不同索引失效的情况-阿里云开发者社区...

    ORACLE MYSQL中join 字段类型不同索引失效的情况 重庆八怪 2016-12-29 780浏览量 简介: 关于JOIN使用不同类型的字段类型,数据库可能进行隐士转换,MYSQL ORACL ...

  6. db h2 数据类型_H2数据库函数及数据类型概述-阿里云开发者社区

    H2数据库函数及数据类型概述 jieforest 2015-01-29 573浏览量 简介: H2数据库函数及数据类型概述 一.H2数据库常用数据类型 INT类型:对应java.lang.Intege ...

  7. rcs开机启动mysql_linux添加开机自启动脚本示例详解-阿里云开发者社区

    linux添加开机自启动脚本示例详解 double2li 2017-04-14 1652浏览量 简介: linux下(以RedHat为范本)添加开机自启动脚本有两种方法,先来简单的;一.在/etc/r ...

  8. mysql double 存储_关于MYSQL中FLOAT和DOUBLE类型的存储-阿里云开发者社区

    关于MYSQL中FLOAT和DOUBLE类型的存储 重庆八怪 2016-04-12 844浏览量 简介: 关于MYSQL中FLOAT和DOUBLE类型的存储 其实在单精度和双精度浮点类型存储中其存储方 ...

  9. a记录 mysql_[a]-和[a]相关的内容-阿里云开发者社区

    HDOJ 1202 The calculation of GPA Problem Description 每学期的期末,大家都会忙于计算自己的平均成绩,这个成绩对于评奖学金是直接有关的.国外大学都是计 ...

最新文章

  1. 滴滴ElasticSearch平台跨版本升级以及平台重构之路
  2. 备份TB级别Oracle数据库的一些技巧
  3. 读取CSV数据的集中方式(Java和Oracle)
  4. 20160419 while练习,复习
  5. kali linux 2.0 web 渗透测试 电子书
  6. Azure站点恢复之----VMM 站点到ASR
  7. [每日一题] OCP1z0-047 :2013-07-15 drop column
  8. 孪生再世代表数字几_能科股份:智能制造需求旺盛,中标多领域数字孪生项目
  9. python定时任务启动与停止_对Python定时任务的启动和停止方法详解
  10. 企业架构TOGAF认证培训
  11. SQL SERVER 2000数据库置疑处理
  12. 阿里矢量图可根据样式修改颜色
  13. DFT频谱泄漏的数学分析及不产生泄漏的条件
  14. python中求绝对值的函数_python如何取绝对值 python取绝对值方法
  15. 济南oracle 认证费用,济南ORACLE管理培训价格
  16. 深度:那些梦碎乐视的造车高人!
  17. Word中的文字如何上下(垂直)居中?
  18. (2010计本3班-杨蒙)面向对象的C--实现链表操作
  19. 初级会计资料-常用会计公式(三)
  20. 华大单片机HC32L130J6TA入坑全纪录(二)——关于SWD下载的问题

热门文章

  1. 神奇!Python编辑公式竟可以如此简单!
  2. 【2020面试真题】这15道面试真题,你能答出几道?
  3. lgg7深度详细参数_探讨CAPP与PDM的深度集成
  4. 开发板移植RTOS操作系统,RTOS操作系统适配开发板整理大全
  5. 使用localStorage统计页面停留时间
  6. 哈啰出行,为城市续航!
  7. ubuntu安装pinfi及其实操记录
  8. 卫生专业计算机考试证书,中国卫生人才卫生网官网_卫生资格证电子证书打印时间...
  9. PointNet论文分析
  10. ASP.NET调用HTML模板