docker - 使用 linux 和 win 提取非 root 命名卷权限的工作方法

标签 docker permissions cross-platform docker-volume

我正在尝试一个简单的工作流程但没有成功,我花了很长时间在 SO 和 github 上测试许多解决方案。 docker 中命名文件夹的权限和更一般的权限卷是一场噩梦 link1 link2恕我直言。
所以我从头开始,试图为我的用例创建一个简单的概念证明。
我想要这个一般的工作流程:

  • Windows 和/或 linux 上的用户构建 Dockerfile
  • 用户运行容器(如果可能,不要以 root 身份)
  • 容器启动一个 crontab,每分钟运行一个脚本写入数据量
  • 用户(在 linux 或 windows 上)从数据卷(不是 root)获得结果,因为权限被正确映射

  • 我用 supercronic因为它在没有 root 权限的情况下在容器中运行 crontab。Dockerfile :
    FROM artemklevtsov/r-alpine:latest as baseImage
    
    RUN mkdir -p /usr/local/src/myscript/
    RUN mkdir -p /usr/local/src/myscript/result
    
    COPY . /usr/local/src/myscript/
    
    WORKDIR /usr/local/src/myscript/
    
    RUN echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories
    RUN apk --no-cache add busybox-suid curl
    
    ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.$
        SUPERCRONIC=supercronic-linux-amd64 \
        SUPERCRONIC_SHA1SUM=9aeb41e00cc7b71d30d33c57a2333f2c2581a201
    
    RUN curl -fsSLO "$SUPERCRONIC_URL" \
     && echo "${SUPERCRONIC_SHA1SUM}  ${SUPERCRONIC}" | sha1sum -c - \
     && chmod +x "$SUPERCRONIC" \
     && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
     && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic
    
    CMD ["supercronic", "crontab"]
    
    crontab文件 :
    * * * * * sh /usr/local/src/myscript/run.sh > /proc/1/fd/1 2>&1
    
    run.sh脚本
    #!/bin/bash
    
    name=$(date '+%Y-%m-%d-%s')
    
    echo "some data for the file" >> ./result/fileName$name
    
    命令:
    # create the volume for result, uid/gid option are not possible for windows 
    docker volume create --name myTestVolume
    
    docker run --mount type=volume,source=myTestVolume,destination=/usr/local/src/myscript/result test
    
    docker run --rm -v myTestVolume:/alpine_data -v $(pwd)/local_backup:/alpine_backup alpine:latest tar cvf /alpine_backup/scrap_data_"$(date '+%y-%m-%d')".tar /alpine_data
    
    当我这样做时,结果文件夹 local_backup它包含的文件有root:root权限,因此启动此容器的用户无法访问文件。
    有没有可行的解决方案,它允许启动 的 windows/linux/mac 用户相同的脚本在没有权限问题的情况下轻松将文件访问到卷中?
    编辑 1:
    该策略首先描述here only使用绑定(bind)卷,并且未命名卷 .我们使用 entrypoint.sh根据 docker run 提供的信息 chown 容器文件夹的 uid/gid。
    我复制粘贴修改后的 Dockerfile :
    FROM artemklevtsov/r-alpine:latest as baseImage
    
    RUN mkdir -p /usr/local/src/myscript/
    RUN mkdir -p /usr/local/src/myscript/result
    
    COPY . /usr/local/src/myscript/
    
    ENTRYPOINT [ "/usr/local/src/myscript/entrypoint.sh" ]
    
    WORKDIR /usr/local/src/myscript/
    
    RUN echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories
    RUN apk --no-cache add busybox-suid curl su-exec
    
    ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.$
        SUPERCRONIC=supercronic-linux-amd64 \
        SUPERCRONIC_SHA1SUM=9aeb41e00cc7b71d30d33c57a2333f2c2581a201
    
    RUN curl -fsSLO "$SUPERCRONIC_URL" \
     && echo "${SUPERCRONIC_SHA1SUM}  ${SUPERCRONIC}" | sha1sum -c - \
     && chmod +x "$SUPERCRONIC" \
     && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
     && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic
    
    CMD ["supercronic", "crontab"]
    
    入口点.sh
    #!/bin/sh
    set -e
    
    addgroup -g $GID scrap && adduser -s /bin/sh -D -G scrap -u $UID scrap
    
    if [ "$(whoami)" == "root" ]; then
        chown -R scrap:scrap /usr/local/src/myscript/
        chown --dereference scrap "/proc/$$/fd/1" "/proc/$$/fd/2" || :
        exec su-exec scrap "$@"
    fi
    
    构建、启动、导出的过程:
    docker build . --tag=test                                                       
    
    docker run -e UID=1000 -e GID=1000 --mount type=volume,source=myTestVolume,destination=/usr/local/src/myscript/result test
    
     docker run --rm -v myTestVolume:/alpine_data -v $(pwd)/local_backup:/alpine_backup alpine:latest tar cvf /alpine_backup/scrap_data_"$(date '+%y-%m-%d')".tar /alpine_data
    
    编辑 2:
    对于 Windows,使用 docker 工具箱和绑定(bind)卷,i found the answer on SO .我使用 c:/Users/MyUsers用于绑定(bind)的文件夹,它更简单。
    docker run --name test -d -e UID=1000 -e GID=1000 --mount type=bind,source=/c/Users/myusers/localbackup,destination=/usr/local/src/myscript/result dockertest --name rflightscraps 
    
    调查结果
  • 使用废弃用户运行 crontab [OK]
  • 本地用户的 UID/GID 映射到容器用户废料 [OK]
  • 导出的数据继续是 root [NOT OK]。
  • Windows/Linux [半OK]
    如果我使用绑定(bind)卷而不是命名卷,它可以工作。但这不是所需的行为,我如何在 Win/Linux 上使用具有正确权限的命名卷...
  • 最佳答案

    让我将答案分为Linux部分和Docker部分两部分。您需要了解两者才能解决此问题。

    Linux 部分

    在 Linux 中以 root 以外的用户身份运行 cronjobs 很容易。

    这可以通过在 docker 容器中创建一个与主机中的 UID 相同的用户并将 crontab 文件复制为/var/spool/cron/crontabs/user_name 来实现。

    来自 man crontab

    crontab is the program used to install, deinstall or list the tables used to drive the cron(8) daemon in Vixie Cron. Each user can have their own crontab, and though these are files in /var/spool/cron/crontabs, they are not intended to be edited directly.



    由于 Linux 通过用户 ID 识别用户,因此在 docker 内部,UID 将绑定(bind)到新创建的用户,而在主机中,UID 将与主机用户绑定(bind)。

    因此,您没有任何权限问题,因为文件归 host_user 所有。现在你应该明白为什么我提到在主机上创建具有相同 UID 的用户了。

    docker 部分

    Docker 将所有目录(或层)视为 UNION FILE SYSTEM。每当您构建图像时,每条指令都会创建一个层,并且该层被标记为只读。这就是 Docker 容器不保存数据的原因。所以你必须明确告诉 docker 一些目录需要使用 VOLUME 来持久化数据。关键词。

    您可以在不明确提及容量的情况下运行容器。如果这样做,docker daemon 会将它们视为 UFS 并重置权限。
    为了保留对文件/目录的更改,包括所有权。相应的文件应在 Dockerfile 中声明为 Volume。

    来自 UNION FILE SYSTEM

    Indeed, when a container has booted, it is moved into memory, and the boot filesystem is unmounted to free up the RAM used by the initrd disk image. So far this looks pretty much like a typical Linux virtualization stack. Indeed, Docker next layers a root filesystem, rootfs, on top of the boot filesystem. This rootfs can be one or more operating systems (e.g., a Debian or Ubuntu filesystem). Docker calls each of these filesystems images. Images can be layered on top of one another. The image below is called the parent image and you can traverse each layer until you reach the bottom of the image stack where the final image is called the base image. Finally, when a container is launched from an image, Docker mounts a read-write filesystem on top of any layers below. This is where whatever processes we want our Docker container to run will execute. When Docker first starts a container, the initial read-write layer is empty. As changes occur, they are applied to this layer; for example, if you want to change a file, then that file will be copied from the read-only layer below into the read-write layer. The read-only version of the file will still exist but is now hidden underneath the copy.



    例子:

    假设我们有一个名为 的用户。主机用户 . 用户标识符 host_user 是 1000 .现在我们要创建一个名为 的用户。 docker_user 在 Docker 容器中。所以我将他的 UID 分配为 1000 .现在,如果这些文件可由 host_user 从主机访问(即通过卷),则 Docker 容器中 docker_user 拥有的任何文件也归 host_user 所有。

    现在您可以与其他人共享绑定(bind)目录,而不会出现任何权限问题。您甚至可以在相应目录上授予 777 权限,允许其他人编辑数据。否则,您可以保留 755 权限,允许其他人复制但只有所有者可以编辑数据。

    我已将目录声明为将更改保留为卷。这将保留所有更改。请注意,一旦将目录声明为卷,在构建时对该目录所做的进一步更改将被忽略,因为这些更改将位于单独的层中。因此,在目录中进行所有更改,然后将其声明为卷。

    这是 Docker 文件。
    FROM alpine:latest
    ARG ID=1000 
    #UID as arg so we can also pass custom user_id
    ARG CRON_USER=docker_user
    #same goes for username
    
    COPY crontab /var/spool/cron/crontabs/$CRON_USER
    RUN adduser -g "Custom Cron User" -DH -u $ID $CRON_USER && \
        chmod 0600 /var/spool/cron/crontabs/$CRON_USER && \
        mkdir /temp && \
        chown -R $ID:$ID /temp && \
        chmod 777 /temp
    
    VOLUME /temp
    #Specify the dir to be preserved as Volume else docker considers it as Union File System
    
    ENTRYPOINT ["crond", "-f", "-l", "2"]
    

    这是crontab
    * * * * * /usr/bin/whoami >> /temp/cron.log
    

    构建图像
    docker build . -t test
    

    创建新卷
    docker volume create --name myTestVolume
    

    使用数据量运行
    docker run --rm --name test -d -v myTestVolume:/usr/local/src/myscript/result test:latest
    

    Whenever you mount myTestVolume to other container you can see the data under /usr/local/src/myscript/result is owned by UID 1000 if no user exist with that UID in that container or the username of corresponding UID.



    使用绑定(bind)卷运行
    docker run --rm --name test - -dv $PWD:/usr/local/src/myscript/result test:latest
    

    当您执行 ls -al /home/host_user/temp您将看到名为 cron.log 的文件。由 **host_user** 创建并拥有.

    将拥有相同的所有权。 docker_user 在 docker 容器中执行 ls -al /temp . cron.log的内容将是 docker_user .

    所以,您的有效Dockerfile应该
    FROM artemklevtsov/r-alpine:latest as baseImage
    
    ARG ID=1000 
    
    ARG CRON_USER=docker_user
    
    RUN adduser -g "Custom Cron User" -DH -u $ID $CRON_USER && \
        chmod 0600 /var/spool/cron/crontabs/$CRON_USER && \
        echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories && \
        apk --no-cache add busybox-suid curl && \
        mkdir -p /usr/local/src/myscript/result && \
        chown -R $ID:$ID /usr/local/src/myscript/result && \
        chmod 777 /usr/local/src/myscript/result
    
    COPY crontab /var/spool/cron/crontabs/$CRON_USER
    
    COPY . /usr/local/src/myscript/
    
    VOLUME /usr/local/src/myscript/result
    #This preserves chown and chmod changes.
    
    WORKDIR /usr/local/src/myscript/
    
    ENTRYPOINT ["crond", "-f", "-l", "2"] 
    

    现在,每当您将数据/绑定(bind)卷附加到 /usr/local/src/myscript/result 时它将由具有 UID 1000 的用户拥有,并且在所有容器中都保持不变,无论哪个容器安装了相同的卷,其对应的用户为 1000 作为文件所有者。

    请注意:我给了777权限,以便与每个人共享。您可以根据自己的意愿在 Dockerfle 中跳过该步骤。

    引用:
  • Crontab manual .
  • User identiier - Wiki .
  • User ID Definition .
  • About storage drivers .
  • UNION FILE SYSTEM .
  • 关于docker - 使用 linux 和 win 提取非 root 命名卷权限的工作方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52433220/

    相关文章:

    docker - Docker-Compose:是否可以在不同容器上强制使用IP地址?

    c# - 如何确定docker容器端口号?

    android - 如何使gps权限可选?

    asp.net-core - .NET Core 是如何实现跨平台或平台无关的?

    c# - 以编程方式重新定位 VLC 窗口

    docker - docker 中的解析器指令是什么

    docker - Docker容器问题无法通信

    linux - 是否可以为每个用户配置 Linux 功能?

    php - CSV 上传适用于本地 WAMP,但不适用于使用 LOAD DATA 的远程 LAMP

    java - 使用 Java 是开发基于 GUI 的会计应用程序的合适语言/平台吗?