Devlog

도커 볼륨으로 데이터 관리하기 본문

데브옵스

도커 볼륨으로 데이터 관리하기

ehdrb92 2023. 1. 8. 23:02

도커 컨테이너의 내부 데이터는 기본적으로 컨테이너가 삭제될 때 모두 삭제된다. 그리고 각 컨테이너는 개별적으로 활동하는 특징을 가진다.

 

만약 서버 부하를 막기 위해 특정 데이터를 처리하여 저장하는 일종의 같은 작업을 하는 컨테이너를  스케일 업하여 여러 개를 두었다고 하면, 첫 번째로 어떤 데이터를 찾을 때 어떤 컨테이너에 저장되었는지 찾기가 힘들 수 있으며 작업 컨테이너가 필요 없어져 삭제를 할 경우 내부 데이터가 사라져 버릴 수 있다.

 

이럴 때 도커 볼륨(volume)을 이용하여 해당 데이터를 보존하고, 각각의 컨테이너가 함께 사용할 수 있다.

 

볼륨은 크게 익명 볼륨, 명명된 볼륨, 바인드 마운트로 나뉜다. 각각의 특징을 이용하고 활용하는 방법을 알아보자.

볼륨이란?

볼륨의 종류들에 이해하기 전에 볼륨이 무엇인지 간단하게 이해하고 넘어가자.

 

볼륨은 컨테이너를 만들 때 볼륨을 지정하면 컨테이너 내부가 아닌 로컬 호스트의 도커가 관리하는 특정 경로에 볼륨이 만들어진다. 만들어진 볼륨은 컨테이너와 연결되어 컨테이너가 해당 볼륨 경로의 데이터를 읽어와 사용할 수 있는 것이다.

익명 볼륨

익명 볼륨은 말 그대로 이름이 없는 볼륨이다. 볼륨을 만들 때 이름을 지정해 줄 수 있고, 안 할 수도 있는데 안 하면 기본적으로 어떤 알 수 없는 긴 문자열의 이름이 배정되면서 익명 볼륨이 만들어진다. 일반적으로 Dockerfile에 VOLUME 명령어를 통해 만들거나, 명령어를 통해 컨테이너를 생성할 때 이름을 지정하지 않고 만들 수 있다.

# Dockerfile에서 익명 볼륨만들기
VOLUME ["/app/data"]

# 명령어로 익명 볼륨만들기
-v "/app/data"

 

익명 볼륨은 특정 하나의 컨테이너와 연결이 된다. 그리고 해당 컨테이너가 존재하는 동안에는 함께 존재하지만 컨테이너가 삭제되는 순간 볼륨의 데이터도 함께 사라져 버린다. 그리고 다른 컨테이너와 데이터를 공유할 수도 없다.

 

그렇다면 볼륨이 있는 의미가 없게 된다는 것인데 왜 사용하는 것일까? 이것은 다음에 나오는 다른 종류의 볼륨을 설명하면서 함께 설명하겠다.

명명된 볼륨

명명된 볼륨은 볼륨을 만들어 줄 때 이름을 명시해준 볼륨을 말한다. 이는 Dockerfile에서는 만들 수 없고, 컨테이너를 만들 때 명령어를 통해 만들 수 있다.

# 명령어로 명명된 볼륨만들기
-v "/app/data:data"

 

명명된 볼륨의 경우에는 연결된 컨테이너가 삭제되더라도 볼륨을 직접적으로 삭제하지 않는 한 데이터가 영구적으로 지속된다. 그리고 하나의 컨테이너에만 사용할 수 있는 것이 아니라 여러 개의 컨테이너가 하나의 명명된 볼륨에 연결하여 데이터를 공유할 수 있다.

 

컨테이너를 명명된 볼륨에 연결시켜주려면 위의 명령어를 컨테이너가 만들어 질 때 함께 명시해주면 된다. 이전에도 말했듯이 볼륨은 도커가 관리하는 로컬 호스트의 특정 경로에 만들어지게 된다. 위의 명령어로 볼륨을 만들면 관리되는 경로에 "app/data/"경로가 만들어지게 되고, 다른 컨테이너에서 위 경로의 볼륨을 명시하면 해당 경로의 데이터를 공유하게 된다.

도커 데스크탑에서 본 볼륨 목록

위 사진은 만들어진 익명 볼륨과 명명된 볼륨의 목록을 도커 데스크탑에서 본모습이다. 익명 볼륨은 알 수 없는 긴 문자열로 되어 있고, feedback이라는 명명된 볼륨도 존재하는 것을 볼 수 있다.

바인드 마운트

마지막으로 바인드 마운트라는 것이 존재한다. 바인드 마운트는 익명 볼륨, 명명된 볼륨과 큰 차이점이 하나 존재한다. 바인드 마운트는 도커에서 관리하는 것이 아닌 로컬 호스트의 유저가 직접 관리하는 것이다. 이 점이 어떤 큰 차이를 불러오는지 예시를 통해 보도록 하자.

 

어떤 특정 서버를 컨테이너를 띄워 사용할 때 내부 코드가 변경되는 상황이 생길 수 있다. 그렇다면 유저의 시스템상에서 코드를 수정한 뒤 이미지를 다시 빌드하고, 컨테이너를 생성하여 가동해야 한다. 이렇게 변경사항이 생길 때마다 이미지를 리빌딩하는 것은 매우 번거로운 일이다. 이때 활용될 수 있는 것이 바인드 마운트이다.

 

우선 바인드마운트를 생성하는 명령어를 보도록하자. 참고로 바인드 마운트 또한 이름을 명시하기에 Dockerfile에서는 생성이 불가능하다.

# 바인드 마운트 생성 명령어 예시
-v /Users/user/project/app:app

바인드 마운트는 도커가 아닌 유저가 직접 관리해야한다. 그렇기에 경로를 설정할 때 로컬 시스템 상에 경로를 직접 지정해주어야 한다. 상대경로를 사용하면 안 되며 절대경로를 통해 루트 경로부터 프로젝트의 최상위 경로까지 타고 올라간 경로로 지정한다.

 

이렇게 바인드 마운트를 만들게 되면 도커 내부 컨테이너의 WORKDIR은 바인드 마운트 경로로 덮어 쓰이게 덮어 쓰이게 된다. 그렇게 해서 로컬 시스템 상에서 데이터들을 변경할 때마다 컨테이너 내부 데이터가 덮어 쓰이게 되면서 직접적으로 변경사항이 반영되게 되는 원리이다.

 

바인드 마운트를 사용하기 전에 한 가지 체크해야 할 사항이 있다. 도커가 로컬 시스템상에 해당 경로에 액세스 권한이 있는지를 확인해야 한다. 도커의 환경설정으로 들어간 후 "Resources > Filesharing"에서 해당 경로가 설정되어 있는지 확인하자.

 

바인드 마운트는 명명된 볼륨과 같이 직접적으로 제거하지 않는 한 영구 보존되며, 여러 컨테이너가 함께 데이터를 공유할 수 있다. 단, 볼륨을 삭제하려면 로컬 시스템 상에서 유저가 직접 해당 경로를 삭제해야 한다. 도커가 관리하지 않기 때문에 도커 컨테이너나 도커 명령어를 통해 목록을 확인해도 바인드 마운트는 표기되지 않는다.

바인드 마운트와 익명 볼륨

바인드 마운트는 어떤 면에서 치명적인 단점을 가지고 있다. 만약 nodejs로 만들어진 서버를 컨테이너로 구동한다고 하자. 이 때도 소스 코드 변경 등의 상황을 고려하여 컨테이너에 바인드 마운트를 이용하여 로컬 시스템 데이터가 컨테이너 내부 데이터를 덮어쓰도록 하였다. 그런데 데이터가 덮어 쓰인다는 점 때문에 문제가 발생한다. 먼저 해당 컨테이너 이미지의 Dockerfile을 보자.

FROM node

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

CMD ["node", "app.js"]

nodejs의 경우 "npm install"명령어를 통해 의존 패키지를 설치할 경우 프로젝트 경로에 "./node_modules"경로를 만들어 내고, 의존 패키지 데이터들이 해당 경로에 보존된다.

 

그런데 로컬 시스템 상에는 의존 패키지를 설치하지 않기에 해당 경로가 존재하지 않는다. 그리고 바인드 마운트에 의해 컨테이너 데이터가 덮어써지게 되면서 해당 경로가 사라져 버린다. 그러면 컨테이너에서 패키지에 의존하는 코드들이 사용할 수 없어지게 되면서 에러가 발생하게 된다.

 

이 문제를 해결하기 위해 사용될 수 있는 게 익명 볼륨이다. 바인드 마운트를 만들 때 보통 프로젝트의 최상위 경로로 지정하게 된다. 이때 아래처럼 하위 경로의 "./node_modules"를 익명 볼륨으로서 추가로 만들어 주는 것이다.

# 바인드 마운트와 익명 볼륨 함께 사용하기
-v $(pwd):/app -v "/app/node_modules"

# $(pwd)는 루트부터 현재 경로까지의 절대 경로를 불러온다.
# 윈도우는 -v "%cd%":/app

앞 단의 "-v $(pwd):/app"는 바인드 마운트이고, 뒷 단의 "-v '/app/node_modules'"는 익명 볼륨이다. 이렇게 볼륨을 지정해주면 컨테이너의 데이터가 덮어 쓰이지만 "/app/node_modules"경로만큼은 덮어 쓰여지지 않게 된다.

 

이유는 도커는 내부적으로 볼륨을 지정해 줄 때 더 구체적으로 하위 경로까지 명시된 볼륨을 우선적으로 반영한다는 점 때문이다. 그렇기에 컨테이너 내부에서 프로젝트 최상단 경로의 모든 부분이 바인드 마운트로서 연결되어 있지만 익명 볼륨으로서 하위 경로로 명시해준 볼륨이 존재한다면 해당 경로 아래의 데이터는 익명 볼륨의 특징을 따르게 된다.

 

그래서 "/app/node_modules"경로는 덮어 쓰이지 않으며 컨테이너가 삭제되면 볼륨도 함께 사라지게 된다. 여기서는 익명 함수로서 예시를 들었지만 명명된 볼륨 역시 구체적인 하위 경로를 명시하여 만들어 주면 해당 경로는 해당 볼륨의 특징을 따르게 된다.

'데브옵스' 카테고리의 다른 글

kubernetes 입문하기  (0) 2023.02.23
Docker-Compose 사용하기  (0) 2023.01.13
컨테이너 내부 서버가 통신 하는 방법  (1) 2023.01.09
Dockerfile 만들기  (0) 2023.01.08