Docker에 대해 알아보기

맑음·2025년 5월 3일
post-thumbnail

Docker란 무엇인가?

Docker는 애플리케이션을 컨테이너(Container) 라는 단위로 격리하여 실행할 수 있게 해주는 오픈소스 플랫폼입니다
운영체제를 공유하기 때문에 훨씬 가볍고 빠르며 어디서나 동일한 환경을 보장할 수 있습니다
이를 통해 개발자는 어떤 환경에서도 동일하게 동작하는 소프트웨어를 만들 수 있습니다
즉, 개발과 운영 사이의 격차를 줄여줍니다

왜 Docker를 사용하는가?

Docker의 사용은 개발, 테스트, 배포 과정에서 다양한 이점을 제공합니다

  • 일관된 환경, 빠른 배포: 개발 환경을 이미지화시켜 개발 및 배포 환경을 쉽게 컨테이너화하여 구축할 수 있습니다
    이는 협업을 원활하게 만들어줍니다
  • 효율적인 리소스 사용: Docker의 컨테이너는 가상 머신보다 빠르고 가볍습니다
    그렇기에 동일한 하드웨어에서 더 많은 애플리케이션 실행이 가능합니다
  • 확장성 및 유연성: Docker를 사용하면 서비스를 독립적인 컨테이너 분리해 필요시 각각 확장이 가능합니다
    또한 이식성이 좋아 다양한 환경에서 실행이 가능합니다
  • 배포 자동화: Docker는 자동화된 테스트 및 배포를 지원합니다 이는 더 빨리 전달하고 운영을 표준화하는데 도움이 됩니다

Docker 기본 개념

Docker를 이해하기 위한 개념은 다음과 같습니다

이미지 (Image)

Docker 이미지컨테이너 실행을 위한 파일 및 설정이 담긴 템플릿입니다

예를 들어, Node.js 애플리케이션을 실행하고 싶으면 Node.js가 설치되어 있는 환경이 필요하게 됩니다
이 모든 것을 미리 구성한 것이 이미지로 볼 수 있습니다
따라서 이미지가 있으면 귀찮게 일일이 설치할 필요가 없어지는 것입니다

이미지는 읽기 전용이며 하나의 이미지에서 여러 컨테이너를 만들 수 있습니다 -> 일대다(1:N) 관계
Docker Hub에는 많은 이미지들이 올라와 있으며 누구나 가져와 사용이 가능합니다

이미지의 특징

이미지의 특징은 다음과 같습니다

  • 읽기 전용: 이미지는 생성 후 변경할 수 없습니다 새로운 설정 혹은 변경이 생길 경우 기존 이미지를 기반으로 새로운 이미지를 만들어야 합니다

  • 레이어 구조 (Layered Architecture): 이미지는 여러 개의 레이어로 구성되어 있으며 각 레이어는 명령어 단위 (RUN, COPY, ADD 등)로 쌓이며 아래에서부터 위로 계층이 형성됩니다

    아래는 레이어 예시입니다

    • Layer 5 : 애플리케이션 소스 복사
    • Layer 4 : 패키지 설치
    • Layer 3 : 시스템 업데이트
    • Layer 2 : 기본 OS 환경 (예: Ubuntu)
    • Layer 1 : scratch (빈 베이스)
  • 효율적인 저장 및 관리: 동일한 레이어를 여러 이미지에서 공유할 수 있습니다 이는 저장 공간을 절약하고 이미지 빌드, 배포 속도를 높일 수 있습니다

    • Copy-On-Write (COW): 기존 이미지의 레이어는 읽기 전용으로 공유하고, 컨테이너가 수정하는 데이터만 새로운 레이어에 복사합니다
      이 방식으로 여러 컨테이너가 동일한 이미지를 공유하면서 변경된 부분만 별도의 레이어에 저장해 공간을 절약하고, 빌드 및 배포 속도를 향상시킬 수 있습니다
  • 유니온 파일 시스템 (Union File System): 여러 레이어를 하나의 파일 시스템처럼 합쳐서 보여주는 기술로 이미지와 컨테이너가 효율적으로 파일을 관리할 수 있게 해줍니다

컨테이너 (Container)

Docker 컨테이너는 이미지를 기반으로 실행되는 격리된 애플리케이션 실행 환경입니다
컨테이너는 실제로 작동하는 애플리케이션 인스턴스이며 하나의 컨테이너는 독립적인 실행 환경을 제공하여 다른 컨테이너에 영향을 미치지 않고 실행이 가능하도록 합니다

컨테이너의 특징

컨테이너의 특징은 다음과 같습니다

  • 격리성: 컨테이너는 다른 컨테이너로부터 완전히 분리된 독립적인 공간에서 실행됩니다
    각 컨테이너는 서로 분리된 파일 시스템, 네트워크, 프로세스를 갖습니다

  • 경량성: 가상머신과 달리 별도의 운영체제를 포함하지 않고 호스트 OS의 커널을 공유해 가볍고 빠르게 실행이 가능합니다

  • 일관성: 어디서 실행하든 동일한 환경을 보장합니다

  • 확장성: 이미지와 컨테이너는 일대다 관계로 하나의 이미지로부터 여러 컨테이너를 쉽게 생성할 수 있습니다

  • 휘발성: 컨테이너는 기본적으로 휘발성이며 컨테이너 내에 저장된 데이터는 삭제 시 함께 사라지게 됩니다
    만약 데이터를 유지하기 위해서는 볼륨 (Volume)을 사용해야 합니다

Docker 구성요소

다른 구성요소들은 다음과 같습니다

Docker Daemon (dockerd)

Docker Daemon은 Docker의 백엔드로 컨테이너와 이미지를 관리하고 Docker 클라이언트와의 상호작용을 처리합니다
이를 통해 컨테이너 실행, 이미지 관리 등 Docker와 관련된 모든 작업이 실행됩니다

Docker Client

Docker Client사용자가 Docker와 상호작용하는 명령줄 도구입니다
사용자는 docker 명령어를 통해 컨테이너를 실행하거나 이미지를 빌드하는 등의 작업을 클라이언트에서 실행하고 이 명령은 Docker Daemon에 전달됩니다

Docker Registry

Docker RegistryDocker 이미지를 저장하고 배포하는 저장소입니다
Docker Hub는 가장 일반적인 공개 레지스트리로 다양한 이미지를 다운로드하고 사용자가 자신의 이미지를 업로드할 수도 있습니다

Docker Volume

Docker Volume은 컨테이너의 휘발성 데이터를 보호하기 위해 사용하는 외부 저장소입니다
컨테이너가 종료되더라도 데이터가 유지되므로 데이터 영속성을 보장합니다

Docker Network

Docker Network컨테이너 간의 통신을 담당합니다
각 컨테이너는 기본적으로 독립적인 네트워크에서 실행되며 Docker 네트워크를 사용하여 여러 컨테이너가 서로 통신할 수 있습니다

Docker 기본 명령어

Docker에서 자주 사용되는 기본 명령어는 다음과 같습니다

이미지 관련 명령어

  • Docker Hub 또는 다른 레지스트리에서 이미지를 다운로드합니다

    $ docker pull <이미지이름>:<태그>
  • 로컬에 있는 Docker 이미지 목록을 확인합니다

    $ docker images
  • 로컬에 있는 이미지를 삭제합니다

    $ docker rmi <이미지이름>

컨테이너 관련 명령어

  • 이미지를 기반으로 컨테이너를 생성하고 실행합니다

    $ docker run <옵션> <이미지이름>
  • 실행 중인 컨테이너 목록을 확인합니다

    $ docker ps
  • 모든 컨테이너(실행 중인 것과 중지된 것 포함)의 목록을 확인합니다

    $ docker ps -a
  • 실행 중인 컨테이너를 중지합니다

    $ docker stop <컨테이너ID>
  • 중지된 컨테이너를 시작합니다

    $ docker start <컨테이너ID>
  • 컨테이너를 재시작합니다

    $ docker restart <컨테이너ID>
  • 실행 중인 컨테이너에서 명령어를 실행합니다

    $ docker exec -it <컨테이너ID> <명령어>

    -it: 상호작용 모드로 터미널을 연결합니다

  • 컨테이너의 로그를 확인합니다

    $ docker logs <컨테이너ID>
  • 중지된 컨테이너를 삭제합니다

    $ docker rm <컨테이너ID>
  • 실행 중인 컨테이너를 강제로 종료합니다

    $ docker kill <컨테이너ID>

네트워크 관련 명령어

  • Docker 네트워크 목록을 확인합니다
    $ docker network ls
  • 특정 Docker 네트워크에 대한 상세 정보를 확인합니다
    $ docker network inspect <네트워크명>

볼륨 관련 명령어

  • 로컬 볼륨 목록을 확인합니다
    $ docker volume ls
  • 새 볼륨을 생성합니다
    $ docker volume create <볼륨이름>
  • 특정 볼륨을 삭제합니다
    $ docker volume rm <볼륨이름>

Dockerfile

DockerfileDocker 이미지를 자동으로 빌드할 수 있는 설정 파일입니다
이 파일에는 이미지를 빌드하는 데 필요한 단계별 명령어가 순차적으로 나열되어 있습니다
Dockerfile은 애플리케이션 환경을 설정하고 필요한 소프트웨어와 종속성을 설치하며 애플리케이션을 실행하는 데 필요한 모든 과정을 정의합니다

주요 명령어 및 역할

주요 명령어는 다음과 같습니다

명령어설명
FROM베이스 이미지를 지정
LABEL이미지에 메타데이터 (key-value) 추가
RUN빌드 시 실행할 명령어 (패키지 설치, 파일 다운로드 등)
COPY호스트 파일/폴더를 이미지에 복사
ADDCOPY와 유사하나, 압축 해제 및 URL 지원
WORKDIR작업 디렉토리 지정, 이후 명령은 이 디렉토리 기준으로 실행
ENV환경 변수 설정
EXPOSE컨테이너에서 열 포트 지정 (문서화 목적)
CMD컨테이너 시작 시 실행할 명령어 (한 번만 사용, 여러 개면 마지막 적용)
ENTRYPOINTCMD 와 유사하나 명령어 고정 실행 (인자 추가 가능)
USER컨테이너 실행 시 사용할 사용자 지정
VOLUME외부 볼륨 마운트 지점 지정
ARG빌드 타임 변수 지정

Dockerfile 예시

아래의 예시는 Node.js 앱을 위한 이미지 빌드 과정을 보여줍니다

FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]

Dockerfile 빌드 및 실행

  • 이미지를 빌드합니다

    $ docker build -t myapp

    -t: tag의 약자로 빌드된 Docker 이미지를 태그한다는 의미입니다
    태그는 이미지를 식별하는 데 사용되는 이름을 지정하는 옵션입니다

  • 컨테이너를 실행합니다

    $ docker run -p 3000:3000 myapp

Docker Compose

Docker Compose여러 개의 컨테이너를 정의하고 관리하는 도구YAML 파일을 사용하여 다수의 컨테이너를 설정할 수 있습니다
예를 들어, 웹 애플리케이션과 데이터베이스 서버를 함께 실행하려면 Docker Compose가 유용합니다

Docker Compose의 주요 특징

Docker Compose의 주요 특징은 다음과 같습니다

  • 여러 컨테이너를 한 번에 생성, 실행, 중지, 삭제가 가능합니다
  • 서비스 간 네트워크 및 볼륨 자동 구성이 가능합니다
  • 서비스 간 의존성 (depend_on) 설정이 가능합니다
  • 환경 변수, 포트 매핑, 볼륨 마운트 등 컨테이너 실행 옵션을 코드로 관리합니다

docker-compose.yml 기본 구조

아래는 docker-compose.yml 파일의 예시입니다

version: '3'
services:
  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=myuser
      - POSTGRES_PASSWORD=mypassword
      - POSTGRES_DB=mydb
    volumes:
      - ./data:/var/lib/postgresql/data

  web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    environment:
      - DB_HOST=db
      - DB_USER=myuser
      - DB_PASSWORD=mypassword
    depends_on:
      - db

Docker Compose 주요 명령어

주요 명령어는 다음과 같습니다

  • 컨테이너를 일괄 실행합니다

    $ docker-compose up -d
  • 컨테이너 일괄 중지 및 삭제합니다

    $ docker-compose down
  • 서비스 상태를 확인합니다

    $ docker-compose ps
  • 로그를 확인합니다

    $ docker-compose logs
  • 이미지를 재빌드합니다

    $ docker-compose up --build -d

Docker 이미지 관리 팁

효율적인 Docker 이미지 관리는 저장 공간의 절약과 빠른 배포, 유지 보수에 도움이 됩니다

  • 작은 베이스 이미지 사용
    Dockerfile에서 alpine, slim과 같은 경량 이미지를 사용하면 이미지 용량을 크게 줄일 수 있습니다

    FROM python:3.11-slim
  • 멀티 스테이지 빌드 (Multi-stage Build) 활용
    Dockerfile에서 빌드 단계와 실행 단계를 분리해 최종 이미지 크기를 최소화할 수 있습니다

    # Stage 1: Build
    FROM node:18 as build
    WORKDIR /app
    COPY . .
    RUN npm run build
    
    # Stage 2: Run
    FROM nginx:alpine
    COPY --from=build /app/dist /usr/share/nginx/html
  • 불필요한 파일 COPY 하지 않기
    .dockerignore 파일을 이용하여 빌드에 필요없는 파일을 제외하는 것이 좋습니다

  • 레이어 최적화
    DockerfileRUN, COPY, ADD 순서를 잘 구성해 캐시 재사용이 가능한 구조로 작성하는 것이 좋습니다

    RUN apt-get update && apt-get install -y \
        curl \
        git \
      && rm -rf /var/lib/apt/lists/*
  • 이미지 정리
    사용하지 않는 이미지나 중지된 컨테이너를 정리해 디스크 용량을 확보할 수 있습니다

    # 사용하지 않는 이미지 삭제
    docker image prune -a
    
    # 중지된 컨테이너와 네트워크 등 모두 정리
    docker system prune -a
    

자주 발생하는 오류

Docker를 사용하다보면 오류를 자주 마주칠 수도 있습니다
아래에 몇가지의 오류들을 작성해보았습니다

  • 포트 충돌 오류
    이미 다른 프로세스가 해당 포트를 사용 중일 경우 오류가 발생합니다

    • 에러 메시지: Bind for 0.0.0.0:80 failed: port is already allocated
    • 해결 방안: 해당 포트를 사용하는 프로세스를 확인하고 컨테이너의 포트를 변경합니다
      # 해당 포트를 사용하는 프로세스 확인
      lsof -i :80
      # 컨테이너 포트 변경
      docker run -p 8080:80 myapp
      사용 중인 컨테이너에서 포트를 점유하는 경우도 있으니 docker ps로 컨테이너 상태도 함께 확인하면 좋습니다
  • 이미지 pull 실패
    이미지가 존재하지 않거나 비공개 상태일 경우 발생합니다

    • 에러 메시지: Error response from daemon: pull access denied for myapp
    • 해결 방안: 이름 오타 확인, 비공개 레포라면 docker login 후 다시 시도
  • Dockerfile 캐시 문제로 변경사항 반영이 안되는 경우

    • 해결 방안: 빌드 시 --no-cache 옵션을 추가합니다
      $ docker build --no-cache -t myapp
      .dockerignore 파일로 불필요한 파일이 빌드에 포함되지 않도록 하는 것도 캐시 문제 예방에 도움됩니다
  • 파일 권한 오류
    COPY한 파일의 권한 문제로 발생합니다

    • 에러 메시지: Permission denied
    • 해결 방안: Dockerfile에 아래 내용을 추가합니다
      RUN chmod +x your_script.sh
      필요하다면 USER 명령어로 실행 사용자를 지정하거나 호스트에서 미리 권한을 맞추는 것도 도움됩니다
  • 컨테이너 실행 후 즉시 종료
    CMDENTRYPOINT에서 실행되는 프로세스가 바로 종료되어 컨테이너도 함께 종료되어 발생합니다

    • 해결 방안: Dockerfile에 아래 내용을 추가합니다
      컨테이너가 대기하도록 tail -fsleep 등을 사용해 테스트 가능합니다
      CMD ["tail", "-f", "/dev/null"]
      로그 확인을 통해 종료 원인을 파악하는 것도 좋은 방법입니다

Kubernetes와의 연동

Docker는 단일 컨테이너 실행에 매우 유용하지만 복잡한 서비스 구성이나 수많은 컨테이너를 효율적으로 운영하려면 한계가 있습니다
이때 등장하는 것이 Kubernetes (쿠버네티스)입니다
Kubernetes컨테이너화된 애플리케이션의 자동 배포, 확장, 관리 기능을 제공하는 오픈소스 플랫폼입니다
Docker컨테이너 개발과 테스트에 최적화되어 있고, Kubernetes는 이러한 컨테이너들을 대규모로 배포·운영·자동화하는 데 강점을 가지고 있습니다

Kubernetes는 컨테이너 오케스트레이션 도구

  • 컨테이너 자동 배포
  • 로드밸런싱
  • 자가 복구
  • 확장성 관리

등을 가능하게 해줍니다

Docker를 잘 다룰 줄 안다면 Kubernetes는 자연스러운 다음 단계입니다
Docker에 익숙해졌다면 더 나아가 Kubernetes에 대해 배워봅시다!

마무리

Docker는 개발, 테스트, 배포 환경을 일관성 있게 유지하고 효율적으로 관리할 수 있도록 도와주는 강력한 도구입니다
처음에는 다소 낯설고 오류도 자주 마주칠 수 있지만 기본 개념과 명령어 그리고 실전에서 자주 발생하는 문제와 해결법을 익혀두면 누구나 쉽게 활용할 수 있습니다

사용할수록 더 매력적인 Docker, 기초적인 개념을 학습했으니 이젠 활용해봅시다!

profile
내일도 맑음

0개의 댓글