Study/Docker & Kubernetes

Docker 이미지와 컨테이너: 왜 사용할까?

slow_life 2025. 3. 14. 05:45

목차

  1. Docker 이미지란?
  2. 컨테이너 실행 및 상태 확인
    1. 컨테이너 실행
    2. 실행 중인 컨테이너 목록 확인
    3. 대화형 모드로 컨테이너 실행
  3. 커스텀 이미지 빌드하기
    1. Dockerfile 작성
    2. 이미지 빌드 및 실행
  4. Docker 최적화: 레이어 기반 아키텍처
    • 레이어 기반 아키텍처란?
    • Dockerfile 간단 최적화 예제
    • CS 개념과의 연관성
  5. Docker 컨테이너 실행 모드
  6. Docker 컨테이너와 이미지의 이름 및 태그 관리
    • 컨테이너 이름 관리
    • 이미지 태그 관리
  7. 마무리


들어가며

지난 시간에 Docker와 가상 머신을 비교하며 Docker가 갖는 이점과 컨테이너에 대해 간략하게 알아보았다.

 

이번 시간에는 아래 내용에 대해 살펴보고자 한다.

  • Docker의 핵심 개념인 컨테이너와 이미지에 대해
  • 이 두 개념이 어떻게 관련이 있는가
  • 활용하는 방법

1. Docker 이미지란?

이미지란 컨테이너의 청사진이라고 할 수 있다.

즉, 실행 가능한 환경을 포함하는 패키지로, 코드와 실행에 필요한 모든 도구를 포함하고 있다.
이미지는 한 번 정의하면 여러 개의 컨테이너를 생성할 수 있으며, 이를 통해 일관된 환경을 유지하면서 애플리케이션을 실행할 수 있다.

  • 이미지(Image): 코드 및 실행 환경 포함
  • 컨테이너(Container): 실행 중인 애플리케이션

2. 컨테이너 실행 및 상태 확인

2-1. 컨테이너 실행

docker run node

이 명령어를 실행하면 컨테이너가 생성되지만 기본적으로 터미널에서 상호작용 할 수 없는 상태로 실행된다.

2-2. 실행 중인 컨테이너 목록 확인

docker ps -a

모든 컨테이너(실행 중 및 중지된 컨테이너 포함)를 확인할 수 있다.

2-3. 대화형 모드로 컨테이너 실행

docker run -it node

-it 플래그를 추가하면 터미널에서 직접 실행할 수 있는 인터랙티브 모드로 실행된다

실행된 컨테이너 내부에서 Node.js REPL을 사용할 수 있다. (예: 1 + 1 입력 시 결과 출력)

*컨테이너와 로컬 환경의 독립성

컨테이너 내부에서 실행되는 Node.js 버전과, 로컬에 설치된 Node.js 버전이 다를 수 있다.

즉, 컨테이너 내부 환경은 호스트 시스템과 독립적이다.

따라서, 로컬에 Node.js를 설치하지 않고도 컨테이너 내부에서 Node.js를 실행할 수 있다.

 

그리고 동일한 이미지 기반으로 여러 개의 컨테이너를 생성할 수 있다.

예시를 들자면, docker run node 명령을 여러 번 실행하면, 같은 Node.js 이미지를 기반으로 한 컨테이너가 여러 개 실행된다.

3. 커스텀 이미지 빌드하기

3-1. Dockefile 작성

FROM node
WORKDIR /app
COPY . /app
RUN npm install
EXPOSE 80
CMD ["node", "server.js"]
명령어 설명
FROM node Node.js 가 설치된 기본 이미지 사용 (Docker Hub)
WORKDIR /app 컨테이너 내부 작업 디렉토리 설정
COPY . /app 로컬 파일을 컨테이너 내부로 복사
RUN npm install  패키지 설치 실행
EXPOSE 80 내부 포트 80 노출 (외부 연결은 '-p' 옵션 필요)
CMD ["node", "server.js"] 컨테이너 시작 시 실행할 명령어 지정

3-2. 이미지 빌드 및 실행

docker build -t my-app .
docker images
docker run -p 3000:80 my-app

// Note: EXPOSE 80은 문서화 용도로, 실제 포트 매핑을 위해 -p 옵션을 사용해야 한다.

4. Docker 최적화: 레이어 기반 아키텍처

이번 시간의 핵심 개념이라고 할 수 있다.

Docker의 레이어 기반 아키텍처빌드 속도를 최적화하고, 변경된 부분만 다시 빌드하는 효율적인 구조를 제공한다.

4-1. 레이어 기반 아키텍처란?

레이어 기반 아키텍처는 Docker에서 만든 개념은 아니다.

소프트웨어 아키텍처에서 오랫동안 사용되어 온 개념으로, 여러 분야에서 활용되고 있다.

Docker는 이러한 개념을 컨테이너 환경에 맞게 적용하여 효율적인 빌드 및 배포를 가능하게 했다.

레이어 구조

Docker 이미지는 여러 개의 레이어로 구성된다. 

Docker는 각 명령어(FROM, COPY, RUN 등)을 독립적인 레이어로 저장하며, 변경되지 않은 레이어는 다시 빌드하지 않고 캐싱된 내용을 재사용한다.

  1. 베이스 이미지 레이어 (FROM node):
    1. 기본적으로 사용되는 이미지. 운영체제(OS), 런타임 환경(Node.js, Python 등)이 포함.
    2. 해당 레이어는 한 번 다운로드되면 변하지 않는 한 다시 다운로드하지 않음.
  2. 의존성 설치 레이어 (RUN npm install):
    1. npm install 또는 pip install 같은 의존성 설치 작업이 포함.
    2. 이 레이어는 코드 변경이 없는 한 캐시를 활용하여 다시 실행되지 않음.
  3. 애플리케이션 코드 레이어 (COPY . /app):
    1. 애플리케이션의 소스 코드가 복사되는 단계.
    2. 코드가 변경될 경우 이 레이어 이후의 레이어가 모두 다시 빌드되어야 함.
  4. 최종 실행 레이어 (CMD ["node", "server.js"]):
    1. 컨테이너 실행 시 동작할 명령어를 지정하는 단계.
    2. 실행 명령 자체는 빌드 프로세스에 영향을 미치지 않음.

4-2. Dockerfile 간단 최적화 예제

FROM node
WORKDIR /app
COPY . /app
RUN npm install
EXPOSE 80
CMD ["node", "server.js"]

문제점: COPY . /app 이후 RUN npm install을 실행하면 코드 변경 시마다 npm install이 실행될 수 있다.

FROM node
WORKDIR /app
COPY package.json /app
RUN npm install
COPY . /app
EXPOSE 80
CMD ["node", "server.js"]

개선: package.json 먼저 복사하여 의존성이 변경되지 않으면 npm install을 재실행하지 않음.

4-3. CS 개념과의 연관성

앞서 말했듯이 레이어 기반 아키텍처는 Docker의 고유한 개념은 아니다.

사용되고 있는 사례들을 몇 가지 찾아보았다.

  1. 캐시 메모리(Cache Memory)
    1. CPU의 L1, L2, L3 캐시는 자주 사용하는 데이터를 저장하여 빠르게 접근할 수 있도록 한다.
    2. Docker도 동일한 원리로, 변경되지 않은 레이어를 캐싱하여 다시 읽지 않고 재사용한다.
  2. 가상 메모리(Virtual Memory)
    1. 여러 프로세스가 같은 OS를 공유하면서 각각 독립적인 환경을 유지할 수 있도록 하는 기술이다.
    2. Docker의 컨테이너도 OS 레벨에서 격리되지만, 공통된 리소스는 공유하여 효율성을 높인다.
  3. 빌드 시스템과 의존성 관리 (Dependency Management)
    1. Gradle, Webpack, Makefile 같은 빌드 도구들은 변경된 파일만 다시 빌드하여 속도를 최적화한다.
    2. Docker도 유사하게 변경된 레이어 이후만 다시 빌드하여 불필요한 작업을 줄인다.
  4. Git의 변경 사항 추적 (Git Diff)
    1. Git이 변경된 파일만 추적하고 업데이트하는 방식처럼, Docker는 변경된 레이어 이후의 단계만 다시 빌드한다.

이처럼 Docker의 레이어 아키텍처는 개발에서 자주 접하는 개념들과 연관되어 있다. (CS의 중요성을 다시 한번 알게 되었다.)

5. Docker 컨테이너 실행 모드

컨테이너는 기본적으로 Attached, Detached 모드로 나뉜다.
이는 터미널과의 연결 여부에 따라 구분되며, 실행 중인 컨테이너의 로그 출력 방식에 영향을 미친다.

실행 모드 설명
Attached 모드 기본 실행 모드, 터미널에 연결된 상태에서 실행됨
Detached 모드 (-d) 백그라운드에서 실행, 터미널과 분리됨

실행 방식 비교

명령어 실행 방식 특징
docker run 새 컨테이너를 생성하여 실행 기본적으로 Attached 모드
docker run -d 새 컨테이너를 백그라운드에서 실행 Detached 모드
docker start 중지된 컨테이너 재시작 기본적으로 Detached 모드
docker start -a 중지된 컨테이너 재시작 (Attached 모드)

6. Docker 컨테이너와 이미지의 이름 및 태그 관리

6-1. 컨테이너 이름 관리

컨테이너를 실행할 때, 기본적으로 Docker는 무작위의 이름을 자동으로 생성한다.

특정한 이름을 부여해서 여러 개의 컨테이너를 목적에 맞게 관리할 수 있다.

 

컨테이너 실행 시 이름 지정

docker run -d --name goalsapp my-image

이 명령어를 실행하면 goalsapp이라는 이름을 가진 컨테이너가 생성된다.

이후 컨테이너를 조작할 때 컨테이너 ID 대신 goalsapp을 사용할 수 있다.

6-2. 이미지 태그 관리

이미지를 생성할 때 특정 버전을 관리하는 것이 중요하다고 한다.

기본적으로 Docker는 latest 태그를 자동으로 붙이지만, 명확한 버전 구분을 위해 직접 태그를 지정할 수도 있다.

 

버전이 있는 이미지 빌드 예제

docker build -t goals:v1 .
docker build -t goals:v2 .

위의 명령어를 실행하면 각각 v1, v2 태그가 부여된 goals 이미지가 생성된다.

이렇게 하면 새로운 기능을 추가하거나 변경 사항을 테스트할 때, 기존 버전을 유지하면서 새로운 버전을 만들 수 있을 것이다.

 

특정 태그가 있는 이미지 실행

docker run -d --name goals-v1 goals:v1

이 명령어를 실행하면 goals:v1 이미지를 기반으로 한 컨테이너가 생성된다.

이후 특정 버전이 필요한 경우, 해당 태그를 사용하여 컨테이너를 실행한다.

 

이러한 태그 관리는 CI/CD 파이프라인에서 유용하게 사용된다고 한다.
예를 들어, 
dev, staging, production 등의 태그를 사용하여 개발 및 배포 환경을 분리할 수 있다.

마무리

처음에는 모호하게만 느껴졌던 컨테이너와 이미지 개념이 어느정도 이해된다.
특히, CS 개념인
캐시 메모리, OSI 7계층, 빌드 시스템의 의존성 관리 등이 Docker의 레이어 기반 아키텍처와 자연스럽게 연결된다는 점이 흥미로웠다. (CS 공부한 게 도움이 되는 순간)

 

Docker는 단순히 컨테이너를 실행하는 도구가 아니라, 소프트웨어 개발과 배포를 최적화하는 시스템이라는 점을 알게 되었다.