Docker에 대해서 알아보자 - 3. docker build & docker compose

2025. 4. 26. 16:33·프로젝트

 

Dockerfile을 기반으로  이미지를 "어디서 어떻게" 빌드할지 설정하고 실행하는 게 docker build의 역할입니다.  내부적으로는 BuildKit이라는 고급 빌더를 사용해서 빌드 환경, 캐시 전략, 플랫폼, 시크릿, SSH, 출력 방식 등을 설정할 수 있습니다.

docker-compose에서는 컨테이너를 어떻게 실행시킬지에 대한 설정을 할 수 있습니다. docker build와 docker compose에 대해서 설명하면서 컨테이너 오케스트레이션 직전까지의 docker 게시물을 마치겠습니다.

📌 docker buildx

docker의 builder에는 레거시 builder와 Buildkit을 사용하는 buildx가 있습니다. buildx는 필요한 파일만 전송, 캐시 재사용, 병렬 실행, 그리고 성능과 편의성 향상등 느리고 비효율적인 레거시 빌더보다 뛰어나기 때문에 buildx에 대해서 알아보겠습니다.

단, Windows의 경우에는 레거시 builder를 사용하기도 한다고 합니다.

 

⚠️ 레거시 builder와 buildx의 Alias는 모두 docker build 이지만, default 설정은 레거시 builder이기 때문에 buildx라고 명시적으로 사용하거나 docker engine의 설정 자체를 바꿔주시면 됩니다.

✅ buildx bake 

docker buildx bake는 여러 Docker 빌드 타겟을 병렬로, 구성 파일(docker-bake.hcl, JSON, 또는 Compose) 기반으로 빌드하는 고수준 빌드 명령어입니다.

# docker-bake.hcl 예시
group "default" {
  targets = ["web", "db"]
}

target "web" {
  context = "./web"
  dockerfile = "Dockerfile.web"
  tags = ["myrepo/web:latest"]
  platforms = ["linux/amd64", "linux/arm64"]
}

target "db" {
  context = "./db"
  dockerfile = "Dockerfile.db"
  tags = ["myrepo/db:latest"]
}

 

docker-bake.hcl 또는 .json 형식의 구성 파일로, 여러 개의 build 타깃을 정의할 수 있습니다. bake는 명령어는 이 설정을 읽고 각 타깃을 병렬로 빌드할 수 있습니다.

✅ buildx build

BuildKit 기반의 고급 빌드 명령어로 Dockerfile을 기반으로 이미지를 빌드하며, 병렬 빌드, 멀티 플랫폼, 캐시, 시크릿 등 고급 기능을 제공합니다.

docker buildx build [OPTIONS] PATH | URL | -

 

OPTIONS :

기본 빌드 설정

 

docker buildx build \
  --platform=linux/amd64,linux/arm64 \
  -t myapp:multi \
  --push \
  .

 

다음과 같이 멀티 플렛폼으로 build 하여 push 하는 예시가 있을 수 있습니다.

 

빌드 동작 관련

  • --no-cache 그리고 --no-cache-filter를 이용해서 버전 업데이트를 할 수 있으면 --pull을 통해서 베이스 이미지를 업데이트 가능합니다.
  • --output , --load, 그리고 --push는 어떤 형태로 어디로 보낼지 설정할 수 있습니다.

빌드 인자/환경/시크릿

  • --build-arg 또는 --secret 을 이용해서 build 시점에만 필요한 변수를 주입해 줄 수 있습니다. 

⭐빌드 캐시

 

--build-context는 ADD처럼 추가 빌드 컨텍스트를 정의하는 기능입니다.

docker buildx build \
  --build-context lib=https://github.com/user/project.git .

 

ADD와 비슷한 기능을 하지만 ADD는 간단한 용도에만 쓰는 것이 좋습니다.
구조화된 빌드, 외부 레포 통합, 이미지 재활용을 할 거면 --build-context가 더 안전하고 강력한 기능입니다.

 

다음으로 cache 기능이 중요합니다.

docker buildx build \
  --cache-from=type=inline,ref=myrepo/myapp:latest \
  --tag myrepo/myapp:latest \
  --cache-to=type=inline \
  --push \
  .

 

예를 들어서, Github Action을 이용한 CI/CD 환경에서는 반복적으로 이미지를 빌드하는 경우 매번 다시 이미지를 빌드하게 됩니다. 이 경우 다음과 같이 이미지 전체를 pull 해오는 것이 아니라 cache만을 받아와서 build를 하면 효과적입니다.

 

이미지 메타 정보 및 증명

빌드 분석/검증

이 옵션들은 빌드를 실행하지 않고도 정보만 뽑거나 검증하거나, 빌드 결과를 파일로 남기는 목적으로 사용됩니다.
특히 CI/CD에서 빠르게 fail fast 하거나, 결과 추적하는 데 유용합니다.

✅ buildx create

docker buildx create [OPTIONS] [CONTEXT|ENDPOINT]

빌드를 실행할 Builder 인스턴스를 만드는 명령어입니다. 특정 컨텍스트나 엔드포인트를 기반으로 독립적인 빌드 환경을 만드며 인스턴스마다 지원하는 플랫폼, 빌드 드라이버, 노드(=실제 빌드가 수행되는 대상) 등이 다릅니다.

 

⚠️ GitHub Actions에서는 대부분의 경우 builder를 "자동으로" 생성하고 "일회성으로" 사용하기 때문에 builder를 만들 필요가 없지만 상황에 따라서는 직접 builder를 만들어 두는 게 필수이거나, 유리한 경우도 있습니다.

 

1️⃣ 로컬에서 멀티 플렛폼을 빌드하고 싶은 경우

docker buildx create --use --platform linux/amd64,linux/arm64 --driver docker-container
  • 멀티 플렛폼을 빌드하기 위해서는 buildx create를 통해서 builder를 만들어 주어야 합니다.

2️⃣ builder에 캐시/시크릿/네트워크 설정 등 커스터마이징이 필요한 경우

docker buildx create \ --name mybuilder \ --buildkitd-config ./buildkitd.toml \ --use
  • 캐시 디렉토리 위치, 프록시, 인증서, 레지스트리 인증, network.host 설정 같은 것들은 builder 설정파일(buildkitd.toml) 이 필요한 CI/CD가 아니라 장기적으로 builder 환경이 만들어져야 하는 경우.

 3️⃣ 클러스터 환경 (쿠버네티스, 원격 서버)에서 BuildKit 관리할 때

docker buildx create --driver kubernetes --name kube-builder

 

  • 하나의 builder로 여러 팀이 공유하는 클러스터에 빌드 분산 가능 CI/CD 인프라가 멀티 노드일 때 필요합니다.
 

✅ docker buildx du

BuildKit 빌드 캐시가 디스크에 얼마나 차지하고 있는지 확인하는 명령어(du = disk usage)

ID                                RECLAIMABLE    SIZE          LAST ACCESSED
12wgll9os87pazzft8lt0yztp*        true           1.704GB       13 days ago
...
Shared:        115.5MB
Private:       10.25GB
Reclaimable:   10.36GB
Total:         10.36GB

✅ docker buildx history

 Build 기록(Record)을 관리하거나 조회하는 명령어 그룹입니다. 빌드를 실행할 때마다 BuildKit은 "빌드 레코드"를 남기는데 이것을 조회, 삭제, 내보내기(export) 하는 용도로 사용합니다.

✅ 기타 기능들

📌 docker-compose.yml

docker-compose.yml은 여러 컨테이너를 실행하기 위한 설정 파일입니다. 복잡한 docker run ... 대신에 yml 파일로 실행 방법을 선언

  • 어떤 이미지를 쓸지
  • 환경변수, 볼륨, 포트 매핑을 어떻게 할지
  • 컨테이너들끼리 어떻게 네트워킹할지

✅ Version & Name

version: '3.8'   # (obsolete 되었지만 여전히 작성 가능, 경고만 뜸)

name: my-app  # 이 프로젝트 이름

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    environment:
      - NGINX_HOST=localhost
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

 

version : 이제 사용하지 않는 것을 권장합니다.

name : Compose 프로젝트의 기본 이름을 설정합니다 (설정 안하면 root 폴더 이름으로 생성됨).

✅ Services

서비스에서는 컨테이너에 대한 입력인자(환경 변수, 포트 매핑 등)을 설정해야 합니다. Docker Compose 파일(docker-compose.yml)은 반드시 최상위에 services 요소를 선언해야 하며 services는 "서비스 이름"을 키로 하고, "서비스 설정"을 값으로 가지는 맵(Map) 형태입니다.

 

기본적으로 docker-compose.yml은 image에서 컨테이너를 띄우는 역할을 문서화해서 관리하는 것이기 때문에 1편에서 길게 설명했던 create와 run 등에서 cpu와 메모리등에 대한 설정을 모두 해줄 수 있습니다. 내용이 많은 관계로 개인적으로 많이 사용할 것 같은 부분들만 알아보겠습니다. (혹시 궁금하시다면 link를 첨부하겠습니다 🔗)

 

depends_on

서비스들 간의 실행 순서를 지정하는 옵션입니다.

 

Short Syntax : 단순히 다른 서비스 이름 나열

services:
  web:
    depends_on:
      - db
      - redis

 

Long Syntax: 조건(condition)과 restart 설정을 추가할 수 있다.

services:
  web:
    depends_on:
      db:
        condition: service_healthy
        restart: true
      redis:
        condition: service_started

 

conditions:

  • service_started: 서비스가 "시작"되면 OK
  • service_healthy: 서비스가 "healthcheck 통과"해야 OK
  • service_completed_successfully: 서비스가 정상 종료되어야 OK
  • required: false를 주면, 의존 서비스가 없어도 경고만 하고 진행

EntryPoint

컨테이너가 실행될 때 기본으로 실행할 엔트리포인트를 설정하는 옵션으로 Dockerfile의 ENTRYPOINT를 덮어씁니다.

entrypoint: /code/entrypoint.sh

 

 

env_file

컨테이너에 전달할 환경 변수 파일(.env) 을 지정합니다.

env_file:
  - ./a.env
  - ./b.env

 

상대 경로만 추천 (절대경로 쓰면 경고) environment:로 직접 설정한 값이 env_file보다 우선합니다.

 

environment

컨테이너에 직접 환경 변수를 설정하는 방법입니다.

# Map 형태
environment:
  RACK_ENV: development
  SHOW: "true"
  USER_INPUT:
  
# Array 형태
environment:
  - RACK_ENV=development
  - SHOW=true
  - USER_INPUT

 

ports

호스트와 컨테이너 간 포트 매핑을 설정하는 항목으로 외부에서 컨테이너 내부 서비스에 접근할 때 필수적입니다.

 

Short Syntax : 간단한 포트 매핑

ports:
  - "3000"
  - "8000:8000"
  - "127.0.0.1:8001:8001"
  - "6060:6060/udp"

 

  • "HOST:CONTAINER" 형식 (호스트IP:호스트포트:컨테이너포트)
  • 호스트 IP는 생략 가능, 생략하면 0.0.0.0으로 모두 바인딩
  • TCP가 기본, UDP를 지정할 수도 있음

 

Long syntax : 고급 옵션 포함

ports:
  - name: web
    target: 80
    published: "8080"
    host_ip: 127.0.0.1
    protocol: tcp
    app_protocol: http
    mode: host

 

  • target: 컨테이너 포트
  • published: 외부에 노출될 포트
  • host_ip: 바인딩할 호스트 IP
  • protocol: tcp/udp 선택
  • app_protocol: HTTP, HTTPS 등 상위 프로토콜 힌트
  • mode: swarm 환경에서 host 또는 ingress 선택

 

✅ Networks

Compose의 networks:는 컨테이너 간 통신을 제어하기 위해 사용됩니다. 기본적으로는 모든 서비스가 하나의 default 네트워크에 연결되지만, 명시적으로 네트워크를 나눠주면 서비스 간 통신을 격리하거나 연결 제어할 수 있습니다.

services:
  proxy:
    build: ./proxy
    networks:
      - frontend
  app:
    build: ./app
    networks:
      - frontend
      - backend
  db:
    image: postgres
    networks:
      - backend

networks:
  frontend:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.host_binding_ipv4: "127.0.0.1"

  backend:
    driver: custom-driver

 

예를 들어서 다음과 같은 경우,

  • proxy ↔ app 통신 가능 (둘 다 frontend 연결)
  • app ↔ db 통신 가능 (backend)
  • proxy ↔ db  불가능 (공통 네트워크 없음 → 격리됨)

Attribute :

✅ Volumes

Volume은 컨테이너들이 공유하는 저장소입니다. docker-compose.yml의 최상단 volumes: 키를 이용해서 여러 서비스가 공유할 수 있는 볼륨을 정의할 수 있습니다.

services:
  backend:
    image: example/database
    volumes:
      - db-data:/etc/data

  backup:
    image: backup-service
    volumes:
      - db-data:/var/lib/backup/data

volumes:
  db-data:

 

위의 예시에서 backend는 db-data라는 volume을 /etc/data 경로로 마운트하고 backup은 /var/lib/backup/data 경로로 volume을 마운트 합니다. volume이 존재할 경우 연결하고 존재하지 않는다면 새로 만들어 줍니다.

Attribute :

✅ Configs

컨테이너 안에 파일형태로 서비스 동작을 변경할 수 있도록 설정을 주입하는 기능입니다. 이미지를 다시 빌드하지 않고 서비스 동작을 변경할 수 있습니다. 해당 파일은 볼륨처럼 컨테이너 파일 시스템에 파일로 마운트 됩니다.

  • 리눅스 컨테이너에서는 /config-name
  • 윈도우 컨테이너에서는 C:\config-name 
configs:
  nginx_conf:
    file: ./nginx.conf

services:
  web:
    image: nginx
    configs:
      - source: nginx_conf
        target: /etc/nginx/nginx.conf

 

 

보통 application.yml, nginx.conf, httpd.conf, config.json 같은 설정 파일들을 어플리케이션 코드와는 별도로 이미지에 포함시키지 않고 관리하는 상황에서 유용합니다.

 

⭐ yml을 이미지에 포함시키지 않고 주입해서 사용하려는 경우, 
배포 환경에 복사하고 컨테이너에서 yml을 마운트 하는 것보다 config를 통해서 컨테이너에 직접 넣어주면 유용할 것 같습니다.

 

Attribute :

✅ Secrets

Secrets는 Configs와 비슷하지만, 민감한 데이터(비밀번호, 인증서, 토큰 등) 를 다루기 위해 특별히 설계된 기능입니다.

 

Secrets를 파일로 만들든 환경변수로 만들든, 컨테이너 내부에서는 파일 형태로 마운트 된다(/run/secrets/<secret-name> 같은 경로).

  • Secret 파일은 읽기 전용이다.
  • 퍼미션도 엄격하게 제한된다 (0444 읽기 권한만).
  • 컨테이너가 실행되는 동안에만 접근할 수 있다.
name : practice

services:
  app:
    image: myapp:latest
    container_name: myapp_container
    ports:
      - "8080:8080"
    environment:
      - SPRING_CONFIG_ADDITIONAL_LOCATION=/run/secrets/
    secrets:
      - source: application_secret_yml
        target: application-secret.yml  # 원하는 파일 이름 지정

secrets:
  application_secret_yml:
    file: ./config/application-secret.yml

 

다음과 같은 방법으로 yml 자체에 민감한 정보가 있다면 아예 secret으로 안전하게 관리할 수도 있습니다.

 

이렇게 작성된 docker-compose.yml을 docker compose up 명령어를 통해서 실행시켜 주시면 끝입니다.

📌 총정리

✅  Dockerfile 만들기

  • 런타임에서 필요한 변수는 env로 포함시키거나 민감한 정보일 경우에 추후에 주입하도록 한다.
  • 빌드시 필요한 변수는 ARG로 명시하거나 민감한 정보일 경우에 secret mount로 주입하도록 한다.
  • 멀티 스테이지 빌드를 통해서 이미지 캐시를 최대한 활용한다.
  • dockerignore를 통해서 민감한 정보를 이미지에 포함시키지 않는다.

✅  buildx 명령어

  • CI/CD 같은 환경에서 builx의 cache 기능을 적극 활용하자.

✅  docker-compose.yml

  • Volume과 network 등 컨테이너를 띄우는 순간에 필요한 설정을 작성한다.
  • Secrets 또는 Config를 이용하여 이미지와 별도로 파일 주입이 가능하다

Reference:

도커 공식 문서 🔗

'프로젝트' 카테고리의 다른 글

배포의 모든 것 - 5. Blue-Green 무중단 배포 적용  (2) 2025.05.01
Clokey 프로젝트 리펙토링 - Github Actions & Docker 최적화  (0) 2025.04.29
Docker에 대해서 알아보자 - 2.Dockerfile  (1) 2025.04.26
Docker에 대해서 알아보자 - 1.Docker의 개념과 생태계  (0) 2025.04.18
배포의 모든 것 - 4. 도메인 연결과 HTTPS 적용하기  (0) 2025.04.03
'프로젝트' 카테고리의 다른 글
  • 배포의 모든 것 - 5. Blue-Green 무중단 배포 적용
  • Clokey 프로젝트 리펙토링 - Github Actions & Docker 최적화
  • Docker에 대해서 알아보자 - 2.Dockerfile
  • Docker에 대해서 알아보자 - 1.Docker의 개념과 생태계
potato-farm
potato-farm
개발 혼자 공부하기
  • potato-farm
    감자밭
    potato-farm
  • 전체
    오늘
    어제
    • 분류 전체보기 (27)
      • ETC (2)
      • 알고리즘 (0)
      • Java (0)
      • DB (2)
      • Spring (0)
      • 프로젝트 (15)
      • Server (3)
      • CS (0)
        • 운영체제 (0)
      • Infra (4)
        • IAC (1)
        • AWS (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • hELLO· Designed By정상우.v4.10.3
potato-farm
Docker에 대해서 알아보자 - 3. docker build & docker compose
상단으로

티스토리툴바