본문 바로가기
스터디/백엔드

도커(Docker) 용어 이해 및 의문점

by dingwoon 2025. 4. 7.


1 Docker의 구성 요소

  • 1) Docker Engine

    전체 Docker의 핵심 플랫폼이다.

    • Docker CLI

      터미널에 Docker 관련 명령어를 입력하는 도구를 말한다.

      명령어들을 입력하면 Docker Daemon에게 요청을 보내는데, 여기서 클라이언트 역할을 한다.

    • Docker Daemon

      백그라운드에서 항상 실행 중인 프로세스이다. dockerd이다.

      Docker CLI의 명령을 받아서 실행한다.

      컨테이너를 생성하고, 이미지를 빌드하고, 네트워크 설정을 하는 등 Docker 관련 모든 동작을 수행하는 핵심 부분이다.

      • Docker Daemon이 컨테이너 마다 있는 관리 프로세스인가? 아니면 Docker 전체를 관리하는 프로세스인가?

        Docker Daemon은 단 하나의 프로세스로 Docker 시스템을 제어하는 중앙 관리자 프로세스이다. 수백 개의 컨테이너가 있더라도 모두 1개의 Daemon에서 관리한다.

        • 근데 컨테이너 마다도 관리하는 프로세스가 있어야 하는거 아닌가?

          각 컨테이너에는 Docker Daemon 같은 별도의 관리 프로세스는 존재하지 않는다. 컨테이너 자체가 하나의 일반 프로세스로 실행된다.

          • 하나의 컨테이너에서 실행되는 여러 프로세스 들이 있을 수 있고, 컨테이너가 여러 개면 파생되는 프로세스도 엄청나게 많을 텐데 그거를 다 Daemon에서 관리하나?

            Daemon은 각 컨테이너 프로세스(PID 1)만 관리한다.

            그리고 거기서 포크되는 다른 자식 프로세스들은 리눅스 커널과 그 PID 1번 프로세스가 관리한다.

    • Docker Engine 정리

      사용자가 Docker CLI를 통해 명령어를 입력하면 Docker Daemon에게 요청이 보내진다. 그리고 Docker Daemon에서 도커의 핵심적인 동작을 수행한다.

  • 2) Dockerfile

    컨테이너를 어떻게 만들지 정의하는 레시피(설계도)이다.

  • 3) Docker Image

    Dockerfile을 기반으로 만들어진 정적인 실행 환경(템플릿)을 말한다.

    Dockerfile을 빌드해서 생성한 결과물이다.

    • 빌드 명령어

      docker build -t likelion-backend:latest .

      위에서 -t는 이미지 이름을 지정할 때 사용한다. : 뒤는 태그이다. (태그는 일반적으로 버전을 명시한다.)

      마지막의 .은 Dockerfile이 위치한 경로를 의미한다.

  • 4) Docker Container

    Docker Image를 실제로 실행한 인스턴스이다.

    컨테이너는 메모리에서 실행 중인 프로세스고, 고유한 IP와 파일시스템을 갖고 있다.

    • 실행 명령어

      docker run --rm -d -p 8082:8082 --name likelion-backend likelion-backend:latest

      --rm → 컨테이너가 종료되면 자동으로 삭제되는 옵션

      -d → Detached 모드로 실행하는 옵션 (백그라운드로 실행됨)

      -p 8082:8082호스트:컨테이너 포트 매핑하는 옵션. 외부의 8082 요청을 컨테이너의 8082로 전달한다. → 외부에서 접근하게 하기 위해서 필수적인 옵션이다. (컨테이너끼리만 통신할 때는 생략해도 됨)

      --name → 컨테이너에 붙일 이름을 설정하는 옵션

      likelion-backend:latest → 실행할 Docker 이미지 이름과 태그

  • 5) Docker Hub

    Docker Hub는 Docker의 이미지들을 공유하고 받아올 수 있는 중앙 저장소이다.

    이미지를 올릴 수 있고, 다른 사람이 올린 이미지를 받을 수도 있다.

2 .dockerignore

Dockerfile에서 COPYADD로 복사할 파일이나 디렉터리를 지정할 수 있다.

이때 .dockerignore에 있는 파일은 복사하지 않는다.

  • 파일을 복사한다는게 어떤 의미지? 진짜 말 그대로 복사하는 건가?

    말 그대로 복사가 맞다.

    물리적으로 복사한다.

  • 보통 어떤 파일을 .dockerignore로 설정하지?

    Docker Image에 넣을 필요가 없는 것들을 설정한다.

    민감한 정보를 포함시키지 않기 위해서나, 빌드 속도를 높이고 최종 이미지의 크기를 줄이기 위해 필요 없는 파일은 이미지에 포함되지 않게 한다.

3 Dockerfile의 필수 요소와 선택 요소

  • 필수 설정 요소

    FROM / COPY / ENTRYPOINT 또는 CMD

    • ENTRYPOINTCMD를 동시에 쓰면 어떻게 되나?

      둘 다 있으면 둘 다 실행된다. CMDENTRYPOINT의 인자를 설정하는 역할이 된다.

      • 근데 둘 다 명령을 실행하는 건데 하나만 있어도 다 가능한거 아닌가? 왜 같은게 두 개나 있지?

        둘은 차이가 있다. ENTRYPOINT는 반드시 실행되는 명령어이다.

        반면 CMD는 기본값으로 사용항 인자/명령어이고, 런타임에 인자가 입력되면 CMD의 인자는 덮어씌워져서 사용되지 않는다.

      • 둘을 같이 사용하는 경우가 있나?

        “실행 명령은 고정하고, 인자는 유연하게 바꾸고 싶을 때”

        ENTRYPOINT로 실행 명령은 고정한다. 그리고 CMD로 디폴트 인자를 설정한다. 이 인자는 런타임에 입력한 인자로 대체가 되기 때문에 실행할 때에 맞게 인자를 설정할 수 있다.

      • 정리

        ENTRYPOINT → 무조건 항상 실행됨

        CMD → Docker 실행 시에 인자를 주면 덮어씌워짐

  • 선택 설정 요소

    RUN, WORKDIR, ENV, EXPOSE, ARG, LABEL

    USER, HEALTHCHECK, VOLUME, ONBUILD

  • 예시

      # [필수] 어떤 베이스 이미지 위에서 작업할지 설정
      FROM eclipse-temurin:21-jdk
    
      # [선택] 이후 모든 명령어는 /app 디렉토리 기준으로 실행됨
      WORKDIR /app
    
      # [선택] 환경 변수 설정 (애플리케이션에서 사용 가능)
      ENV SPRING_PROFILES_ACTIVE=prod
    
      # [선택] 빌드 시점에 사용할 인자 정의 (옵션)
      ARG JAR_FILE=build/libs/myapp.jar
    
      # [필수] 소스 코드나 JAR 파일을 컨테이너 안으로 복사
      # 현재 호스트의 디렉토리(첫 번째 인자)의 내용을 컨테이너 내부(두 번째 인자)로 복사함
      # 기준이 되는 위치는 WORKDIR에서 설정한 위치가 기준임
      # 이때 .dockerignore에 설정되어 있는 것은 제외됨
      COPY . .
    
      # [선택] 라벨을 붙여 이미지 정보 관리 가능
      LABEL maintainer="you@example.com"
      LABEL version="1.0.0"
    
      # [선택] 헬스 체크 설정 (컨테이너 정상 여부 확인)
      HEALTHCHECK CMD curl --fail http://localhost:8080/actuator/health || exit 1
    
      # [선택] 사용할 포트 지정 (문서화 목적, 외부 접근 가능하게 하려면 docker run -p 필요)
      EXPOSE 8080
    
      # [필수] 컨테이너 실행 시 실행할 명령어
      ENTRYPOINT ["java", "-jar", "build/libs/myapp.jar"]
    • 근데 왜 ENTRYPOINT는 리스트 형태로 값을 넘기는거지?

      명령어를 넘기는 방법에는 두 가지가 있다.

      • 명령어를 문자열로 넘기기(Shell 형식)

        문자열로 넘길 경우 Docker는 내부적으로 /bin/sh -c와 함께 명령어를 실행한다. 그렇게 되면 명령어가 쉘을 통해 해석되고 쉘 문법에 따라 예기치 못한 실행 결과가 나올 수 있다. (쉘에서는 특수문자가 연산자로 쓰이고, 환경에 따라 쉘 문법이 다를 수 있다.)

        • /bin/sh -c가 뭐길래 그게 쉘로 실행되는 거지?

          /bin/sh -c "명령어"/bin/sh 프로그램을 실행하면서 뒤에 것을 인자로 넘기는 것이다.

          -c 옵션은 인자로 넘긴 문자열을 명령어로 해석해서 실행하라는 뜻이다.

          여기서 /bin/sh 프로그램은 가장 기본적인 POSIX 쉘이고, 그렇기 때문에 해당 명령은 쉘에서 실행된다.

          그리고 당연히 리눅스에서 프로그램을 실행할 때는 fork 되어서 자식 프로세스에서 실행된다.

      • 리스트로 넘기기(Exec 형식)

        Exec 형식으로 넘길 경우 Docker는 쉘 없이 명령어를 직접 실행한다. 특수 문자나 공백 같은 것들이 쉘 문법에 따라 해석되는 것이 아니라 있는 그대로 실행된다.

      • 결론

        쉘 문법에 영향을 받지 않고 인자가 정확히 나뉘어서 전달되는 Exec 형식을 사용하는 것이 권장된다.

        • 그러면 Shell 형식은 쓸 일이 없나?

          복잡한 쉘 스크립트나 파이프라인을 쓸 때 적합하다.

        • 추가적인 차이

          ENTRYPOINTCMD에서 Exec 형식으로 실행되는 명령어는 해당 컨테이너 가상화 환경에서 반드시 PID 1로 실행된다. 반면 Shell 형식으로 실행 시에는 앞서 설명했듯이 PID 1인 프로세스가 아니라 그 자식 프로세스에서 실행된다.

          리눅스에서 PID 1번은 프로세스 트리의 루트이기 때문에 아주 특별하게 취급된다. 따라서 안정적인 실행을 위해 서버 프로세스 같은 메인 앱은 반드시 Exec 형식으로 실행되어야 한다.

4 Docker와 Docker Compose의 차이

Docker는 “단일 컨테이너 실행”에 초점이 있다.

Docker Compose는 “여러 컨테이너를 한 번에 정의하고 실행”하는 도구이다.

  • 실행 방법

    Docker는 docker run으로 실행하고, Docker Compose는 docker compose up으로 실행한다.

  • 브릿지 네트워크

    Docker로 개별 docker run을 실행해도 기본적으로 브릿지 네트워크로 연결이 된다.

    하지만, Docker Compose처럼 컨테이너 이름으로 DNS처럼 다른 컨테이너에 접근할 수는 없다.

5 Docker Compose의 파일 구조

  • 필수 설정 요소

    version / services / image 또는 build

    • imagebuild를 동시에 쓰면 어떻게 되나?

      build를 이용해서 Dockerfile 기반으로 이미지를 새로 빌드하고, image에 설정된 이름으로 빌드된 이미지에 이름을 붙인다.

      • image만 쓴 경우 → 이미 존재하는 이미지를 사용함

        • 이미지 리스트에 해당하는 이미지가 없으면 어떻게 되나?

          로컬에 지정한 이미지가 없을 경우 자동으로 Docker Hub에서 이미지를 자동으로 pull하려고 시도한다.

          → Docker Hub에도 없다면 에러가 발생한다.

      • build만 쓴 경우 → 이름 없는 임시 이미지로 컨테이너를 만듦

  • 선택 설정 요소

    ports, volumes, environment, depends_on, networks, restart, command

  • 예시

      # [필수] Compose 버전 명시
      version: "3.8"
    
      # [필수] 서비스 정의
      services:
        backend:
          # [필수] image와 build 둘 중 하나는 반드시 써야한다.
          # 이미지로 실행할지, 빌드로 생성할지 설정
          build: .
          image: likelion-backend:latest
    
          # [선택] 포트 매핑
          ports:
            - "8082:8082"
    
          # [선택] 환경 변수
          environment:
            - SPRING_PROFILES_ACTIVE=prod
            - DB_HOST=db
    
          # [선택] 볼륨 마운트
          volumes:
            - ./logs:/app/logs
    
          # [선택] 네트워크 설정 (명시해도 되고 안 해도 됨)
          networks:
            - likelion-net
    
          # [선택] db가 먼저 실행되도록 설정
          depends_on:
            - db
    
        db:
          image: mysql:8.0
          restart: always
    
          environment:
            MYSQL_ROOT_PASSWORD: password
            MYSQL_DATABASE: likelion
    
          volumes:
            - db-data:/var/lib/mysql
    
          networks:
            - likelion-net
    
      # [선택] 네트워크 정의
      networks:
        likelion-net:
    
      # [선택] 볼륨 정의
      volumes:
        db-data:
    • version은 입력 안 한다는 말이 있던데?

      과거 Compose (v1)은 여러 버전 스펙이 존재했다. 하지만 Compose v2는 단일 스펙으로 통일되어서 Docker CLI가 알아서 최적 버전으로 해석한다.

    • volume은 복사하는건가? 아니면 심볼릭 링크 같은게 생성되는건가?

      복사가 아니다. 심볼릭 링크도 아니다. 컨테이너 환경에서 특정 경로에 마운트되는 것이다. 해당 디스크 볼륨을 동시에 마운트하는 것이다.

    • networks는 왜 명시 안 해도 되는거지? 만약 서비스의 networks를 다르게 하면 어떻게 되는거지?

      Docker Compose는 networks:가 없으면 자동으로 기본 네트워크를 생성한다.

      여러 서비스 그룹을 다른 네트워크로 분리하고 싶거나 네트워크 이름을 명시적으로 관리하고 싶을 때 명시한다.

      서비스의 networks를 다르게 하면 각각의 네트워크끼리는 DNS 이름으로 통신할 수 없다. (서비스 이름으로 통신 불가능)

      • 그럼 어떻게 통신가능하지? 외부 호스트를 통해야 하나?

        외부 포트와 연결을 설정하고 호스트를 통해 접근해야 한다.

    • 서비스의 하위에 있는 volumes와 아예 밑에 있는 volumes는 무슨차이지?

      서비스의 volumes는 해당 컨테이너에 마운트하기 위한 설정이다.

      서비스 바깥에 있는 volumes는 사전에 볼륨 이름을 정의하는 것이다. 이를 설정하지 않으면 Docker가 자동으로 규칙에 따라서 볼륨 이름을 설정한다.

      → 의도한 이름과 다를 수 있고 명확하게 추적이 어려울 수 있음.

'스터디 > 백엔드' 카테고리의 다른 글

Docker에 대한 의문점 1편  (0) 2025.04.02
[백엔드] 쿠키(Cookie)란?  (0) 2024.12.13