Linux内核又躺枪了 之《docker挂载volume的权限问题》

我对docker不太成熟的认知!写的不好望指出!互相学习、交流!

对docker的认知

为什么说Linux内核又躺枪了,本人不想背锅,只能把锅甩给Linux 内核了,哈哈哈…

docker依赖于Linux的两个内核特性:

1
2
Namespaces - 命名空间
Control groups(cgroups)- 控制组

Namespaces

命名空间提供了一种系统资源的隔离,包括了文件系统、网络、进程等。

docker有以下5种命名空间:

1
2
3
4
5
PID:进程隔离
NET:网络管理接口
IPC:管理跨进程通信访问
MNT:管理挂载点
UTS:隔离内核和版本标识

Control groups

这是Linux内核提供的一种可以限制,记录,隔离物理进程组的机制。他提供了以下功能:

1
2
3
4
资源限制
优先级设定
资源计量
资源控制

docker 基本能力

1
2
3
4
5
6
Namespaces和Control groups带给了容器下面的能力:

文件系统的隔离:每个容器都有自己的root文件系统
进程隔离:每个容器都运行在自己的进程环境中
网络隔离:每个容器间虚拟网络接口和ip地址都是分开的
资源隔离和分组:Control groups可以将CPU和内存之类的资源独立分配给每个docker容器

场景

我们在使用docker容器的时候,经常要给容器里面挂载本地服务器目录,从而实现某个文件在宿主机和容器内部都可以访问的效果。

例如:在容器内部进行删除、添加、复制、剪切、读写文件,说白了就是访问挂载的卷

问题 - docker挂载volume的权限问题

报错:一般都是在进行一系列操作抛出权限问题

1
2
3
touch: cannot touch '/var/jenkins/copy_file.log' : Permission denied
can not write to  '/var/jenkins/copy_file.log' . Wrong volume permission?
# 这是在说你没有权限在这个目录里面创建文件,以及写东西

原因

1
因为容器共享宿主机的uid,如果不指定user,容器内部默认使用root用户来运行,容器内部用户的权限与外部用户相同,所以一定要确保容器执行者的权限和挂载数据卷对应

扩展干货…(后续会继续补充)

1、为什么docker输出的文件权限为 root?

Docker容器运行的时候,如果没有指定user,容器内部默认以root用户运行

2、怎么确定运行的用户为root?

可以通过容器里的执行用户的id是0,输出文件的权限也是0。

当docker容器运行在宿主机上的时候,仍然只有一个内核。容器共享宿主机的内核,所以所有的uid和gid都受同一个内核来控制。
比如,当一个进程尝试去写文件,内核会检查创建这个进程的的user的uid和gid,来决定这个进程是否有权限修改这个文件。

1
2
3
4
5
# 怎么查看用户的UID和GID?
(1)id 用户名
(2)cat /etc/passwd | grep 用户名
# root:x:0:0:root:/root:/bin/bash
# 两个bai0就是uid和gid

3、为什么容器里的用户名不一定和宿主内核一样呢?

比如,test 容器的用户叫做 test, 而本机没有 test 这个用户。这是因为username不是Linux kernel的一部分。简单的来说,username是对uid的一个映射。由于权限控制的依据是uid,而不是username。

1
That’s because the username (and group names) that show up in common linux tools aren’t part of the kernel, but are managed by external tools (/etc/passwd, LDAP, Kerberos, etc). So, you might see different usernames, but you can’t have different privileges for the same uid/gid, even inside different containers

解决方案

方案一 : 关闭SELinux

  • 临时关闭(不用重启机器)SELinux
1
2
# 执行命令
setenforce 0
  • 修改配置文件需要重启机器(或者使用source使其生效,没有亲测,不确定是否有效)
1
2
# 修改/etc/selinux/config 文件
将SELINUX=enforcing改为SELINUX=disabled

方案二:指定user

  • 使用run的时候指定
1
2
3
# 通过volume挂载的方式,指定运行user为1001, 启动容器 test:
docker run -d --name test -p 8080:8080 -u 1001:1001 -v $(pwd):/tmp test
# docker run -u 可以指定宿主机运行docker命令的用户, -u指定的uid就是docker实际运行的进程拥有者
  • 亦可以在dockerfile的时候指定
1
2
3
4
5
6
RUN useradd -r -u 1001 -g test test
USER test
# -g:指定用户所属的群组。值可以使组名也可以是GID。用户组必须已经存在的,期默认值为100,即users。
# -u:指定用户ID号。该值在系统中必须是唯一的。0~499默认是保留给系统用户账号使用的,所以该值必须大于499。
# -r:建立系统账号。
# USER指令用于指定容器执行程序的用户身份,默认是 root用户。