Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

(시리즈 글이 2개 있습니다.)
Linux: 127. "USER ..." 설정이 된 Docker 컨테이너의 호스트 측 볼륨 권한
; https://www.sysnet.pe.kr/2/0/14030

Linux: 128. "USER ..." 설정이 된 Docker 컨테이너의 호스트 측 볼륨 권한 (2)
; https://www.sysnet.pe.kr/2/0/14031




"USER ..." 설정이 된 Docker 컨테이너의 호스트 측 볼륨 권한

테스트를 위해 다음과 같은 Dockerfile을 작성 후,

$ echo ${USER}
testusr

$ echo "FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base" > Dockerfile

컨테이너를 빌드한 다음, volume을 매핑해 실행해 봅니다.

$ docker build -t dotnet_test .

$ docker run --rm -it -v dotnet_test_dir:/app --name dotnet_test_inst dotnet_test /bin/bash

컨테이너 내부에서 sleep 명령어를 실행한 후,

// dotnet_test_inst 컨테이너 내부에서 실행

root@fecc129e121d:/# echo "TEST" > /app/test.txt

root@fecc129e121d:/# sleep 200

호스팅하는 리눅스 서버에서 해당 프로세스를 확인해 보면 root 권한으로 실행 중임을 알 수 있습니다.

// docker 호스팅 서버에서 실행

$ ps aux | grep " sleep 200"
root     2950631  0.0  0.0   2492  1304 pts/0    S+   13:15   0:00 sleep 200

그렇다면 당연히 매핑된 volume도 root 소유권이 됩니다.

// docker 호스팅 서버에서 실행

$ docker volume inspect dotnet_test_dir
[
    {
        "CreatedAt": "2025-10-11T04:15:18Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/mnt/docker-data/docker/volumes/dotnet_test_dir/_data",
        "Name": "dotnet_test_dir",
        "Options": null,
        "Scope": "local"
    }
]

$ ls -la /mnt/docker-data/docker/volumes/dotnet_test_dir/_data      
total 4
drwxr-xr-x 2 root root 30 Oct 11 13:43 .
drwx-----x 3 root root 27 Oct 11 13:15 ..
-rw-r--r-- 1 root root  5 Oct 11 13:43 test.txt

$ cat /mnt/docker-data/docker/volumes/dotnet_test_dir/_data/test.txt
TEST

$ sudo ls -la /mnt/docker-data/docker/volumes/ | grep dotnet_test_dir
drwx-----x   3 root     root     27 Oct 11 13:15 dotnet_test_dir

$ sudo ls -la /mnt/docker-data/docker/volumes/dotnet_test_dir
total 24
drwx-----x   3 root root     27 Oct 11 13:15 .
drwxrwxr-x 238 root test  20480 Oct 11 13:15 ..
drwxr-xr-x   2 root root      6 Oct 11 13:15 _data

그리고 root 이외의 사용자/그룹에 대해서는 'w' 쓰기 권한이 없습니다.




저렇게 생성한 volume을 그대로 두고, Dockerfile을 "User app"으로 시작하도록 수정해 보겠습니다.

// runtime:8.0 이미지에는 "app"이라는 비루트 사용자가 미리 정의돼 있음

$ echo -e "FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base\nUSER app" > app.Dockerfile

$ docker build -f app.Dockerfile -t dotnet_test_non_root .

우선 볼륨 매핑 없이 컨테이너를 실행해 볼까요?

$ docker run -it --name dotnet_test_non_root_inst dotnet_test_non_root /bin/bash

app@4638f8e580ed:/$ whoami
app

app@4638f8e580ed:/$ id -u
1654

app@4638f8e580ed:/$ echo $UID
1654

app@4638f8e580ed:/$ id
uid=1654(app) gid=1654(app) groups=1654(app)

app@4638f8e580ed:/$ sleep 200

그럼 저 프로세스(sleep 200)의 소유권을 docker 호스팅 서버에서 확인해 보면,

$ ps aux | grep " sleep 200"
1654     2954740  0.0  0.0   2492  1316 pts/0    S+   13:25   0:00 sleep 200

이전에 "root"로 나왔던 사용자 칼럼의 값이 저렇게 번호(위의 경우 1654)로 나옵니다. 그러니까, 컨테이너를 위해 격리된 user namespace 내에서만 존재하는 사용자이기 때문에 호스팅 서버에서는 저렇게 UID(사용자 ID)로만 표시되는 것입니다.

그렇다면 이제 이전에 매핑했던 볼륨을 "USER ..."로 실행한 컨테이너 내에서 접근하면 어떻게 될까요?

$ docker run --rm -it -v dotnet_test_dir:/app --name dotnet_test_non_root_inst dotnet_test_non_root /bin/bash

app@66d0fca6897b:/app$ ls -la /app
total 4
drwxr-xr-x 2 root root 30 Oct 11 04:43 .
drwxr-xr-x 1 root root 25 Oct 11 04:46 ..
-rw-r--r-- 1 root root  5 Oct 11 04:43 test.txt

app@66d0fca6897b:/app$ cat /app/test.txt
TEST

app@66d0fca6897b:/app$ echo "TEST2" > test2.txt"
bash: test2.txt: Permission denied

app@66d0fca6897b:/app$ mkdir test
mkdir: cannot create directory 'test': Permission denied

보는 바와 같이, "USER ..." 설정의 추가로 인해 기존에 생성해 두었던 볼륨을 읽기 전용으로만 접근할 수 있게 되었습니다. 따라서, 새롭게 변경한 환경에서 기존 볼륨을 사용하고 싶다면 호스팅 서버에서 해당 볼륨의 소유권을 적절하게 변경해 주어야 합니다.




그렇다면 궁금해지는군요. 처음부터 "USER ..." 설정을 사용한 docker 컨테이너에 볼륨이 매핑되었다면 해당 볼륨의 소유권은 어떻게 될까요?

$ docker run --rm -it -v dotnet_test_dir2:/app --name dotnet_test_non_root_inst2 dotnet_test_non_root /bin/bash

// docker 호스팅 서버에서 실행

$ docker volume inspect dotnet_test_dir2
...[생략]...

$ ls -la /mnt/docker-data/docker/volumes/dotnet_test_dir2/_data
total 0
drwxr-xr-x 2 root root 10 Oct 11 13:54 .
drwx-----x 3 root root 27 Oct 11 13:54 ..

오호~~~, root로 나옵니다. 그렇다면 저렇게 "USER ..." 설정이 된 컨테이너에서는 해당 볼륨에 대해 쓰기 권한이 없다는 건데요, 실제로 컨테이너 내부에서 시도해 보면,

$ docker run --rm -it -v dotnet_test_dir2:/app --name dotnet_test_non_root_inst2 dotnet_test_non_root /bin/bash

app@813e761de7e4:/$ id
uid=1654(app) gid=1654(app) groups=1654(app)

app@813e761de7e4:/$ cd /app

app@813e761de7e4:/app$ echo "TEST" > test.txt
bash: test.txt: Permission denied

app@813e761de7e4:/app$ mkdir test
mkdir: cannot create directory 'test': Permission denied

쓰기가 안 됩니다. 이에 대해 검색해 보면,

How to give non-root user in Docker container access to a volume mounted on the host
; https://stackoverflow.com/questions/39397548/how-to-give-non-root-user-in-docker-container-access-to-a-volume-mounted-on-the

대략 해법으로 제공되는 것이 3가지 정도인데요, 첫 번째는 "USER ..." 설정을 하지 말고 컨테이너를 기존처럼 실행한 다음 내부에서 "exec runuser -u ..." 명령어로 프로그램을 (제한된 권한으로) 실행하도록 바꾸는 것입니다. 해당 답변은 +67의 투표를 얻었는데... 여러분은 어떤가요? ^^ 방법이 마음에 드시나요?

두 번째는, 호스트 측에서 컨테이너의 uid/gid에 해당하는 값으로 볼륨 디렉터리의 소유권을 변경하는 것입니다. 예를 들어, 위와 같은 환경에서는 컨테이너의 uid/gid가 모두 1654이므로, 호스트 측에서 다음과 같이 볼륨 권한을 바꿔야 합니다.

// docker 호스팅 서버에서 실행

$ sudo chown -R 1654:1654 /mnt/docker-data/docker/volumes/dotnet_test_dir2

$ sudo ls -la /mnt/docker-data/docker/volumes/dotnet_test_dir2
total 24
drwx-----x   3 1654 1654    27 Oct 11 13:54 .
drwxrwxr-x 239 root rnd  20480 Oct 11 13:54 ..
drwxr-xr-x   2 1654 1654    30 Oct 11 14:13 _data

그럼 저렇게 1654:1654로 소유권이 변경되었으므로 컨테이너 내부에서 쓰기가 가능해집니다.

마지막 세 번째 방법은, 그냥 아예 "chmod 777 ..."로 볼륨 디렉터리의 권한을 바꾸자는 건데요, 이건 -3개의 투표를 얻어맞았습니다. ^^




참고로, 컨테이너 내에서 설정한 "USER ..."의 사용자 명은 해당 컨테이너 이미지에 미리 정의된 값입니다. 예를 들어, 위에서 사용한 "mcr.microsoft.com/dotnet/runtime:8.0" 이미지의 경우 "app" 사용자는 다음과 같이 정의돼 있어,

app@119fdfa35e6d:/$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
...[생략]...
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
app:x:1654:1654::/home/app:/bin/sh

그래서 "1654"라는 uid/gid가 할당된 것입니다. 또는, "USER ..." 설정이 없는 Dockerfile 설정이라면 docker run 시에 --user 옵션을 사용해 USER 명령어를 흉내 낼 수 있습니다.

// USER 설정이 없다면 root 권한으로 실행됨

$ docker run --rm -it --name dotnet_test_inst dotnet_test /bin/bash
root@6c18a4075d7a:/# id
uid=0(root) gid=0(root) groups=0(root)

// --user 옵션으로 USER 설정을 대신

$ docker run --rm -it --user 1654:1654 --name dotnet_test_inst dotnet_test /bin/bash
app@4c2548122e80:/$ id
uid=1654(app) gid=1654(app) groups=1654(app)

그렇다면 이미 USER 설정이 있는 경우 --user 옵션을 주면 어떻게 될까요?

$ docker run --rm -it --user 0:0 --name dotnet_test_non_root_inst2 dotnet_test_non_root /bin/bash
root@339c5441731b:/# id
uid=0(root) gid=0(root) groups=0(root)

보는 바와 같이 USER 설정을 무시하고 --user 옵션이 우선 적용됩니다. 그리고 --user, -u 옵션은 docker exec 명령어에서도 그대로 적용할 수 있습니다.




마지막으로 위의 글을 실습했다면! ^^

docker rmi dotnet_test
docker rmi dotnet_test_non_root

docker volume rm dotnet_test_dir
docker volume rm dotnet_test_dir2




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 10/20/2025]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13832정성태11/27/202410041Windows: 274. Windows 7부터 도입한 conhost.exe
13831정성태11/27/20248884Linux: 111. eBPF - BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_MAP_TYPE_RINGBUF에 대한 다양한 용어들
13830정성태11/25/202411861개발 환경 구성: 736. 파이썬 웹 앱을 Azure App Service에 배포하기
13829정성태11/25/202411656스크립트: 67. 파이썬 - Windows 버전에서 함께 설치되는 py.exe
13828정성태11/25/20249603개발 환경 구성: 735. Azure - 압축 파일을 이용한 web app 배포 시 디렉터리 구분이 안 되는 문제파일 다운로드1
13827정성태11/25/202410829Windows: 273. Windows 환경의 파일 압축 방법 (tar, Compress-Archive)
13826정성태11/21/202410638닷넷: 2313. C# - (비밀번호 등의) Console로부터 입력받을 때 문자열 출력 숨기기(echo 끄기)파일 다운로드1
13825정성태11/21/202411982Linux: 110. eBPF (bpf2go) - BPF_RINGBUF_OUTPUT / BPF_MAP_TYPE_RINGBUF 사용법
13824정성태11/20/20249351Linux: 109. eBPF (bpf2go) - BPF_PERF_OUTPUT / BPF_MAP_TYPE_PERF_EVENT_ARRAY 사용법
13823정성태11/20/202411161개발 환경 구성: 734. Ubuntu에 docker, kubernetes (k3s) 설치
13822정성태11/20/202410982개발 환경 구성: 733. Windbg - VirtualBox VM의 커널 디버거 연결 시 COM 포트가 없는 경우
13821정성태11/18/202410908Linux: 108. Linux와 Windows의 프로세스/스레드 ID 관리 방식
13820정성태11/18/202411380VS.NET IDE: 195. Visual C++ - C# 프로젝트처럼 CopyToOutputDirectory 항목을 추가하는 방법
13819정성태11/15/20248330Linux: 107. eBPF - libbpf CO-RE의 CONFIG_DEBUG_INFO_BTF 빌드 여부에 대한 의존성
13818정성태11/15/202413580Windows: 272. Windows 11 24H2 - sudo 추가 [1]
13817정성태11/14/20249967Linux: 106. eBPF (bpf2go) - (BPF_MAP_TYPE_HASH) Map을 이용한 전역 변수 구현
13816정성태11/14/202411020닷넷: 2312. C#, C++ - Windows / Linux 환경의 Thread Name 설정파일 다운로드1
13815정성태11/13/20249159Linux: 105. eBPF - bpf2go에서 전역 변수 설정 방법
13814정성태11/13/202410325닷넷: 2311. C# - Windows / Linux 환경에서 Native Thread ID 가져오기파일 다운로드1
13813정성태11/12/202412000닷넷: 2310. .NET의 Rune 타입과 emoji 표현파일 다운로드1
13812정성태11/11/202415351오류 유형: 933. Active Directory - The forest functional level is not supported.
13811정성태11/11/20249962Linux: 104. Linux - COLUMNS 환경변수가 언제나 80으로 설정되는 환경
13810정성태11/10/202411454Linux: 103. eBPF (bpf2go) - Tracepoint를 이용한 트레이스 (BPF_PROG_TYPE_TRACEPOINT)
13809정성태11/10/202411144Windows: 271. 윈도우 서버 2025 마이그레이션
13808정성태11/9/202411958오류 유형: 932. Linux - 커널 업그레이드 후 "error: bad shim signature" 오류 발생
13807정성태11/9/20249550Linux: 102. Linux - 커널 이미지 파일 서명 (Ubuntu 환경)
1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...