Docker/Kubernetes 정리 4

윤석주·2023년 2월 24일
1

docker

목록 보기
5/10

Understanding Data Categories / Different Kinds of Data

데이터는 서로 다른 종류가 존재합니다. 기본적으로 다음과 같은 데이터들이 존재하죠.

  • Application (Code + Environment)
    • Written & provided by developer
    • Added to image and container in build phase
    • "Fixed": Can't changed once image is built
    • Read-only, hence stored in Images
  • Temporary App Data (entered user input)
    • Fetched / Produced in running container
    • Stored in memory or temporary files
    • Dynamic and changing, but cleared regularly
    • Read + write, temporary, hence stored in Containers
  • Permanent App Data (user accounts, ...)
    • Fetched / Produced in running container
    • Stored in files or a database
    • Must not be lost if container stops / restarts
    • Read + write, permanent, stored with Containers & Volumes

쉽게 말하면 코드와 같이 바뀌지 않고 fix된 데이터와(이미지에 포함), 어플리케이션의 변수나 사용자 input 등 인메모리에 존재하는 r/w이 가능한 데이터(컨테이너에 포함), 그리고 사용자 계정 정보와 같이 컨테이너에서 생성되었지만 영구적으로 유지되어야 하는 데이터입니다.

여기서 보통 문제가 되는 부분은 세 번째 부분입니다. 컨테이너에서 생성되었지만 영구적으로 유지해야하는 데이터죠. 우리는 이 부분에서 "Volume"이라는 개념의 도움을 받을 수 있습니다.

Understanding the Problem

세 번째 데이터에서 어떤 문제가 발생하는지 확인해봅시다. 컨테이너에서 생성되는 세 번째 종류의 데이터들은 컨테이너가 종료되면 사라집니다. 컨테이너가 문제가 생기거나, 코드가 업데이트되어 재실행/재생성 된다면 세 번째 데이터는 유지되지 않고 사라지죠.

물론 데이터베이스를 사용하면 유저정보등은 다시 가져올 수 있습니다. 하지만 만약 컨테이너 인스턴스가 여러개 떠있고, 각각이 데이터베이스를 전부 연결하면 어떨까요? 불편한 부분이 생길겁니다. 같은 데이터를 공유하기 위해 여러개의 인스턴스가 데이터베이스에 연결하고, 성능적으로도 좋지 않을 수 있죠.

또 데이터베이스에 저장하기엔 애매한 데이터도 존재할 수 있습니다. 환경변수와 같은 정보는 모든 컨테이너 인스턴스가 공유해야 하지만 데이터베이스에 저장하기엔 애매할 수 있죠.

즉, 여러개의 컨테이너 인스턴스가 공유하면서, 컨테이너의 라이프사이클과 상관없이 데이터를 유지할 수 있는 방법이 필요합니다.

Introducing Volumes

도커는 Volume이라는 built-in 기능을 가지고 있습니다. 이 기능은 위에서 살펴본 문제를 해결하도록 도와줍니다.

Volume은 쉽게 말하면 host machine의 folder입니다. 컨테이너 내부의 폴더도 아니고 이미지에도 없으며, 해당 컨테이너를 돌리고 있는 호스트 머신의 하드드라이버에 있죠.

호스트 머신에 존재하는 볼륨 폴더는 컨테이너의 특정 폴더와 매핑될 수 있습니다. 컨테이너 안의 폴더와 컨테이너 밖의 호스트머신 폴더를 연결할 수 있는 개념이죠.

컨테이너 내부에서 볼륨에 read/write가 가능하죠. 이러한 개념을 통해 컨테이너가 셧다운되어도, 볼륨 네부의 데이터는 살아남고 따라서 데이터를 계속 유지할 수 있습니다.

Unsuccessful Try

볼륨의 개념은 알았습니다. 이제 저 개념을 어떻게 적용할 수 있는지 살펴봅시다. 먼저 옳지 않은 방법부터 살펴보겠습니다.

간단한 방법은 Dockerfile 내부에 다음과 같이 볼륨을 정의하는 방법입니다.
VOLUME [${container_path}]
이후 다시 이미지를 빌드하면 볼륨이 적용됩니다. 해당 이미지로 컨테이너를 생성하면 컨테이너도 잘 뜨는걸 확인할 수 있습니다.

여기서 permenent data를 fs.rename() 등의 함수를 사용하여 볼륨에 저장하면, 오류가 발생하는 것을 볼 수 있습니다(무한 스피닝이 뜰겁니다..).
docker logs ${container} 명령어로 로그를 살펴보면 다음과 같은 오류를 확인할 수 있습니다.
UnhandlePromiseRejectionWarning: Error: EXDEV: cross-device link not permitted, rename ...
해당 함수는 cross device에 대한 저장을 지원하지 않기에 발생하는 문제로 fs.copyFile()과 fs.unlink() 함수등으로 수정하여 실행이 필요합니다.

이제 컨테이너를 중지하고 재생성해도 볼륨에 데이터가 존재해야 합니다. 하지만 실제로 컨테이너를 중지하고 재생성해서 확인해보면 데이터가 유지되지 않는 것을 확인할 수 있습니다.

Named Volumes To The Rescue

Docker에서 제공하는 External Data Storages는 크게 두 타입이 존재합니다.

  • Volumes (Managed by Docker)
    • Anonymous Volumes
    • Named Volumes
    • Docker sets up a folder / path on your host machine, exact location is unknown to you(dev). Managed via docker volume commands.
  • Bind Mounts (Managed by you)

첫 번째는 도커에서 관리하는 Volume으로 호스트머신의 특정 path에 도커가 생성하고 관리합니다. 개발자는 해당 패스를 모르죠.

또한 named, anonymous(unnamed) 볼륨으로 나뉩니다. 이중 후자는 이름이 없는 볼륨으로, 컨테이너가 없어지면 사라지는 볼륨입니다. 우리가 방금 Dockerfile에 적용한 볼륨이 바로 이 볼륨을 생성하는 것이죠. 따라서 Anonymous Volume이 생성되었고 컨테이너가 사라지면 사라지기에 데이터가 유지되지 않은 겁니다. (와아~ )

하지만 Named Volume는 컨테이너가 셧다운되어도 살아남아있습니다. 따라서 우리가 원하는 문제를 해결하기 위해선 Named Volume을 사용해야 하는 것이죠.

그럼 이건 어떻게 적용할 수 있을까요? 다음과 같은 방법을 통해 추가할 수 있습니다(Dockerfile의 볼륨생성하는 코드는 없애줍시다).
docker run -d -p 3000:80 --rm --name yourApp -v ${volume_name}:${container_path} yourImage

Dockerfile에서와 달리 컨테이너를 실행할 때 -v 옵션을 통해 Named Volume을 적용할 수 있습니다.
-v ${volume_name}:${container_path} 형식으로 작성합니다(anonymous volume 생성도 가능합니다).

이제 컨테이너를 실행하고 데이터를 볼륨에 저장하는 기능을 실행해봅시다. 그리고 컨테이너를 셧다운하고, 재생성해봅시다.

그리고, 해당 데이터가 존재하는지 확인해봅시다. 이제 컨테이너 인스턴스와 상관없이 데이터가 볼륨에 저장되어 나타나는 것을 확인할 수 있습니다.

Anonymous volumes가 컨테이너를 셧다운하면 삭제되는 것을 위에서 확인했습니다. 그러나 이는 --rm 옵션을 통해 컨테이너를 생성하는 경우에만 해당합니다. 만약 이 옵션이 없다면 컨테이너를 종료하고 docker rm 명령어를 통해 컨테이너를 직접 삭제하더라도 Anonymous Volume은 계속 존재하게 됩니다.
하지만 docker run 명령어로 새로운 컨테이너를 생성하면 그 컨테이너는 다시 새로운 Anonymous Volume을 생성하여 사용합니다. 즉, 삭제되지 않더라도 해당 컨테이너에서는 사용되지 않는 것이죠.
이런 과정이 반복되면 사용하지 않는 anonymous volume들이 많이 생기게 되어 성능에 악영향을 줄 수 있습니다. 이럴때는 docker volume rm VOL_NAME 이나 docker volume prune 명령어를 통해 사용하지 않는 volume을 지워주는 편이 좋습니다(anonymous volume인 경우 docker가 임의로 volume명을 지어줍니다. 해당 데이터를 이용하면 됩니다).

출처

profile
웹 개발을 공부하고 있는 윤석주입니다.

0개의 댓글