관리 메뉴

코딩하는 락커

섹션3. 데이터 관리 및 볼륨으로 작업하기 본문

🐳 도커와 쿠버네티스

섹션3. 데이터 관리 및 볼륨으로 작업하기

락꿈사 2022. 6. 29. 17:39

데이터 카테고리 / 다양한 종류의 데이터 이해하기

  • 데이터의 종류
    1. 애플리케이션 데이터
      • 소스 코드 + 애플리케이션이 실행되는 환경
      • 개발자에 의해 작성된 데이터
      • 이미지를 빌드할 때 코드가 이미지에 복사됨
      • 컨테이너를 실행할 때 제공된 환경에서 코드를 사용함
      • 이미지가 빌드되면 변경할 수 없으므로 코드가 변경되면 새 이미지를 빌드해야 함
      • read-only data
    2. 임시 애플리케이션 데이터
      • 애플리케이션이 실행되는 동안 생성된 데이터
        • ex) 서버에서 실행 중인 컨테이너로 넘어온 사용자의 데이터(사용자가 입력폼에 입력한 데이터)
      • 로컬 파일 시스템, 이미지에 저장되지 않는 데이터
      • 코드의 변수에 저장할 수 있음
      • 메모리에만 저장하거나 DB에 저장할 수도 있음
      • 컨테이너에 저장됨
        • 즉, 이미지 위에 도커에 의해 추가된 레이어에 저장됨
          • 추가된 레이어는 로직logic
          • 이미지를 인식하고 이미지의 파일 시스템을 인식하며, 복사하지 않고 파일 시스템을 미러링 하는 로직으로 되어 있음
          • 파일 시스템을 조작할 수 있음
      • 컨테이너가 종료될 때 데이터를 잃음
      • read-write data
    3. 영구 어플리케이션 데이터
      • 컨테이너에서 실행 중인 애플리케이션 내에서 만들어지는 데이터
      • 컨테이너가 중지되고 다시 시작되더라도 그대로 저장되는 데이터
        • ex) 사용자 계정
      • 파일이나 데이터베이스에 저장됨
      • read-write data
        • 애플리케이션이 실행되는 동안 작성되고 영구적으로 컨테이너와 볼륨에 저장됨
      

실제 앱 분석하기

  • DockerFile 작성
# docker hub 에서 node 14 이미지를 가져옴
FROM node:14

# 컨테이너의 작업 디렉토리를 /app으로 설정
WORKDIR /app

# package.json 파일을 작업 디렉토리(.)에 복사
COPY package.json .

# 컨테이너에 node를 실행하기 위한 nmp install을 실행
RUN npm install

# 나머지 코드 파일을 작업 디렉토리(.)에 복사
COPY . .

# 포트 80에서 수신을 대기하도록 포트 노출
EXPOSE 80 

# 이미지를 기반으로 컨테이너가 시작될 때 node 실행파일을 사용하여 server.js를 실행함
CMD ["node", "server.js"]
  • 빌드

  • 실행
    • -p 3000:80: 로컬 컴퓨터 3000번에 컨테이너 내부 포트 80 연결
    • -d: detached 모드로 실행
    • feedback-app: feedback-app으로 컨테이너 이름 지정
    • --rm: 컨테이너를 중지할 때마다 자동으로 제거

  • 실행 결과

  • 로컬 환경 
    • feedback/test.txt 파일을 찾을 수 없음
    • 로컬 폴더와 이미지 내부 파일 시스템 사이의 연결은 없음
    • test.txt 파일은 컨테이너 내부에 생성되어 실행중인 컨테이너 내부에서만 그 파일을 찾을 수 있음

 

 

문제 이해하기

  • 컨테이너 재시작(컨테이너가 삭제되었다가 다시 만들어짐)
    • rm 태그 없이 시작

  • 실행 결과
    • feedback/test.txt를 가져올 수 없음
    • 컨테이너를 삭제했기 때문

  • 컨테이너 실행 중지 후 재시작
    • 이번엔 제거되지 않음

  • 실행 결과
    • 다시 로드 했을 때 파일이 여전히 존재함
    • 컨테이너를 중지했지만 제거하지는 않았기 때문

 

정리

  • 이미지에는 자체 내부 파일 시스템이 존재하는데, 이 자체 내부 파일 시스템은 컨테이너 내부에 있음
  • 컨테이너를 시작하면 이미지 위에 얇은 read-write 레이어로 컨테이너가 추가됨
  • 그리고 컨테이너는 파일을 생성할 때 이미지에 저장하는 것이 아니라 추가된 read-write 레이어에 저장함
  • 컨테이너를 중지하고 다시 시작할 경우
    • 자체 내부 파일 시스템이 삭제되지 않음
  • 컨테이너를 삭제하고 다시 시작할 경우
    • 변경되지 않는 이미지만 남게 됨
    • 자체 내부 파일 시스템이 삭제됨

 

 

볼륨 소개

  • 볼륨
    • 도커의 내장 기능
    • 데이터를 유지하도록 도움
  • 볼륨의 작동 방식
    • 볼륨은 호스트 컴퓨터(로컬 컴퓨터)의 폴더로 컨테이너나 이미지에 있는 것이 아님
    • 즉, 도커가 인식하는 호스트 컴퓨터(내 컴퓨터)에 있는 폴더로서 도커 컨테이너 내부의 폴더에 매핑됨
  • 볼륨 vs COPY
    • COPY
      • 실제로 복사하도록 명령한 경로와 파일의 스냅샷을 취한 다음, 파일과 폴더를 이미지에 복사함
      • 지속적인 연결이 없으며, 이미지에 복사되는 일회성 스냅샷
    • 볼륨
      • 컨테이너 내부의 폴더를 호스트 컴퓨터 상의 컨테이너 외부 폴더와 연결할 수 있음
      • 두 폴더의 변경 사항은 다른 폴더에 추가됨
      • 따라서 호스트 컴퓨터에 파일을 추가하면 컨테이너 내부에 액세스 할 수 있고, 컨테이너가 매핑된 경로에 파일을 추가하면 호스트 머신에서도 사용할 수 있음
      • 컨테이너가 종료되거나 삭제된 경우에도 지속되며 계속 존재함
      • 컨테이너는 볼륨에 있는 데이터를 읽고 쓸 수 있음

 

 

첫 번째, 실패한 시도

  • Dockerfile 수정
FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80 

# 컨테이너 파일 시스템 내부의 다양한 경로를 특정할 수 있음
# "/app/feedback": 컨테이너 내에서 필요한 데이터가 저장되는 경로
VOLUME [ "/app/feedback" ]

CMD ["node", "server.js"]
  • 빌드
    • -t feedback-node:v=volumes: feedback-node라는 이름을 지정하고 volumes 태그를 주어 빌드

  • 실행

  • 결과
    • 호스트 컴퓨터에 저장되지 않음 (원래 강의에서는 에러가 나야 한다고 했는데 에러는 안남)

  • 코드 수정

  • 컨테이너 중지 후 이미지 삭제

  • 빌드 후 컨테이너 실행

  • 결과
    • 잘 실행됨
    • 그러나 컨테이너를 중지했다가 다시 실행해봤는데 파일이 또 존재하지 않음

 

 

명명된(named) 볼륨으로 구조하기!

  • 도커의 외부 데이터 저장 메커니즘
    1. 볼륨
    2. 바인드 마운트
  • 볼륨의 타입
    1. 익명 볼륨
      • 현재 사용하고 있는 볼륨
      • 컨테이너가 존재하는 동안에만 실제로 존재하고 컨테이너가 삭제되면 사라짐
    2. 명명 볼륨
      • 컨테이너가 종료된 후에도 볼륨이 유지됨
      • 새 컨테이너가 시작되면 볼륨이 복구되고 폴더가 복구되어 해당 폴더에 저장된 모든 데이터를 계속 사용할 수 있음
      • 따라서 명명 볼륨은 영구적이어야 하는 데이터나, 편집하거나 직접 볼 필요가 없는 중요한 데이터에 적합
      • 여러 컨테이너에 연결될 수 있음
  • 익명 볼륨과 명명 볼륨 모두 호스트 컴퓨터에 도커의 일부 폴더와 경로를 설정하나, 도커가 경로를 관리할 뿐이고 우리는 그 위치를 알 수 없음
  • 위 코드에서 컨테이너 내부 경로는 지정했으나 호스트 컴퓨터의 경로는 지정하지 않았으므로 미러링된 폴더가 어디에 있는지 모름
  • 볼륨에 접근하는 방법
    • 자동 생성 볼륨이므로 암호화 된 이름으로 저장됨
$ docker volume ls

 

명명 볼륨 생성

  • Dockerfile 수정
    • 도커 파일 내부에 명명된 볼륨을 생성할 수는 없으므로 VOLUME 명령어 삭제
FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80 

CMD ["node", "server.js"]
  • 실행
    • -v : 컨테이너에 볼륨 추가
    • /app/feedback: 볼륨에 저장하고자 하는 컨테이너 파일 시스템 내부 경로
    • feedback: 도커가 지정한 볼륨의 이름

  • 결과
    • feedback이라는 이름의 명명 볼륨이 만들어졌음
    • 컨테이너를 중지했다가 재시작해도  feedback/awesome.txt가 남아있는 것을 확인

  • 익명 볼륨 제거하기
$docker volume rm VOL_NAME
$docker volume prun

 

 

바인드 마운트 시작하기(코드 공유)

  • 볼륨 vs 바인드 마운트
    • 볼륨
      • 도커의 의해 관리되는 볼륨의 위치, 즉 호스트 컴퓨터의 파일 시스템 상의 볼륨이 어디에 있는지 알지 못함
      • 경로를 알 수 없기 때문에 편집이 불가능함
    • 바인드 마운트
      • 호스트 컴퓨터 상에 매핑될 컨테이너의 경로를 설정할 수 있음 (따라서 경로를 알 수 있음)
      • 경로를 알 수 있으므로 소스코드 편집 또한 가능함
  • 바인드 마운트
    • 영구적으로 편집 가능한 데이터에 적합
    • 소스 코드를 넣을 수 있고, 따라서 항상 최신 코드에 액세스 하도록 할 수 있음
  • 바인드 마운트로 실행
    • -v (두번째 -v 옵션) "/Users/roxyyujin/Docker & Kubernetes/data-volumes-01-starting-setup:/app":
      • /Users/roxyyujin/Docker & Kubernetes/data-volumes-01-starting-setup: 호스트 컴퓨터 상의 경로. 해당 경로 안의 모든 코드와 모든 콘텐츠. 절대경로로 작성해야 함. 프로젝트 경로로 작성.
      • /app: 호스트 컴퓨터 상의 경로와 연결될 컨테이너의 작업 디렉토리
    • 도커 설정에 지금 공유되고 있는 폴더(이나 그 상위 폴더)가 file sharing 목록에 있는지 확인해야 함
    • 이제 프로젝트의 전체 폴더가 컨테이너 내부의 app 폴더에 명명된 볼륨으로 마운트 됨
    • -v $(pwd):/app 를 사용하면 전체 경로를 복사하여 사용하지 않아도 됨

  • 결과
    • 충돌남
    • 실행된 컨테이너가 없는 것을 확인

  •  다시 실행 (--rm 옵션 제거)
    • 실행이 중지된 컨테이너에서 찾을 수 있음

  • 로그 출력
    • express 모듈을 찾는데 실패했다는 것 확인
      • 노드 코드가 실행하는 시작하지도 않은 것을 의미함
      • 종속성이 없기 때문에 실행되지 않음

 

 

다른 볼륨 결합 & 병합하기

  • 위의 문제는 -v을 사용한 바인드 마운트로 인한 문제
    • 처음에 dockefile로 인해 모든 종속성을 설치함 
    • 그러나 마운트를 컨테이너에 바인딩하면서 위에서 설치한 종속성를 비롯한 app 폴더의 모든 것을 로컬 폴더로 덮어써 버리고, 로컬 폴더에는 설치된 종속성이 없으므로 문제가 발생함
  • 컨테이너와 볼륨의 상호작용
    • -v 플래그를 사용하여 마운트 가능
    • 컨테이너 내부의 어떤 파일이 있을 경우 외부 볼륨에 추가됨
    • 컨테이너가 시작하면 볼륨에서 파일을 찾음 (그러나 우리는 명명볼륨의 경로를 알 수 없고, 그렇기 때문에 바인드 마운트를 사용함 )
  • 컨테이너와 바인드 마운트의 상호작용
    • -v 플래그를 사용하여 마운트 가능
    • 칸테이너 내부의 어떤 파일이 있을 경우 바인드된 볼륨에 추가됨
    • 컨테이너가 시작하면 로컬 호스트의 바인드 마운트에서 파일을 찾고, 로컬 호스트의 폴더와 그 안에 있는 콘텐츠가 도커 컨테이너에 있는 내용을 덮어씀
    • 따라서 컨테이너에 파일이 있을 경우, 도커에 알려서 내부 파일 시스템에 외부에서 덮어쓰지 않아야 할 부분이 있음을 알려야 함
      • 또다른 익명 볼륨을 추가하는 것으로 해결할 수 있음
  • 익명 볼륨 추가하기
    • -v ( 세번째 -v 옵션) /app/node_modules
      • Dockerfile의 npm install 파트가 /app/node_modules에 바인딩 되어 두번째 -v 옵션으로 바인드 마운트 된 /app 폴더 안에 /app/node_modules 라는 폴더를 생성하여 실행됨(종속성이 설치됨)

  • 실행

  • 결과
    • your feedback plz로 수정하자 수정된 코드가 바로 반영됨

 

 

NodeJS 특화 조정: 컨테이너에서 Nodemon 사용하기

  • 새 피드백을 저장할 때 마다 로그를 출력하도록 수정

  • 그러나 로그가 출력되지 않음

  • 로그를 출력하기 위해서는 컨테이너를 중지했다가(자동 삭제되어) 새로 생성해서 시작해야함

  • 이러한 문제를 해결하기 위한 개발 중에 변경 사항이 즉시 반영되도록 패키지를 추가하여 코드 수정
    • nodemon 패키지는 server.js 파일에서 무엇이든 변경할 때마다 서버가 자동으로 다시 시작되게 함

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80 

# 원래 코드
# CMD ["node", "server.js"]
# 아까 추가한 start 스크립트를 사용하여 내부적으로 nodemon을 사용하도록 함
CMD ["npm", "start"]
  • 실행

  • 결과
    • 소스 코드가 변경되었을 때 서버가 재시작된 것을 확인할 수 있음

 

볼륨 & 바인딩 마운트: 요약

 

읽기 전용 볼륨 살펴보기

  • 볼륨은 기본적으로 read-write임
  • 컨테이너에서 실행중인 애플리케이션이 로컬 호스트 내부의 파일을 변경하지 않게 하기 위해서 읽기 전용으로 전환할 필요가 있음
  • 읽기 전용 볼륨으로 전환하기
      • read-only를 의미하는 ro를 추가
      • 호스트 컴퓨터에서는 여전히 파일을 변경할 수 있음
      • 컨테이너와 컨테이너에서 실행하는 어플리케이션에만 영향을 줌
      • 그러나 지금은 app이라는 전체 폴더를 read-only로 지정해놓았으므로 쓰기 기능이 수행되어야 하는 feedback폴더와 temp 폴더에도 읽기만 할 수 있게 제한이 걸려 있음
      • 그러나 -v feedback:/app/feedback으로 read&write 볼륨을 만들어 놓았기 때문에 (더 구체적인 하위 폴더 볼륨을 특정하면 하위 볼륨이 메인 볼륨보다 우선시 됨으로 app/feedback은 read&write로 설정됨)
      • 따라서 -v /app/temp 옵션을 주어 app/feedback도 read&write로 설정해줌-v "/Users/roxyyujin/Docker & Kubernetes/data-volumes-01-starting-setup:/app:ro"  

  • 결과

 

 

Docker 볼륨 관리하기

  • 현재 활성화중인 모든 컨테이너의 볼륨 리스팅 하기
    • 명명 볼륨
      • -v feedback:/app/feedback
    • 익명 볼륨
      • -v /app/temp 으로 생성
      • -v /app/node_modules으로 생성
    • 바인드 마운트는 도커가 관리하는 것이 아니기 때문에 리스팅 되지 않음
$ docker volume ls

  • 수동으로 볼륨 생성하기
$ docker volume create {볼륨명}

  • 볼륨 조사하기
    • mountpoint를 알 수 있음
    • 그러나 이 mountpoint는 시스템에서 찾을 수 있는 경로가 아니라 도커가 시스템 상에 설정한 가상 머신 내부의 경로임 (그리고 그 경로는 다시 다르게 매핑됨)
$ docker volume inspect {볼륨명}

  • 볼륨 삭제하기
    • 볼륨 삭제 시 볼륨 안에 있는 모든 데이터가 사라짐
$ docker volume rm {볼륨명}

  • 사용하지 않는 모든 볼륨 제거
$ docker volume prune

 

"COPY" 사용 vs 바인드 마운트 사용

  • COPY
    • 이미지에 코드의 스냅샷이 넣어짐
    • 프로덕션 환경에서 컨테이너를 가동하는데 사용됨
    • 컨테이너를 배포할 때 프로덕션용 스냅샷 컨테이너가 필요하므로 꼭 필요함
  • 바인드 마운트
    • 이미지에 코드의 스냅샷이 없음
    • docker run 명령어로 수행되며, 개발 중에 바인드 마운트를 사용하여 실시간으로 업데이트 하는데 사용됨

 

 

모든 것을 복사하진 마세요: "dockerignore" 파일 사용하기

  • .dockerignore 파일을 추가하여 복사되는 내용을 제한할 수 있음
# node_modules 폴더를 추가하여 이미지에 복사되지 않게 함
# node_modules 폴더가 있는 경우 이 폴더는 이미지에 복사되지 않음
# 대신 npm install로 인해 이미지 내부에 생성된 node_modules를 유지함
node_modules
  • .git이나 Dockerfile등을 추가해서 무시하도록 할 수 있음

 

 

환경 변수 & ".env" 파일 작업

  • 도커는 빌드 타임 인수(docker build)와 런타인 환경 변수(docker run)를 지원함
  • 빌드 타임 인수와 런타인 환경 변수를 사용하여 보다 유연한 이미지와 컨테이너를 만들 수 있음
  • 빌드 타임 인수
    • 전체 애플리케이션 코드에서 사용할 수 있음
    • docker build --build -- arg
  • 환경 변수
    • Dockerfile 내부에서 사용할 수 있음
    • docker run --env
  • 환경변수 사용하기
    • 전역적으로 사용 가능한 Process 객체의 노드 코드에 접근하여 env키의 PORT 환경 변수를 가져옴
    • Dockerfile에서 전역적으로 사용 가능한 환경 변수를 설정해줄 것
    • precess.env.PORT: 수신 대기에 사용해야 하는 실제 포트 번호를 지니고 있음 (지금은 없음)

FROM node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

# 환경 변수 PORT를 80으로 지정
ENV PORT 80

# 환경 변수 $PORT에서 수신을 대기하도록 포트 노출
EXPOSE $PORT 

CMD ["npm", "start"]
  • 빌드 후 실행

  • 결과

  • 환경변수 추가하기
    • -p 3000:8000 --env PORT=8000
      • 환경 변수 PORT를 8000으로 설정하여 컨테이너에서도 8000으로 사용하고 server.js에서도 8000으로 사용함
      • 이미지를 리빌드 할 필요는 없음

  • 결과

  • 환경변수 파일을 만들어 사용하는 것도 가능

 

 

 빌드 인수(ARG) 사용하기

  • ARG를 사용하여 Dockefile 수정
FROM node:14

# 빌드 인수 DEFAULT_PORT를 80으로 지정
# 이 인수는 server.js에서는 사용할 수 없음
# dockerfile에서만 사용가능(그러나 모든 명령에서 사용할 수 있는 것은 아님
# ex. cmd에서는 사용불가. ARG는 컨테이너가 시작될 때 실행되는 런타임 명령어이므로
ARG DEFAULT_PORT=80

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

# 환경 변수 PORT를 빌드 인수 $DEFAULT_PORT으로 지정
ENV PORT $DEFAULT_PORT

# 환경 변수 $PORT에서 수신을 대기하도록 포트 노출
EXPOSE $PORT 

CMD ["npm", "start"]
  • 빌드
    • 80포트를 노출한 web-app이 빌드됨

  • 빌드
    • 8000포트를 노출한 dev가 빌드됨

 

Comments