콘텐츠로 건너뛰기

Docker에서 PHP 파일 업로드 설정 방법 | php.ini · Nginx · docker-compose

카테고리: Docker·PHP
이 기사는 현재 일본어로만 제공됩니다. 번역본은 순차적으로 공개될 예정입니다.

Docker로 구축한 환경에서는 "로컬에서 설정했는데 컨테이너에 반영되지 않음" 또는 "Nginx 컨테이너를 수정했지만 PHP-FPM 컨테이너 설정이 남아있음"과 같은 문제가 자주 발생합니다. 이는 Docker 컨테이너가 독립된 환경을 가지고 있어서 설정 파일이 호스트와 컨테이너 간에 분리되어 있기 때문입니다. 본 글에서는 Nginx 컨테이너와 PHP-FPM 컨테이너의 조합으로 파일 업로드 설정을 올바르게 수행하는 방법을 체계적으로 설명합니다.

Docker에서의 PHP 커스텀 설정 방법

PHP 컨테이너에 사용자 정의 php.ini 설정을 적용하는 방법은 주로 두 가지입니다: Dockerfile에서 이미지 빌드 시 파일을 복사하는 방법과 docker-compose에서 볼륨 마운트를 사용하는 방법입니다.

방법 1: Dockerfile에서 <code>php.ini</code> 복사

# Dockerfile(PHP-FPM コンテナ)
FROM php:8.3-fpm-alpine

# 必要な拡張をインストール
RUN docker-php-ext-install pdo_mysql opcache

# カスタム php.ini をコンテナにコピー
# php.ini-development または php.ini-production をベースにする
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini

# または conf.d ディレクトリに追加設定ファイルとして置く(推奨)
# 既存設定を上書きせず、変更したい値だけ記述できる
COPY ./docker/php/custom.ini /usr/local/etc/php/conf.d/99-custom.ini

WORKDIR /var/www/html

방법 2: docker-compose에서 볼륨 마운트

# docker-compose.yml
services:
  php:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
    volumes:
      # アプリのソースコード
      - ./src:/var/www/html
      # php.ini をマウント(ファイル変更が即反映される)
      - ./docker/php/custom.ini:/usr/local/etc/php/conf.d/99-custom.ini:ro

볼륨 마운트 방식은 개발 환경에 적합하며 설정 파일을 수정하고 컨테이너를 재시작하기만 하면 반영됩니다. 프로덕션 환경에서는 이미지에 복사하는 Dockerfile 방식이 권장됩니다(이미지의 재현성을 유지하기 위해).

필요한 설정값과 php.ini의 작성 방법

파일 업로드와 관련된 설정값은 4가지입니다. 이들을 함께 설정한 <code>custom.ini</code> 파일을 준비합니다.

; docker/php/custom.ini
; ============================================
; ファイルアップロード関連の設定
; ============================================

; ファイルアップロードを有効化(デフォルトOnだが明示する)
file_uploads = On

; 1ファイルあたりのアップロード上限
; Nginx の client_max_body_size と合わせること
upload_max_filesize = 100M

; POSTリクエスト全体の上限
; upload_max_filesize より大きくすること(フォームフィールドのオーバーヘッド分)
post_max_size = 110M

; ============================================
; メモリ・実行時間の設定
; ============================================

; PHPスクリプトが使用できる最大メモリ
; post_max_size より大きくすること
memory_limit = 256M

; スクリプトの最大実行時間(秒)
; 大きなファイルのアップロードや処理に対応
max_execution_time = 300

; リクエストデータ(POSTやファイル)の読み込み最大時間(秒)
; -1 で max_execution_time に従う
max_input_time = 300

php.ini 설정 파일의 위치를 확인하는 방법

컨테이너 내에서 어떤 php.ini가 로드되고 있는지 확인하려면 다음 명령을 사용합니다.

# コンテナ内で実行(docker exec 経由)
docker exec -it <コンテナ名> php -i | grep "php.ini"

# 出力例:
# Configuration File (php.ini) Path => /usr/local/etc/php
# Loaded Configuration File         => /usr/local/etc/php/php.ini

# Additional .ini files parsed(conf.d の追加設定)
docker exec -it <コンテナ名> php -i | grep "additional"
# Additional .ini files parsed => /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini,
#   /usr/local/etc/php/conf.d/99-custom.ini

# 特定の設定値を確認
docker exec -it <コンテナ名> php -i | grep -E "upload_max|post_max|memory_limit"
# upload_max_filesize => 100M
# post_max_size       => 110M
# memory_limit        => 256M

웹을 통해 확인하는 경우 <code>phpinfo()</code>를 사용합니다.

<?php
// /var/www/html/public/info.php (一時的なデバッグ用)
phpinfo();
// ※ 本番環境では削除すること(サーバー情報が丸見えになる)
Docker 2 컨테이너 (Nginx + PHP-FPM) 업로드 흐름 Client Browser Nginx 컨테이너 Nginx client_max_body_size PHP-FPM 컨테이너 PHP-FPM upload_max_filesize post_max_size App /var/www HTTP POST FastCGI :9000 두 제한값이 일치하지 않으면 업로드 실패 Docker 2 컨테이너 구성 업로드 경로
그림 1: Nginx + PHP-FPM 2 컨테이너 구성 업로드 흐름

Nginx 컨테이너와 PHP-FPM 컨테이너 간의 설정 관계

Docker의 일반적인 PHP 구성은 Nginx 컨테이너와 PHP-FPM 컨테이너를 분리하는 2개 컨테이너 아키텍처입니다. 각각 독립적인 제한을 가지므로 <strong>둘 다 적절히 설정되지 않으면 업로드가 실패합니다</strong>.

クライアント
    │
    ▼(HTTP リクエスト)
┌──────────────────────────────┐
│  Nginx コンテナ              │  ← client_max_body_size で制限
│  client_max_body_size 100m;  │     ここを超えると 413 エラー
└──────────────────────────────┘
    │
    ▼(FastCGI プロトコル)
┌──────────────────────────────┐
│  PHP-FPM コンテナ            │  ← upload_max_filesize / post_max_size で制限
│  upload_max_filesize = 100M  │     ここを超えると UPLOAD_ERR_INI_SIZE
│  post_max_size = 110M        │
└──────────────────────────────┘
# docker/nginx/conf.d/default.conf
server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;
    index index.php;

    # Nginx レベルのアップロード上限
    # PHP の upload_max_filesize / post_max_size 以上にする
    client_max_body_size 110m;

    # タイムアウト設定(大容量ファイル処理用)
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass php:9000;   # PHP-FPMコンテナのサービス名:ポート
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;

        # FastCGI のタイムアウト設定
        fastcgi_read_timeout 300s;
        fastcgi_send_timeout 300s;
    }
}

docker-compose.yml의 완전한 설정 예

Nginx + PHP-FPM + 볼륨 마운트를 포함한 완전한 docker-compose.yml의 예를 보여줍니다.

version: '3.8'

services:
  # Nginx コンテナ
  nginx:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    volumes:
      # アプリのソースコード(Nginx から静的ファイルを配信)
      - ./src:/var/www/html/public:ro
      # Nginx 設定ファイル
      - ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
    depends_on:
      - php
    networks:
      - app-network

  # PHP-FPM コンテナ
  php:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
    volumes:
      # アプリのソースコード
      - ./src:/var/www/html
      # PHP カスタム設定(開発時はマウントで即反映)
      - ./docker/php/custom.ini:/usr/local/etc/php/conf.d/99-custom.ini:ro
      # アップロードファイルの一時保存先(権限に注意)
      - upload_tmp:/tmp/php-uploads
    environment:
      PHP_UPLOAD_TMP_DIR: /tmp/php-uploads
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  upload_tmp:
    driver: local
# docker/php/Dockerfile
FROM php:8.3-fpm-alpine

# 必要なシステムパッケージ
RUN apk add --no-cache \
    libpng-dev \
    libjpeg-turbo-dev \
    libwebp-dev \
    freetype-dev

# PHP 拡張のインストール
RUN docker-php-ext-configure gd \
        --with-freetype \
        --with-jpeg \
        --with-webp && \
    docker-php-ext-install \
        gd \
        pdo_mysql \
        opcache \
        exif

# アプリユーザーを作成(www-data と権限を合わせる)
RUN addgroup -g 1000 appuser && \
    adduser -u 1000 -G appuser -s /bin/sh -D appuser

USER appuser
WORKDIR /var/www/html

설정이 반영되었는지 확인하는 방법

설정을 변경한 후에는 항상 컨테이너를 다시 시작하고 설정이 올바르게 로드되었는지 확인합니다.

# コンテナの再起動(設定ファイル変更を反映)
docker compose restart php

# または特定の設定値だけ確認
docker compose exec php php -r "echo ini_get('upload_max_filesize'), PHP_EOL;"
docker compose exec php php -r "echo ini_get('post_max_size'), PHP_EOL;"
docker compose exec php php -r "echo ini_get('memory_limit'), PHP_EOL;"

# 複数の値をまとめて確認
docker compose exec php php -r "
foreach (['upload_max_filesize', 'post_max_size', 'memory_limit', 'max_execution_time'] as \$key) {
    echo \$key . ' = ' . ini_get(\$key) . PHP_EOL;
}"

# Nginx の設定確認
docker compose exec nginx nginx -T | grep -E "client_max_body|server_name"

# Nginx の設定をリロード(コンテナ再起動なし)
docker compose exec nginx nginx -s reload

일반적인 문제와 해결방법

증상 원인 해결책
413 Request Entity Too Large Nginx <code>client_max_body_size</code> 초과 Nginx 설정을 높이고 <code>nginx -s reload</code> 실행
PHP 레벨에서 UPLOAD_ERR_INI_SIZE <code>upload_max_filesize</code> 초과 custom.ini를 수정하고 컨테이너 재시작
설정을 변경해도 반영되지 않음 컨테이너 재시작 필요 / 설정 파일의 마운트 경로가 잘못됨 <code>docker compose restart php</code> / 경로 확인
임시 디렉토리 쓰기 실패 PHP 컨테이너의 사용자 권한과 <code>/tmp</code> 권한 불일치 볼륨 권한 설정 및 <code>upload_tmp_dir</code> 확인
대용량 파일에서의 타임아웃 <code>fastcgi_read_timeout</code> 또는 <code>max_execution_time</code>이 너무 짧음 두 값 모두 올리기

프로덕션 환경을 위한 추가 사항

프로덕션 환경에서는 다음 사항도 고려하세요.

  • php.ini 는 이미지 빌드 시 <code>COPY</code> 로 포함시키고 볼륨으로 마운트하지 않습니다(재현성을 위해)
  • <code>display_errors = Off</code>・<code>log_errors = On</code>을 반드시 설정하기
  • 업로드 대상이 S3 같은 클라우드 스토리지인 경우, 컨테이너 로컬에 대한 임시 저장은 요청 처리 중에만 유지하세요.
  • 다중 레플리카(수평 확장) 구성에서는 로컬 업로드 대상을 사용할 수 없습니다. 대신 S3 또는 공유 스토리지(EFS, NFS)를 사용하세요.
  • PHP-FPM의 프로세스 수 (<code>pm.max_children</code>)도 대용량 업로드가 동시에 많이 발생하면 메모리를 압박합니다

이 기사에서 사용할 수 있는 테스트 파일 (무료)

  • → <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">경계값 테스트 파일 목록</a> — upload_max_filesize 설정값 전후에서 오류 재현 및 확인
  • → <a href="/ja/files/images/" class="text-primary-600 dark:text-primary-400 hover:underline">테스트 이미지 목록</a> — Docker 환경에서 다양한 크기의 업로드 동작 검증

관련 기사

  • → <a href="/ja/blog/nginx-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Nginx client_max_body_size를 올바르게 설정하는 방법 | 업로드 제한 문제 해결</a>
  • → <a href="/ja/blog/php-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">PHP에서 파일 업로드를 구현하는 방법 | 검증・저장・보안 완벽 가이드</a>
  • → <a href="/ja/blog/laravel-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">Laravel 파일 업로드 구현 가이드|검증·Storage·S3 대응</a>

자주 묻는 질문

Docker에서 PHP의 upload_max_filesize를 변경하려면?

사용자 정의 <code>php.ini</code> 파일을 만들고 Dockerfile을 통해 컨테이너 내부의 <code>/usr/local/etc/php/conf.d/</code>에 복사합니다.

docker-compose.yml에서 Nginx와 PHP-FPM의 업로드 설정을 하려면?

Nginx용 nginx.conf에 client_max_body_size를 설정하고, PHP용 php.ini에 upload_max_filesize를 설정한 후 각각 volumes로 마운트합니다.

Docker 환경에서 업로드 설정이 반영되지 않는 이유는?

php.ini의 설치 위치가 잘못되었을 수 있습니다. <code>php -i | grep upload_max_filesize</code> 로 현재 설정값을 확인하고, Loaded Configuration File 에서 어떤 ini 파일이 읽혀있는지 확인하세요.