도커 컨테이너에도 단일 드라이브로 된 파일 시스템이 있다. 이 파일 시스템의 내용은 이미지 속 파일로부터 만들어지는데, 우리도 이 과정을 이미 배운 적이 있다. Dockerfile 스크립트에서 COPY 인스트럭션을 사용해 파일을 이미지로 복사하면, 이 이미지로 실행한 컨테이너에도 같은 경로에 복사된 파일이 있다. 그리고 앞서 도커 이미지는 여러 개의 레이어 형태로 저장된다고 설명했었다. 컨테이너의 디스크 역시 이 이미지 레이어를 순서대로 합쳐 만들 가상 파일 시스템이다.
모든 컨테이너는 독립된 파일 시스템을 갖는다. 같은 이미지에서 실행한 여러 개의 컨테이너는 처음에는 디스크의 내용이 모두 같지만, 그중 한 컨테이너에서 애플리케이션이 파일을 수정해도 다른 컨테이너나 이미지는 영향을 받지 않는다. 디스크에 데이터를 쓰는 컨테이너를 여럿 실행하고 출력을 확인해 보면 이를 확인할 수 있다.
docker container run --name rn1 diamol/ch06-random-number
docker container run --name rn2 diamol/ch06-random-number
컨테이너를 실행하면 텍스트 파일에 무작위 숫자를 쓰는 스크립트가 실행된다. 그리고 컨테이너를 종료하면 Exited 상태가 된다. 이 두 컨테이너는 같은 이미지로부터 실행됐으나 파일 시스템의 내용은 서로 다르다.
docker container cp 명령으로 컨테이너와 로컬 컴퓨터 간에 파일을 복사할 수 있다. 이 명령에 파일의 경로와 이름을 지정하면 무작위 숫자가 쓰인 텍스트 파일을 로컬 컴퓨터로 복사해 파일의 내용을 확인할 수 있다.
docker container cp rn1:/random/number.txt number1.txt
docker container cp rn2:/random/number.txt number2.txt
cat number1.txt
cat number2.txt
두 컨테이너는 모두 같은 경로에 파일을 생성했다. 이 파일을 로컬 컴퓨터로 복사해서 내용을 확인해 보니 두 파일의 내용이 서로 달랐다. 이로써 컨테이너의 파일 시스템이 서로 독립적임을 알 수 있었다. 여기서는 단지 파일 하나의 내용이지만, 같은 데이터베이스 엔진 이미지로 실행된 두 컨테이너가 서로 전혀 다른 데이터를 담을 수도 있는 것이다.
컨테이너의 파일 시스템은 단일 디스크다. 그러나 이 디스크는 도커가 여러 출처로부터 합쳐 만들고 컨테이너에 전달한 가상 파일 시스템이다. 이 출처는 기본적으로 이미지 레이어와 컨테이너의 기록 가능 레이어로 구성되는데, 이미지 레이어는 모든 컨테이너가 공유하지만 기록 가능 레이어는 컨테이너마다 다르다.
모든 컨테이너가 공유하는 이미지 레이어는 읽기 전용이고, 각 컨테이너가 따로 갖는 기록 가능 레이어는 컨테이너와 같은 생애주기를 갖는다. 이미지 레이어는 이미지를 내려받는 순간부터 삭제할 때까지 로컬 컴퓨터의 이미지 레이어에 존재한다. 그러나 컨테이너의 쓰기 가능 레이어는 컨테이너를 실행할 때 생성되면 컨테이너를 삭제할 때 함께 삭제된다.
기록 가능 레이어를 새 파일을 만드는 데만 사용하는 것은 아니다. 기존 이미지 레이어에 있는 파일을 수정할 수도 있다. 도커는 기록 중 복사(copy-to-write)라는 방법을 사용해 읽기 전용 레이어의 파일을 수정할 수 있다. 컨테이너에서 이미지 레이어에 포함된 파일을 수정하려 하면, 먼저 도커가 이 파일을 쓰기 가능 레이어로 복사해 온 다음 쓰기 가능 레이어에서 파일을 수정한다. 컨테이너나 애플리케이션에는 이 과정이 드러나지 않지만, 바로 이 방법이 도커가 스토리지를 매우 효율적으로 사용할 수 있는 비법이다.
유상태 컨테이너를 본격적으로 다루기 전에 한 가지 예제를 더 살펴보자. 이번 실습에는 컨테이너를 실행해 이미지 레이어에 포함된 파일의 내용을 출력헤 볼 것이다. 그리고 파일을 수정한 다음 다시 변경된 파일 내용을 확인한다.
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
이번에는 로컬 컴퓨터에서 컨테이너로 파일을 복사했다. 이 파일이 컨테이너가 내용을 출력하는 파일이다. 이 파일이 컨테이너가 내용을 출력하는 파일이다. 컨테이너를 재시작해 보면 똑같은 스크립트가 실행되며 파일의 내용을 출력하지만 이번에는 파일의 내용이 달라졌다.
컨테이너 속 파일을 수정하면 컨테이너의 동작에 영향을 미친다. 그러나 이미지를 공유하는 다른 컨테이너나 이미지는 영향을 받지 않는다. 수정된 파일은 해당 컨테이너의 기록 가능 레이어에만 존재하기 때문이다. 새로운 컨테이너는 이미지로부터 받은 최초의 내용을 담은 파일 시스템을 가지며, f1 컨테이너가 삭제되면 수정된 파일도 사라진다.
docker container run --name f2 diamol/ch06-file-display
docker container rm -f f1
docker contianer cp f1:/input.txt
출력된 내용은 에러가 뜰 것이다. 새로 실행한 컨테이너는 이미지로부터 받은 원래 내용의 파일을 사용하며, 처음 만든 컨테이너를 삭제하면 그 파일 시스템과 함께 수정된 파일도 사라진다.
컨테이너 파일 시스템은 컨테이너와 같은 생애주기를 갖는다. 컨테이너가 삭제되면 이 컨테이너의 기록 가능 레이어와 여기서 수정된 데이터도 함께 삭제된다. 도커를 사용하면 컨테이너 삭제를 밥 먹듯이 하게 된다. 실무에서는 새 이미지를 빌드하고 오래된 컨테이너를 삭제한 다음 새 이미지에서 실행한 컨테이너로 대체하는 방법으로 애플리케이션을 업데이트한다. 이 과정에서 기존 컨테이너에 있는 수정된 데이터는 모두 손실된다. 새 컨테이너는 이미지에서 받은 파일만 갖고 있기 때문이다.
애플리케이션이 계산 비용이 큰 계산 결과처럼 몇 가지 일시적 데이터를 캐싱만 하는 경우라면 새 컨테이너가 빈 캐시를 갖고 시작하더라도 문제가 없다. 하지만 그렇지 않은 경우라면 아마 재앙이 일어날 것이다. 컨테이너로 데이터베이스를 실행해 사용했는데 데이터베이스 버전을 업데이트했더니 모든 데이터가 사라졌다고 생각해 보자.