zoukankan      html  css  js  c++  java
  • Docker volume权限导致的几个问题

    个人学习笔记,谢绝转载!!!
    原文:https://www.cnblogs.com/wshenjin/p/10518785.html


    挂宿主目录的权限问题

    由于容器和宿主机共用了一套内核,因此同一个uid对应的容器用户和宿主机用户(哪怕用户名不同)对于内核权限控制而言都是同一个用户。而默认情况下,如果未做特殊配置,容器里的进程默认是以root用户运行的。

    这里就有两个问题:

    1. 直接以root权限运行容器不安全
    2. 以其他用户运行容器可能因为挂载宿主机目录的权限问题而蛋疼

    先说第二个问题:

    导致这个问题的原因,一般是因为容器内该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。

    详参:https://www.oschina.net/translate/docker-and-the-pid-1-zombie-reaping-problem?spm=5176.100239.blogcont5545.17.qOGovX

    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
    
  • 相关阅读:
    container宽度
    NO.14 两个div并排,左边为绝对宽度,右边为相对宽度
    CSS3旋转图片效果收集
    背景图片问题
    CSS动画
    前端比较有用的网址
    JS如何判断IE和火狐与Chrome浏览器
    JAVA多线程面试题目
    JAVA多线程之Semaphore
    阿里多线程笔试题
  • 原文地址:https://www.cnblogs.com/wshenjin/p/10518785.html
Copyright © 2011-2022 走看看