컨테이너의 데이터를 DB로 영구 저장하기
컨테이너는 각각 독립된 파일 시스템을 가지며, 이는 이미지의 레이어들을 기반으로 구성됩니다.
컨테이너가 실행될 때마다 그 이미지의 레이어를 사용하여 자신만의 공간(스크래치 공간)을 생성합니다. 이 공간에서 파일을 만들거나 변경하거나 삭제할 수 있습니다.
한 컨테이너에서 파일을 추가하거나 수정하는 경우, 같은 이미지를 사용하더라도 다른 컨테이너에는 영향을 주지 않습니다.
이는 각 컨테이너가 완전히 독립적으로 동작한 다는 것을 잘 보여주는 하나의 예로 볼 수 있습니다.
alpine 컨테이너를 실행하고 컨테이너에서 파일 하나를 생성합니다.
새로운 컨테이너를 실행하면서 greeting.txt 파일 내용을 출력해봅시다.
docker run alpine cat greeting.txt
greeting.txt 파일이 없다는 에러 메시지가 출력됩니다.
이처럼 같은 이미지를 사용해서 컨테이너를 띄워도, 각 컨테이너는 독립적인 환경에서 동작하기 때문에 한 컨테이너의 변경사항은 다른 컨테이너에 영향을 미치지 않습니다.
컨테이너는 파일을 생성, 삭제, 변경할 수 있습니다. 하지만 컨테이너를 제거하면 이 파일들이 모두 사라집니다.
하지만 볼륨을 사용하면 컨테이너가 삭제되어도 파일들을 보존할 수 있습니다.
볼륨은 컨테이너와 호스트 간에 파일을 공유할 수 있는 방법을 제공합니다. 이를 통해 컨테이너를 삭제해도 데이터를 유지할 수 있습니다.
컨테이너 내의 디렉토리를 호스트의 파일시스템과 연결하면 컨테이너 내에서 이루어진 변경사항(저장 및 수정한 데이터)을 호스트에도 저장할 수 있습니다.
이렇게 해서 컨테이너를 재실행하거나, 다른 컨테이너를 실행할 때에도 호스트에 저장한 데이터를 계속 사용할 수 있습니다.
이전 todo 앱에서 다시 시작해볼 겁니다.
docker volume create todo-db
docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=//etc/todos --name getting-started getting-started
저는 Windows 환경에서 git bash를 사용중이어서 //를 사용했습니다.(자세한 정보)
컨테이너에서 실행한 앱에 접속해서 todo 리스트를 추가합니다.
docker stop getting-started
docker rm getting-started
docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=//etc/todos --name getting-started getting-started
다시 컨테이너를 실행해도 투두 리스트가 유지되어 있습니다.
체크를 진행하고 과정을 반복해봅시다.
마찬가지로 유지되어 있습니다.
$ docker volume inspect todo-db
[
{
"CreatedAt": "2024-05-10T03:16:02Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
"Name": "todo-db",
"Options": null,
"Scope": "local"
}
]
docker volume inspect <volume-name>
을 명령을 통해 volume을 사용할 때 Docker가 어디에 데이터를 저장하고 있는지 확인할 수 있습니다.
바인드 마운트는 호스트의 파일 시스템에 있는 디렉토리를 컨테이너에 연결하여, 호스트에서 파일을 변경할 때 컨테이너에서도 즉시 변경사항을 볼 수 있게 해줍니다.
이는 개발 중에 매우 유용하며, 소스 코드를 컨테이너에 마운트하여 실시간으로 코드 수정사항을 반영할 수 있습니다.
이를 통해 파일을 저장하는 즉시 컨테이너에서 코드 변경 사항을 볼 수 있습니다. 이는 파일 시스템의 변경을 감시하고 그에 반응하는 컨테이너 내 프로세스를 실행할 수 있다는 의미입니다.
docker run -it --mount type=bind,src="/$(pwd)",target=/src ubuntu bash
저는 windows에서 git bash를 사용중이어서 src에 /를 붙였습니다.
실행 후 확인해보면 현재 프로젝트 파일들이 컨테이너의 /src 경로에 마운트 되어있는 것을 확인할 수 있습니다.
컨테이너에서 myfile.txt
를 만들고 컨테이너와 호스트에서 각각 확인해봅시다.
이번엔 호스트에서 myfile.txt
를 삭제하고 컨테이너와 호스트에서 각각 확인해봅시다.
이 과정을 통해 호스트와 컨테이너 간에 파일이 어떻게 공유되고 변경사항이 양쪽에 즉시 반영되는지를 확인할 수 있습니다. 이제 바인드 마운트를 사용하여 소프트웨어 개발에 활용할 수 있습니다.
Docker CLI 또는 Docker Desktop을 사용해서 바인드 마운트와 함께 컨테이너를 실행할 수 있습니다.
여기서는 CLI로 해보겠습니다.
docker run -dp 127.0.0.1:3000:3000 \
-w //app --mount type=bind,src="/$(pwd)",target=/app \
node:18-alpine \
sh -c "yarn install && yarn run dev"
마찬가지로 git bash에서 사용하는 명령어입니다.
-w
플래그는 working directory를 설정하는 플래그입니다.--mount type=bind,src="/$(pwd)",target=/app
명령을 사용해서 호소트의 현재 경로를 컨테이너의 /app 디렉토리에 바인드 마운트를 진행합니다.node:18-alpine
이미지를 앱을 실행할 기본 이미지로 사용합니다.sh -c "yarn install && yarn run dev"
alpine 버전은 bash가 없어서 sh로 쉘을 사용할 수 있습니다.yarn run dev
로 앱을 실행합니다.docker logs -f 9caefa43ae1216773d0171daef61fb987562a986e9c4ec27e61c779fd85f177e
-f
플래그를 사용하면 컨테이너에서 새로운 로그가 생성될 때마다 즉시 터미널에 표시되어, 컨테이너의 현재 상태를 지속적으로 모니터링 할 수 있습니다.
yarn install v1.22.19
[1/4] Resolving packages...
warning sqlite3 > node-gyp > make-fetch-happen > cacache > @npmcli/move-file@1.1.2: This functionality has been moved to @npmcli/fs
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 53.00s.
yarn run v1.22.19
$ nodemon -L src/index.js
[nodemon] 2.0.22
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node src/index.js`
Using sqlite database at /etc/todos/todo.db
Listening on port 3000
호스트 머신에서 소스 코드를 변경하고 변경 사항을 적용하면, 바인트 마운트 덕분에 컨테이너에서도 파일 변경을 감지합니다.
이 프로젝트에서는 nodemon으로 서버를 실행중이므로 변경사항을 감지할 때 nodemon이 서버를 자동으로 재시작합니다.
변경사항을 적용하니 컨테이너에서 실행중인 앱이 재시작 되었습니다.
변경사항을 웹 브라우저에서 확인해봅시다.
변경사항이 잘 적용되었습니다.
https://docs.docker.com/storage/
https://docs.docker.com/reference/cli/docker/