개요


 

Docker 컨테이너에서 Python이 실행되면서 생성된 __pycache__ 폴더가 root 사용자로 생성되면서, Host 쪽에서 git clean과 같은 작업이 자동으로 수행될 때 특정 사용자로 진행되기 때문에, __pycache__ 폴더를 삭제할 수 없다는 에러(Permission denied)가 발생하게 되어, 아래와 같은 개념에 대한 이해와 해결방법 중에 하나를 시도해볼 수 있따.

1. 리눅스/유닉스 기본 개념 (가장 중요)

이 문제를 이해하는 근본적인 배경입니다.

  • 루트(Root) 사용자: 리눅스 시스템의 '슈퍼 유저' 또는 '관리자'입니다. 모든 파일과 프로세스에 대한 모든 권한을 가집니다. (UID 0)
  • 파일 소유권(Ownership) 및 권한(Permissions): 리눅스의 모든 파일과 디렉터리에는 '소유자(user)'와 '소유 그룹(group)'이 정해져 있습니다. 이 소유자와 그룹에 따라 읽기(r), 쓰기(w), 실행(x) 권한이 결정됩니다.
  • UID (User ID)와 GID (Group ID): 리눅스 시스템은 내부적으로 사용자를 '이름'(예: my_user)이 아닌 숫자 ID로 관리합니다.
    • UID: 사용자 고유 번호 (예: root는 항상 0)
    • GID: 그룹 고유 번호
    • 시스템은 my_user라는 이름이 아닌 1000이라는 UID를 보고 권한을 확인합니다.
  • 셸(Shell) 명령어:
    • id -u: 현재 로그인한 사용자의 UID를 출력합니다. (예: 1000)
    • id -g: 현재 로그인한 사용자의 GID를 출력합니다. (예: 1000)
    • whoami: 현재 로그인한 사용자의 이름을 출력합니다. (예: my_user)
  • 서브셸 (Subshell) $(...): 셸 명령어 안에서 또 다른 명령어를 실행하고 그 결과값을 가져올 때 씁니다.
    • echo $(id -u)는 id -u가 실행된 결과(예: 1000)를 echo 명령어로 출력합니다.
  • 환경 변수 (Environment Variable): 셸 또는 프로세스가 사용할 수 있는 설정값입니다.
    • $HOME: 현재 로그인한 사용자의 홈 디렉터리 경로. (예: /home/my_user)
    • CURRENT_UID=... : CURRENT_UID라는 이름의 변수를 설정하는 문법입니다.

2. 도커(Docker) 핵심 개념

포스팅의 문제 상황과 해결책이 적용되는 환경입니다.

  • 호스트(Host) vs. 컨테이너(Container):
    • 호스트: Docker가 설치된 실제 컴퓨터 (예: 여러분의 노트북).
    • 컨테이너: 호스트 위에서 실행되는 격리된 애플리케이션 실행 환경.
  • 도커 이미지(Image): 컨테이너를 실행하기 위한 '설계도' 또는 '템플릿'.
  • 커널 공유 (Shared Kernel): 도커 컨테이너는 호스트 OS의 커널(kernel)을 공유합니다. 이것이 매우 중요합니다. 커널을 공유한다는 것은 UID와 GID 목록도 공유한다는 뜻입니다.
    • 즉, 호스트에서 UID 1000은 컨테이너 내부에서도 UID 1000으로 인식됩니다.
  • 볼륨 마운트 (Volume Mount) (-v):
    • 이것이 문제의 핵심 원인입니다.
    • 호스트 머신의 특정 폴더(예: /home/my_user/project)컨테이너 내부의 특정 경로(예: /app)에 '연결'하는 기술입니다.
    • 컨테이너가 /app 폴더에 파일을 쓰면, 실제로는 호스트의 /home/my_user/project 폴더에 파일이 쓰여집니다.

3. 핵심 문제와 해결책

위의 개념들을 조합하여 내용을 재구성해 보겠습니다.

문제 상황 (The Problem)

  1. 호스트에서 'my_user' (UID 1000) 사용자가 프로젝트 폴더를 마운트하여 도커 컨테이너를 실행합니다. (-v /home/my_user/project:/app)
  2. 도커 컨테이너는 기본적으로 root 사용자 (UID 0)로 실행됩니다.
  3. 컨테이너 내부의 프로세스(빌드 스크립트 등)가 /app 폴더에 파일을 생성합니다.
  4. 이 파일은 컨테이너의 사용자(root, UID 0) 권한으로 생성됩니다.
  5. 커널을 공유하므로, 이 파일은 호스트의 /home/my_user/project 폴더에도 소유자가 root (UID 0)인 파일로 생성됩니다.
  6. 결과: 호스트의 'my_user' (UID 1000)가 git clean 등으로 이 파일을 지우려고 하면 권한 없음(Permission denied) 오류가 발생합니다.

해결책 (The Solution)

컨테이너를 실행할 때부터 호스트 사용자의 UID/GID와 동일한 권한으로 실행시킵니다.

  1. docker run --user [UID]:[GID]
    • --user 옵션은 컨테이너 내부의 프로세스를 지정된 UID와 GID로 실행하도록 강제합니다.
  2. 동적으로 UID/GID 설정하기
    • 1000:1000처럼 하드코딩하는 대신, id 명령어와 서브셸을 사용합니다.
    • docker run --user $(id -u):$(id -g) ...
    • 이렇게 하면 docker run --user 1000:1000 ... (여러분의 UID/GID가 1000이라고 가정)으로 자동 변환되어 실행됩니다.
  3. 결과:
    • 컨테이너 내부 프로세스가 UID 1000으로 실행됩니다.
    • 컨테이너가 마운트된 볼륨(/app)에 파일을 생성하면, 이 파일의 소유자는 UID 1000이 됩니다.
    • 호스트에서도 이 파일의 소유자가 UID 1000 (즉, 'my_user')이므로, 'my_user'가 자유롭게 파일을 수정하고 삭제할 수 있습니다.

4. Docker Compose 및 한계점

  • Docker Compose:
    • docker-compose.yml 파일은 서브셸($(...))을 지원하지 않습니다.
    • 대신 환경 변수($VAR 또는 ${VAR})를 지원합니다.
    • 따라서 user: ${CURRENT_UID}라고 파일에 적어두고,
    • 실행 시 셸에서 CURRENT_UID=$(id -u):$(id -g) docker-compose up과 같이 변수를 주입하여 실행합니다.
  • 한계점 (Gotchas):
    • "Nameless" (이름 없음): 컨테이너는 UID/GID 1000을 알지만, 이 번호에 매핑된 '이름'(예: my_user)은 모릅니다. (컨테이너 내부의 사용자 목록 /etc/passwd 파일에 없기 때문). 그래서 whoami를 실행하면 "I have no name!" 같은 오류가 나옵니다.
    • "HOME-less" (홈 디렉터리 없음): 이름이 없으므로 $HOME 환경 변수에 설정된 홈 디렉터리 경로도 없습니다. 이로 인해 $HOME 경로에 무언가를 쓰려는 일부 프로그램(예: gem install)이 오류를 일으킬 수 있습니다.

출처


https://medium.com/redbubble/running-a-docker-container-as-a-non-root-user-7d2e00f8ee15

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기