docker virtiofs volume mount symlink creation failure

## 증상
macOS Docker 에서 bind mount(-v) 된 경로에서 tar 압축 해제 시 symlink 포함 파일이 Permission denied 로 실패한다.
tar: re2-2025-11-05/python/LICENSE: Cannot open: Permission denied
tar: Exiting with failure status due to previous errors

ls 로 확인하면 symlink 대신 퍼미션 ---------- 인 0바이트 파일이 생성되어 있다.
---------- 1 root root 0 Aug 17 23:47 LICENSE

## 원인

Docker for Mac 은 호스트와 컨테이너 간 파일 공유에 VirtioFS 드라이버를 사용한다.

VirtioFS 는 호스트와 가상머신(VM) 간 파일 공유를 위한 파일시스템 프로토콜이다.
QEMU/KVM 의 virtio 가상화 프레임워크 위에서 동작하며, FUSE 프로토콜을 기반으로 호스트 파일시스템을 VM 내부에 마운트한다.
Docker for Mac 은 Linux VM 위에서 컨테이너를 실행하는데, -v bind mount 시 macOS 호스트 파일을 이 Linux VM 에 공유해야 한다.
이전에는 gRPC-FUSE 를 사용했으나, 성능 개선을 위해 VirtioFS 가 기본 파일 공유 드라이버로 채택되었다.
macOS (호스트) ←→ VirtioFS ←→ Linux VM ←→ 컨테이너

VirtioFS 는 gRPC-FUSE 대비 파일 I/O 성능이 크게 향상되지만, 일부 POSIX 파일시스템 동작(퍼미션 000 파일 생성, symlink 등)에서 호환성 문제가 있다.

tar 가 symlink 를 풀 때 내부적으로 다음 순서로 동작한다.
1. openat("dst", O_CREAT|O_EXCL, 000)  ← placeholder 파일 생성 (퍼미션 000)
2. 실제 타겟 파일 생성
3. unlinkat("dst")                       ← placeholder 삭제
4. symlinkat("../src", "dst")            ← symlink 생성

VirtioFS 는 퍼미션 000 으로 파일을 생성하는 openat() 호출을 Permission denied 로 거부한다.
1단계에서 실패하므로 symlink 가 만들어지지 않고 깨진 0바이트 파일만 남게 된다.
이 문제는 같은 디렉토리 내 symlink 에서는 발생하지 않고, 다른 디렉토리를 가리키는 symlink(예: python/LICENSE → ../LICENSE) 에서만 발생한다.

컨테이너 내부 경로(/tmp등)에서는 Linux 네이티브 파일시스템(overlay2)을 사용하므로 정상 동작한다.
./openat ./mounted/demo   → Permission denied (VirtioFS)
./openat /tmp/demo         → OK (overlay2)

## 해결 방법
1. named volume 사용 (권장)

bind mount 대신 Docker named volume 을 사용하면 Docker VM 내부 파일시스템이 사용되어 symlink 가 정상 동작한다.

# named volume 생성해서 마운트
docker volume create my_source_vol
docker run -dit \
    -v my_source_vol:/workspace/source \
    my_image

# 호스트 소스를 named volume 으로 복사
docker cp ./source/. "container_name:/workspace/source"

2. 컨테이너 내부 경로에서 빌드

소스를 컨테이너 내부 경로(`/tmp` 등)로 복사한 후 빌드한다.

docker exec -it my_container /bin/bash -c "
    cp -a /mounted/source /tmp/source &&
    cd /tmp/source &&
    ./build.sh /mounted/output"

3. VirtioFS 대신 다른 마운트 방식 사용
VirtioFS 를 사용하지 않으면 symlink 가 정상 생성된다. 다만 파일 I/O 성능이 저하될 수 있다.

# Docker Desktop 사용 시
Docker Desktop → Settings → General → "VirtioFS" 체크 해제

# Colima 사용 시
Colima 는 vmType 에 따라 사용 가능한 마운트 방식이 다르다.

vmType 종류
vz: macOS 자체 가상화 프레임워크(Virtualization.Framework) 사용. 네이티브라 빠르지만 macOS 13+ 필요
qemu: 오픈소스 하드웨어 에뮬레이터. CPU, 메모리, 디스크 등을 소프트웨어로 에뮬레이션한다. vz 보다 느리지만 다양한 마운트 타입 지원

mountType 종류:
virtiofs: macOS Virtualization.Framework 기반, 가장 빠름 (vmType vz 전용)
sshfs: SSH 기반 파일 전송, virtiofs 보다 느리지만 안정적 (vmType qemu 전용)
9p: Plan 9 프로토콜 기반, 가장 안정적 (vmType qemu 전용)

vmType vz 를 사용하는 경우 virtiofs 만 지원되므로 마운트 타입 변경으로는 해결할 수 없다.
이 경우 해결 방법 1(named volume) 또는 2(컨테이너 내부 경로에서 빌드)를 사용해야 한다.

현재 마운트 타입 확인:

# json 출력에서 mount_type, driver(vmType) 확인
colima status --json | jq '{mount_type, driver}'

# 또는 설정 파일 직접 확인
# colima.yaml 위치: ~/.config/colima/default/colima.yaml
grep -E 'mountType|vmType' ~/.config/colima/default/colima.yaml

vmType qemu 로 변경하면 sshfs 또는 9p 를 사용할 수 있지만 비추천한다.
macOS 에서 QEMU 바이너리에 com.apple.security.hypervisor entitlement 서명이 필요한데, 서명 에러가 발생하는 경우가 많고 vz 대비 성능도 떨어진다.

# qemu 시작 시 서명 에러 예시
"QEMU binary does not seem properly signed with the com.apple.security.hypervisor entitlement"

또한 mountType 은 VM 생성 후 변경 불가하므로 삭제 후 재생성해야 한다.

# colima start --mount-type 만으로는 변경되지 않는다
WARN[0001] 'volume mount type' cannot be updated after initial setup, discarded

qemu 로 변경 시도 후 다시 vz 로 되돌리려면 VM 을 삭제하고 재생성해야 한다.

# 기존 VM 삭제
colima delete --force

# vz + virtiofs 로 재생성 (기존 옵션에 맞게 조정)
colima start --vm-type vz --mount-type virtiofs --cpu 10 --memory 4 --disk 100

결론: Colima + vz 환경에서는 마운트 타입 변경이 현실적이지 않으므로 해결 방법 1(named volume) 또는 2(컨테이너 내부 경로에서 빌드)를 사용하자.

comments:

댓글 쓰기