[Docker] 컨테이너 내부 데이터 영속적으로 관리하기 (Docker volume, mount)
Docker 에서 생성되는 데이터를 영속적으로 관리하기 위한 효과적인 방법으로 볼륨과 마운트가 있습니다.
도커에서 제공하는 이 2가지 기능을 이요하여 로컬(Host)의 폴더와 컨테이너의 내부 폴더를 동기화 시킬 수 있습니다.
- Docker Volume
- Docker Bind Mount
이번 포스팅에서는 이 2가지 기능에 대해 정리해보고자 합니다.
[목차]
- Docker Container 내부에서 Data 저장 과정
- Docker 의 영속적 데이터 저장 방법
- 1) Volumes
- (1) 익명 볼륨 - Anonymous Volumes
- (2) 이름이 있는 볼륨 (명명된 볼륨) - Named Volumes
- (3)❗️ Volume의 메모리 저장 위치 (Mount Point)
- 2) Docker Bind Mount - 바인드 마운트
- 3) Tmpfs Mount
- 1) Volumes
레츠고!
1. Docker Container 내부에서 Data 저장 과정
도커의 이미지와 컨테이너는 "레이어"를 쌓아가면서 만들어지고 → 그렇기 때문에 도커 이미지는 "불변"을 유지할 수 있습니다.
이 말을 실행중인 현재 파일을 저장하고자 하는 "컨테이너 입장"에서 "정리"해보자면,
- Docker Image를 빌드할 때 생성되는 레이어는 "호스팅 파일 시스템"에서 분리된 "자체 내부 파일 시스템"이 존재하고
- 이 이미지를 기반으로 도커 컨테이너를 시작하면 이미지 위에 "read-write" 레이어(Writable Layer) 로 컨테이너가 추가됩니다.
- 따라서 기본적으로 "read-write" 레이어는 이미지 파일 시스템에 엑세스할 수 있지만!
- 이미지 자체는 "read-only" 레이어이기 때문에 컨테이너는 파일을 생성할 때 상단에 추가되는 자체 "read-write" 레이어에 저장합니다.
이전 게시글에서 Docker 의 이미지를 지우는 방법 중 하나인 "docker run --rm" 옵션에 대해 말한적이 있었습니다.
결국 우리가 사용하게되는 도커의 방식은 [코드 변경] → [이미지 빌드] → [컨테이너화 & 실행] 이기 때문에
Docker 컨테이너는 굉장히 자주 삭제되는 품목입니다.
- (컨테이너를 삭제시키지 않고 Git 과 같은 VSC(Version Control System) 을 이용하여 계속 사용하는 방법도 있습니다.)
📌하지만 컨테이너를 제거한다면 컨테이너 레이어에 저장된 데이터가 같이 삭제되기 때문에 컨테이너가 삭제되어도 생존해야만하는 데이터를 처리하는 방법이 필요합니다.
- 예를들어) 에러로그, 실행로그, 실행파일, 환경변수 세팅 파일, 덥프파일 등등.
2. Docker 의 영속적 데이터 저장 방법
💡 Docker 에서는 Container 에 쓰여진 데이터가 도커 생명주기와 관련없이 영속적으로 저장이 되게하거나, Data를 안전하고 성능이 좋게 관리하기 위해 아래 3가지 방법을 제공합니다.
- Volumes
- Anonymous Volumes
- Named Voumes
- bind mounts
- tmpfs mount (
3가지 방식 모두 Docker에서 Data 를 안전하게(영속적으로) 저장시킬 수 있는 방법이지만,
도커에서는 volume 을 가장 안전한 방법이라고 이야기 하고있으니,, 뭘 사용해야할지 모를때는 volume을 사용하라고 합니다.
1) Volumes
도커 볼륨이란?
- 볼륨은 Docker 컨테이너에서 생성하고 사용하는 테이터를 지속하기 위해 선호되는 도커에서 제공하는 메커니즘입니다.
- Docker 볼륨은 Docker 에서 완전하게 관리하는 호스트 머신의 폴더입니다.
- 컨테이너나 이미지에 있는게 아닌 호스트 컴퓨터에 장착된 하드 드라이브에 존재하여 사용가능하거나, 컨테이너로 매핑되는 것을 의미합니다.
- 그렇기때문에 볼륨을 사용하면 컨테이너 내부에서 외부 파일에 접근할 수 있고, 외부에서 내부파일에 접근할 수 있는 "링크"를 제공해줍니다.
- → 이러한 매커니즘으로 보륨을 통해 데이터를 유지할 수 있고, 연결된 컨테이너가 종료되더라도 계속 존재합니다.
(단, 익명 불륨은 제외)
이러한 도커 볼륨에는 "익명 볼륨(Anonymous Volumes)" 과 "명명 볼륨(Named Volumes)" 2가지가 존재하고, 명확한 차이점이 존재합니다.
(1) 익명 볼륨 - Anonymous Volumes
- 컨테이너 내부와 볼륨을 연결할 때, 연결할 볼륨을 명시하지 않으면 Docker Host 내에서 임의의 고유한 이름의 볼륨을 부여하고, 이를 이름이 부여되지 않았다 하여 익명 볼륨이라 합니다.
- 익명 볼륨은 컨테이너가 존재하는 동안에만 존재합니다. 즉, 컨테이너가 종료된다면 볼륨도 사라집니다.
- 실질적으로 컨테이너가 제거될 때 자동으로 제거되는 것은 아니지만, 새로운 컨테이너가 실행되면 새로운 익명 볼륨이 매핑되므로 기존의 익명 볼륨이 사용되지 않고 무의미하게 쌓이게만 됩니다.
- 👏🏻 만약 '--rm' 옵션으로 컨테이너를 실행한다면, 컨테이너가 종료될 때 자동으로 익명볼륨도 삭제 됩니다.
[익명 볼륨을 컨테이너에 추가하는 방법 2가지]
1. DockerFile 에 추가하기
- 이미지를 만드는 Dockerfile 에 "VOLUME" 명령어 추가하기
- VOLUME["컨테이너 내부 경로"]
- 내부 경로만 존재하기 때문에 도커에서 관리하는 특정한 폴더에서 볼륨이 관리되어집니다.
- 그렇기 호스트에서 해당 파일을 찾기 힘듭니다.
- 편집이 필요없거나, 직접 볼 필요가 없는 중요한 데이터에 적합한 볼륨 방식 입니다.
- 익명 볼륨은 외부 경로보다 컨테이너 내부 경로의 우선 순위를 높이는데 사용할 수 있습니다.
FROM node
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . /app
EXPOSE 80
#docker 익명 볼륨
VOLUME ["app/feadback"]
CMD ["node", "server.js"]
2. "-v" 옵션 사용하기
- docker run "-v" 옵션을 사용하여 실행하는 컨테이너에 볼륨을 매핑할 수 있습니다.
- docker run -v ['컨테이너 내부경로'] ['이미지 이름'] 으로 실행하면, 마찬가지로 컨테이너 내부경로만 존재하기 때문에 익명 볼륨이 매핑됩니다.
- ex) docker run -v app/feadback anonymous-test-volumes1:latest
(2) 이름이 있는 볼륨 (명명된 볼륨) - Named Volumes
- Named Volumes 은 DockerFile 내부에서 생성할 수는 없고 컨테이너를 실행할 때 볼륨명을 지정하여 해당 이름으로 Docker Host 내에 볼륨을 부여할 수 있습니다.
- ex) docker run -d -p 3000:80 --rm --name "컨테이너 이름" -v ['볼륨 이름':'컨테이너 내부경로'] ['이미지 이름']
- 👏🏻 이름이 부여된 볼륨은 컨테이너 종료 후에도 도커에 의해 삭제되지 않고 유지됩니다.
- 또한 볼륨이 생성되면, 다른 컨테이너에서도 해당 볼륨에 연결하여 데이터 공유가 가능합니다.
→ 1년전에 만들었던 Named 볼륨입니다. 실행중인 컨테이너가 없지만 해당 볼륨이 살아있는걸 볼 수 있습니다.
→ 참고로 익명볼륨은 '--rm' 옵션으로 실행하면 사라지지만 명명된 볼륨은 해당옵션으로 실행해도 컨테이너를 종료할 때 사라지지 않습니다.
(3)❗️ Volume의 메모리 저장 위치 (Mount Point)
- volume 은 Docker(Linux 에서는 /var/lib/docker/volume/) 가 관리하는 Host File System 의 일부에 Data가 저장됩니다.
👏🏻 단 OS 가 리눅스가 아닌, 윈도우 맥이라면, 저 경로로 찾을 수 없습니다
- Mac os, windows os 에서 volume 을 찾을 수 없는 이유는 직접 os를 점유하지 않고, 호스트 os 를 공유하는 도커의 특성입니다.
- 리눅스 os 가 아니라면, Docker 는 Host 의 Kernel Socket을 공유합니다.
- 즉, Host Linux kernel을 대신할 것이 필요하기 때문에, Host 가 리눅스라면 있는걸 그대로 사용하면 되지만, 다른 OS 라면 Linux Kernel이 아니기 때문에 OS 위에 VM 으로 Linux 를 게스트 os 로 구동하고, 그 위에 Docker Engine을 구동합니다.
- 즉, 위의 명령어를 치면 나오는 주소인 "MountPoint" 는 Vm의 "MountPoint" 임을 의미합니다.
- 다른 OS 에서 Docker Volume에 접근하기 위해서는 screen 명령어 같은걸로 vm에 접속후 접근할 수 있다고 합니다.
2) Docker Bind Mount - 바인드 마운트
바인드 마운트란
- Host 파일을 로컬 폴더 원하는 곳에 저장시키는 방법입니다. (원하는 폴더 동기화)
- volume은 고정된 위치에서 데이터를 관리하기 때문에 원하는 곳에 파일을 저장시킬 수 없습니다.
- Bind Mount를 사용하여 저장되는 Data는 System File이거나 Directory일 수 있습니다.
- Docker Host 또는 Docker Container의 Non-Docker 프로세서들이 언제든지 저장된 Data를 수정할 수 있습니다.
docker run -v '내가 지정할 로컬 경로':/app/dockerDir --name nginx-conatiner2 -p 8082:80 -d nginx
💡 (:) 콜론 앞에 로컬 머신 경로가 붙으면 바인드 마운트가 되고, 경로가 아닌 것이 붙으면 볼륨 이름으로 취금되어 명명된 볼륨이 됩니다
💡 바인드 마운트가 될 때, 호스트 머신에 마운트된 파일이 → 도커 내부 파일을 덮어 쓰기 때문에, 만약 이미지가 빌드될 떄 생성되는 종속성 폴더 같이 컨테이너 내부에 생성되는 보호해야할 파일이 존재한다면, 익명 볼륨을 사용하여 우선순위를 높여 보호할 수 있습니다.
3) Tmpfs Mount
- Tmpfs : Temp File System
- 메모리에만 Data를 저장하는 방법입니다.
- 즉, 영구적인 기억 장치가 아닌 휘발성 메모리에 저장됩니다.
- tmpfs mount는 Host System의 Memory에만 Data가 저장되며, 절대로 Host의 File System에는 저장되지 않는다고 합니다.
Tmpfs 방식의 Mount 는 이 글에서 자세한 사항을 다루진않고 공식문서로 대체하겠습니다!
https://docs.docker.com/storage/tmpfs/
이렇게 Docker 컨테이너의 내부 데이터를 영속적으로 관리하는 방법에대해 정리해보았습니다.
도커 공식문서에서는 Bind Mount 보다 Volume이 성능이 더 좋다고 하고 권장하는 방식이라고 하는데
리눅스가 환경이 아니라면 Host Os에서 Volume 디렉토리에 접근하기가 꽤 귀찮고 까다로워서..
저는 Bind Mount를 주로 사용하게 되는 경향이 있었습니다
하지만 도커에서는 엄연히 Volume을 선호하고 보통의 Cloud 환경또한 Linux 기반의 os 로 시작하는게 보편적이기 때문에
볼륨을 유연하게 사용하는 방법에대해 고민해 봐야겠습니다 :)
* 참고
- Docker Docs : https://docs.docker.com/storage/volumes/
- 강의 : Udemy - Docker & Kubernetes : 실전 가이드