0Pricing
PHP Academy · 강의

다단계 빌드와 최적화

이미지 크기를 줄이고 빌드와 실행 환경을 분리합니다.

다단계 빌드와 최적화은(는) CoddyKit의 무료 PHP Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PHP Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

멀티 스테이지를 사용하는 이유

단일 스테이지 이미지는 Composer, 빌드 의존성, 개발 헤더, 그리고 tests/ 폴더를 운영 환경까지 포함합니다. 멀티 스테이지 빌드를 사용하면 용량이 큰 빌더 스테이지에서 컴파일과 설치를 수행한 다음, 완성된 산출물만 작은 런타임 스테이지로 복사할 수 있습니다.

그 결과 이미지 크기와 공격 표면이 줄어들고, 가져오는 속도가 빨라지며, 컴파일러를 운영 환경에 배포하지 않게 됩니다.

스테이지 이름 지정

각 FROM ... AS name은 새 스테이지를 시작합니다. 이후 스테이지에서는 COPY --from=name을 사용해 이전 스테이지의 파일을 가져올 수 있습니다. 최종 스테이지만 이미지가 되며, 중간 스테이지는 삭제됩니다(단, 캐시에는 남습니다).

# Stage 1: dependencies
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist --ignore-platform-reqs

# Stage 2: runtime
FROM php:8.3-fpm-alpine AS runtime
WORKDIR /app
COPY --from=vendor /app/vendor ./vendor
COPY . .

빌드 의존성 분리

확장 기능을 컴파일하려면 autoconf, gcc, 개발 헤더가 필요하지만, 이들은 런타임에 포함되어서는 안 됩니다. 빌더 스테이지에서 설치 도구를 사용한 다음, 컴파일된 .so 파일과 이에 맞는 conf.d ini를 깨끗한 런타임 스테이지로 복사하십시오.

FROM php:8.3-fpm-alpine AS ext-builder
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions redis igbinary opcache intl

FROM php:8.3-fpm-alpine AS runtime
# Copy compiled extensions + their enable configs
COPY --from=ext-builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=ext-builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/

레이어 캐시 순서

Docker는 위에서 아래 순서로 레이어를 캐시하며, 변경된 레이어 아래의 모든 레이어를 무효화합니다. 변경 빈도가 낮은 항목부터 높은 항목 순서로 배치하십시오.

  • 기반 이미지 + 확장 기능 (드묾)
  • composer.lock + 설치 (가끔)
  • 애플리케이션 소스 코드 (커밋마다)
  • 자동 로드 덤프 (커밋마다)

따라서 코드만 변경하면 캐시된 vendor 레이어를 전부 재사용할 수 있습니다.

# BAD: copying all source before composer install
# busts the vendor layer on every code change
COPY . .
RUN composer install

# GOOD: lock first, then source
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-autoloader
COPY . .
RUN composer dump-autoload --optimize

BuildKit 캐시 마운트

BuildKit(DOCKER_BUILDKIT=1)을 사용하면 빌드 간에 유지되지만 이미지에는 포함되지 않는 영구 캐시를 마운트할 수 있습니다. Composer 전역 캐시에 적합한 방식으로, 반복 빌드에서 패키지를 다시 다운로드하지 않아도 됩니다.

# syntax=docker/dockerfile:1
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN --mount=type=cache,target=/tmp/composer-cache \
    COMPOSER_CACHE_DIR=/tmp/composer-cache \
    composer install --no-dev --prefer-dist

이미지 크기 측정

레이어별 내역을 살펴 불필요하게 커진 부분을 찾으십시오. docker history는 각 명령이 추가한 크기를 보여 주고, dive와 같은 도구는 낭비되는 공간을 보여 줍니다. 목표는 컴파일러, Composer, 개발 의존성이 없는 런타임 스테이지를 만드는 것입니다.

# Compare sizes
docker images myapp

# Per-layer contribution
docker history --no-trunc --format '{{.Size}}\t{{.CreatedBy}}' myapp:latest

# Deep inspection of wasted bytes
dive myapp:latest

최종 스테이지 정리

런타임 스테이지에는 Composer, 확장 기능 설치 스크립트 또는 테스트 모음을 포함해서는 안 됩니다(NOT). 빌더에서 vendor와 소스 코드를 복사하고, 최종 스테이지에서는 절대로 composer를 실행하지 마십시오. 그곳에서 설치 스크립트를 실행해야 한다면 사용 후 제거하십시오.

FROM php:8.3-fpm-alpine AS runtime
WORKDIR /app

# bring extensions + vendor in from builders — no Composer here
COPY --from=ext-builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=ext-builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
COPY --from=vendor /app/vendor ./vendor
COPY . .

USER www-data
CMD ["php-fpm"]

Distroless / Scratch의 한계

PHP는 완전히 비어 있는 scratch에서는 실행할 수 없습니다. libc와 공유 라이브러리가 필요하기 때문입니다. 현실적으로 사용할 수 있는 최소 기반은 alpine(musl) 또는 Distroless 방식의 최소 Debian입니다. Alpine이 가장 작지만 glibc를 기대하는 네이티브 라이브러리에 주의하십시오. NSS/ICU에서 세그멘테이션 오류가 발생하면 php:8.3-fpm-bookworm으로 전환하십시오.

# Smallest practical PHP runtime
FROM php:8.3-fpm-alpine

# If musl causes native-lib issues (e.g., some ICU edge cases),
# the glibc Debian slim variant is the safe fallback:
# FROM php:8.3-fpm-bookworm

스테이지 대상 지정

하나의 Dockerfile에서 --target을 사용해 개발 환경과 운영 환경을 모두 제공할 수 있습니다. 런타임 위에 dev 스테이지를 추가하고 Composer 개발 의존성과 Xdebug를 다시 포함하십시오. 운영 환경에서는 --target=runtime으로, 로컬에서는 --target=dev으로 빌드합니다.

FROM runtime AS dev
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions xdebug @composer
USER root
RUN composer install   # includes dev deps

# Build prod:  docker build --target runtime -t app:prod .
# Build dev:   docker build --target dev     -t app:dev  .

레이어 크기 이해하기

간단한 사고 모델을 사용하면 캐시 동작을 예측하는 데 도움이 됩니다. 이 CLI 예제는 제한 없이 레이어가 증가하는 전형적인 실수와 증가량을 제한한 경우를 시뮬레이션하여, 정리를 동일한 RUN에 포함해야 하는 이유를 보여 줍니다.

<?php
// Simulate layer sizes (MB) for two strategies
$installSteps = [120, 8, 8, 8];

// Separate RUN per step keeps temp files in layers
$separate = array_sum($installSteps);

// Single RUN with cleanup removes temp files before commit
$combined = max($installSteps); // peak, then cleaned

echo "Separate layers total: {$separate} MB\n";
echo "Combined+cleanup:       {$combined} MB\n";
echo 'Saved: ' . ($separate - $combined) . " MB\n";
?>

하나의 RUN에서 설치와 정리 함께 하기

각 RUN은 하나의 레이어입니다. 이후 레이어에서 파일을 삭제해도 이전 레이어에 바이트가 남아 있으므로 이미지 크기는 줄어들지 않습니다. 하나의 RUN 안에서 설치하고 사용한 다음 정리하여 임시 파일이 커밋되지 않도록 하십시오.

RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS && \
    pecl install redis && \
    docker-php-ext-enable redis && \
    apk del .build-deps && \
    rm -rf /tmp/pear /var/cache/apk/*

빠른 확인

빌드 의존성을 설치한 동일한 RUN에서 제거해야 하는 이유는 무엇입니까?

요약

멀티 스테이지 빌드를 사용하면 컴파일러와 개발 의존성이 운영 환경에 포함되지 않습니다. 스테이지 이름을 지정하고 COPY --from으로 산출물을 복사하는 방법, 캐시를 위해 레이어를 변경 빈도가 낮은 것부터 높은 순서로 배치하는 방법, Composer에 BuildKit 캐시 마운트를 사용하는 방법, docker history/dive로 크기를 측정하는 방법, alpine과 glibc 중 적절한 것을 선택하는 방법, 개발 및 운영 스테이지를 대상으로 지정하는 방법, 설치와 정리를 하나의 RUN에 결합하는 방법을 배웠습니다.

자주 묻는 질문

“다단계 빌드와 최적화” 강의는 무료인가요?

네 — “다단계 빌드와 최적화” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PHP Academy 강의 전체를 잠금 해제할 수 있습니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“다단계 빌드와 최적화”에서 뭘 배우나요?

이미지 크기를 줄이고 빌드와 실행 환경을 분리합니다. 브라우저에서 직접 실행하는 실습 코드로 PHP Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

PHP Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 PHP Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“다단계 빌드와 최적화” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 PHP Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 PHP Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. PHP 애플리케이션 컨테이너화
  2. 다단계 빌드와 최적화
  3. 로컬 스택을 위한 Docker Compose
  4. GitHub Actions로 CI/CD 구축
← PHP Academy(으)로 돌아가기