본문 바로가기
개발공부 개발새발/Docker

Docker ) 도커 볼륨과 바인드 마운트

by 휴일이 2024. 3. 18.

 

도커 데이터

  • 우리가 도커 파일을 작성할 때 복사했던 소스코드 이미지, 변경 불가. (read-only)
    • 새 소스 코드로 업데이트하려면 이미지를 새로 생성해야함.
  • 임시 애플리케이션 데이터. (컨테이너) (read-write)
    • 애플리케이션이 실행되는 동안 생성. → 사용자가 입력 폼에 뭔가 입력한다거나..할 때 컨테이너가 처리할 때 임시 데이터
    • 컨테이너가 종료될 때 데이터가 없어져도 상관 없음. 말 그대로 임시 데이터.
  • 영구 애플리케이션 데이터 (read-write)
    • 파일에 저장하거나 데이터베이스에 데이터를 컨테이너에 그 데이터를 가져와 저장
    • 컨테이너가 삭제되더라도 살아있어야하는 데이터. (유저 데이터라던지)
    • 그것은 “볼륨”
💡 이미지는 read-only, 컨테이너는 read-write. 컨테이너는 이미지와 격리된 상태기 때문에 같은 이미지로 만들어진 컨테이너여도 서로 다른 데이터를 가지고 있을 수 있다. 하지만 이것은 다르게 말하면 컨테이너가 종료될 때 → 컨테이너에 저장되어 있는 데이터들은 삭제된다는 것!
그래서 우리는 영구 애플리케이션 데이터를 저장할 “Vloume”이 필요하다!

 

도커 볼륨

호스트 머신(컴퓨터)의 디렉토리. 호스트 컴퓨터에 장착된 하드 드라이브에 존재. (그러나 어디있는지는..) 컨테이너와 “매핑”됨.

→ 하지만 참고로 arm (애플 실리콘)은 리눅스 위에 도커를 설치하기 때문에 경로가 내 맥이 아니라 리눅스 위에 있음…참고…ㅎ

  • 도커 호스트의 하드 드라이브에 존재. (컨테이너 외부에 존재)
    • 컨테이너가 종료되어도 데이터 삭제 X
  • 컨테이너와 매핑 가능.
    • 볼륨에 있는 데이터 read-write 가능 !

주의

  exists(finalFilePath, async (exists) => {
    if (exists) {
      res.redirect('/exists');
    } else {
      // 이전에는 파일을 이동시킬 수 있었지만, 볼륨이 추가된 이후에는 파일을 "이동" 시키는 것은 불가능.
      // await fs.rename(tempFilePath, finalFilePath);
      await fs.copyFile(tempFilePath, finalFilePath); // 파일을 복사 후
      await fs.unlink(tempFilePath); // 수동 삭제
      res.redirect('/');
    }
  });
  • 도커는 컨테이너 파일을 다른 폴더로 이동시키지 않음. 컨테이너 밖으로 이동시킬뿐.
  • 만약 이처럼 파일을 볼륨으로 “이동” 시키는 코드가 있을 경우
    • 복사 → 이전 파일 삭제 시켜야 함.

볼륨 종류 (type)

  • 익명 볼륨
  • 명명 볼륨

Anonymous 익명 볼륨

  • VOLUME [ "/app/feedback" ] → 호스트 경로를 매핑하지 않았기 때문에 익명 볼륨으로 생성
  • 도커가 관리하기 때문에 컨테이너가 내려가면 볼륨도 삭제 됨

Named 익명 볼륨

  • 영구적이어야 하는 데이터, 편집하거나 직접 볼 필요 없는 데이터를 저장하자!
    • 실제로 엑세스 할 필요 없는 데이터.
  • 컨테이너가 내려가도 폴더, 볼륨 그대로 사용 가능.
  • 새 컨테이너를 시작해도 볼륨이 그대로 유지.

익명 볼륨 추가

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . /app

EXPOSE 80

# 볼륨 생성 명령어
# /app/feedback -> 컨테이너 외부 폴더와 매핑할 컨테이너 내부 폴더
VOLUME [ "/app/feedback" ]

CMD [ "node", "server.js" ]
  • /app/feedback → 컨테이너 내부 폴더와 매핑된다.

명명 볼륨 추가

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . /app

EXPOSE 80

# 볼륨 생성 명령어 주석 처리
# VOLUME [ "/app/feedback" ]

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

# 실행 명령어
docker run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback feedback-node:volumes
  • -v feedback:/app/feedback
    • -v 볼륨 추가 명령어
    • feedback:/app/feedback 볼륨이름:컨테이너내부폴더경로

  • 컨테이너를 삭제해도 존재함을 확인 가능!

볼륨 삭제

docker volume rm 볼륨이름

# 안쓰는 볼륨들 전부 삭제
docker volume prune

바인드 마운트

볼륨과는 다르게 직접 호스트 머신과 경로 매핑 → 소스 코드를 넣을 수도 있어, 컨테이너 재배포를 하지 않더라도 소스코드 변경 가능?!

  • 영구적이고 편집 가능한 데이터에 적합.
  • 이미지가 변경되는 것이 아니라, 컨테이너에만 변경되는 것 ! → 이미지가 아닌 컨테이너와 연결되어있으니
  • 도커가 로컬 호스트 폴더를 덮어쓰지 않음.
# 마운트 명령어
-v 호스트머신실제경로:컨테이너내부폴더경로

# 명명 볼륨 외에, 내 호스트 프로젝트 폴더에 바인드 마운트 해버리기
# 모든 호스트 폴더를 /app 폴더에 바인딩 한다는 것을 명심하기 ! -> 그저 덮어씌우기만 하는 게 아님
docker run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback
-v /Users/holidayk/Desktop/docker-study/data-volumes-03/Docker-Volume:/app feedback-node:volumes

# OS 에 따라 전체 경로를 복사하지 않고 아래 명령어로 대체 가능 !
# macOS / Linux
-v $(pwd):/app
# Windows
-v "%cd%":/app

주의

  • 공유 폴더에 도커가 엑세스 권한이 있는지 확인 → Docker Decktop 에서 확인 가능
  • 경로 문자에 특수문자 등이 있으면 문자가 깨질 수 있음, 따옴표 안에 넣어도 좋아요!
    • -v "/Users/holidayk/Desktop/docker-study/data-volumes-03/Docker-Volume:/app"
  • 덮어씌우기만 하는 게 아니라 ‘바인딩’ 한다는 것 명심.

오류

# 이 명령어로 컨테이너를 생성, 실행하게 되면
docker run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback
-v /Users/holidayk/Desktop/docker-study/data-volumes-03/Docker-Volume:/app feedback-node:volumes

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . /app
# 위의 '복사' 명령어들이 의미가 없음
# -> 어차피 컨테이너 생성할 때 /app 폴더로 내 로컬 프로젝트를 바인드 마운트 하기 때문...
# -> 그래서 종속성 설치 (npm install) 명령어가 무효화되기 때문에 에러 발생 !
# -> 도커에게 덮어쓰지 말아야 할 파일이 있다는 것을 알려줘야 함. -> 익명 볼륨으로 해결!

EXPOSE 80

# 볼륨 생성 명령어 주석 처리
# VOLUME [ "/app/feedback" ]

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

오류 해결

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . /app

EXPOSE 80

# 여기에서 익명 볼륨을 생성해줘도 되지만, 이미지를 리빌드해야하니 컨테이너 실행 명령어에서 익명 볼륨 생성
# VOLUME [ "/app/feedback" ]

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

# 컨테이너 실행
docker run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback \\n
-v /Users/holidayk/Desktop/docker-study/data-volumes-03/Docker-Volume:/app \\n
-v /app/node_modules \\n
feedback-node:volumes \\n
  • -v /app/node_modules
    • 충돌이 있을 경우 더 길고 구체적인 경로에 바인딩함.
    • 즉, /app/node_modules 폴더는 도커가 사용하는 폴더로 덮어씀 (예외로 살아남음)
    • 나머지 코드는 내코드 + 종속성 다운로드 디렉토리만 도커가 설치한 종속성 사용하기 !

→ 이렇게 하면 도커를 사용해도 html 파일은 고냥 바로 업데이트 가능!

💡 하지만 이렇게 해도 js 코드를 바꾸는 건 적용되지 않는다. 어떻게 하면 적용할 수 있을까용?

 

컨테이너 재시작 없이 Node.js 코드 바로 적용하기

컨테이너 재시작하지 않고 노드 코드도 바로 적용할 수 있도록 해봅시다!

package.json

{
  "name": "data-volume-example",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "author": "Maximilian Schwarzmüller / Academind GmbH",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1"
  },
   "scripts": {
   // nodemon 으로 server.js 실행 (서버 시작)
    "start": "nodemon server.js"
  },
  "devDependencies": {
	  // 서버 변경 감지 종속성
    "nodemon": "3.1.0"
  }
}

Dockerfile

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80

# nodemon 으로 서버 시작
CMD [ "npm", "start" ]
# CMD [ "node", "server.js" ]
  • 이제 server.js 에서 코드를 바꾸면 변경을 감지하여 서버가 자동으로 재시작.

요약

# 익명 볼륨
docker run -v /app/data

# 명명 볼륨 (이름 추가)
docker run -v data:/app/data

# 바인드 마운트 (로컬 호스트 머신 절대 경로 추가)
docker run -v /path/to/code:/app/code

 

  익명 볼륨  명명 볼륨  바인드 마운트
설명 컨테이너에 연결 된 볼륨 이름이 있는 볼륨
도커 파일에서 생성 불가
컨테이너를 실행할 때 -v 로만 생성
호스트 머신에 연결된 볼륨(데이터 저장 위치 알고 있음)
컨테이너 제거 볼륨도 함께 제거 볼륨 존재 볼륨 존재
볼륨 제거 컨테이너가 제거되면 함께 제거 CLI 명령으로 제거 실제로 호스트 머신에서 삭제해야 함.
컨테이너 간 데이터 공유 불가 (컨테이너가 종료되면 제거) 가능 가능
데이터 저장 부적합 적합 적합
용도 컨테이너에 이미 존재하는 특정 데이터 잠금 (덮어씌움 방지)
외부 경로보다 컨테이너 내부 경로의 우선 순위를 높이는데에 사용.(바인드 마운트와 함께)
컨테이너간의 데이터 공유 컨테이너 리빌드 없이 라이브 데이터 제공

정리

  • 볼륨이란?
    • 컨테이너 외부 특정 폴더에 연결된 Docker 컨테이너 내부의 폴더 또는 파일
  • 볼륨은 도커에서 관리하므로 호스트 폴더(컨테이너 내부 경로에 매핑된)가 어디에 있는지 반드시 알 필요는 없음.
  • 익명 볼륨은 --rm 으로 시작된 컨테이너가 중지되면 제거됨.
    • 컨테이너가 중지될 때 삭제하는 구문
  • 명명 볼륨의 장점
    • 컨테이너를 제거해도 살아남는다.
  • 바인드 마운트란?
    • 내가 인지하고 있는 특정 호스트 머신 상의 경로, 일부 컨테이너 내부 경로에 매핑 됨.
    • 컨테이너에 라이브 뎅터 제공 (리빌드 필요 X)
  • 익명 볼륨의 쓰임
    • 외부 경로보다 컨테이너 내부 경로의 우선 순위를 높이는 데에 사용 가능.

읽기 전용 볼륨 (read-only)

docker run -d -p 3000:80 --rm --name feedback-app -v feedback:/app/feedback 
-v /Users/holidayk/Desktop/docker-study/data-volumes-03/Docker-Volume:/app:ro 
-v /app/node_modules feedback-node:volumes

# :ro 를 붙이면 read-only 로, 도커가 해당 폴더에 쓸 수 없음.
-v /localhost/path:/app:ro
  • 바인드 마운트 할 때, :ro 추가하면 read-only 적용
  • 도커가 쓰기 불가능

→ 만약 read-only 때문에 이후 /app/node_module 폴더를 쓸 수 없다고 뜨면 프로젝트 폴더에 node_module 폴더를 만들어둔 후 명령어 실행하면 정상 동작.

  • read-only 라 도커가 직접 폴더를 만들 수 없어서 write-read 허용하는 디렉토리는 호스트가 직접 만들어놔야하는듯.

명령어

# 활성화 중인 모든 볼륨 listing
# 바인드 마운트는 도커가 관리하는 게 아니라 로컬 폴더를 컨테이너 내부 폴더에 바인딩하는 거라, ls 해도 안 뜸
docker volume ls

# 자체 볼륨 생성 (도커에 맡길 때가 많긴 함. 직접 생성할 이유 없다면 굳이?)
docker volume create [OPTIONS] [VOLUME]

# 도커 제어 정보 가져오기
# 생성 날짜, 드라이버, 이름, 옵션 등...
docker volume inspect 도커

# 볼륨 제거
# 볼륨을 특정 컨테이너가 사용하고 있다면 컨테이너 중지 후 제거 가능
docker volume rm 도커이름

# 지금 사용하지 않는 모든 볼륨 제거
docker volume prune

“COPY” vs 바인드 마운트

바인드 마운트를 하는데도 굳이 이미지가 생성될 때 COPY 명령어로 스냅샷에서 복사하는 이유가 뭘까?

  • docker run 으로 개발 중에 바운드 마운트를 사용해서 실행 중인 컨테이너에 즉시 반영
  • 개발을 마치면 실제로 이 컨테이너를 가져와 서버에 넣음.
    • 하지만 이 땐 바인드 마운트 사용하지 않음
  • 그래서 COPY 명령어로 스냅샷 이미지 유지 !
728x90