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

비밀번호

댓글 작성자
 




... 106  107  108  [109]  110  111  112  113  114  115  116  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11308정성태9/13/201724613VS.NET IDE: 121. 비주얼 스튜디오에서 일부 텍스트 파일을 무조건 메모장으로만 여는 문제파일 다운로드1
11307정성태9/13/201727639오류 유형: 421. System.Runtime.InteropServices.SEHException - 0x80004005
11306정성태9/12/201725623.NET Framework: 682. 아웃룩 사용자를 위한 중국어 스팸 필터 Add-in
11305정성태9/12/201726763개발 환경 구성: 334. 기존 프로젝트를 Visual Studio를 이용해 Github의 신규 생성된 repo에 올리는 방법 [1]
11304정성태9/11/201723403개발 환경 구성: 333. 3ds Max를 Hyper-V VM에서 실행하는 방법
11303정성태9/11/201728041개발 환경 구성: 332. Inno Setup 파일의 관리자 권한을 제거하는 방법
11302정성태9/11/201723404개발 환경 구성: 331. SQL Server Express를 위한 방화벽 설정
11301정성태9/11/201721604오류 유형: 420. SQL Server Express 연결 오류 - A network-related or instance-specific error occurred while establishing a connection to SQL Server.
11300정성태9/10/201727402.NET Framework: 681. dotnet.exe - run, exec, build, restore, publish 차이점 [3]
11299정성태9/9/201725443개발 환경 구성: 330. Hyper-V VM의 Internal Network를 Private 유형으로 만드는 방법
11298정성태9/8/201729105VC++: 119. EnumProcesses / EnumProcessModules API 사용 시 주의점 [1]
11297정성태9/8/201725787디버깅 기술: 96. windbg - 풀 덤프에 포함된 모든 닷넷 모듈을 파일로 저장하는 방법
11296정성태9/8/201726892웹: 36. Edge - "이 웹 사이트는 이전 기술에서 실행되며 Internet Explorer에서만 작동합니다." 끄는 방법
11295정성태9/7/201726062디버깅 기술: 95. Windbg - .foreach 사용법
11294정성태9/4/201724986개발 환경 구성: 329. 마이크로소프트의 CoreCLR 프로파일러 예제 빌드 방법 [1]
11293정성태9/4/201726671개발 환경 구성: 328. Visual Studio(devenv.exe)를 배치 파일(.bat)을 통해 실행하는 방법
11292정성태9/4/201725154오류 유형: 419. Cannot connect to WMI provider - Invalid class [0x80041010]
11291정성태9/3/201725069개발 환경 구성: 327. 아파치 서버 2.4를 위한 mod_aspdotnet 마이그레이션
11290정성태9/3/201729088개발 환경 구성: 326. 아파치 서버에서 ASP.NET을 실행하는 mod_aspdotnet 모듈 [2]
11289정성태9/3/201726943개발 환경 구성: 325. GAC에 어셈블리 등록을 위해 gacutil.exe을 사용하는 경우 주의 사항
11288정성태9/3/201723524개발 환경 구성: 324. 윈도우용 XAMPP의 아파치 서버 구성 방법
11287정성태9/1/201732413.NET Framework: 680. C# - 작업자(Worker) 스레드와 UI 스레드 [11]
11286정성태8/28/201720068기타: 67. App Privacy Policy
11285정성태8/28/201728231.NET Framework: 679. C# - 개인 키 보안의 SFTP를 이용한 파일 업로드파일 다운로드1
11284정성태8/27/201726655.NET Framework: 678. 데스크톱 윈도우 응용 프로그램에서 UWP 라이브러리를 이용한 비디오 장치 열람하는 방법 [1]파일 다운로드1
11283정성태8/27/201722194오류 유형: 418. CSS3117: @font-face failed cross-origin request. Resource access is restricted.
... 106  107  108  [109]  110  111  112  113  114  115  116  117  118  119  120  ...