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

비밀번호

댓글 작성자
 




... 16  17  18  19  20  [21]  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13530정성태1/15/202414819닷넷: 2203. C# - Python과의 AES 암호화 연동파일 다운로드1
13529정성태1/15/202414546닷넷: 2202. C# - PublishAot의 glibc에 대한 정적 링킹하는 방법
13528정성태1/14/202415651Linux: 68. busybox 컨테이너에서 실행 가능한 C++, Go 프로그램 빌드
13527정성태1/14/202416719오류 유형: 892. Visual Studio - Failed to launch debug adapter. Additional information may be available in the output window.
13526정성태1/14/202416806닷넷: 2201. C# - Facebook 연동 / 사용자 탈퇴 처리 방법
13525정성태1/13/202414945오류 유형: 891. Visual Studio - Web Application을 실행하지 못하는 IISExpress
13524정성태1/12/202414203오류 유형: 890. 한국투자증권 KIS Developers OpenAPI - GW라우팅 중 오류가 발생했습니다.
13523정성태1/12/202415283오류 유형: 889. Visual Studio - error : A project with that name is already opened in the solution.
13522정성태1/11/202416721닷넷: 2200. C# - HttpClient.PostAsJsonAsync 호출 시 "Transfer-Encoding: chunked" 대신 "Content-Length" 헤더 처리
13521정성태1/11/202414351닷넷: 2199. C# - 한국투자증권 KIS Developers OpenAPI의 WebSocket Ping, Pong 처리
13520정성태1/10/202415246오류 유형: 888. C# - Unable to resolve service for type 'Microsoft.Extensions.ObjectPool.ObjectPool`....' [1]
13519정성태1/10/202414015닷넷: 2198. C# - Reflection을 이용한 ClientWebSocket의 Ping 호출파일 다운로드1
13518정성태1/9/202415838닷넷: 2197. C# - ClientWebSocket의 Ping, Pong 처리
13517정성태1/8/202413458스크립트: 63. Python - 공개 패키지를 이용한 위성 이미지 생성 (pystac_client, odc.stac)
13516정성태1/7/202415555닷넷: 2196. IIS - AppPool의 "Disable Overlapped Recycle" 옵션의 부작용
13515정성태1/6/202413691닷넷: 2195. async 메서드 내에서 C# 7의 discard 구문 활용 사례 [1]
13514정성태1/5/202413500개발 환경 구성: 702. IIS - AppPool의 "Disable Overlapped Recycle" 옵션
13513정성태1/5/202415488닷넷: 2194. C# - WebActivatorEx / System.Web의 PreApplicationStartMethod 특성
13512정성태1/4/202416035개발 환경 구성: 701. IIS - w3wp.exe 프로세스의 ASP.NET 런타임을 항상 Warmup 모드로 유지하는 preload Enabled 설정
13511정성태1/4/202414956닷넷: 2193. C# - ASP.NET Web Application + OpenAPI(Swashbuckle) 스펙 제공
13510정성태1/3/202415170닷넷: 2192. C# - 특정 실행 파일이 있는지 확인하는 방법 (Linux)
13509정성태1/3/202416192오류 유형: 887. .NET Core 2 이하의 프로젝트에서 System.Runtime.CompilerServices.Unsafe doesn't support netcoreapp2.0.
13508정성태1/3/202417874오류 유형: 886. ORA-28000: the account is locked
13507정성태1/2/202416481닷넷: 2191. C# - IPGlobalProperties를 이용해 netstat처럼 사용 중인 Socket 목록 구하는 방법파일 다운로드1
13506정성태12/29/202313832닷넷: 2190. C# - 닷넷 코어/5+에서 달라지는 System.Text.Encoding 지원
13505정성태12/27/202317647닷넷: 2189. C# - WebSocket 클라이언트를 닷넷으로 구현하는 예제 (System.Net.WebSockets)파일 다운로드1
... 16  17  18  19  20  [21]  22  23  24  25  26  27  28  29  30  ...