[백엔드 기초] 5. Docker
Web/Backend

[백엔드 기초] 5. Docker

Docker

도커: 컨테이너 기반오픈소스 가상화 플랫폼

 

다양한 프로그램, 실행환경을 컨테이너로 추상화하고 동일한 인터페이스를 제공하여 프로그램의 배포 및 관리를 단순하게 해줌. 백엔드 프로그램, 데이터베이스 서버, 메시지 큐등 어떤 프로그램도 컨테이너로 추상화할 수 있고 조립PC, AWS, Azure, Google cloud등 어디에서든 실행할 수 있음.

 

Container

운영체제를 기반으로 만들어진 대부분의 Software는 그 실행을 위하여 OS와 Software가 사용하는 동적 Library에 대하여 의존성을 가짐. 즉, Software의 실행을 위해선 OS와 Library를 포함, Software가 필요로 하는 파일 등으로 구성된 실행환경이 필요한데, 하나의 시스템 위에서 둘 이상의 Software를 동시에 실행하려고 한다면 문제가 발생할 수 있음.

예를 들어, Software A와 B가 동일한 Library를 사용하지만 서로 다른 버전을 필요로 하는 경우라던지 두 software의 운영 체제가 다를 경우 등 다양한 경우에서 문제가 발생할 수 있음. 이런 상황에서 가장 간단한 해결책은 두 Software를 위한 시스템을 각각 준비하는 것인데, 시스템을 각각 준비할 경우 비용의 문제가 발생(10개의 software일 경우 10개의 시스템이 필요).

이러한 문제점을 효율적으로 해결한 것이 바로 컨테이너. 컨테이너(Container)는 개별 Software의 실행에 필요한 실행환경을 독립적으로 운용할 수 있도록 기반환경 또는 다른 실행환경과의 간섭을 막고 실행의 독립성을 확보해주는 운영체계 수준의 격리 기술을 의미. 컨테이너는 애플리케이션을 실제 구동 환경으로부터 추상화할 수 있는 논리 패키징 메커니즘을 제공함.

 

 

컨테이너를 가상 머신(Virtual Machine)에 비교하여 생각하면 이해하기 쉬움. 호스트 운영체제에서 구동되며 그 바탕이 되는 하드웨어에 가상으로 액세스하는 Linux, Windows 등의 게스트 운영체제를 의미. 컨테이너는 가상 머신과 마찬가지로 애플리케이션을 관련 라이브러리 및 종속 항목과 함께 패키지로 묶어 소프트웨어 서비스 구동을 위한 격리 환경을 마련해 줍니다. 그러나 아래에서 살펴보듯 VM과의 유사점은 여기까지. 컨테이너를 사용하면 개발자와 IT 운영팀이 훨씬 작은 단위로 업무를 수행할 수 있으므로 그에 따른 이점도 훨씬 많음.

 

가상 머신은 하드웨어 스택을 가상화함. 컨테이너는 이와 달리 운영체제 수준에서 가상화를 실시하여 다수의 컨테이너를 OS 커널에서 직접 구동. 컨테이너는 훨씬 가볍고 운영체제 커널을 공유하며, 시작이 훨씬 빠르고 운영체제 전체 부팅보다 메모리를 훨씬 적게 차지함.

 

Container 기술 자체는, 격리된 실행환경을 제공하기 위한 인프라 기술이긴 하지만, Docker는 이미 그 이름에서 향기를 품고 있듯이, 그 목적부터 Software 또는 Service의 빠르고 효율적인 Shipping에 강하게 집중하고 있음. 때문에 Docker는 근간이 되는 격리 기술을 넘어 Container와 Image, 네트워크와 서비스, 보안 등 Software의 배포와 생명 주기를 관리할 수 있는 다양한 주변 기능에 초점이 맞추어져 있다고 할 수 있음.

 

 

이미지(Image)

이미지는 컨테이너 실행에 필요한 파일과 설정값등을 포함하고 있는 것으로 상태값을 가지지 않고 변하지 않음(Immutable). 컨테이너는 이미지를 실행한 상태라고 볼 수 있고 추가되거나 변하는 값은 컨테이너에 저장됨. 같은 이미지에서 여러개의 컨테이너를 생성할 수 있고 컨테이너의 상태가 바뀌거나 컨테이너가 삭제되더라도 이미지는 변하지 않고 그대로 남아있음.

 

레이어 저장방식

도커 이미지는 컨테이너를 실행하기 위한 모든 정보를 가지고 있기 때문에 보통 용량이 수백메가MB에 이르기 떄문에, 처음 이미지를 다운받을 땐 크게 부담이 안되지만 기존 이미지에 파일 하나 추가했다고 수백메가를 다시 다운받는다면 매우 비효율적일 수 밖에 없음.

 

도커는 이런 문제를 해결하기 위해 레이어(layer)라는 개념을 사용하고 유니온 파일 시스템을 이용하여 여러개의 레이어를 하나의 파일시스템으로 사용할 수 있게 해줌. 이미지는 여러개의 읽기 전용 read only 레이어로 구성되고 파일이 추가되거나 수정되면 새로운 레이어가 생성됨. ubuntu 이미지가 A + B + C의 집합이라면, ubuntu 이미지를 베이스로 만든 nginx 이미지는 A + B + C + nginx가 됨. webapp 이미지를 nginx 이미지 기반으로 만들었다면 A + B + C + nginx + source 레이어로 구성됨. webapp 소스를 수정하면 ABCnginx 레이어를 제외한 새로운 source(v2) 레이어만 다운받으면 되기 때문에 굉장히 효율적으로 이미지를 관리할 수 있음.

 

컨테이너를 생성할 때도 레이어 방식을 사용하는데 기존의 이미지 레이어 위에 읽기/쓰기(read-write) 레이어를 추가합니다. 이미지 레이어를 그대로 사용하면서 컨테이너가 실행중에 생성하는 파일이나 변경된 내용은 읽기/쓰기 레이어에 저장되므로 여러개의 컨테이너를 생성해도 최소한의 용량만 사용함.

 

이미지 만들기

도커는 이미지를 만들기 위해 컨테이너의 상태를 그대로 이미지로 저장하는 방법을 사용함.

어떤 애플리케이션을 이미지로 만든다면 리눅스만 설치된 컨테이너에 애플리케이션을 설치하고 그 상태를 그대로 이미지로 저장함. 가상머신의 스냅샷과 비슷한 방식. 이때 핵심 명령어는 파일을 복사하는 COPY와 명령어를 실행하는 RUN. DockerFile을 만드는 방법은 실제 쉘 스크립트의 내용을 그대로 옮기면 됨.

 

Dockerfile

도커는 이미지를 만들기 위해 Dockerfile이라는 파일에 자체 DSL(Domain-specific language)언어를 이용하여 이미지 생성 과정을 기록함. 굉장히 간단하지만 유용한 아이디어인데, 서버에 어떤 프로그램을 설치하려고 이것저것 의존성 패키지를 설치하고 설정 파일을 만들었던 경험이 있다면 더 이상 그 과정을 블로깅 하거나 메모장에 적지 말고 Dockerfile로 관리하면 됨. 이 파일은 소스와 함께 버전 관리 되고 원한다면 누구나 이미지 생성과정을 보고 수정할 수 있음.

도커는 컨테이너에 애플리케이션 실행환경이 함께 배포되는 방식(아예 실행환경 자체를 배포하는 방식)

=> 따라서 도커는 환경의 영향을 덜 받고 배포가 간편함.

FROM python:3.7-alpine3.9

ENV OJ_ENV production

ADD ./app
WORKDIR /app

HEALTHCHECK --interval=5s--retries=3CMDpython2/app/deploy/health_check.py

RUN apk add--update--no-cache build-base nginx openssl curl unzip supervisor jpeg-dev zlib-dev postgresql-dev freetype-dev&&\
    pip install--no-cache-dir-r/app/deploy/requirements.txt&&\
    apk del build-base--purge

RUN curl-L  https://github.com/skku-npc/skku-coding-platform/releases/download/v0.1-alpha/dist.tar.gz-o dist.tar.gz&&\
    tar-zxf dist.tar.gz&&\
    rm dist.tar.gz

ENTRYPOINT /app/deploy/entrypoint.sh

 

sudo 없이 사용하기

docker는 기본적으로 root권한이 필요함

root가 아닌 사용자가 sudo없이 사용하려면 해당 사용자를 docker그룹에 추가합니다.

sudo usermod -aG docker $USER # 현재 접속중인 사용자에게 권한주기
sudo usermod -aG docker your-user # your-user 사용자에게 권한주기

 

컨테이너 실행하기

도커를 실행하는 명령어는 다음과 같음.

docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

 

자주 사용하는 옵션들

옵션 설명
-d detached mode, 흔히 말하는 백그라운드 모드
-p 호스트와 컨테이너의 포트를 연결 (포워딩)
-v 호스트와 컨테이너의 디렉토리를 연결 (마운트)
-e 컨테이너 내에서 사용할 환경변수 설정
–name 컨테이너 이름 설정
–rm 프로세스 종료시 컨테이너 자동 제거
-it -i와 -t를 동시에 사용한 것으로 터미널 입력을 위한 옵션
–link 컨테이너 연결 [컨테이너명:별칭]

 

 

YAML (.yml)

야믈 이라고 읽음(Yet Another Markup Language)

사람이 쉽게 읽을 수 있는 데이터 직렬화 양식.

현재는 주로 XML과 JSON이 데이터 직렬화에 사용되고, YAML 은 ‘가벼운 마크업 언어’ 로 사용하려고 하고 있음.

 

 

Docker Compose

도커는 복잡한 설정을 쉽게 관리하기 위해 YAML방식의 설정파일을 이용한 Docker Compose라는 툴을 제공함. Docker Compose에서는 컨테이너 실행에 사용되는 옵션과 컨테이너 간 의존성을 모두 docker-compose.yml 파일에 적어두고, 컴포즈용 명령어를 사용하여 컨테이너들을 실행, 관리합니다.

  • docker build vs. docker-compose build: 도커 이미지 만들기(=클래스 선언을 애플리케이션에 로드)
  • docker run의 옵션들 vs. docker-compose.yml: 이미지에 붙이는 장식들(=인스턴스의 변수들)
  • docker run vs. docker-compose up: 장식 붙은 이미지를 실제로 실행(=인스턴스 생성)

클래스와 인스턴스의 예를 들면, 어떤 인스턴스를 자주 생성해야 해서 필요한 변수를 딕셔너리로 저장해두는 것과 비슷함. 이를 그림으로 나타내면 다음과 같음.

version: "3.8"
services:
  oj-redis:
    image: redis:4.0-alpine
    container_name: oj-redis
    restart: always
    volumes:
      - ./data/redis:/data

  oj-postgres:
    image: postgres:10-alpine
    container_name: oj-postgres
    restart: always
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=onlinejudge
      - POSTGRES_USER=onlinejudge
      - POSTGRES_PASSWORD=onlinejudge

  judge-server:
    image: skkunpc/judge-server
    container_name: judge-server
    restart: always
    read_only: true
    cap_drop:
      - SETPCAP
      - MKNOD
      - NET_BIND_SERVICE
      - SYS_CHROOT
      - SETFCAP
      - FSETID
    tmpfs:
      - /tmp
    volumes:
      - ./data/backend/test_case:/test_case:ro
      - ./data/judge-server/log:/log
      - ./data/judge-server/run:/judger
    environment:
      - SERVICE_URL=http://judge-server:8080
      - BACKEND_URL=http://coding-platform:8000/api/judge_server_heartbeat/
      - TOKEN=CHANGE_THIS
# - judger_debug=1

coding-platform:
    image: skkunpc/coding-platform
    container_name: coding-platform
    restart: always
    depends_on:
      - oj-redis
      - oj-postgres
      - judge-server
    volumes:
      - ./data/backend:/data
    environment:
      - POSTGRES_DB=onlinejudge
      - POSTGRES_USER=onlinejudge
      - POSTGRES_PASSWORD=onlinejudge
      - JUDGE_SERVER_TOKEN=CHANGE_THIS
			# - FORCE_HTTPS=1
      # - STATIC_CDN_HOST=cdn.oj.com
ports:
      - "0.0.0.0:80:8000"
      - "0.0.0.0:443:1443"

 

Volume

Docker 컨테이너(container)에 쓰여진 데이터는 기본적으로 컨테이너가 삭제될 때 함께 사라지게 됨. Docker에서 돌아가는 많은 애플리케이션이 컨테이너의 생명 주기와 관계없이 데이터를 영속적으로 저장을 해야하고, 여러 개의 Docker 컨테이너가 하나의 저장 공간을 공유해서 데이터를 읽거나 써야 함.

이렇게 Docker 컨테이너의 생명 주기와 관계없이 데이터를 영속적으로 저장할 수 있도록 Docker는 두가지 옵션을 제공함. 첫번째는 Docker 볼륨(volume), 두번째는 바인드 마운트(bind mount).

 

데이터 볼륨을 생성하려면 docker container run 명령에 -v 옵션을 사용하면 됨.

docker container run [options] -v 호스트_디렉터리:컨테이너_디렉터리 리포지토리명[:태그] [명령] [명령인자]

 

Portainer

Docker의 이미지, 컨테이너, 네트워크등을 쉽게 관리할 수 있게 도와주는 GUI Web 서비스

오픈소스로 배포되고 있어 무료로 사용이 가능하며, shell 프롬프트에서 Docker 명령을 일일이 수행할 필요가 없이 Web UI 로 손쉽게 관리 할 수 있음.

Portainer | Open Source Container Management GUI for Kubernetes, Docker, Swarm
Portainer is a universal container management tool. It works with Kubernetes, Docker, Docker Swarm and Azure ACI and allows you to manage containers without needing to know platform-specific code.
https://www.portainer.io/

 

 

Docker Hub

도커에서 제공하는 이미지 호스팅 서비스

 

Docker Hub
https://hub.docker.com/

클라우드 상에 저장소를 생성하여 도커 이미지를 저장해 둘 수 있고, 필요 시마다 로컬 PC에 다운로드하여 사용할 수 있음.

 

728x90