zoukankan      html  css  js  c++  java
  • Docker学习笔记之docker volume 容器卷的那些事(二)

    0x00 概述

    如果你读了docker volume 容器卷的那些事(一),我想应该不会遇到下面这些问题的,毕竟是具有指导意义的。本篇文章的内容依旧是有关 volume 的内容,主要讲诉的是如何解决非 root 用户下的文件映射问题。博主将自己常遇到的一些问题总结如下。

    事情要从博主使用 prometheus 说起。当时博主使用的执行脚本类似下面这种:

    $ docker run --rm 
        --name prometheus 
        -p 9090:9090 
        -v "$(pwd)"/data:/prometheus 
        prom/prometheus:v2.0.0

    应该是在其版本 2.0.0 之前,博主使用 prometheus 一切正常。突然有一天冒出这样的错误:

    level=info ts=2017-12-22T12:40:09.154479277Z caller=main.go:314 msg="Starting TSDB"
    level=error ts=2017-12-22T12:40:09.154587496Z caller=main.go:323 msg="Opening storage failed" err="open DB in /prometheus: open /prometheus/872424405: permission denied"

    什么情况!发生了什么?没有权限?明明没有该执行脚本,不应该的啊。这才想起来咱刚刚更新过 prometheus 镜像的版本(该版本优化很大,故及时跟进)。没办法,看看它的 Dockerfile 更新了什么 #Use user nobody in Dockerfile。在 Dockerfile 中明显的看到:

    USER       nobody

    从以前的 root 用户切换到了 nobody 用户(为了安全考虑)。

    而我们映射的目录:

    drwxr-sr-x    2 root     root            40 Dec  5 02:41 data/

    看到我们的 data 目录的拥有者依然是 root 用户,权限的问题必然出现了。

    那么,如果你依然固执的要这样做(不使用命名容器卷)。这里提供了几种解决的办法,供参考。

    在某些情况下,即使使用下面方法也不能达到效果,可能你需要尝试关闭 SELinux:setenforce 0(临时关闭)

    更改目录拥有者

    是的,非常容易的想到,既然这个映射出来的文件夹所有者不是 nobody,我给它改成 nobody 不就可以了吗?

    首先,我们找到 nobody 用户的 id:

    # 找到它的原始镜像执行命令。
    $ docker run --rm quay.io/prometheus/busybox cat /etc/passwd
    
    ...
    nobody:x:65534:65534:nobody:/home:/bin/false

    发现,其 id 为 65534(其实这些用户uid是约定的),执行如下命令:

    $ sudo chown -R 65534 data
    
    $ ls -al data
    drwxr-sr-x    3 65534   root            60 Dec 22 12:59 data/

    可以看到 data 目录的所有者已经改为了 uid 为 65534 的用户。再次执行运行 prometheus 的脚本,成功。

    Data Container

    是的,你可以使用 Data Container 的方式进行容器卷的共享,这样也能够解决权限的问题。其基本运行方式是:

    # 声明一个容器卷 /data,并在 /data 目录下新建 a.txt 文件
    $ docker run --name data_container -v /data alpine touch /data/a.txt
    
    # 挂载容器卷,查看 /data 目录下的内容
    $ docker run --volumes-from container_name alpine ls /data
    a.txt

    当执行第二条命令时,你会看到了 a.txt 文件,说明挂载数据容器成功了。

    需要说明的是,最好用同一个镜像运行数据容器,这样才能保证两者的 UID 一致,然也会出现权限问题。数据容器应该是执行一条命令就退出。

    再把前面 prometheus 的例子拿来实践一下。首先,在 prometheus 的 Dockerfile 中我们看到:

    # 声明容器卷
    VOLUME     [ "/prometheus" ]
    ...
    # 入口
    ENTRYPOINT [ "/bin/prometheus" ]

    原来 prom/prometheus 镜像就声明了一个容器卷,那么我们就不必再多次一举了。但我们需要覆盖 ENTRYPOINT 指令。

    $ docker run --name data_container --entrypoint="" prom/prometheus:v2.0.0 ls

    然后再次执行:

    $ docker run --rm 
        --name prometheus 
        -p 9090:9090 
        --volumes-from data_container 
        prom/prometheus:v2.0.0

    成功。

    切换用户

    有没有更好的方式去实现呢?有的,这种方式较第一种优点是自动化,不需要手动更改文件权限。具体流程是:

    1. 切换为 root 用户。
    2. 更改目录权限到当前非 root 用户。
    3. 用 gosu 以非 root 用户执行命令。

    这里需要自行书写 Dockerfile 构建镜像。具体实现类似下面,新建 Dockerfile:

    FROM prom/prometheus:v2.0.0
    USER root
    
    RUN mkdir -p /usr/local/bin 
      && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64" 
      && chmod +x /usr/local/bin/gosu 
     
    COPY entrypoint.sh /entrypoint.sh
    ENTRYPOINT ["/entrypoint.sh"]
    CMD [ "--config.file=/etc/prometheus/prometheus.yml", 
          "--storage.tsdb.path=/prometheus", 
          "--web.console.libraries=/usr/share/prometheus/console_libraries", 
          "--web.console.templates=/usr/share/prometheus/consoles" ]

    其中 entrypoint.sh 的内容如下,它的目的就是将我们的目录的权限改成非 root 用户的权限:

    #!/bin/sh
    
    chown -R nobody /prometheus
    gosu nobody prometheus "$@"

    这里提到了 gosu 工具,用它来替换 sudo 从某种意义上解决了信号传递和 TTY 的问题,更多详情到其项目首页。

    然后我们构建镜像,执行最初的运行脚本,成功。我们查看下映射到宿主机上的目录:

    $ ls -al data
    drwxr-sr-x    3 nobody   root            80 Jan 11 11:09 data
    
    # 进入容器查看进程
    $ ps
    PID   USER     TIME   COMMAND
        1 root       0:00 {entrypoint.sh} /bin/sh /entrypoint.sh ...
        6 nobody     0:00 prometheus --config.file=/etc/prometheus/prometheus.yml ...

    注意,standard_init_linux.go:195: exec user process caused "exec format error" 得到这个错误,可能是你没有指定运行 entrypoint.sh 的 shebang。指定如 #!/bin/sh 即可。

     

    本文链接:https://deepzz.com/post/the-docker-volumes-permissions.html

  • 相关阅读:
    linux内核中GNU C和标准C的区别
    linux内核中GNU C和标准C的区别
    Getting start with dbus in systemd (02)
    Getting start with dbus in systemd (01)
    Getting start with dbus in systemd (03)
    物理内存相关的三个数据结构
    数据类型对应字节数(32位,64位 int 占字节数)
    Linux kernel 内存
    共模电感的原理以及使用情况
    [原创]DC-DC输出端加电压会烧毁
  • 原文地址:https://www.cnblogs.com/JetpropelledSnake/p/10518309.html
Copyright © 2011-2022 走看看