Skip to main content

Command Palette

Search for a command to run...

도커 볼륨&바인드 마운트

Updated
5 min read
도커 볼륨&바인드 마운트

볼륨

문제

파일 시스템이 컨테이너 내부에 있기 때문에, 컨테이너를 삭제하면 모든 데이터가 날아간다. 같은 이미지로 빌드해도 이전의 '데이터' 자체는 날아가버린다.

이건 컨테이너의 격리 개념 때문에 그런 것이지만, 만약에 컨테이너에서 생성된 데이터를 유지하고 싶은데, 컨테이너를 삭제해야한다면 곤란할 수 있다.

볼륨이란?

도커의 볼륨이라는 개념이 위 문제를 해결하는 데 도움이 된다. 볼륨은 도커 컨테이너 내부에 있는 것이 아니라, 우리의 호스트 머신(로컬)에 존재하는 폴더이다.

볼륨은 도커가 인식하는 호스트 머신의 폴더로, 도커 컨테이너 내부의 폴더에 매핑된다. 이렇게 들으면 COPY 명령과 비슷해보일 수 있지만 볼륨은 상호작용이 가능하다. 컨테이너에서 데이터를 생성해도 이것이 호스트 머신의 폴더에도 반영될 수 있다는 것이다.

그렇기 때문에 컨테이너를 삭제해도 볼륨은 존재하기 때문에 데이터 보존에 볼륨을 사용할 수 있다.

볼륨 사용하기

rename메서드-> 실패

그래서 만약

VOLUME ["/app/feedback"]

해당 부분을 추가해서 볼륨을 설정한 후 이미지를 다시 빌드하고 컨테이너를 실행하면 정상적으로 작동하는가? 그렇지 않다. 이를 실행하고 인풋을 주면

입력이 정상적으로 처리되지 못하고 무한 버퍼링에 빠져버린다.

로그를 조회하면

docker logs feedback-app                                                                                                       ─╯
(node:1) UnhandledPromiseRejectionWarning: Error: EXDEV: cross-device link not permitted, rename '/app/temp/lgtm.txt' -> '/app/feedback/lgtm.txt'
(Use `node --trace-warnings ...` to show where the warning was created)
(node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:1) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

cross-device link not permitted라고 나온다. 사실 이건 도커의 문제는 아니고 rename 메서드가 어째선지 작동하지 않아서 생기는 문제이다.

if (exists) {
      res.redirect('/exists');
    } else {
      await fs.copyFile(tempFilePath, finalFilePath);
      await fs.unlink(tempFilePath);
      res.redirect('/');

rename메서드를 위와 같이 고쳐주고 다시 빌드 후 실행한다.

그러면 무한 버퍼링이 걸리는 문제는 해결했지만 여전히 컨테이너를 삭제하면 같이 데이터가 날아가버리는 문제가 존재한다. 볼륨을 설정했는데도 말이다.

명명된 볼륨과 익명 볼륨

익명 볼륨

이 문제는 사실 놀랍게도 볼륨의 이름과 관련이 있다.

아까 볼륨을 추가할 때 도커파일에 VOLUME ["/app/feedback"]이라고만 했지, 볼륨에 딱히 이름을 지어주지 않았다. 이런 것을 익명 볼륨(Anonymous Volumes)라고 한다. 이름을 만들지 않으면 자동생성되기 때문에

docker volume ls                                                                                                               ─╯
DRIVER    VOLUME NAME
local     ed26b82f96792adffb87c2913ba73f5bf75d9c853a31c35bac643ecd947d79a4

이런 식의 괴상한 이름이 만들어진다.

그런데 이런 식의 익명 볼륨이 어떤 문제가 있냐 하면, 익명 볼륨은 컨테이너가 존재하는 동안에만 실제로 존재한다. 따라서 위의 문제는 컨테이너를 삭제할 때 볼륨도 같이 삭제되면서 데이터가 남지 않게 되는 것이었다.

그럳데 사실 이것은 틀렸습니다

사실 익명 볼륨은 죄가 없다. 사실 진짜 문제는 컨테이너를 생성할 때 --rm옵션을 추가했었는데 익명 볼륨의 경우에는 컨테이너를 삭제하면 볼륨이 이 옵션 때문에 같이 날아가게 된다. 그래서 사실 --rm옵션을 추가하지 않으면 컨테이너를 날려도 볼륨은 유지된다.

명명된 볼륨

그럼 이제 명명된 볼륨(named volume)을 지어볼 차례다. 명명된 볼륨은 도커파일에 적는 것이 아니라

docker run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback feedback-node:volumes2

위와 같이 -v 볼륨이름:경로옵션을 추가하여 컨테이너를 만들어주면 된다. -v옵션에 이름을 주지 않으면 익명 볼륨을 만들 수 있다.

이런 식으로 명명 볼륨을 만들어주면 컨테이너를 삭제했다가 다시 만들어도 -v 볼륨이름:경로 옵션이 같다면 여전히 이전 컨테이너에서 줬던 데이터가 살아있게 된다.


바인드 마운트

바인드 마운트란?

바인드 마운트는 볼륨과 비슷하게 호스트 머신의 폴더를 이용하는 것은 맞지만, 볼륨과 달리 호스트 머신에서 경로를 지정할 수 있다. 볼륨의 경우 이 작업을 도커가 자동적으로 수행하게 된다.

바인드 마운트 없이 수정사항을 반영하려면 이미지를 다시 빌드하고, 컨테이너를 다시 실행해야하는데, 바인드 마운트를 통해 수정사항을 즉각 반영시킬 수 있다.

바인드 마운트도 명명된 볼륨처럼 터미널 명령으로 수행한다.

-v "호스트 머신 절대 경로":/app

과 같이 -v 옵션을 사용해서 추가해주면 되는데 호스트 머신의 절대경로이기 때문에

-v $(pwd):/app

으로 해도 된다.

따라서 아래 명령을 실행해보면

docker run -d -p 3000:80 --name feedback-app -v feedback:/app/feedback -v "/home/max/devops/study_docker/section3:/app" feedback-node:volumes2
docker ps                                                                                                                      ─╯
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

실행중인 컨테이너에 없다. 로그를 조회해보면 종속성 문제임을 알 수 있는데, 이 옵션을 추가하기 전까지는 정상 작동하였다.

이는 바인드 마운트가 적용되면서 컨테이너 내부의 /app내용이 호스트의 내용으로 덮어씌워졌기 때문에, 호스트의 종속성이 실행하고자 하는 컨테이너와 맞지 않아 실행 직후 종료되는 것이다.

다른 볼륨 결합 &병합하기

위 문제는

docker run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback -v "작업폴더전체경로":/app" -v /app/node_modules feedback-node:volume

와 같이 익명 볼륨을 하나 추가해줌으로써 해결할 수 있다. 이 익명 볼륨이 컨테이너의 /app/node_modules에 마운트되면서 호스트의 /app이 덮어씌워지더라도 /app/node_modules는 호스트와 분리된 환경이 되어, 의존성 문제가 해결되는 것이다.

바인드 마운트가 성공적으로 적용되면 html을 수정한 것이 다시 빌드하지 않아도 컨테이너에 즉각 반영된다.

그러나 이것은 html 수정 같은 정적 코드 수정에 한정되지 JS코드를 수정하는 등의 작업은 위 방법으로 즉각 적용시킬 수 없다.

Node한정 nodemon을 활용하여 동적 코드 변경을 적용시켜야 한다. 이를 이용하면 서버가 자동으로 재시작하기 때문에 JS코드의 변경도 컨테이너에 적용시킬 수 있다.

COPY vs 바인트 마운트

테스트 시에 바인드 마운트를 통해 COPY가 도커파일에 없어도 작동 자체는 할 수 있지만, 바인드 마운트를 이를 대체하기 위한 것은 아니다.

바인드 마운트는 수정사항을 즉각 반영하고 싶을 때 사용하는(개발 도중) 용도이지, COPY와는 용도가 다르다.

바인드 마운트는 호스트 파일에 의존하기 때문에 배포 시에 COPY를 대체할 수 없다.


dockerignore

도커도 git처럼 도커가 관리하지 않기를 원하는 파일을 .dockerignore파일로 제명시킬 수 있다. 예를 들어 .dockerignore에 추가한 파일은 COPY . .명령어에서 이미지로 복사되지 않는다.

환경 변수 관리하기

도커에는 2가지 타입의 변수가 있는데 빌드 타임 인수런타임 환경 변수이다.

빌드 타임 환경 변수는 이미지를 빌드하는 동안 사용되는 변수로 도커파일에서 ARG를 사용하여 설정한다.

런타임 환경 변수는 컨테이너 실행(docker run) 시 설정되는 환경 변수로 도커파일 내부에서 docker run옵션에 --env옵션을 붙여 사용할 수 있다. 환경변수가 많은 경우 .env파일에서 관리하고 docker run --env-file 로 옵션을 붙여줄 수 있다.

ARG 빌드 인수

ARG는 도커 이미지 빌드 시에만 사용되는 변수로 주로 빌드 시점에 필요한 설정 값을 전달하기 위해 사용된다.

ENV는 이미지와 컨테이너 모두에서 사용할 수 있지만 ARG는 이미지에만 사용된다.

14 views

More from this blog

락프리 데이터 구조와 알고리즘

여기서는 락프리 데이터 구조를 설명한다. 락프리(lock-free) 란 배타락을 이용하지 않고 처리를 수행하는 데이터 구조 및 그에 대한 조작 알고리즘을 총칭한다. 왜 락프리인가? 전통적인 동시성 제어 방법인 뮤텍스나 세마포어는 여러 문제점을 가지고 있다: 성능 저하: 락 경합(lock contention)으로 인한 대기 시간 데드락: 여러 스레드가 서로의 락을 기다리는 상황 우선순위 역전: 낮은 우선순위 스레드가 높은 우선순위 스레드를 ...

Jul 27, 20257 min read126

소프트웨어 트랜잭셔널 메모리

소프트웨어 트랜잭셔널 메모리 동시성 프로그래밍에서 공유 자원에 대한 안전한 접근은 항상 중요한 과제다. 전통적으로 뮤텍스 락과 같은 비관적 락(Negative Lock) 방식을 사용해왔다. 이 방식은 크리티컬 섹션에 진입하기 전에 반드시 락을 획득해야 하며, 락을 얻지 못하면 코드 실행 자체가 블록된다. 하지만 이와는 다른 접근 방식이 있다. 바로 낙관적 락(Optimistic Lock) 방식인데, 이는 "일단 실행하고 나중에 검증하자"는 철학...

Jul 20, 202517 min read263

공평한 배타 제어

공평한 배타 제어 여기서는 공평한 배타 제어에 대해 설명한다. 먼저 컨텐션(contention) 이라는 개념을 이해할 필요가 있다. 컨텐션이란 여러 스레드가 동시에 같은 락을 획득하려고 경쟁하는 상황을 말한다. 컨텐션이 높을수록 스레드들이 락을 기다리는 시간이 길어지고 성능이 저하된다. 이러한 컨텐션 상황은 시스템 아키텍처에 따라 더욱 복잡해질 수 있다. 특히 비균일 메모리 접근(Non-Uniform Memory Access, NUMA) 와 같...

Jul 13, 20259 min read21

KernelSnitch[논문 리뷰]

Paper 1. Intro 이 글은 NDSS 2025에서 발표된 KernelSnitch 논문을 소개이다. 이 연구는 커널의 평범한 데이터 구조체들이 가진 본질적인 특성이 어떻게 심각한 보안 취약점이 되는지를 보여준다. 핵심은 이러하다: "데이터 구조체의 크기에 따른 접근 시간 차이를 이용해 커널의 비밀 정보를 유출할 수 있다" 여기서는 커널 힙 포인터 유출에 집중해서 설명한다. 이 공격이 성공하면 KASLR을 우회하고 더 심각한 커널 익스플로...

Jul 11, 20257 min read131

멀티태스크와 액터 모델

멀티태스크 협조적/비협조적 멀티태스크 선점: 프로세스와의 협조 없이 수행하는 컨택스트 스위칭이라고는 하나, 결국 뺏어오는 게 가능하냐의 문제다. 협조적 멀티태스크(비선점형, cooperative): 각각의 프로세스가 자발적으로 컨택스트 스위칭을 수행하는 멀티태스크 방식. 장점: 멀티태스크 매커니즘을 구현하기 쉽다. 단점: 프로세스가 자발적으로 컨텍스트 스위칭을 해야하는데, 만약 버그가 발생하여 프로세스가 무한 루프에 빠지거나 정지하게 되면 그 ...

Jul 6, 20252 min read25
M

MaxLog

35 posts