이 글은 mydocker 프로젝트를 통해 컨테이너의 데이터 지속성을 구현하는 과정을 설명합니다. 특히 -v 옵션을 활용해 호스트 시스템의 디렉터리를 컨테이너 내부에 마운트하는 방식으로, 컨테이너 종료 후에도 데이터가 유지되도록 구현했습니다.
기본 개념: bind mount
bind mount은 하나의 파일 시스템 위치를 다른 위치에 연결하는 기술입니다. 이는 두 디렉터리 간에 동일한 내용을 공유하게 하며, 파일 복사 없이도 실시간 동기화가 가능합니다. 예시:
mount -o bind /host/path /container/path
이렇게 설정하면 컨테이너 내부에서 작성된 데이터는 직접 호스트의 디렉터리에 반영됩니다. 이 원리는 데이터 볼륨의 핵심 기반이 됩니다.
구현 단계
- CLI 인자 처리:
run명령어에-v플래그 추가. 형식은-v <호스트 경로>:<컨테이너 경로>. - 컨테이너 시작 전 마운트:
- 오버레이 파일시스템(overlayfs) 생성 후,
volumeExtract함수로 매핑 정보 분석. mountVolume함수에서 실제bind mount수행.
- 오버레이 파일시스템(overlayfs) 생성 후,
- 컨테이너 종료 후 정리:
- 먼저 볼륨 마운트 제거 (
umount). - 오버레이 파일시스템 언마운트 및 임시 디렉터리 삭제.
- 먼저 볼륨 마운트 제거 (
주요 함수 분석
volumeExtract
입력된 문자열을 콜론(:)으로 분할하여 호스트 경로와 컨테이너 경로를 추출합니다.
func volumeExtract(volume string) (string, string, error) {
parts := strings.Split(volume, ":")
if len(parts) != 2 {
return "", "", fmt.Errorf("invalid volume format")
}
return parts[0], parts[1], nil
}
mountVolume
호스트 디렉터리 생성 → 컨테이너 내 경로 계산 → mount -o bind 실행.
func mountVolume(mntPath, hostPath, containerPath string) {
os.Mkdir(hostPath, 0777)
containerPathInHost := path.Join(mntPath, containerPath)
os.Mkdir(containerPathInHost, 0777)
cmd := exec.Command("mount", "-o", "bind", hostPath, containerPathInHost)
cmd.Run()
}
umountVolume
반대 순서로 마운트 해제. 반드시 bind mount 이후에만 삭제 가능.
func umountVolume(mntPath, containerPath string) {
path := path.Join(mntPath, containerPath)
exec.Command("umount", path).Run()
}
테스트 결과
- 비존재 디렉터리 마운트:
/root/volume가 없더라도 자동 생성되며, 컨테이너 내부에서 파일 쓰기 시 호스트에 즉시 반영됨. - 기존 디렉터리 재사용: 이미 존재하는 디렉터리에 마운트해도 기존 데이터 보존 확인.
- 지속성 검증: 컨테이너 종료 후에도 호스트의 볼륨 디렉터리에 파일이 그대로 유지됨.
핵심 포인트 요약
- 컨테이너의 루트는
/root/merged이며, 이는 호스트 상의 실제 디렉터리입니다. - 따라서 컨테이너 내
/tmp는 호스트의/root/merged/tmp에 대응됩니다. - 결국
bind mount는/root/volume→/root/merged/tmp로 이루어져야 합니다.
이 구현은 실제로 도커의 볼륨 기능과 동일한 동작을 제공하며, 파일 시스템 격리와 리소스 관리 기능을 통합한 진정한 컨테이너 환경을 구성하는 데 중요한 한 걸음입니다.