Docker 볼륨 마운트 시 'permission denied' 오류 해결 방법

DockerBeginner
지금 연습하기

소개

Docker 는 개발자가 애플리케이션을 쉽게 패키징하고 배포할 수 있도록 해주는 강력한 컨테이너화 플랫폼입니다. 사용자가 자주 겪는 문제 중 하나는 Docker 에서 볼륨을 마운트할 때 발생하는 'permission denied' 오류입니다. 이 오류는 컨테이너가 호스트 머신의 파일이나 디렉토리에 접근할 수 있는 적절한 권한이 없을 때 발생합니다.

이 Lab 에서는 Docker 볼륨을 사용할 때 발생하는 permission denied 오류를 식별하고, 문제 해결 및 해결하는 방법을 배우게 됩니다. 이 튜토리얼을 마치면 Docker 볼륨의 작동 방식, 권한이 볼륨에 미치는 영향, 그리고 올바른 권한으로 볼륨을 설정하는 모범 사례를 이해하게 될 것입니다.

Docker 볼륨 이해

Docker 볼륨은 Docker 컨테이너에서 생성되고 사용되는 데이터를 영구적으로 저장하기 위한 메커니즘입니다. 컨테이너의 수명 주기와 관계없이 데이터를 저장할 수 있으므로 애플리케이션 데이터를 백업, 공유 및 관리하기가 더 쉬워집니다.

Docker 볼륨을 살펴보고 기본 볼륨을 생성하여 작동 방식을 이해해 보겠습니다.

Docker 볼륨이란 무엇인가요?

Docker 볼륨은 몇 가지 중요한 목적을 수행합니다.

  • 컨테이너가 제거된 경우에도 데이터를 유지합니다.
  • 컨테이너 간에 데이터를 공유할 수 있습니다.
  • 스토리지 관리를 컨테이너 관리와 분리합니다.
  • 컨테이너의 쓰기 가능한 레이어에 쓰는 것보다 더 나은 성능을 제공합니다.

Docker 볼륨 생성 및 관리

먼저, 간단한 Docker 볼륨을 생성해 보겠습니다.

docker volume create my_volume

모든 볼륨을 나열하려면:

docker volume ls

다음과 유사한 출력을 볼 수 있습니다.

DRIVER    VOLUME NAME
local     my_volume

새로 생성된 볼륨을 검사하여 호스트 머신에 저장된 위치를 확인해 보겠습니다.

docker volume inspect my_volume

출력은 볼륨에 대한 세부 정보를 표시합니다.

[
  {
    "CreatedAt": "2023-XX-XX....",
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/my_volume/_data",
    "Name": "my_volume",
    "Options": {},
    "Scope": "local"
  }
]

Mountpoint는 Docker 가 호스트 시스템에 볼륨 데이터를 저장하는 위치입니다.

볼륨 마운트 테스트

볼륨을 마운트하고 데이터를 기록하는 컨테이너를 실행해 보겠습니다.

docker run --rm -v my_volume:/data alpine sh -c "echo 'Hello from Docker!' > /data/test.txt"

이 명령어는 다음을 수행합니다.

  • --rm 플래그를 사용하여 임시 Alpine Linux 컨테이너를 생성합니다 (종료 시 제거됨).
  • my_volume을 컨테이너 내부의 /data 디렉토리에 마운트합니다.
  • "Hello from Docker!"를 볼륨의 test.txt라는 파일에 씁니다.

이제 다른 컨테이너에서 데이터를 읽어 데이터가 유지되는지 확인해 보겠습니다.

docker run --rm -v my_volume:/data alpine cat /data/test.txt

다음과 같은 출력을 볼 수 있습니다.

Hello from Docker!

이것은 Docker 볼륨이 서로 다른 컨테이너에서 데이터를 유지하는 방법을 보여줍니다.

Permission Denied 시나리오 생성

이제 기본적인 Docker 볼륨 사용법을 이해했으므로 'permission denied' 오류를 생성하는 시나리오를 만들어 보겠습니다. 이를 통해 문제의 원인과 해결 방법을 이해할 수 있습니다.

테스트 디렉토리 설정

먼저, 호스트 머신에 디렉토리를 만들고 특정 권한을 가진 파일을 만들어 보겠습니다.

mkdir -p ~/project/docker-test
echo "This is a test file." > ~/project/docker-test/testfile.txt
chmod 700 ~/project/docker-test/testfile.txt

이 명령어는 다음을 수행합니다.

  1. 프로젝트 폴더에 docker-test라는 디렉토리를 생성합니다.
  2. 내용이 있는 테스트 파일을 생성합니다.
  3. 파일 권한을 소유자 (사용자) 만 읽기, 쓰기 및 실행할 수 있도록 설정합니다.

파일의 권한을 확인해 보겠습니다.

ls -la ~/project/docker-test/

다음과 유사한 출력을 볼 수 있습니다.

total 12
drwxr-xr-x 2 labex labex 4096 XXX XX XX:XX .
drwxr-xr-x X labex labex 4096 XXX XX XX:XX ..
-rwx------ 1 labex labex   19 XXX XX XX:XX testfile.txt

파일 권한이 700 (-rwx------) 으로 설정되어 있음을 확인하세요. 즉, 소유자 (사용자) 만 파일을 읽고, 쓰고, 실행할 수 있습니다.

Permission Denied 오류 발생

이제 Docker 컨테이너 내에서 이 파일에 접근해 보겠습니다.

docker run --rm -v ~/project/docker-test:/app ubuntu cat /app/testfile.txt

다음과 유사한 오류 메시지를 볼 수 있습니다.

cat: /app/testfile.txt: Permission denied

이는 기본적으로 Docker 컨테이너가 컨테이너 내부에서 root 사용자로 실행되지만, 해당 root 사용자가 호스트 사용자와 동일한 사용자 ID 에 매핑되지 않기 때문입니다. Docker 가 호스트 디렉토리를 마운트하면 권한 검사가 원래 파일 권한 및 사용자 ID 를 기반으로 계속 적용됩니다.

문제 이해

permission denied 오류는 다음과 같은 이유로 발생합니다.

  1. 호스트의 파일은 사용자 (labex) 가 소유합니다.
  2. 파일 권한은 700 으로 설정되어 있습니다 (소유자만 접근 가능).
  3. Docker 컨테이너는 다른 사용자 ID(일반적으로 UID 0 인 root) 로 실행됩니다.
  4. 컨테이너 사용자가 "root"임에도 불구하고 마운트된 볼륨에 접근할 때 호스트 root 사용자와 동일한 권한을 갖지 않습니다.

이것을 더 잘 이해하기 위해 사용자 ID 를 확인해 보겠습니다.

echo "Host user ID: $(id -u)"
docker run --rm ubuntu bash -c "echo Container user ID: \$(id -u)"

이것은 호스트에서 사용자 ID 로 실행되는 동안 (아마도 1000) 컨테이너가 사용자 ID 0 (root) 으로 실행됨을 보여줍니다. 컨테이너 내부에서 "root"임에도 불구하고 호스트에 마운트된 파일에 접근할 때 컨테이너의 root 사용자는 여전히 호스트의 권한 검사를 받습니다.

Permission Denied 오류 해결

이제 permission denied 오류의 원인을 이해했으므로 이를 해결하기 위한 몇 가지 방법을 살펴보겠습니다.

방법 1: 호스트에서 파일 권한 수정

가장 간단한 방법은 호스트의 파일 권한을 변경하여 다른 사용자가 액세스할 수 있도록 허용하는 것입니다.

chmod 755 ~/project/docker-test/testfile.txt

이렇게 하면 권한이 755 (-rwxr-xr-x) 로 변경되어 모든 사용자가 파일을 읽고 실행할 수 있지만 소유자만 수정할 수 있습니다.

컨테이너에서 다시 파일에 접근해 보겠습니다.

docker run --rm -v ~/project/docker-test:/app ubuntu cat /app/testfile.txt

이제 파일의 내용을 볼 수 있습니다.

This is a test file.

이것은 파일이 이제 컨테이너의 사용자를 포함하여 호스트 시스템의 "others"에 의해 읽을 수 있기 때문에 작동합니다.

방법 2: --user 플래그 사용

또 다른 방법은 Docker 에 호스트 사용자와 동일한 사용자 ID 로 컨테이너를 실행하도록 지시하는 것입니다.

## Reset the file permissions to be restrictive
chmod 700 ~/project/docker-test/testfile.txt

## Get your user ID and group ID
USER_ID=$(id -u)
GROUP_ID=$(id -g)

## Run the container with your user ID
docker run --rm --user $USER_ID:$GROUP_ID -v ~/project/docker-test:/app ubuntu cat /app/testfile.txt

이제 제한적인 권한에도 불구하고 파일 내용을 읽을 수 있습니다.

This is a test file.

이것은 다음 이유로 작동합니다.

  1. 호스트 사용자와 동일한 사용자 ID 로 컨테이너를 실행합니다.
  2. 파일의 권한은 해당 사용자 ID 에 대한 액세스를 허용합니다.
  3. Docker 는 사용자 ID 를 컨테이너의 프로세스로 전달합니다.

--user 플래그는 호스트 파일에 제한적인 권한을 유지해야 할 때 특히 유용합니다.

방법 3: 소유자 및 그룹 ID 조정

이 방법을 설명하기 위해 다른 사용자가 소유한 새 파일을 만들어 보겠습니다.

## Create a file as root
sudo bash -c 'echo "This is a root-owned file." > ~/project/docker-test/rootfile.txt'
sudo chown root:root ~/project/docker-test/rootfile.txt
sudo chmod 600 ~/project/docker-test/rootfile.txt

## Let's see what we have
ls -la ~/project/docker-test/

출력은 다음과 같아야 합니다.

total 16
drwxr-xr-x 2 labex labex 4096 XXX XX XX:XX .
drwxr-xr-x X labex labex 4096 XXX XX XX:XX ..
-rw------- 1 root  root    25 XXX XX XX:XX rootfile.txt
-rwx------ 1 labex labex   19 XXX XX XX:XX testfile.txt

이제 root 로 실행되는 컨테이너에서 root 소유 파일에 접근해 보십시오.

docker run --rm -v ~/project/docker-test:/app ubuntu cat /app/rootfile.txt

내용을 볼 수 있습니다.

This is a root-owned file.

이것은 다음 이유로 작동합니다.

  1. 컨테이너는 기본적으로 root (UID 0) 로 실행됩니다.
  2. 파일은 호스트에서 root (UID 0) 가 소유합니다.
  3. 권한 (600) 은 소유자가 파일을 읽을 수 있도록 허용합니다.

이것은 이름뿐만 아니라 실제 사용자 ID 가 중요하다는 것을 보여줍니다. 컨테이너의 사용자 ID 가 파일의 소유자 ID 와 일치하면 소유자에게 필요한 권한이 있는 경우 권한 검사가 성공합니다.

Docker 볼륨 권한에 대한 모범 사례

이제 권한 문제를 해결하는 방법을 이해했으므로 적절한 권한으로 Docker 볼륨을 설정하기 위한 몇 가지 모범 사례를 논의해 보겠습니다.

명명된 볼륨 사용

명명된 볼륨은 Docker 에서 관리하며 바인드 마운트보다 일반적으로 더 나은 권한 처리를 제공합니다. 명명된 볼륨을 만들고 동작 방식을 살펴보겠습니다.

## Create a named volume
docker volume create data_volume

## Write to the volume as root in a container
docker run --rm -v data_volume:/data ubuntu bash -c "echo 'Created by root user' > /data/rootfile.txt && ls -la /data"

## Read from the volume as a non-root user
docker run --rm --user 1000:1000 -v data_volume:/data ubuntu cat /data/rootfile.txt

두 작업 모두 권한 문제 없이 작동하는 것을 알 수 있습니다. 이는 Docker 가 바인드 마운트와 달리 명명된 볼륨에 대해 권한을 다르게 처리하기 때문입니다.

일관된 개발 환경 구축

개발 환경의 경우 호스트와 컨테이너 간에 일관된 사용자 ID 를 설정하는 것이 유용한 경우가 많습니다.

## Create a Dockerfile for a development container
mkdir -p ~/project/dev-container
cat > ~/project/dev-container/Dockerfile << EOF
FROM ubuntu:22.04

ARG USER_ID=1000
ARG GROUP_ID=1000

RUN apt-get update && apt-get install -y sudo

## Create a non-root user with the same ID as the host user
RUN groupadd -g \${GROUP_ID} developer && \\
    useradd -u \${USER_ID} -g \${GROUP_ID} -m -s /bin/bash developer && \\
    echo "developer ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/developer

USER developer
WORKDIR /home/developer

CMD ["bash"]
EOF

## Build the development container
cd ~/project/dev-container
docker build -t dev-container .

이제 호스트 디렉토리를 마운트하여 이 컨테이너를 실행할 수 있습니다.

docker run --rm -it -v ~/project/docker-test:/home/developer/project dev-container bash

컨테이너 내부에서 파일에 접근해 보십시오.

ls -la ~/project
cat ~/project/testfile.txt

이것은 다음 이유로 작동합니다.

  1. 컨테이너 사용자는 호스트 사용자와 동일한 UID 를 갖습니다.
  2. 파일 권한은 해당 UID 에 대한 액세스를 허용합니다.
  3. 마운트된 디렉토리는 동일한 소유권 및 권한을 유지합니다.

일관된 볼륨 설정을 위해 Docker Compose 사용

더 복잡한 애플리케이션의 경우 Docker Compose 를 사용하여 일관된 볼륨 구성을 유지할 수 있습니다. 간단한 Docker Compose 파일을 만들어 보겠습니다.

mkdir -p ~/project/compose-test
cat > ~/project/compose-test/docker-compose.yml << EOF
version: '3'

services:
  app:
    image: ubuntu
    user: "\${UID}:\${GID}"
    volumes:
      - ./data:/app/data
    command: ["bash", "-c", "echo 'Running as user \$(id -u):\$(id -g)' > /app/data/output.txt && cat /app/data/output.txt"]

volumes:
  app_data:
EOF

## Create the data directory
mkdir -p ~/project/compose-test/data

## Run with your user ID
cd ~/project/compose-test
UID=$(id -u) GID=$(id -g) docker compose up

이 접근 방식은 다음과 같습니다.

  1. 환경 변수를 사용하여 사용자 ID 와 그룹 ID 를 Docker Compose 에 전달합니다.
  2. 컨테이너가 사용자 ID 로 실행되도록 설정합니다.
  3. 사용자가 액세스할 수 있는 로컬 디렉토리를 마운트합니다.

실행 후 출력 파일을 확인하십시오.

cat ~/project/compose-test/data/output.txt

다음과 같은 내용을 볼 수 있습니다.

Running as user 1000:1000

이는 컨테이너가 사용자 ID 로 실행되었고 마운트된 볼륨에 쓰기 위한 적절한 권한을 가졌음을 확인합니다.

요약

이 Lab 에서는 Docker 에서 볼륨을 마운트할 때 'permission denied' 오류를 식별, 문제 해결 및 해결하는 방법을 배웠습니다. 다룬 주요 내용은 다음과 같습니다.

  • Docker 볼륨의 작동 방식과 데이터 지속성에 대한 이점 이해
  • 호스트 디렉토리를 볼륨으로 마운트할 때 권한 문제 식별
  • 여러 기술을 사용하여 권한 오류 해결:
    • 호스트에서 파일 권한 조정
    • 특정 사용자 ID 로 컨테이너 실행
    • 파일 및 디렉토리의 소유권 관리
  • 적절한 권한으로 Docker 볼륨을 설정하기 위한 모범 사례:
    • 더 나은 권한 처리를 위해 명명된 볼륨 사용
    • 일치하는 사용자 ID 로 일관된 개발 환경 구축
    • 복잡한 볼륨 설정을 위해 Docker Compose 사용

이러한 기술은 데이터 지속성 및 적절한 권한 관리가 중요한 실제 시나리오에서 Docker 를 사용하는 데 필수적입니다. Docker 가 마운트된 볼륨에 대한 권한을 처리하는 방식을 이해하면 일반적인 문제를 방지하고 더 강력한 컨테이너화된 애플리케이션을 만들 수 있습니다.