Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

nomad-programmer

[DevOps/Docker] 여러 컨테이너 관리 및 Docker Compose의 개요 본문

DevOps/Docker

[DevOps/Docker] 여러 컨테이너 관리 및 Docker Compose의 개요

scii 2020. 12. 4. 02:04

Docker에서 움직이는 웹 애플리케이션을 제품 환경에서 운용할 때는 애플리케이션 서버, 로그 서버, 프록시 서버 등과 같이 여러 개의 컨테이너들을 연계하여 작동시킨다.

여러 컨테이너 관리

웹 시스템의 실행 환경은 웹 서버, 프록시 서버 등과 같이 역할이 다른 여러 개의 서버를 연계하여 작동시키는 것이 일반적이다. 그래서 Docker에서 여러 개의 컨테이너를 연계시켜 운용 관리할 때 알아두어야 할 기초 지식에 대해 설명한다.


웹 3계층 시스템 아키텍처

애플리케이션을 가동시키려면 여러 개의 서버에 기능과 역할을 분할하여 인프라의 전체 구성을 정한다. 이것을 인프라 아키텍처라고 한다. 

아키텍처는 우리말로 '설계 사상' 이라는 뜻이다.

대규모 웹 시스템의 경우 몇 개의 서브 시스템/기능으로 나누어 시스템을 개발하는 경우가 많기 때문에 애플리케이션 개발 기술 및 플랫폼 기술에 정통한 IT 아키텍트를 중심으로 인프라의 처리 방식을 결정한다.

웹 3계층 아키텍처는 웹 애플리케이션의 대표적인 인프라 아키텍처 중 하나로, 웹 시스템의 서버들을 역할별로 다음 3개로 나누는 사상을 말한다.

프론트 서버 (Frontend Server)

클라이언트의 웹 브라우저가 보낸 HTTP 요청을 받아, HTTP 응답을 반환하는 서버 기능을 가진다. 이 서버 기능은 웹 프론트 서버 또는 그냥 웹 서버라고 한다.

웹 서버의 기능은 미들웨어로 구축하는 경우도 있으며, 오픈소스인 Nginx, Microsoft의 IIS(Internet INformation Services), Apach, Tomcat 등이 있다. 요청의 처리가 메인 업무이므로 부하가 높은 경우는 스케일러블하게 처리 대수를 늘리고, 로드밸런서 등과 같은 기기를 사용하여 부하분산을 한다.

애플리케이션 서버 (Backend Server)

애플리케이션 서버는 업무 처리를 실행하는 서버이다. 결제 처리, 수주 처리 등 애플리케이션의 처리를 실행하는 프로그램의 실행 환경이 된다. 애플리케이션 서버 기능도 프론트 서버 기능과 마찬가지로 미들웨어로 구축하는 경우도 있다.

데이터베이스 (DB) 서버

데이터베이스 서버는 영구 데이터를 관리하기 위한 서버이다. 애플리케이션의 처리 실행에서 발생하는 영구 데이터는 RDBMS(Relational Database Management System) 기능을 갖고 있는 미들웨어에서 관리된다. 주요 RDBMS로는 오픈소스인 MariaDB, MySQL, PostgreSQL, Oracle의 Oracle Database 등이 있다.

또한 NoSQL은 RDBMS와는 다른 새로운 방식을 총칭하는 것이다. 병렬분산처리나 유연한 스키마 설정 등이 특징이며, 주요 방식으로는 KVS(Key-Value 스토어)나 도큐먼트 지향 데이터베이스(Document Database) 등이 있다. 대량의 데이터 축적이나 병렬처리가 특이기 때문에 많은 사용자의 액세스를 처리할 필요가 있는 온라인 시스템 등에서 널리 이용되고 있다. 

오픈소스인 Redis나 MongoDB 등이 유명하다.

영구 데이터는 높은 가용성이 요구되기 때문에 클러스터링과 같은 기술로 다중화하는 경우가 많다. 또한 만일의 장애에 대비하여 데이터의 백업이나 원격지 보관 등과 같은 대책이 필요하다. 데이터베이스를 조작하는 처리는 부하가 걸리는 경우도 있으므로 시스템 전체의 병목 부분이 되는 경우도 있다. 그래서 운용 상황에 따라 OS나 미들웨어의 파라미터 설정의 변경과 같은 퍼포먼스 튜닝이 필요하다.

대부분의 웹 시스템은 이 웹 3계층 아키텍처 구성을 취하고 있다. 논리적인 분할이므로 부하가 적은 시스템에서도 동일한 노드 상에서 실행하는 것도 가능하며, 클라우드 시스템 등에서 실행시킬 때는 부하에 따른 오토스케일(Auto-Scaling) 기능을 사용하는 것이 좋다.


영구 데이터의 관리

시스템을 운용하다 보면 다양한 데이터가 생성되고 축적된다. 이러한 데이터는 프로그램이 종료되어도 스토리지와 같은 기억장치에 저장된다. 이러한 데이터를 영구 데이터라고 하는데, 시스템의 가동 시간에 따라 증감하거나 변화해 간다는 특징이 있다. 그런데 스토리지의 저장 영역에는 한계가 있으며 고장 등으로 데이터가 소멸될 가능성도 있기 때문에 이러한 영구 데이터를 적절히 관리할 필요가 있다.

데이터의 백업 및 복원

서버의 장애에 대비해 데이터를 백업한다. 데이터베이스 안에는 업무에 대한 기밀 정보도 포함되어 있으므로 보안 대책이나 적절한 운용 규칙을 정할 필요가 있다. 백업은 테이프 디바이스와 같은 물리 매체인 경우도 있는가 하면 클라우드상의 스토리지 서비스를 이용하는 경우도 있다. 또한 재해에 대비하여 원격지에 보관하는 경우도 있다. 그리고 글로벌하게 사업을 전개하는 퍼블릭 클라우드 서비스를 이용함으로써 해외의 데이터센터에 백업을 저가로 보관할 수도 있다.

로그 수집

시스템이나 애플리케이션은 각 서버상에 저장하는 경우도 있지만 여러 개의 서버로 된 분산 환경에서 통합 감시를 하는 경우는 로그 수집 전용 서버를 마련하는 것이 일반적이다. 또한 사용자 인증 시의 액세스 로그 등은 보안의 감사 로그로서 장기 저장이 의무화되는 경우도 있다. 

Unix 계열 OS의 경우 syslogd라는 데몬을 사용하여 커널이나 애플리케이션으로부터 수집한 로그를 관리한다.

Docker는 컨테이너를 사용하여 서버 기능을 제공한다. 컨테이너는 필요에 따라 생성하거나 파기하는 운용에 적합하며, 하나의 컨테이너에는 하나의 프로세스를 구성해 두는 것이 대원칙이다.

그래서 애플리케이션의 실행 모듈이나 각종 라이브러리의 모듈, 미들웨어의 설정 파일 등은 서버 프로세스가 작동하는 컨테이너 안에 저장하고, 시스템 가동 후에 생성되는 영구 데이터에 대해서는 별도의 인프라 아키텍처를 검토하는 것이 바람직하다.


컨테이너에서 영구 데이터를 다룰 때 주의점

웹 시스템에서는 데이터 스토어에 고객 정보나 매출 정보와 같은 중요한 데이터가 저장된다. 이러한 데이터는 사라지면 매우 곤란하기 때문에 미션 크리티컬 시스템이라 불리는 시스템은 대부분 다중 구성을 하거나 만일의 장애나 사고에 대비하고 있다. 또한 원격지에 백업이나 세대 관리 등도 하는 것이 일반적이다. 인프라 구축이나 운용에서 가장 어려운 점은 바로 이러한 영구 데이터의 관리에 있다고 해도 과언이 아니다.

한편 Docker 컨테이너는 웹 프론트 서버와 같이 트래픽의 증감 등에 맞춰 필요할 때 실행하고 필요 없어지만 파기시킨다는 일회성 운용에 적합하다.

그 때문에 컨테이너 안은 중요한 영구 데이터를 저장하는 데는 적합하지 않다. 따라서 인프라 전체의 아키텍처나 엄격한 운용 규칙 등에 맞춰 어떤 시스템을 컨테이너에서 운용할지를 검토할 필요가 있다.  예를 들면 미션 크리티컬 시스템을 컨테이너에서 운용하는 것이 적절한지 숙고하는 것이 중요하다.

Docker의 기능으로서는 데이터 전용 컨테이너에서 데이터를 관리하는 방법이나 로컬 호스트를 마운트하여 영구 데이터를 저장해 두는 방법 등이 있다. 어떤 방법이든 데이터의 증가량이나 I/O량은 어느 정도인지, 데이터를 어느 정도로 다중화할지, 어떤 운용으로 백업/복원을 해야 할지를 정해 두는 것이 필요하다.

Docker는 애플리케이션 실행 환경의 측면에서 보면 트래픽의 변동이 큰 대규모 웹 시스템이나 여러 디바이스로부터 데이터를 처리할 필요가 있는 IoT, 머신 리소스를 유효하게 활용하여 대량의 처리를 단시간에 하는 과학기술계산 분야 등에서 위력을 발휘한다. 이 때문에 비용 절감을 목적으로 기존 시스템을 이전하는 용도보다 지금까지 없었던 새로운 시스템을 검토하거나 도입하는 용도에 더 적합하다.

Docker는 애플리케이션 환경과 인프라 환경을 모아서 구성 관리를 할 수 있는 Dockerfile, Docker Image, Docker Registry를 사용함으로써 애플리케이션의 이식성이 올라간다는 큰 장점을 갖고 있다. 따라서 애플리케이션 개발 환경/테스트 환경이나 데모 환경 등 높은 이식성을 요구되는 경우부터 검토해갈 것을 권장한다. Docker는 도입만 하면 기존의 시스템의 문제점을 모두 해결하는 그런 '만능 카드'가 아니므로, 시스템 전체를 조감하여 적재적소에 Docker를 도입해야 한다.


Docker Compose

웹 시스템에서는 여러 개의 Docker 컨테이너가 협력하면서 작동한다. Docker Compose는 여러 컨테이너를 모아서 관리하기 위한 툴이다.

Docker Compose는 'docker-compose.yml' 이라는 파일에 컨테이너의 구성 정보를 정의함으로써 동일 호스트상의 여러 컨테이너를 일괄적으로 관리할 수 있다.

Docker Compose

Compose 정의 파일은 웹 애플리케이션의 의존관계 (데이터베이스, 큐, 캐시, 애플리케이션 등)를 모아서 설정할 수 있다. 이 정의 파일을 바탕으로 docker-compose 명령을 실행하면 여러 개의 컨테이너를 모아서 시작하거나 정지할 수 있다. 또한 컨테이너의 구성 정보를 YAML 형식의 파일로 관리할 수 있으므로 지속적 deploy 이나 지속적 인티그레이션 프로세스에 있어서 자동 테스트를 할 때의 환경 구축에도 그대로 이용할 수 있다.


Docker Compose를 이용한 웹 애플리케이션 서버 구축

다음과 같은 구성의 웹 애플리케이션 실행 환경을 구축해보자.

웹 컨테이너의 구성

# app.py

#!/usr/bin/env python3
# encoding=utf8

from flask import Flask, render_template
from redis import Redis
import os, random

appp = Flask(__name__)
redis = Redis(host='redis', port=5678)

images = [
    'img01.jpg',
    'img02.jpg',
    'img03.jpg'
]

@app.route('/')
def index():
    image_path = '/static/images/' + random.choice(images)
    count = redis.incr('count')
    return render_template('index.html', image_path=image_path, access_count=count)
    
if __name__ == '__main__':
    port = int(os.environ.get('PORT', 80))
    try:
        app.run(host='0.0.0.0', port=port, debug=True)
    except Exception as err:
        print(err)
// requirements.txt

Flask==1.0
redis
// Dockerfile

# base image
FROM python:3.6

# maintainer
LABEL maintainer "scii"
LABEL version "1.0"

# upgrade pip
RUN pip install --upgrade pip

# install path
ENV APP_PATH=/opt/imageview

# 파이썬 모듈 설치
COPY requirements.txt ${APP_PATH}/
RUN pip install --no-cache-dir -r ${APP_PATH}/requirements.txt

# 파일/디렉토리 복사
COPY app.py ${APP_PATH}/
COPY templates/ ${APP_PATH}/templates/
COPY static/ ${APP_PATH}/static/

# port 설정
EXPOSE 80

# flask 서버 실행
CMD ["python", "/opt/imageview/app.py"]
// docker-compose.yml

version: '3.3'
services:
  # webserver 서비스
  webserver:
    build: .
    ports:
     - "80:80"
    depends_on:
     - redis

  # Redis 서비스
  redis:
    image: redis:4.0

Compose 정의 파일의 맨 앞에는 버전을 지정한다. 위의 구성에서는 'webserver'와 'redis' 라는 이름의 서비스 2개를 정의하고 있다.

webserver 서비스는 현재 디렉토리에 있는 Dockerfile에 정의한 구성의 이미지를 빌드 (build: .) 하고, 외부에 대해 80번 포트를 공개하여 컨테이너를 시작한다. 또한 이 컨테이너는 redis 서비스에 의존 (depends_on:) 하고 있다.

redis 서비스는 Docker Hub에 공개되어 있는 Redis의 공식 이미지인 'redis' 이미지로 하여 컨테이너를 시작하고 있다.


여러 Docker 컨테이너 시작

이제 다음의 명령을 실행하여 정의한 2개의 컨테이너를 시작한다. Docker Compose 에서는 각각의 컨테이너를 작동시키기 위한 이미지의 다운로드나 빌드를 하나의 명령으로 모두 실행한다.

// 웹 애플리케이션의 시작

$ docker-compose up

Pulling redis (redis:4.0)...
4.0: Pulling from library/redis
Status: Downloaded newer image for redis:4.0
...
Building webserver
Step 1/11 : FROM python:3.6
3.6: Pulling from library/python
...

로그를 확인해 보면 먼저 Docker Hub로부터 redis 서비스에서 사용할 redis:4.0의 이미지가 다운로드된다. 그 다음 webserver에서 사용할 이미지가 Dockerfile을 바탕으로 빌드된다.

그리고 준비가 끝난 redis 서비스의 컨테이너를 시작하고, 계속해서 webserver 서비스의 컨테이너가 시작된다.

가동 중인 컨테이너의 상태를 확인할 때는 다음의 명령을 실행하면 된다.

// 컨테이너 확인

$ docker-compose ps

컨테이너를 확인하면 redis 컨테이너와 webserver 컨테이너가 각각 하나씩 실행되고 있다는 것을 알 수 있다.


여러 Docker 컨테이너 정지 / 삭제

Docker Compose에서 관리하고 있는 컨테이너들을 정지시킬 때는 다음의 명령을 실행한다.

// 컨테이너 정지

$ docker-compose stop

Docker Compose에서 이용한 리소스를 삭제하려면 다음의 명령을 실행한다. 그러면 docker-compose에서 만들어졌던 컨테이너들이 삭제될 것이다.

// 리소스 삭제

$ docker-compose down

이와 같이 여러 의존관계에 있는 컨테이너들을 모아서 관리할 수 있는 것이 Docker Compose 이다. 

Comments