회사소개
회사 소개 팀 소개 연혁
서비스 쇼케이스 비전 테크 FAQ 문의
무료 상담 신청

Alpine Docker에서 Laravel 운영하기 — 실전 체크리스트

가벼운 Alpine Linux 기반 Docker 컨테이너에서 Laravel을 운영할 때 발생하는 PHP 익스텐션 누락, bash 없음, 퍼미션 문제 등을 해결하는 실전 가이드입니다.

Alpine Docker에서 Laravel 운영하기 — 실전 체크리스트

1. Alpine을 쓰는 이유

표준 Debian/Ubuntu 기반 PHP 이미지는 크기가 400~700MB입니다. Alpine Linux 기반은 5MB 베이스에 필요한 것만 추가하면 최종 이미지가 80~120MB 수준입니다. CI/CD 빌드 시간과 레지스트리 저장 비용이 크게 줄어듭니다.

2. Dockerfile 기본 구조

FROM php:8.2-fpm-alpine

# Alpine 패키지 설치 (bash 없음, /bin/sh 사용)
RUN apk add --no-cache \
    nginx \
    nodejs npm \
    git curl \
    # PHP 익스텐션 의존성
    libpng-dev jpeg-dev libwebp-dev \
    oniguruma-dev icu-dev \
    libzip-dev \
    mysql-client

# PHP 익스텐션
RUN docker-php-ext-configure gd --with-jpeg --with-webp \
 && docker-php-ext-install \
    pdo_mysql bcmath gd intl zip opcache \
    mbstring pcntl

# Composer 설치 (공식 이미지에서 복사)
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html

# PHP 최적화 설정
COPY docker/php.ini /usr/local/etc/php/conf.d/app.ini

# 앱 코드 복사 & 빌드
COPY . .
RUN composer install --no-dev --optimize-autoloader \
 && npm ci && npm run build \
 && rm -rf node_modules

# 퍼미션 설정
RUN chown -R www-data:www-data storage bootstrap/cache \
 && chmod -R 775 storage bootstrap/cache

CMD ["/bin/sh", "-c", "php-fpm -D && nginx -g 'daemon off;'"]

3. Alpine 주의사항 — 빠지는 함정들

bash 없음 → /bin/sh 사용

# ❌ bash 없어서 실행 불가
RUN /bin/bash -c "source .env"

# ✅ /bin/sh 사용
RUN /bin/sh -c "cat .env | grep APP_KEY"

# proc_open 에서도 /bin/bash 지정하면 오류
// ❌ Laravel에서 bash 경로 지정하지 않기
$process = new Process(['/bin/bash', '-c', 'ls']);

// ✅ sh 사용 또는 직접 명령
$process = new Process(['ls', '-la']);

npm이 nodejs와 별개

# ❌ nodejs만 설치하면 npm 없음
RUN apk add nodejs

# ✅ nodejs와 npm 둘 다 명시
RUN apk add nodejs npm

# 또는 특정 버전 고정
RUN apk add nodejs=20.x npm

PostgreSQL 드라이버 체크

// ❌ 'postgres'만 체크 — 실제로 'pgsql'을 사용
if ($driver === 'postgres') { ... }

// ✅ 두 가지 모두 체크
if ($driver === 'postgres' || $driver === 'pgsql') { ... }

4. Nginx + PHP-FPM 설정

# docker/nginx.conf
server {
    listen 80;
    root /var/www/html/public;
    index index.php;

    # 정적 파일 캐시
    location ~* \.(css|js|jpg|png|svg|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

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

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    }

    # 보안 헤더
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
}

5. docker-compose.yml

services:
  app:
    build: .
    ports:
      - "80:80"
    environment:
      APP_ENV: production
      APP_KEY: ${APP_KEY}
      DB_HOST: db
    volumes:
      - ./storage/logs:/var/www/html/storage/logs
    depends_on:
      - db

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
      - ./database/sql:/docker-entrypoint-initdb.d  # SQL 자동 실행

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes

volumes:
  db_data:

6. GitHub Actions CI/CD

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build -t myapp:latest .

      - name: Push to Registry
        run: |
          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker tag myapp:latest ghcr.io/myorg/myapp:latest
          docker push ghcr.io/myorg/myapp:latest

      - name: Deploy to Server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull ghcr.io/myorg/myapp:latest
            docker compose up -d --force-recreate app
            docker exec myapp php artisan config:cache
            docker exec myapp php artisan route:cache
            docker exec myapp php artisan view:cache

작성자

동지커뮤니케이션

DevOps 팀

공유하기
동지 AI 어시스턴트
온라인
{{ msg.content }}
{{ msg.summary }}
  • {{ b }}
이어서 물어보세요
{{ msg.time }}
Powered by AI — 동지커뮤니케이션