依旧是工作中遇到的问题,熬夜解决了,写博客记录下。

场景描述

甲方生产环境使用 k8s 部署两家乙方的服务,甲方在集群物理机上为两个乙方创建了用户(user_a,user_b)和各自的磁盘目录(share_a,share_b)。现在因为业务需求,两个用户需要在 share_a 目录下创建子目录并将各自服务的输出保存的到其中,子目录的名称两个乙方约定好,并且谁先用到谁创建,子目录区权限775,不然一个创建了目录,另一个没写权限。好死不死,我们是 user_b。


解决思路

原本 dev,愣是干成了 ops,好在问题不大,外加自己头铁,研究研究…。

  1. 容器肯定要挂载 share_a 目录,服务中通过代码创建 775 权限的子目录。这个问题已经搞定了。
  2. 容器肯定不能以 root 权限运行,因为谁创建谁是 owner。所以必须以 user_b 用户运行。
  3. 因为是 775 权限,所以 user_a 和 user_b 必须是同组用户,不然也是没有写权限的。

总结:构建镜像时创建两个用户 user_a ,user_b 并将后者加入前者的 groups 中,因为要在人家的目录下干活。


原型测试

1.在物理机上创建两个用户,分别加入对方用户组中。再分别为两个用户创建各自的磁盘目录,将目录设置成 775 权限。两用户都对对方磁盘目录有读写权限。

sudo useradd user_a -u 2341 # 创建用户时会以用户名为组名同时创建用户组。
sudo passwd user_a  # 设置密码,不然没法切换用户。 
su user_a # 输入密码切换用户。
mkdir -p /tmp/user_a # 为 user_a 用户创建磁盘

sudo useradd user_b -u 2381
sudo passwd user_b
su user_b
mkdir -p /tmp/user_b

sudo usermod -a -G user_a user_b # 用户:user_b 加入用户组:user_a
sudo usermod -a -G user_b user_a #

结果:

# 查看磁盘目录
ll /tmp
drwxrwxr-x 2 user_a   user_a       6 Aug 18 18:07 user_a
drwxrwxr-x 2 user_b   user_b       6 Aug 19 11:27 user_b
# 查看用户
cat /etc/passwd
user_a:x:2341:2341::/home/user_a:/bin/bash
user_b:x:2381:2381::/home/user_b:/bin/bash
# 查看用户所属组
groups user_a
user_a : user_a user_b
groups user_b
user_b : user_b user_a

物理机上的场景就算是构建出来了。 a,b 两用户都可以写对方的磁盘目录。

2.再构建容器内部的场景,其实和物理机是一样的,创建两个用户并互加组再挂在磁盘目录。

(1)创建 Dockerfile

FROM spack/centos7:v0.18.0 #  这个随便找的

RUN adduser user_a -u 2341 \
    && adduser user_b -u 2381 \
    && usermod -a -G user_a user_b

WORKDIR /home/user_b

(2) deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-b
  labels:
    app: user-b
spec:
  selector:
    matchLabels:
      app: user-b
  template:
    metadata:
      labels:
        app: user-b
    spec:
      securityContext:
        runAsUser: 2381 # 设置容器内当前用户为 user_b
      nodeSelector:
        kubernetes.io/hostname: sh02 # 因为是测试,将 pod 调度到指定的物理机上。
      containers:
        - image: yang:01
          name: user-b
          command: ["sleep"]
          args: ["infinity"]
          volumeMounts:
            - name: a
              mountPath: /home/user_b/dir_a 
            - name: b
              mountPath: /home/user_b/dir_b
      volumes:
        - name: a
          hostPath:
            path: /tmp/user_a # 挂载 user_a 的磁盘目录
        - name: b
          hostPath:
            path: /tmp/user_b  # 挂载 user_b 的磁盘目录

容器启动后查看:

[user_b@user-b-9cd656cb4-46fc6 ~]$ whoami
user_b
[user_b@user-b-9cd656cb4-46fc6 ~]$ pwd
/home/user_b
[user_b@user-b-9cd656cb4-46fc6 ~]$ ll ./
total 0
drwxrwxr-x 2 user_a user_a 40 Aug 19 07:23 dir_a
drwxrwxr-x 2 user_b user_b  6 Aug 19 07:09 dir_b

磁盘上两个目录挂载进来了,而且目录的权限和所属用户与原来状况一致。

当前用户 user_b 向 dir_a 中写文件:

[user_b@user-b-9cd656cb4-46fc6 ~]$ touch dir_a/file-created-by_user_b.txt
[user_b@user-b-9cd656cb4-46fc6 ~]$ ll dir_a/
total 0
-rw-rw-r-- 1 user_b user_b 0 Aug 19 07:23 file-created-by_user_b.txt

最后在物理机的磁盘上查看

(base) [deployer@sh02 yang]$ ll /tmp/user_a/
total 0
-rw-rw-r-- 1 user_b user_b 0 Aug 19 15:23 file-created-by_user_b.txt

写入成功。


小细节

上面 yaml 将磁盘目录挂载到容器中,并且磁盘目录的权限保持不变,必须要求磁盘目录(/tmp/user_a)是真实存在的。如果将一个不存在的磁盘目录挂载到容器中,在 pod 启动时 k8s 会在物理机上递归创建该目录,由于 k8s 是以 root 权限运行的,所以该目录的 owner 是 root 且 755 权限。此时因为 user_b 不在 root 用户组里,所以没有写权限。

不存在的目录:/tmp/user_a/not_exist

containers:
        - image: yang:01
          name: user-b
          command: ["sleep"]
          args: ["infinity"]
          volumeMounts:
            - name: a
              mountPath: /home/user_b/dir_a 
      volumes:
        - name: a
          hostPath:
            path: /tmp/user_a/not_exist

pod 启动后进入容器查看

[user_b@user-b-5c6dbcd55c-dfs6b ~]$ ll
total 0
drwxr-xr-x 2 root root 6 Aug 19 07:48 dir_a
[user_b@user-b-5c6dbcd55c-dfs6b ~]$ touch dir_a/file-created-by_user_b.txt
touch: cannot touch 'dir_a/file-created-by_user_b.txt': Permission denied

权限不够,甭想写。