도커는 유니온 파일 시스템을 사용하고
- 호스트 OS와 컨테이너 앱에서 발생하는 데이터를 공유
- 데이터의 영속성과 지속성을 보장하기 위해
(컨테이너는 삭제시 앱 내에서 발생한 데이터가 사라짐)
데이터와 비즈니스 로직을 분리하기 위한 매커니즘이다.
volume 은 어디 생성되나?
Linux 에서는 기본적으로 호스트 OS의 `/var/lib/docker/volumes` 에서 관리한다.
Windows, Mac 에서는 해당 경로에서 찾을 수 없다.
이는 의도한 것으로 사실 volume 은 호스트 OS에서 직접 읽고 쓰는 작업을 위한 용도가 아니라, 컨테이너에서 쓸 영속 데이터를 관리하는 용도이기 때문이다.
호스트 OS에서 직접 쓸 데이터(ex. source code)는 volume 이 아닌 bind mount 를 쓰는게 좋다.
그래서, 언제 왜 쓰나?
- 호스트와 분리된 독립적 데이터 공간 사용
- 영속적인 데이터
(컨테이너 삭제시에도 데이터 영속) - 여러 컨테이너간 공유가 필요할 때
- 이식성 보장.
ex. database data.
mac, windows 에는 /var/lib/docker 가 없다.
volume 관련 명령어
- 생성
Docker team 에서 매우 권장하는 방법이다. 가독성있는 이름을 정해야한다.
$ docker volume create [volume-name]
- 목록
# 볼륨 목록 및 정보 확인
$ docker volume ls
- 상세보기
# 특정 볼륨의 상세 정보
$ docker volume inspect [volume-name]
결과 예시
# docker volume insepct fb0fe13cc80c113dd63da551fc6b476f7a1c0e8c884a430cf3d5cce07kdzdf3aa
[
{
"CreatedAt": "2022-05-17T11:44:06Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/fb0fe13cc80c113dd63da551fc6a906f7a1c0e8c884a430cf3d5cce0731df3aa/_data",
"Name": "fb0fe13cc80c113dd63da551fc6b476f7a1c0e8c884a430cf3d5cce07kdzdf3aa",
"Options": null,
"Scope": "local"
}
]
docker 이미지 생성시 별도로 volume name 을 지정하지 않으면 docker 가 자동생성한 해시 값이 입력된다.
사용자 입장에선 편리해보이지만, volume 을 재사용할 케이스가 있다면 별도로 volume name 을 지정하는 것이 좋다.
-v 옵션의 우선권은 'host' 가 갖는다.
Host-Path 에 host.txt , Contaienr Path 에 container.txt 가 있는 상태에서 볼륨을 매핑하면 어떻게될까?
# 컨테이너 시작시 host.txt 만 남아있음
$ docker run -it --name app \
-v [Host-Path]:[Container-Path] \
[image-name]
그러나 또 예외 상황이 있다.
# 볼륨 이름을 임의로 지정하고 시작시
# 컨테이너 볼륨의 데이터가 보존된다.
$ docker volume create app-volume
$ docker run -it --name app \
-v app-volume:[Container-Path] \
[image-name]
bind mount
volume 과 유사한 기능을 제공한다.
`--mount`
호스트 파일 시스템의 절대경로:컨테이너 내부 경로를 직접 마운트한다.
컨테이너 삭제시에도 호스트에는 데이터가 영구 보관되나 파일 소유 유저와 그룹이 상이하다는 점에 유의해야한다.
# --mount 옵션으로 바인드 마운트
$ docker run -d -it --name bind-test1 \
--mount type=bind,source="$(pwd)",target=/var/log \
ubuntu:latest
호스트 OS에서 마운트 경로에 host.txt 라는 파일을 생성해보자
# host os에서 파일 생성
$ touch host.txt
$ ls -al
drwxr-xr-x 3 [User-name] [User-group] 96 5 18 17:16 .
drwxr-xr-x+ 82 [User-name] [User-group] 2624 5 18 17:15 ..
-rw-r--r-- 1 [User-name] [User-group] 0 5 18 17:16 host.txt
컨테이너에서 파일 소유자를 확인해보자
# container 에서 생성된 파일 확인
$ cd /var/log
$ ls -al
total 4
drwxr-xr-x 3 root root 96 May 18 08:16 .
drwxr-xr-x 11 root root 4096 Apr 28 12:04 ..
# 소유자 유저와 그룹이 모두 루트로 지정되있다.
-rw-r--r-- 1 root root 0 May 18 08:16 host.txt
대부분의 상황에서 volume 사용이 더 낫다.
volume 은 Host OS 에 대해 종속성이 전혀 없이 도커 자체 내에서 모든 것이 해결되기 때문이다.
bind mount는 절대 경로를 사용하기 때문에 Host OS와 의존성이 생길 수 밖에 없다.
실전 예제
우리는 Windows Local 컴퓨터에서 개발하고 Ubuntu 환경의 도커 이미지 , 컨테이너에서 빌드하고 실행해보고 싶을 수 있다.
Project root 디렉토리의 모든 소스 파일을 컨테이너로 통째로 옮기고싶다.매번 파일을 docker cp 명령어로 옮기는 것도 귀찮은데 좋은 방법이 없을까?
Dockerfile
# golang 이미지 받아오기
FROM golang
# 프로젝트 루트 Dockerfile을 실행한다
# 현재 디렉토리의 모든 파일을 container 의 /project 경로에 복사한다.
COPY . /project
# /project 디렉토리로 위치 이동
WORKDIR /project
# go file build
RUN go build .
Intellij 로 컨테이너 띄우기
Intellij IDEA 가 있다면 Dockerfile 설정에서 컨테이너 이름과 경로를 지정하라.
CLI 로 컨테이너 띄우기
$ docker run --it --name project-test -v [Project-Directiory]:[Container-Directory]
이제 호스트 컴퓨터 <-> 컨테이너 소스코드가 마운트되었다. (매번 변경사항 있을 때 마다 파일을 옮길 필요가 사라졌다.)
빌드만 Container 에서 실행해볼 수 있다.
Volume 은 이미지보다 컨테이너에서 매핑시키는게 좋다.
흔히 Dockerfile 로 이미지를 만드는데, 도커 이미지는 명령어 하나당 하나의 Layer 를 갖고 캐싱을한다.
이말인 즉슨 Dockerfile 내에 VOLUME 을 명시한 경우 해당 VOLUME 데이터가 조금이라도 변경되면 캐싱되지 못하고 rebuild 를 해야한다는 뜻이다.
아래와 같은 경우가 그 예다.
/app/feedback 에 자주 데이터 변경이 있는 경우 매번 해당 layer rebuild 가 발생하여 이미지 생성에 시간소요가 걸릴 수 밖에 없다.
따라서 VOLUME 을 Dockerfile 보다는
아래와 같이 컨테이너 실행시 직접 지정해주는 방식이 낫다.
$ docker run -it --rm -d --name "<container-name>" -v <volume-name>:<container-path>
공통으로 쓰이는 파일 모듈은 별도 data container 를 만들자.
-v 옵션으로 여러 볼륨을 매핑시킬 수도 있겠지만
--volumes-from 이란 효자 옵션이 있다.
여러 컨테이너에서 공통으로 접근할 볼륨이 많아질 땐 이 옵션을 사용하여 data-container 에 지정된 모든 볼륨을 mount 하는 방식이다.
구성 절차는 다음과 같다.
- 동일한 이미지로부터 data-container 를 생성하며 모든 필요한 -v 옵션 명시
- 앱 컨테이너를 띄울 때 --volumes-from <data-container-name> 입력
- 띄워진 app container 에 data-container 에서 사용중인 모든 볼륨이 마운트됨.
근데 진짜 data-container 를 언제쓰지?
특정한 db 버전을 띄워야할 때 등.
매우 제한적이다.
이러나 저러나, 사실 왠만하면 그냥 -v named volume 을 쓰는게 낫다.
Q & A
Q1. default path /var/lib/docker/volume 은 대체 어디임?
A1. Linux 기준 HOST OS default 경로임. `/var/lib/docker/volumes` 여기에 `mongo-storage` 라는 디렉토리를 자동 생성해준 케이스임.
Q2. 근데 /var/lib/docker 는 linux 기준인데.. windows, mac 은 어케되는거지?
A2. 내부적으로 linux 를 사용한다고함 (Virtual machine 으로)
Q3. Bind mount 는 절대경로 할 수밖에 없을텐데? 상대경로 되나?
A3. 안됨, 대신 naming volume 을 생성하면 알아서 `/var/lib/docker/volume` 내에 해당 이름으로 볼륨 디렉토리가 생성되기 때문임.
Bind mount 는 host os -> container 방향으로 live data 를 이미지 rebuild 없이 전달하기 위해서 사용하는 것임.
Q4. Dockerfile 에서 명시한 VOLUME [""] 는 container 킬 때 쓰는 명령어인 -v 방식이랑 무슨 차이가 있지?
A4. Dockerfile 내에서는 익명 volume 밖에 못만든다.
-v 옵션을 통해서 named volume 을 만들 수 있다. (Recommended)
Q5. anonymous volume 은 쓸 이유가 없지않나?
A5. 있긴함. container internal path 에 우선권을 갖게 해야만 할때 (ex. node_modules)
나머지 경우는 모두 host os 우선권을 가지게됨.
Q5. <volume-name>:<container-path> 로 볼륨 생성시 container path 가 존재하지 않으면 directory 자동생성하나?
A5. Yes, 자동 생성해줌.
Q6. volume 을 여러개 매핑 시킬 수 있는데, 충돌나지 않는가?
A6. 여러 path 가 있는 경우 긴 path 가 우선권을 갖고 매핑된다.
🔗 Reference
'기타 > Docker' 카테고리의 다른 글
[Docker] Host to container network (0) | 2023.04.19 |
---|---|
[Docker] volume with CLI (0) | 2022.05.19 |
[Docker] Docker compose (0) | 2021.12.20 |
Docker 개념 (0) | 2021.12.18 |
[Docker] 주요 명령어 (0) | 2020.11.30 |