로드밸런싱 알고리즘 성능 비교 연구[3]

Chu Sang Yoon·2025년 7월 17일

lab

목록 보기
3/11

3-1. Docker 개념 익히기

들어가며

프로그램 개발시 로컬에서는 돌아가지만 서버에서는 안되는 경우가 빈번하게 발생합니다

개발자의 로컬환경과 운영 서버의 환경이 달라서 발생하는 문제입니다

그렇다면 개발 환경을 통째로 박스에 담아서 가져갈 수 있다면?

⇒ 그 박스가 바로 Docker입니다

1. Docker가 해결하는 문제

1.1 전통적인 개발 환경의 문제점

개발자의 환경

- Windows 10
- Java 8
- MySQL 5.7
- Node.js 14.x

서버 환경

- Ubuntu 20.04
- Java 11
- MySQL 8.0
- Node.js 16.x

→ 개발 환경이 다르기 때문에 코드가 같더라도 다르게 동작하거나 실행이 안될 수 있음

1.2 환경 차이로 인한 문제들

라이브러리 버전 충돌

  • A 프로젝트: Python 3.7 + Django 2.2
  • B 프로젝트: Python 3.9 + Django 4.0
  • 같은 컴퓨터에서 둘 다 개발하기 어려움

운영체제 의존성

  • Windows에서 개발한 프로그램
  • Linux 서버에서 실행 시 경로, 라이브러리 문제발생

복잡한 설치 과정

  • 새로운 팀원이 합류할 때마다 개발 환경 설정에 시간 소모

1.3 Docker의 해결책

“Build once, Run anyWhere”

Docker는 애플리케이션과 그 실행환경을 하나의 패키지로 묶어서 어디서든 동일하게 실행하도록 함

[애플리케이션 + 실행환경] = Docker 컨테이너

2. Docker의 핵심 개념

2.1 가상화 vs 컨테이너화

전통적인 가상화(Virtual Machine)

물리 서버
├── 호스트 OS (Windows/Linux)
├── 하이퍼바이저 (VMware, VirtualBox)
└── VM1
    ├── 게스트 OS (완전한 Ubuntu)
    ├── 라이브러리들
    └── 애플리케이션
└── VM2
    ├── 게스트 OS (완전한 CentOS)
    ├── 라이브러리들
    └── 애플리케이션
  • 문제점
    • 각 VM마다 완전한 OS가 필요
    • 메모리, CPU 낭비 심함
    • 부팅 시간 오래걸림

Docker 컨테이너화

물리 서버
├── 호스트 OS (Linux)
├── Docker Engine
└── 컨테이너1
    ├── 애플리케이션 라이브러리만
    └── 애플리케이션
└── 컨테이너2
    ├── 애플리케이션 라이브러리만
    └── 애플리케이션
  • 단점
    • OS 커널 공유로 리소스 효율성
    • 초 단위 빠른 시작
    • 가벼운 용량

2.2 Docker의 핵심 구성요소

Docker Image(도커 이미지)

  • 애플리케이션 실행에 필요한 모든 것을 포함한 템플릿
  • 읽기 전용
  • 레이어 구조로 구성

Docker Container(컨테이너)

  • 이미지를 실행한 인스턴스
  • 실제로 프로세스가 돌아가는 공간
  • 격리된 환경

Dockerfile

  • 이미지를 만들기 위한 설명서
  • 텍스트 파일로 작성
  • 예시
# Java 17 베이스 이미지
FROM openjdk:17-jdk-slim

# 작업 디렉터리 설정
WORKDIR /app

# Gradle wrapper와 설정 파일 복사
COPY gradlew ./
COPY gradle ./gradle
COPY build.gradle ./
COPY settings.gradle ./

# gradlew 실행 권한 부여
RUN chmod +x gradlew

# 의존성 다운로드 (캐시 최적화)
RUN ./gradlew dependencies --no-daemon

# 소스 코드 복사
COPY src ./src

# 애플리케이션 빌드 (테스트 제외)
RUN ./gradlew clean build -x test --no-daemon

# JAR 파일 위치 확인 및 복사 (plain.jar 제외)
RUN find /app/build/libs -name "*.jar" ! -name "*plain.jar" -exec cp {} /app/app.jar \;

# 애플리케이션 실행
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

Docker Registry

  • 이미지를 저장하고 공유하는 저장소
  • 예시: Docker Hub

2.3 이미지와 컨테이너의 관계

클래스와 인스턴스(=객체) 같은 관계

이미지 (설계도)     →     컨테이너 (실행체)
Ubuntu:20.04      →     ubuntu_container1
                  →     ubuntu_container2
                  →     ubuntu_container3

Node.js:16        →     web_server1
                  →     web_server2
  • 하나의 이미지로 여러개의 컨테이너를 실행시킬 수 있음

3. Docker의 장점과 활용 분야

3.1 주요 장점

  1. 일관된 환경
  • 개발, 테스트, 운영 환경 동일화
  1. 빠른 배포
  • 몇 초만에 애플리케이션 시작 가능
  • 롤백도 즉시 가능
  1. 확장성
  • 필요에 따라 컨테이너 개수 조절
  • 마이크로 서비스 아키텍처에 최적
  1. 격리성
  • 각 컨테이너는 독립적
  • 하나가 죽어도 다른 것에 영향 X
  1. 효율성
  • VM보다 훨씬 가벼움
  • 리소스 효율적 사용

3.2 주요 활용 분야

마이크로서비스 아키텍처

전체 서비스를 작은 단위로 분할
- 사용자 인증 서비스 (컨테이너)
- 결제 서비스 (컨테이너)  
- 알림 서비스 (컨테이너)
- 상품 관리 서비스 (컨테이너)

CI/CD 파이프라인

개발 → 테스트 → 배포 과정 자동화
각 단계마다 동일한 Docker 환경 사용

클라우드 마이그레이션

온프레미스 → AWS/Azure/GCP
Docker 컨테이너로 쉽게 이전

4. Dockerfile 작성방법

4.1 Dockerfile 기본 구조

# 베이스 이미지 선택
FROM node:16-alpine

# 작업 디렉토리 설정
WORKDIR /app

# 패키지 파일 복사
COPY package*.json ./

# 의존성 설치
RUN npm install

# 소스 코드 복사
COPY . .

# 포트 노출
EXPOSE 3000

# 실행 명령
CMD ["npm", "start"]

4.2 주요 Dockerfile 명령어

FROM

  • 베이스 이미지 지정
  • 모든 Dockerfile은 FROM으로 시작

WORKDIR

  • 컨테이너 내 작업 디렉토리 설정
  • 이후 명령어들의 기본 경로

COPY vs ADD

  • COPY: 파일/디렉토리 복사(권장)
  • ADD: 압축 해제, URL 다운로드 기능 추가

RUN

  • 이미지 빌드 시 실행할 명령어
  • 패키지 설치, 환경 설정 등

EXPOSE

  • 컨테이너가 사용할 포트 명시
  • 실제 포트 바인딩은 docker run에서 이루어짐

CMD vs ENTRYPOINT

  • CMD: 기본 실행 명령(덮어쓰기 가능)
  • ENTRYPOINT: 고정 실행 명령

4.3 Dockerfile 작성 팁

  1. 레이어 캐싱 활용
# 변경이 적은 것을 먼저
COPY package*.json ./
RUN npm install

# 변경이 많은 것을 나중에  
COPY . .
  1. .dockerignore 사용
node_modules
.git
.env
*.log
  1. 멀티 스테이지 빌드
# 빌드 스테이지
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 실행 스테이지
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
  • 기존 방식
    • 이미지 크기 거대: 개발 도구들까지 포함
    • 보안취약: 소스코드, 개발 도구 노출
    • 비용증가: 큰 이미지 = 느린 배포 = 높은 네트워크 비용
  • 멀티 스테이지 빌드
    • 빌드는 따로, 실행은 따로
    • 이미지 크기 최적화가능
    • 보안 향상
    • 배포속도 향상과 비용 절검

5. Docker Compose

5.1 Docker Compose가 필요한 이유

복잡한 애플리케이션의 문제

웹 애플리케이션 실행시

  • 웹 서버 컨테이너
  • 데이터베이스 컨테이너
  • 캐시 서버 컨테이너
  • 로드밸런서 컨테이너

→ 개별적으로 실행하기에는 너무 복잡함

Docker Compose의 해결책

→ 하나의 YAML 파일로 여러 컨테이너를 정의하고 함께 관리

5.2 docker-compose.yml의 예시 석

name: returnwork

services:
  app:
    image: ${DOCKER_IMAGE}
    ports:
      - "8080:8080"
    depends_on:
      - mysql
      - redis
    deploy:
      resources:
        limits:
          memory: 400M
    environment:
      - SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-prod,jwt,redis}
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/${DB_NAME:-returnWork}
      - SPRING_DATASOURCE_USERNAME=${DB_USERNAME:-root}
      - SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD:-1234}
      - JWT_SECRET=${JWT_SECRET:-}
      - SPRING_DATA_REDIS_HOST=redis
      - SPRING_DATA_REDIS_PORT=6379
    networks:
      - app-network
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_PASSWORD:-1234}
      - MYSQL_DATABASE=${DB_NAME:-returnWork}
      - MYSQL_USER=${DB_USERNAME:-chusammin}
      - MYSQL_PASSWORD=${DB_PASSWORD:-1234}
    ports:
      - "3306:3306"
    deploy:
      resources:
        limits:
          memory: 400M
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - app-network
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    deploy:
      resources:
        limits:
          memory: 100M
    volumes:
      - redis_data:/data
    networks:
      - app-network
    restart: unless-stopped
    command: redis-server --appendonly yes

volumes:
  mysql_data:
  redis_data:

networks:
  app-network:
    driver: bridge

전체 구조 분석

	┌─────────────────────────────────────┐
	│           app-network               │
│  ┌──────────┐  ┌──────────┐  ┌────────┐ │
│  │   app    │  │  mysql   │  │ redis  │ │
│  │ :8080    │  │ :3306    │  │ :6379  │ │
│  └──────────┘  └──────────┘  └────────┘ │
	└─────────────────────────────────────┘
  1. App 서비스(Spring Boot)
app:
  image: ${DOCKER_IMAGE}           # 환경변수로 이미지 지정 
  ports: "8080:8080"              # 웹 서버 포트
  memory: 400M                    # 메모리 제한 
  restart: unless-stopped         # 자동 재시작 
  • 환경변수 활용: ${DOCKER_IMAGE} - CI/CD에서 동적으로 변경 가능
  • 의존성 관리: depends_on 으로 DB/캐시 서버 먼저 실행
  • Spring프로필: prod , jwt , redis - 운영환경 설정
  1. MySQL 서비스(데이터 베이스)
mysql:
  image: mysql:8.0                # 안정적인 8.0 버전
  environment:
    - MYSQL_DATABASE=returnWork   # 자동 DB 생성
    - MYSQL_USER=chusammin       # 별도 사용자 생성 
  volumes:
    - mysql_data:/var/lib/mysql   # 데이터 지속성 
  • 데이터 지속성: 볼륨으로 데이터 보존
  • 보안: root외 별도 사용자 생성
  • 자동 초기화: DB와 사용자 자동 생성
  1. Redis 서비스 (캐시/세션)
redis:
  image: redis:7-alpine           # 가벼운 Alpine 버전
  command: redis-server --appendonly yes  # 데이터 지속성 활성화
  memory: 100M                    # 적은 메모리 할당
  volumes:
    - redis_data:/data            # 캐시 데이터 지속성
  • 경량화: Alpine 이미지로 용량 최소화
  • AOF 모드: --appendonly yes 로 데이터 손실 방지
  • 적절한 메모리: 캐시용으로 100MB면 충분

5.3 주요 Docker Compose 명령어

# 모든 서비스 시작
docker-compose up

# 백그라운드 실행
docker-compose up -d

# 모든 서비스 중지
docker-compose down

# 특정 서비스만 실행
docker-compose up web

# 로그 확인
docker-compose logs

# 스케일링 (컨테이너 개수 조절)
docker-compose up --scale web=3

6. Docker 네트워킹

6.1 Docker 네트워크 종류

Bridge Network(기본)

  • 같은 호스트의 컨테이너들 간 통신
  • 외부와의 연결은 포트 포워딩 필요

Host Network

  • 호스트의 네트워크를 직접 사용
  • 성능이 좋지만 격리성 떨어짐

Overlay Network

  • 여러 Docker 호스트에 걸쳐 있는 네트워크
  • Docker Swarm에서 사용

None Network

  • 네트워크 연결 없음
  • 완전 격리된 환경

6.2 컨테이너 간 통신

컨테이너 이름으로 통신

services:
  web:
    image: nginx
  
  api:
    image: node:16
    # web 컨테이너에 접근할 때
    # http://web:80 으로 접근 가능

포트 매핑

# 호스트의 8080 포트를 컨테이너의 80 포트에 연결
docker run -p 8080:80 nginx

7. Docker 볼륨과 데이터 관리

7.1 데이터 지속성 문제

컨테이너는 기본적으로 임시적인 특징

  • 컨테이너 삭제 시 내부 데이터도 함께 삭제
  • 데이터베이스 데이터, 업로드 파일 등은 별도 관리 필요

7.2 Docker 볼륨 종류

Named Volumne(권장)

# 볼륨 생성
docker volume create my_data

# 볼륨 사용
docker run -v my_data:/app/data nginx

Bind Mount

# 호스트 디렉토리를 컨테이너에 마운트
docker run -v /host/path:/container/path nginx

tmpfs Mount

# 메모리에만 저장 (임시 데이터용)
docker run --tmpfs /tmp nginx

8. Docker 보안

8.1 주요 보안 고려사항

컨테이너 격리

  • 각 컨테이너는 독립적인 프로세스 공간
  • 하지만 호스트 커널은 공유

권한 관리

  • 루트 권한으로 실행하지 않기
  • 필요 최소한의 권한만 부여

이미지 보안

  • 신뢰할 수 있는 베이스 이미지 사용
  • 정기적인 보안 업데이트

8.2 보안 모범 사례

  1. 비-루트 사용자 사용
FROM node:16-alpine

# 사용자 생성
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# 사용자 전환
USER nextjs

COPY --chown=nextjs:nodejs . .
CMD ["npm", "start"]
  1. 민감한 정보 관리
# 환경변수로 비밀번호 전달 (비추천)
docker run -e DB_PASSWORD=secret myapp

# Docker Secrets 사용 (권장)
echo "secret_password" | docker secret create db_password -
  1. 이미지 스캔
# 보안 취약점 스캔
docker scan myapp:latest

9. Docker vs 다른 기술들

9.1 Docker vs VM

구분DockerVirtual Machine
리소스 사용적음많음
시작 시간초 단위분 단위
격리 수준프로세스 레벨하드웨어 레벨
호환성Linux 기반OS 무관

9.2 Docker vs Kubernetes

Docker

  • 단일 호스트에서 컨테이너 실행
  • 개발, 테스트 환경에 적합
  • 간단한 마이크로서비스

Kubernetes

  • 여러 호스트에서 컨테이너 오케스트레이션
  • 대규모 운영 환경
  • 자동 확장, 로드밸런싱, 장애 복구

9.3 언제 Docker를 사용할까

Docker 사용 권장

  • 마이크로서비스 아키텍처
  • 개발 환경 통일화 필요
  • CI/CD 파이프라인 구축
  • 클라우드 마이그레이션
  • 레거시 애플리케이션 현대화

Docker 사용 주의

  • 단일 모놀리식 애플리케이션 (큰 장점 X)
  • GUI 애플리케이션
  • 높은 보안이 필요한 환경(추가 보안 조치 필요)

마무리

이제 Docker의 기본 개념을 이해했으니, 다음편에서는 실제로 Docker Compose를 사용해서

4개의 웹서버 환경을 구축하고 로드밸런싱 테스트를 위한 기반을 마련하겠습니다.

Phase 1: 계획 및 이론

  • 📋 1편: 목적 및 계획 - 연구 동기와 전체 로드맵
  • 🔍 2편: 로드밸런싱 정복 - 개념, 필요성, 종류

Phase 2: 환경 구축 및 설계

  • 🐳 3-1편: Docker 개념 학습하기 - 개념, 특징, 명령어
  • 🐳 3-2편: Docker 실습 - 4개 서버 환경 구축하기
  • ⚙️ 4편: 6가지 알고리즘 탐구 - 각각의 원리와 특징

Phase 3: 구현 및 테스트

  • 💻 5편: Spring Boot로 구현하기 - 실제 코딩 과정
  • 📊 6편: 성능 테스트 & 결과 분석 - 21회 테스트 데이터

Phase 4: ML 확장 및 마무리

  • 🧠 7편: ML로 똑똑하게 만들기 - Flask + 머신러닝
  • 🎯 8편: 최종 결과 및 후기 - 성과와 아쉬운 점

0개의 댓글