1. Container 속 데이터 유실 2. Docker volume을 사용하는 Container 3. File system mount를 사용하는 Container 4. File system mount 한계점 5. Container의 File system이 만들어지는 원리 6. 연습 문제
Container의 file system은 Docker가 여러 출처로부터 합쳐서 만들어 Container에 전달한 단일 디스크(linux는 /dev/sda1, window는 C:/), 즉 가상 file system이다. (Image Layer + Writeable Layer)
Image Layer
모든 Container가 공유
읽기 전용
Image를 삭제할 때까지 Local PC의 Image Layer에 존재
Writeable Layer
Container 개별 관리
Container와 같은 생명 주기 (종료 X, 삭제 시 소멸)
기록 중 복사(copy-on-write) 방식으로 읽기 전용인 Image Layer의 file을 수정할 수 있다.
디스크 공간 절약 : CoW 전략으로 여러 Container가 동일한 Image Layer 공유 가능하며, 중복 데이터를 줄이고 디스크 공간 절약
빠른 Container 생성 : 새로운 Conatiner 생성 시, 기존 Image Layer를 복사하지 않아도 된다.
변경 사항 격리 : 각 Container는 자체 writeable Layer를 가지므로, 변경사항이 외부에 영향을 주지 않는다.
2️⃣ 컨테이너가 생성한 파일을 수정하고 재시작해보기
docker container run --name f1 diamol/ch06-file-display
echo "http://eltonstoneman.com" > url.txt
docker container cp url.txt f1:/input.txt
docker container start --attach f1
기존 출력값이 없어지고 덮어씌운 정보가 출력된다.
3️⃣ 새로운 컨테이너에서 수정된 데이터 삭제 확인
docker container run --name f2 diamol/ch06-file-display
docker container rm -f f1
docker container cp f1:/input.txt .
위 실행 결과를 토대로 2가지 사실을 알 수 있다.
동일한 Image로 Container를 실행하면 원래 파일 내용이 출력된다. 즉, f1 Container와 f2 Container는 독립적이다.
처음 만든 Container를 삭제하면 해당 Container의 기록 가능 Layer도 수정된 데이터와 함께 영구 삭제된다.
📌 Volume의 필요성
실무에서는 new Image를 build하고 old Conatiner를 삭제한 다음, new Image에서 실행한 Container로 대체하는 방법으로 애플리케이션을 업데이트한다.
이 과정에서 기존 Container에서 수정된 데이터는 모두 손실된다.
Docker는 이런 상황을 위해 도커 볼륨(Docker volume)과 마운트(Mount)를 추가했다.
이 둘은 Container와 별개의 Life cycle을 갖는다.
2. Docker volume을 사용하는 Container
📌 Docker volume
Docker에서 storage를 다루는 단위 (Container를 위한 USB)
volume은 Container와 독립적이며, 별도의 생애 주기를 갖지만 연결할 수 있다.
즉, Stateless Container에서 Statefull Container를 관리할 수 있다.
사용 방법
수동으로 직접 volume을 생성해 Container에 연결
Dockerfile에서 VOLUME Instruction 사용
VOLUME <target-directory>
1️⃣ volume이 사용된 Mult-stage build Dockerfile
FROM diamol/dotnet-sdk AS builder
WORKDIR /src
COPY src/ToDoList.csproj .
RUN dotnet restore
COPY src/ .
RUN dotnet publish -c Release -o /out ToDoList.csproj
# app image
FROM diamol/dotnet-aspnet
WORKDIR /app
ENTRYPOINT ["dotnet", "ToDoList.dll"]
# set in the base image - `/data` for Linux, `C:\data` for Windows
VOLUME $SQLITE_DATA_DIRECTORY
# set in the base image - `root` for Linux, `ContainerAdministrator` for Windows
USER $SQLITE_USER
COPY --from=builder /out/ .
docker container run --name todo1 -d -p 8010:80 diamol/ch06-todo-list
docker container inspect --format '{{.Mounts}}' todo1
docker volume ls
할 일을 등록하면 volume에 저장된다.
두 번째 명령어 결과로 volume id, 호스트 컴퓨터상 경로, 연결된 container file system 경로 등을 확인할 수 있다.
volumn은 Docker에서 Image, Container와 동급인 요소다.
2️⃣ 같은 Image로 생성한 Container에서 내용 확인하기
docker container run --name todo2 -d diamol/ch06-todo-list
docker container exec todo2 ls /data
docker container run -d --name t3 --volumes-from todo1 diamol/ch06-todo-list
docker container exec t3 ls /data
todo2는 별도의 volume을 생성하기 때문에 /data 디렉토리가 비어있다.
t3의 경우엔 todo1과 데이터베이스를 공유(--volumes-from)하므로 데이터가 들어가 있다.
하지만 file에 대해 여러 Container의 동시 접근을 허용하는 것은 위험하다.
💡 volume은 Container 간 파일 공유보다는 State 보존을 위한 목적으로 사용하라.
이전 to-do 애플리케이션은 로그 출력을 최소한으로 줄이도록 설정되어 있었지만, /app/config 경로가 존재할 경우 해당 디렉터리에서 추가 설정 파일을 로드한다.
Host PC에 위치한 설정 파일에는 좀 더 상세한 내용까지 로그를 출력하도록 설정되어 있고, Container가 해당 설정 파일을 읽어 로그 설정을 수정한다.
✒️ Docker volume과 mount 차이점 정리
(좌) volume / (우) bind-mount
Docker Volume은 Docker에 의해 만들어지고 관리되는 객체다. 주로 Host의 /var/lib/docker/volumes/ 에 저장되며, Container에 mount하지 않아도 미리 생성이 가능하다. 보통 DB같은 데이터들을 저장할 때 많이 사용한다.
Docker volume을 사용하지 않고 Host 디렉토리에 직접 access한다. Container가 지워지더라도 Host에 파일이 남아 데이터를 보존할 수 있다. 보안에 취약하다는 단점이 있다.
docker container run --mount type=bind,source=$source,target=$target (Container 이름)
4. File system mount 한계점
📌 Scenario 1
🤔 Container의 mount 대상 디렉토리가 이미 존재하고 Image Layer에 해당 디렉토리의 file이 포함돼 있는 경우
이미 존재하는 대상 디렉토리에 mount하면, 기존 디렉토리를 완전히 대체한다.
따라서, Image에 포함되어 있던 원래 file은 사용할 수 없다.
$source="$(pwd)/new".ToLower(); $target="/init"
docker container run diamol/ch06-bind-mount
docker container run --mount type=bind,source=$source,target=$target,readonly diamol/ch06-bind-mount
Image Layer에서 받은 file이 mount file로 대체되어 결과가 바뀌었다.
📌 Scenario 2
🤔 Host PC의 file 하나를 Container에 이미 존재하는 디렉터리로 mount하는 경우
디렉터리 file이 합쳐져 Image에서 온 file과 Host에서 mount된 file이 모두 나타난다.
단, window Container에서는 해당 기능을 제공하지 않아 동작이 달라진다.
Linux에서는 file를 mount 할 수 있지만, Linux는 디렉토리만을 mount 할 수 있다.
Container file system은 Window Container와 Linux Container의 동작이 일치하지 않는 몇 안 되는 영역 중 하나다.
이번 챕터에서 경로 문자열을 OS 별로 다르게 환경 변수로 정의하는 것도 같은 이유다.
📌 Scenario 3. 매우 드물고 복잡한 경우
🤔 분산 file system을 Container에 bind-mount 하는 경우
매우 드물지만, 한 번 발생하면 회피할 도리가 없는 케이스다.
분산 file system 메커니즘은 Local PC OS의 file system과 다른 경우가 많아, 지원을 하지 않는 동작이 있을 수 있다.
bind-mount의 original storage가 Container의 모든 file system을 제공하지 않을 수 있다.
이 사실은 애플리케이션을 실행해 보기 전까지는 미리 파악할 수도 없다.
Container에 분산 storage를 mount 할 거라면, 성능 측면에서도 Local storage와 큰 차이가 있다는 것을 고려하라.
디스크를 많이 사용하는 Application이 모든 file 입출력을 위해 Network를 거쳐야 한다면 Application이 멈출 수도 있다.
✒️ 분산 파일 시스템(Distributed File System)
Client가 자신의 컴퓨터에 있는 것처럼 Server에 저장된 데이터에 access하고 처리할 수 있는 Client-Server 기반의 Application이다. Client가 Server의 file에 접근하면 Server는 Client에게 file의 copy를 전송하고, 해당 copy는 데이터가 처리되는 동안 Client의 컴퓨터에 cache된 후 Server로 반환된다. Hadoop의 경우 DFS는 하나의 file을 3개의 file로 만들어 3군데 저장한다. (이렇게 하면 어떠한 일이 있더라도 데이터가 유실되지 않는다는 것이 수학적으로 증명되었기 때문이다.)
5. Container의 File system이 만들어지는 원리
📌 유니언 파일 시스템(Union File System)
Docker가 다양한 출처로부터 모아 만든 단일 가상 디스크로 구성된 file system (모든 Container가 갖는다.)
OS마다 다른 방식으로 구현되어 있다.
Docker가 알아서 Host OS에 맞게 최선의 구현을 선택하므로, 상세한 구현은 무시해도 된다.
Container는 Union File System으로 물리적 위치가 서로 다른 file과 directory에 단일 디스크처럼 접근 가능하다.
기록 가능 Layer는 하나밖에 가질 수 없으나, 여러 개의 Image Layer, 하나 이상의 volume mount와 bind mount를 Container에 연결할 수 있다.
📌 Container Storage 구성할 때 고려할 점
writeable Layer
단기 저장에 적합하다.
비용이 비싼 계산, Network를 통해 저장해야 하는 데이터 캐싱 등
Container 삭제 시 유실되도 무관한 데이터 위주
로컬 bind-mount
Host PC와 Container 간 데이터 공유
개발자 PC의 소스 코드를 전달할 때, Local PC에서 Image build 없이도 수정 사항이 Container로 전달된다.
분산 bind-mount
Network Storage와 Container 간에 데이터 공유
가용성은 높지만 리스크가 크다.
읽기 전용으로 설정 파일 전달, 공유 캐시로 활용, 읽기 쓰기 가능으로 데이터를 저장해 동일 Network 상의 모든 Container나 PC와 데이터를 공유하는데 적합하다.
volume mount
Container와 Volume(Docker 객체) 간 데이터 공유
Application이 volume에 데이터를 영구 저장한다.
Container를 교체 하는 방식으로 Application을 업데이트해도, 이전 버전 Container 데이터 유지가 가능하다.
Image Layer
Container 초기 file system 구성
후속 Layer와 이전 Layer 충돌 시, 후속 Layer가 데이터를 덮어 쓴다.
Layer는 읽기 전용이며 여러 Container가 공유한다.
6. 연습 문제
to-do Application을 Container로 실행하되 미리 등록된 할 일이 없는 상태로 Application이 시작되도록 Storage를 설정하라.
docker rm -f $(docker ps -aq)로 모든 컨테이너 삭제
diamol/ch06-lab Image로 Container 실행 후 현재 등록된 할 일 확인 (이때 mount를 추가해 Container 실행)