✅ 배포 방식의 변화 과정

▶ 전통적 배포 (Traditional Depolyment)

  • 구성: 하드웨어 -> 운영체제 -> 애플리케이션
  • 설명: 물리적인 컴퓨터 한 대에 하나의 OS를 설치하고, 애플리케이션이 해당 운영 체제에서 직접 실행되는 방식이다.
  • 문제점
    • 자원 공유 문제: 애플리케이션들이 동일한 OS를 공유하므로, 같은 리소스(라이브러리, 네트워크, CPU)를 사용하게 된다. 이로 인해 성능 문제가 발생하게 된다.
    • 확장성 부족: 새로운 애플리케이션을 설치하거나 추가하려면 운영 체제 및 하드웨어의 환경에 맞게 설치하고 설정해야 한다.
  • 배포 방식
    • 하나의 물리적 서버가 있다.
    • 물리적 서버 위에 운영 체제를 설치한다.
    • 동일한 운영 체제에서 여러 프로덕트를 실행한다. 발생하는 문제들을 피하려면 물리적 서버를 추가로 구매해야 한다.

 

▶ 가상화 배포 (Virtualized Deployment)

  • 구성: 하드웨어 -> 운영 체제 -> 하이퍼바이저 -> 가상 머신 (VM)
  • 설명: 하이퍼바이저를 통해 여러 가상 머신(VM)을 실행하는 방식이다. 각 VM은 자체 운영 체제를 가지고 있으며, 애플리케이션과 필요한 라이브러리가 포함되어 있어 독립적으로 실행된다. 
  • 문제점
    • 자원 사용의 비효율성: 각 VM이 자체 운영 체제를 포함하고 있어, 독립적인 프로덕트를 추가할 때마다 불필요한 OS 인스턴스가 생성되므로 확장성이 떨어지고 리소스 부담이 크다.
  • 배포 방식
    • 하나의 물리적 서버가 있다.
    • 물리적 서버 위에 하이퍼바이저를 설치한다.
    • 하이퍼바이저를 이용해 여러 개의 가상 머신(VM)을 생성한다. 각 가상 머신은 자체 운영 체제를 갖추고 있어 독립적인 서버처럼 동작한다.
    • 각 프로덕트를 각각의 가상머신에 배포한다.
가상화(Virtualization): 물리적 하드웨어 자원을 논리적으로 분리하여 여러 개의 가상 환경으로 사용할 수 있게 하는 기술이다. 즉, 하나의 서버에 여러 운영 체제를 동시에 설치하고, 이를 통해 여러 애플리케이션을 독립적으로 실행할 수 있는 방식이다.

 

▶ 컨테이너 배포 (Container Depolyment)

  • 구성: 하드웨어 -> 운영 체제 -> 컨테이너 런타임(Docker 등) -> 컨테이너
  • 설명: 컨테이너 배포는 애플리케이션과 필요한 라이브러리를 컨테이너라는 경량화된 가상 환경에서 실행하는 방식이다. 각 컨테이너는 운영 체제의 커널을 공유하지만, 서로 독립적으로 실행되며 필요한 라이브러리와 종속성을 포함한다. 컨테이너는 가상 머신과 달리 별도의 운영 체제를 포함하지 않으므로 자원 소모가 적고, 빠른 실행과 배포가 가능하다.
  • 문제점
    • 보안 격리 문제: 특정 프로그램이 운영 체제에 문제를 일으킬 경우, 해당 OS에서 실행 중인 다른 컨테이너에도 영향을 미칠 수 있다.
  •   방식
    • 하나의 물리적 서버가 있다.
    • 물리적 서버 위에 운영 체제를 설치한다.
    • 운영 체제 위에 컨테이너 런타임(Docker 등)을 설치한다. 컨테이너 런타임은 컨테이너를 생성하고 관리하는 역할을 한다.
    • 컨테이너 런타임을 이용해 여러 개의 컨테이너를 생성한다. 각 컨테이너는 애플리케이션과 필요한 라이브러리만을 포함한다.
    • 각 프로덕트를 각각의 컨테이너에 배포한다.
컨테이너(Container): 애플리케이션과 그에 필요한 모든 라이브러리, 종속성, 설정 파일 등을 하나의 단위로 묶어 독립적인 서버처럼 사용할 수 있는 경량화된 가상 환경이다.
컨테이너 런타임(Container Runtime): 컨테이너를 실제로 실행하고 관리하는 소프트웨어이다. 컨테이너 이미지에서 컨테이너를 생성하고, 이를 실행하거나 중지하는 등의 작업을 수행한다. 예로 Docker, containerd, CRI-O, runc 등이 있다.

 

▶ 정리

  전통적 배포 가상화 배포 컨테이너 배포
컴퓨터 물리적 컴퓨터 1대 물리적 컴퓨터 1대 + 다수의 가상 머신 컴퓨터 형태에 영향받지 않음
운영 체제 (OS) 물리적 컴퓨터 1대에 OS 1개 설치 물리적 컴퓨터 OS 1대 + 각 가상머신마다 OS 설치 컴퓨터 형태와 관계없이 OS 1개 설치
리소스 컴퓨터 자원을 여러 프로그램이 공유 사용 하이퍼바이저를 통해 가상 머신별 개별적 자원 할당 OS에서 각 컨테이너에 자원을 할당하고 관리
격리 수준 프로그램 간 격리되지 않음, 간섭 발생 가능 각 가상 머신이 완전히 격리됨 프로그램 실행 환경은 격리되지만, OS는 공유
문제 전이 가능성 특정 프로그램의 문제가 시스템 전체 중단 가능 특정 가상 머신의 문제는 다른 가상 머신에 영향 없음 특정 프로그램의 문제는 다른 프로그램에 영향을 미치지 않지만, 해당 문제가 OS에 영향을 줄 경우 시스템 중단 가능성 있음

 

 

✅ 도커 (Docker)

도커(Docker)는 애플리케이션을 신속하게 배포하고 운영하기 위해 컨테이너(Container) 기술을 사용하는 플랫폼이다. 

🔽 도커 구성 요소

  • 도커 파일 (Dockerfile)
  • 도커 이미지 (Docker Image)
  • 도커 컨테이너 (Docker Container)

 

▶ 도커 구성 요소: 1. 도커 파일 (Dockerfile)

Dockerfile은 Docker 이미지를 생성하기 위한 설정 파일이다. Dockerfile을 사용하면 이미지를 빌드하는 과정을 자동화하고, 빌드 과정에 대한 세부 사항을 정의할 수 있다. 이를 통해 이미지 빌드를 일관성 있게 반복할 수 있으며, 이미지 구성을 버전 관리 시스템으로 관리하기에도 용이하다.

🔽 Dockerfile 작성 단계

  1. 베이스 이미지 지정(FROM): 어떤 이미지(운영체제)를 기반으로 도커 이미지를 만들지 지정한다.
  2. 작업 디렉토리 설정(WORKDIR): 이미지 내에서 작업 디렉토리를 설정하여 파일을 추가하거나 명령을 실행할 위치를 지정한다.
  3. 필요한 파일 복사(COPY 또는 ADD): 호스트 시스템의 파일을 컨테이너 내부로 복사한다.
  4. 패키지 설치(RUN): 이미지를 빌드하는 과정에서 필요한 패키지나 소프트웨어를 설치한다.
  5. 환경변수 설정(ENV): 컨테이너 실행 시 필요한 환경 변수를 설정한다.
  6. 의존성 설치(RUN 또는 COPY 후 RUN): 필요한 라이브러리나 의존성을 설치한다.
  7. 포트 설정(EXPOSE): 컨테이너가 외부에서 접근 가능한 포트를 명시한다.
  8. 명령 실행(CMD 또는 ENTRYPOINT): 컨테이너가 실행될 때 기본으로 실행할 명령을 정의한다.

🔽 Dockerfile 명령어

명령어 설명 예시
FROM 베이스 이미지 지정 FROM openjdk:17-jdk-alpine
WORKDIR 작업 디렉토리 설정 WORKDIR /app
COPY 호스트 시스템에서 파일, 디렉토리를 컨테이너 내부로 복사 COPY . /app
ADD COPY와 유사하지만, URL에서 직접 파일을 다운로드하거나 압축파일을 풀 수 있는 기능 포함 ADD https://example.com/file.zip /app
RUN 컨테이너 내에서 실행할 명령어 정의 RUN apt-get update && apt-get install -y curl
ENV 환경 변수 설정 ENV JAVA_HOME /usr/lib/jvm/java-17-openjdk
EXPOSE 컨테이너에서 외부로 노출할 포트 지정 EXPOSE 8080
CMD 컨테이너 시작 시 기본으로 실행할 명령어 CMD ["java", "-jar", "app.jar"]
ENTRYPOINT 컨테이너 시작 시 실행할 명령어 설정 ENTRYPOINT ["java", "-jar", "app.jar"]
ARG Dockerfile 빌드 시 넘겨줄 수 있는 변수 ARG JAR_FILE=target/app.jar
VOLUME 컨테이너와 호스트 간의 공유 디렉토리 설정 VOLUME /app/data

🔽 Dockerfile 빌드

docker build 명령은 Docker 이미지를 빌드하기 위해 사용되는 명령어이다. 이 명령어를 통해 Dockerfile에 정의된 지시사항을 순차적으로 수행하여 이미지를 구성하고 빌드할 수 있다. 빌드 과정 중 Dockerfile에 정의된 단계들이 순차적으로 실행되며, 최종적으로 Docker 이미지가 생성된다.

docker build [옵션] [Dockerfile 경로]
  • 옵션
    • -t, --tag: 빌드된 이미지에 태그 지정, "이미지 이름:태그" 형식으로 사용하며, 생략 시 기본으로 latest 태그가 설정
    • -f, --file: 사용할 Dockerfile의 경로를 지정, 기본값은 ./Dockerfile
    • -q, --quiet: 빌드 진행 상황 메시지를 출력하지 않음
    • --build-arg: Dockerfile 내의 ARG 지시문에 전달할 빌드 인자를 지정

 

▶ 도커 구성 요소: 2. 도커 이미지 (Docker Image)

Docker 이미지는 애플리케이션과 해당 애플리케이션을 실행하는 데 필요한 모든 것을 포함한 불변의 파일 시스템 스냅샷이다. 이 이미지에는 코드, 런타임, 라이브러리, 환경 변수, 설정 파일 등이 포함되어 있으며, 이를 기반으로 도커 컨테이너가 생성된다.

🔽 특징

  • 불변성: 도커 이미지는 변경되지 않으며, 이미지를 수정하려면 새로운 이미지로 빌드해야 한다.
  • 레이어 구성: 도커 이미지는 여러 개의 읽기 전용 레이어로 구성된다. 각 레이어는 추가된 파일이나 변경사항을 나타내며, 도커는 이러한 레이어들을 합쳐 하나의 파일 시스템으로 사용한다. 도커는 DockerHub에 이미지 다운로드 또는 업로드 시 변경된 레이어만 전송하므로 효율적으로 이미지를 관리할 수 있다.
  • 이미지 관리 및 배포: 도커 이미지는 DockerHub와 같은 서비스에서 버전 관리 및 배포(push & pull)가 가능하다.
  • Dockerfile 사용: 도커 이미지는 Dockerfile이라는 파일을 사용해 생성된다.
  • 컨테이너 생성의 기반: 도커 이미지를 기반으로 도커 컨테이너가 생성된다. 컨테이너는 이미지의 가상화된 실행 인스턴스이다.

 

▶ 도커 구성 요소: 3. 도커 컨테이너 (Docker Container)

Docker 컨테이너는 Docker 이미지를 기반으로 실행되는 가상화된 환경이다. 이미지에서 생성된 인스턴스로, 애플리케이션이 실제로 실행되는 공간이다.

🔽 특징

  • 읽기/쓰기 가능: 컨테이너는 이미지와 달리 읽기와 쓰기가 가능하다. 이미지를 기반으로 읽기/쓰기 레이어를 추가하여 컨테이너가 생성되며, 변경된 내용은 이 레이어에 저장된다.
  • 효율적인 자원 사용: 여러 개의 컨테이너가 동일한 이미지를 기반으로 생성되더라도, 각 컨테이너는 변경된 부분만을 읽기/쓰기 레이어에 기록하므로 최소한의 용량을 사용한다.
  • 명시적 삭제 필요: 컨테이너가 종료되더라도 자동으로 삭제되지 않는다. 삭제하려면 명시적으로 삭제 명령을 실행해야 하며, 삭제 시 컨테이너에서 생성된 파일도 함께 제거된다.
  • 독립된 실행 환경: 하나의 서버에 여러 개의 컨테이너를 실행할 수 있으며, 각 컨테이너는 서로 완전히 독립된 환경에서 실행되기 때문에 다른 컨테이너나 호스트에 영향을 주지 않는다.
  • 호스트 자원 공유: 컨테이너는 호스트 운영체제의 커널과 자원을 공유한다. 따라서 가상 머신보다 가벼운 성능을 제공한다.

🔽 Docker Container 실행

docker run 명령어는 도커 컨테이너를 실행하는 명령어이다.

docker run [옵션] 이미지 [인자]
  • 옵션
    • -d: 백그라운드에서 실행
    • -p: 포트 바인딩
    • --name: 컨테이너 이름 지정
    • -e: 환경 변수 설정
    • -v: 볼륨 마운트
  • 이미지: 도커 이미지의 이름이다. 컨테이너를 생성할 이미지 이름을 명시한다. (ex: nginx, mysql:5.7 등)
  • 인자: 실행될 명령어나 인자를 지정한다. (ex: /bin/bash, npm start 등)

 

 

✅ 도커 포트 바인딩

도커 컨테이너는 기본적으로 외부 네트워크와 격리되어 있어 외부에서 직접 접근할 수 없다. 도커 포트 바인딩은 컨테이너 내부에서 실행되는 서비스나 애플리케이션을 외부에서 접근할 수 있도록 포트를 연결하는 과정이다.

🔽 명령어 예시

포트를 바인딩하려면 docker run 명령어에서 -p 또는 --publish 옵션을 사용한다.

docker run -d -p 8080:80 my-web-app
  • 이 예시는 호스트의 8080 포트를 컨테이너 내부의 80 포트에 바인딩한다. 따라서 localhost:8080으로 접근하면 컨테이너 내부의 80 포트에서 제공하는 웹 서비스에 연결된다.

 

 

✅ 도커 컴포즈

Docker Compose는 여러 개의 컨테이너를 정의하고, 이들을 한 번에 관리할 수 있는 도구이다. 보통 하나의 애플리케이션은 여러 서비스(예: 웹 서버, 데이터베이스 등)로 구성되며, 각각의 서비스는 별도의 도커 컨테이너로 실행된다. 도커 컴포즈를 사용하면 이러한 여러 컨테이너를 한 번에 설정하고 관리할 수 있다.

 

▶ 특징

  • 멀티 컨테이너 설정 관리: 복잡한 애플리케이션 환경을 코드로 정의하여 여러 컨테이너를 손쉽게 시작하고 종료할 수 있다.
  • YAML 파일 사용: docker-compose.yml 파일을 사용하여 다양한 컨테이너 설정(서비스, 네트워크, 볼륨 등)을 정의한다.
  • 간편한 명령어: 단일 명령어로 모든 서비스들을 일괄적으로 실행(docker-compose up)하거나 종료(docker-compose down)할 수 있다.

 

▶ docker-compose.yml 속성

속성 설명 예시
version docker-compose.yml 파일의 버전을 정의 version: '3.8'
services 여러 컨테이너를 정의하는 부분 services:
  web:
    image: nginx
  app:
    build: .
build 이미지를 빌드할 디렉토리를 지정 build: .
image 서비스에 사용할 도커 이미지를 정의 image: my_image:latest
container_name 컨테이너의 이름을 설정 container_name: my_app_container
ports 호스트와 컨테이너 간의 포트 매핑을 정의 ports:
  - "8080:80"
volumes 호스트와 컨테이너 간의 파일 시스템을 공유하는 설정 volumes:
  - ./data:/var/lib/data
environment 컨테이너 실행 시 환경 변수를 설정 environment:
  - JAVA_OPTS=-Xmx512m
depends_on 특정 서비스가 다른 서비스보다 먼저 실행되도록 순서를 지정 depends_on:
  - db
networks 서비스를 연결하는 네트워크를 정의 networks:
  - my_network
command 컨테이너가 시작될 때 실행할 명령어를 지정 command: ["npm", "start"]
restart 컨테이너가 종료되었을 때 재시작할 정책을 설정 restart: always

 

▶ 명령어

1. 컨테이너 실행

# 컨테이너 실행
docker-compose up

# 컨테이너를 백그라운드 모드에서 실행
docker-compose up -d

# 기존 컨테이너를 삭제하고 새로 생성하여 실행
docker-compose up --force-recreate

# 이미지를 다시 빌드한 후 컨테이너 실행
docker-compose up --build

2. 컨테이너 종료

# 컨테이너 종료
docker-compose down

# 컨테이너 일시 중단
docker-compose stop

 

 

✅ 도커의 워크 플로우

1. Dockerfile 작성: 애플리케이션 실행 환경을 정의하는 Dockerfile을 작성한다.

# 베이스 이미지로 OpenJDK 사용
FROM openjdk:17-jdk-alpine

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

# 호스트의 JAR 파일을 컨테이너로 복사
COPY target/app.jar /app/app.jar

# 환경 변수 설정 (필요시)
ENV JAVA_OPTS=""

# 컨테이너가 실행될 때 JAR 파일을 실행하도록 설정
ENTRYPOINT ["java", "-jar", "app.jar"]

2. 이미지 빌드: Dockerfile을 사용하여 도커 이미지를 빌드한다.

docker build -t my_image .

3. 컨테이너 실행: 도커 이미지를 기반으로 컨테이너를 실행하여 애플리케이션을 구동한다.

docker run -p 8080:80 my_image

4. docker-compose.yml 파일 작성: 여러 컨테이너를 쉽게 관리하고 동시에 실행하기 위해 docker-compose.yml 파일을 작성한다.

version: '3.8'  # docker-compose 파일의 버전 지정

services:
  app:  # 서비스 이름
    image: my_image  # 빌드된 도커 이미지 이름
    build:
      context: .  # Dockerfile의 위치
      dockerfile: Dockerfile  # 빌드 시 사용할 Dockerfile 지정 (기본값은 Dockerfile)
    ports:
      - "8080:80"  # 호스트의 8080 포트를 컨테이너의 80 포트에 연결
    environment:
      - JAVA_OPTS=-Xms512m -Xmx1024m  # 컨테이너 실행 시 사용할 환경 변수
    volumes:
      - ./logs:/app/logs  # 호스트의 logs 디렉토리를 컨테이너의 /app/logs에 마운트

  # 필요한 경우 다른 서비스 추가
  database:
    image: postgres:14-alpine  # PostgreSQL 데이터베이스 이미지 사용
    environment:
      POSTGRES_USER: user  # 데이터베이스 사용자 이름
      POSTGRES_PASSWORD: password  # 데이터베이스 비밀번호
      POSTGRES_DB: my_database  # 데이터베이스 이름
    ports:
      - "5432:5432"  # 호스트의 5432 포트를 컨테이너의 5432 포트에 연결
    volumes:
      - pgdata:/var/lib/postgresql/data  # 데이터 유지용 볼륨

volumes:
  pgdata:  # 볼륨 선언 (데이터 유지)

5. 도커 컴포즈 활용: Compose를 통해 실행한다.

docker-compose up

 

 

✅ 도커 이미지 도커 허브에 업로드

  • 도커 허브에 로그인: 도커 허브(https://hub.docker.com)에 로그인한다. 
  • 도커 허브에서 레포지토리 생성:
    • 도커 허브에 로그인한 후, 오른쪽 상단의 "Create Repository" 버튼을 클릭한다.
    • 레포지토리 이름과 설정(public/private 등)을 지정하고 "Create" 버튼을 눌러 레포지토리를 생성한다.
  • 도커 CLI에 로그인: 도커 CLI에서 도커 허브에 로그인한다.
docker login
  • 도커 이미지 푸시: 도커 허브에 이미지를 업로드한다. (버전명 존재 시 버전명까지 필수)
docker push 도커허브계정명/도커레포지토리:태그명

 

 

📍 참고

soeun2537