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

rey*_*n64 5 permissions cross-platform docker docker-volume

我正在尝试一个简单的工作流程而没有成功,我花了很多时间来测试SO和github上的许多解决方案.在docker中命名文件夹和更多特权权限的权限是一个噩梦link1 link2 imho.

所以我从头重新开始,尝试为我的用例创建一个简单的概念证明.

我想要这个一般工作流程:

  • Windows和/或Linux上的用户构建Dockerfile
  • 用户运行容器(如果可能不是root用户)
  • 容器启动一个crontab,每分钟运行一次脚本写入数据卷
  • 用户(在Linux或Windows上)从数据卷(而不是root)获取结果,因为权限已正确映射

我使用supercronic它是因为它在容器中运行crontab而没有root权限.

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"]
Run Code Online (Sandbox Code Playgroud)

crontab文件中:

* * * * * sh /usr/local/src/myscript/run.sh > /proc/1/fd/1 2>&1
Run Code Online (Sandbox Code Playgroud)

run.sh脚本

#!/bin/bash

name=$(date '+%Y-%m-%d-%s')

echo "some data for the file" >> ./result/fileName$name
Run Code Online (Sandbox Code Playgroud)

命令:

# 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
Run Code Online (Sandbox Code Playgroud)

当我这样做时,结果文件夹local_backup和它包含的文件具有root:root权限,因此启动此容器的用户无法访问这些文件.

有没有一个有效的解决方案,它允许启动相同脚本的 Windows/linux/mac用户轻松访问文件到卷而没有权限问题?

编辑1:

此处首次描述的策略适用于绑定卷,而不是命名卷.我们entrypoint.sh根据docker run给出的信息使用a 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"]
Run Code Online (Sandbox Code Playgroud)

entrypoint.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
Run Code Online (Sandbox Code Playgroud)

构建,启动,导出的过程:

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
Run Code Online (Sandbox Code Playgroud)

编辑2:

对于Windows,使用docker toolbox和binded volume,我在SO上找到了答案.我使用c:/Users/MyUsers文件夹进行绑定,它更简单.

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 
Run Code Online (Sandbox Code Playgroud)

调查结果

  • crontab与废料用户一起运行[确定]
  • 本地用户的UID/GID映射到容器用户废料[确定]
  • 导出的数据仍然是root [不行].
  • Windows/Linux [HALF OK]

    如果我使用绑定卷而不是命名卷,它可以工作.但这不是理想的行为,我如何在Win/Linux上使用具有正确权限的命名卷...

Man*_*ani 2

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

Linux部分

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

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

man crontab

crontab 是用于安装、卸载或列出用于驱动 Vixie Cron 中 cron(8) 守护进程的表的程序。每个用户都可以有自己的 crontab,尽管这些文件位于 /var/spool/cron/crontabs中,但它们不适合直接编辑。

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

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

码头工人部分

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

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

来自联合文件系统

事实上,当容器启动时,它会被移动到内存中,并且卸载启动文件系统以释放 initrd 磁盘映像使用的 RAM。到目前为止,这看起来非常像典型的 Linux 虚拟化堆栈。事实上,Docker 在启动文件系统之上又分层了一个根文件系统 rootfs。该 rootfs 可以是一个或多个操作系统(例如,Debian 或 Ubuntu 文件系统)。Docker 调用每个文件系统镜像。图像可以一层一层地叠加。下面的图像称为父图像,您可以遍历每一层,直到到达图像堆栈的底部,其中最终图像称为基础图像。最后,当从镜像启动容器时,Docker 会在下面的任何层之上安装一个读写文件系统。这是我们希望 Docker 容器运行的任何进程都将执行的地方。当Docker第一次启动一个容器时,初始的读写层是空的。当发生变化时,它们会被应用到这一层;例如,如果要更改一个文件,那么该文件将从下面的只读层复制到读写层。该文件的只读版本仍然存在,但现在隐藏在副本下方。

例子:

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

现在您可以与其他人共享绑定的目录,而不会出现任何权限问题。你甚至可以给相应目录赋予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"]
Run Code Online (Sandbox Code Playgroud)

这是 crontab

* * * * * /usr/bin/whoami >> /temp/cron.log
Run Code Online (Sandbox Code Playgroud)

塑造形象

docker build . -t test
Run Code Online (Sandbox Code Playgroud)

创建新卷

docker volume create --name myTestVolume
Run Code Online (Sandbox Code Playgroud)

以数据量运行

docker run --rm --name test -d -v myTestVolume:/usr/local/src/myscript/result test:latest
Run Code Online (Sandbox Code Playgroud)

每当您挂载到其他容器时 ,如果该容器中不存在具有该 UID 的用户或相应 UID 的用户名,则myTestVolume您可以看到下面的数据由 UID 1000/usr/local/src/myscript/result拥有。

使用绑定卷运行

docker run --rm --name test - -dv $PWD:/usr/local/src/myscript/result test:latest
Run Code Online (Sandbox Code Playgroud)

当您执行以下操作时,ls -al /home/host_user/temp您将看到名为的文件cron.log已创建并由 拥有**host_user**

执行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"] 
Run Code Online (Sandbox Code Playgroud)

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

请注意:我已授予777权限以便与所有人分享。您可以根据自己的方便程度跳过 Dockerfle 中的该步骤。

参考:

  1. crontab 手册
  2. 用户标识符 - 维基百科
  3. 用户 ID 定义
  4. 关于存储驱动程序
  5. 联合文件系统