Docker volume权限导致的几个问题
挂宿主目录的权限问题
由于容器和宿主机共用了一套内核,因此同一个uid对应的容器用户和宿主机用户(哪怕用户名不同)对于内核权限控制而言都是同一个用户。而默认情况下,如果未做特殊配置,容器里的进程默认是以root用户运行的。
这里就有两个问题:
- 直接以root权限运行容器不安全
- 以其他用户运行容器可能因为挂载宿主机目录的权限问题而蛋疼
先说第二个问题:
导致这个问题的原因,一般是因为容器内该uid的用户对宿主目录无权限导致的。 比如,容器里的mysql用户的uid是2000,而宿主当前mysql用户uid是1000,即便宿主要挂载的目录权限是mysql:mysql,容器里看到的权限也是1000:1000而权限被拒绝。
还有一种情况,宿主不存在被挂载的目录。 Docker会以root权限先创建该目录再挂载。 这就导致以普通用户运行的容器进程无权限访问该目录
这个问题最简单的处理方式就是先手动chmod 777 或者chown 2000:2000 ,再启动容器。不过这不是长久之计也违背了docker的初衷。
对于这个问题,目前大家最好的处理方式,就在entrypoint脚本里先对需要本地挂载的目录做权限配置,再启动服务。因为执行entrypoint脚本则是在启动阶段(start)所以在entrypoint.sh中可以对volume做权限配置。 当然,权限配置需要root权限,这就需要以root启动容器。
第一个问题:
以root权限运行容器,会导致容器中的进程有了适当的机会,它就可以控制宿主机上的一切!如何解决比较好呢?
有些服务可以直接以root启用,并配置其他用户运行。
比如:
mysqld可以
mysqld --defaults-file=/etc/my.cnf --user=mysql
memcached可以
memcached -u www
甚至像ngx php-fpm 这类的服务可以直接在配置文件里指定运行worker进程的用户而master进程保持root运行
但对于像Redis这类服务呢?这类服务就需要使用Dockerfile 的USER指令去指定整个容器内部的运行用户了,当然还可以在entrypoint脚本里采用su
命令切到相关的用户运行服务进程,但这种情况就会导致容器里出现一个sh父进程,如下:
[root@docker_121.201.47.196 ~]# docker exec -it redis ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 09:20 pts/0 00:00:00 /bin/sh /entrypoint /redis-se
root 6 1 0 09:20 pts/0 00:00:00 su - redis -s /bin/sh -c /redis-
redis 7 6 0 09:20 ? 00:00:02 /redis-server 0.0.0.0:6001
root 27 0 0 09:42 pts/1 00:00:00 ps -ef
这里的Redis就是直接在entrypoint脚本里采用su命令切到"redis"用户启动的。
可以发现容器里的除了redis-server,还有pid为1的sh进程。如果存粹只是为了运行Redis进程,其实也没啥问题。不过从规范的角度,容器里不应该运行不必要的多余的进程。
而且上面的栗子还存在一个问题,就是PID1进程是sh而不是redis-server,这样docker stop 该容器时,里面的redis-sever进程将无法接收到关闭的信号而导致超时十秒被docker强杀,这在需要数据落地的场景下是不被允许的。比如mysql的docker容器一般只跑mysqld进程而不需要mysqld_safe。
gosu
那对于上面redis引出的这个问题,该如何解决最好呢?
这里推荐在entrypoint脚本里使用gosu来替换su,来切换用户运行程序
具体gosu的解析,可以参考:https://segmentfault.com/a/1190000004527476
Redis的栗子
对于上面扯的几个问题,这里用redis作为栗子重写Dockerfile https://www.cnblogs.com/wshenjin/p/9328763.html
Dockerfile:
FROM centos
COPY ["src","/src"]
RUN groupadd -g 1002 redis \
&& useradd -u 1002 -g redis -s /sbin/nologin redis ;\
yum -y install tcl-8.5* make gcc gcc-c++ \
&& yum clean all ;\
cd /src/ \
&& cp redis_entrypoint.sh / \
&& cp gosu /usr/local/bin/ \
&& cp redis.conf /etc/ \
&& tar xf redis-3.2.7.tar.gz \
&& cd redis-3.2.7/ \
&& make MALLOC=libc \
&& cp src/{redis-benchmark,redis-check-aof,redis-cli,redis-sentinel,redis-server} /usr/local/bin/ ;\
cd / ;\
mkdir -p /data/redis \
&& chown redis:redis -R /data/redis ;\
echo "/usr/local/bin/redis-cli -h 127.0.0.1 -p 6001 -a $(awk '/^requirepass/{print $NF}' /etc/redis.conf) save" >> /root/redis_stop ;\
echo "sleep 5s" >> /root/redis_stop ;\
echo "/usr/local/bin/redis-cli -h 127.0.0.1 -p 6001 -a $(awk '/^requirepass/{print $NF}' /etc/redis.conf) shutdown" >> /root/redis_stop ;\
echo "/usr/local/bin/redis-cli -h 127.0.0.1 -p 6001 -a $(awk '/^requirepass/{print $NF}' /etc/redis.conf) info" >> /root/redis_info ;\
echo "/usr/local/bin/redis-cli -h 127.0.0.1 -p 6001 -a $(awk '/^requirepass/{print $NF}' /etc/redis.conf) bgsave" >> /root/redis_bgsave ;\
chmod 700 /root/redis_* ;\
yum remove -y iputils* \
bind* \
vim* \
make \
cmake \
cpp \
acl \
rootfiles \
lzo \
readline-devel \
python-chardet \
hostnamed \
bus-python \
gobject-introspection \
libxml2-python \
python-gobject-base \
basesystem \
libgomp \
libstdc++-devel \
glibc-headers \
mpfr \
passwd \
yum-plugin-ovl \
dbus-glib \
python-kitchen \
ncurses-devel \
kernel-headers \
gpg-pubkey \
yum-util ;\
userdel mail ;\
userdel ftp ;\
userdel games ;\
cp /usr/share/zoneinfo/Asia/Shanghai /etc/ ;\
ln -svf /etc/Shanghai /etc/localtime ;\
rm -rf /src /root/.bash* /root/.cshrc /root/.tcshrc /var/cache /usr/share/zoneinfo
EXPOSE 6001
HEALTHCHECK --interval=60s --timeout=5s CMD /usr/local/bin/redis-cli -h 127.0.0.1 -p 6001 -a $(awk '/^requirepass/{print $NF}' /etc/redis.conf) ping | grep -i PONG || exit 1
ENTRYPOINT ["/redis_entrypoint.sh"]
redis_entrypoint.sh:
#/bin/sh
chown redis:redis -R /data/redis/ #redis数据目录,log pid rdb。
exec gosu redis /bin/sh -c "/usr/local/bin/redis-server /etc/redis.conf"
Redis用到的唯一目录是/data/redis/,entrypoint脚本会进行权限处理。当挂载宿主目录时,不论宿主目录如何,entrypoint脚本都会处理好。
这样得到的容器内部进程就如下:
[root@docker_121.201.47.196 ~]# docker exec -it redis ps -ef
UID PID PPID C STIME TTY TIME CMD
redis 1 0 0 Mar08 pts/0 00:12:15 /usr/local/bin/redis-server 0.0.0.0:6001
root 18206 0 0 17:42 pts/1 00:00:00 ps -ef
Docker volume权限导致的几个问题的更多相关文章
- 解决 Windows Docker 安装 Gitlab Volume 权限问题
本文首发于我的个人博客,解决 Windows Docker 安装 Gitlab Volume 权限问题 ,欢迎访问! 记录一下 Windows10 下 Docker 安装 Gitlab 的步骤. Ca ...
- Docker volume speed up npm install
上一节决定在Jenkins中采用Docker作为构建环境,于是就可以为所欲为的使用各种node版本编译我们的项目.解决了版本切换问题.然而,Docker设计的目的就是纯净的执行环境,因此每次运行doc ...
- docker volume创建、备份、nfs存储
docker存储volume #环境 centos7.4 , Docker version 17.12.0-ce docker volume创建.备份.nfs存储 #docker volume 数据存 ...
- 理解OpenShift(5):从 Docker Volume 到 OpenShift Persistent Volume
理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...
- Docker学习笔记之docker volume 容器卷的那些事(二)
预览目录 更改目录拥有者 Data Container 切换用户 参考文章 0x00 概述 如果你读了docker volume 容器卷的那些事(一),我想应该不会遇到下面这些问题的,毕竟是具有指导意 ...
- Docker学习笔记之docker volume 容器卷的那些事(一)
预览目录 volume 方式 相关用例 使用方式 使用 volume driver bind mount 方式 相关用例 使用方式 配置selinux标签 配置macOS的安装一致性 tmpfs 方式 ...
- Docker - 命令 - docker volume
概述 docker volume 命令 背景 docker 容器的存储, 通常需要独立于镜像 docker volume 就是负责这块的命令 1. 写在 docker volume 之前 概述 doc ...
- windows 文件权限导致的 git 问题
windows 文件权限导致的 git 问题 在 windows 上使用 git 时,会遇到明明什么都没有改动,但是 git status 显示一堆文件被修改.这时,通过 git diff 可看到什么 ...
- 关于修改了db2 instance下面文件夹权限导致的不可连接
前一段时间,我修改了db2inst1目录下的所有文件的权限,目的是方便其他用户访问和查看里面的文件信息.可是修改了之后,我用IBM data studio就始终连接不上数据库了. 查看了错误代码,看提 ...
随机推荐
- 切换controller 后面的最好不要用id参数,不然会根据路由规则改变
//切换actionResult return RedirectToAction("Edit", "EngineeringCase", ...
- Hibernate--Day01
Hibernate是一个面向对象的持久化框架 持久化: 1,把内存中的Java对象保存到存储设备上面: 2,最好的解诀方案:把对象持久化到数据库里面: 3, 在Java里面,把对象持久化到数据库只能使 ...
- java基础 第八章课后习题
1.什么是二重循环?在内层循环中使用continue和break语句,程序如何跳转? 答:二重循环就是一个循环结构体内又包含另一个完整的循环结构. continue语句跳转时是跳过了内层循环中的剩余语 ...
- Html转义字符列表
Symbol Code Entity Name ™ ™ € € Space ! ! " " " # # $ $ % % & & ...
- dlib编译成静态库及被其它程序调用
一.git下载:https://github.com/davisking/dlib 官网:http://dlib.net/ 二.vs中编译成静态库 1.在vs2015中创建静态库工程(vs2015以上 ...
- 判断为false的情况
console.log(''==false) //true console.log('0' == false) //true console.log(null == false) //false, ...
- Spark入门到精通--(第九节)环境搭建(Hive搭建)
上一节搭建完了Hadoop集群,这一节我们来搭建Hive集群,主要是后面的Spark SQL要用到Hive的环境. Hive下载安装 下载Hive 0.13的软件包,可以在百度网盘进行下载.链接: h ...
- jstack命令
先是有jps查看进程号: hollis@hos:~$ jps 29788 JStackDemo1 29834 Jps 22385 org.eclipse.equinox.launcher_1.3.0. ...
- Oracle查询重复数据并删除,只保留一条记录
前言 项目中,在“资源目录-在线编目”中,资源项子表存在多条重发数据,需要进行数据清理,删除重发的数据,最终只保留一条相同的数据. 操作的表名:R_RESOURCE_DETAILS 操作步骤 一.重复 ...
- levmar : Levenberg-Marquardt库编译
levmar : Levenberg-Marquardt 是非线性优化的一个库 1.使用CMake生成sln项目,编译 clapack库 在levmar工程中,打开misc.c文件,在最开始添加#in ...