Docker로 Laravel & PHP 완벽한 개발 환경 구축하기
Nginx, PHP-FPM, MySQL, Redis를 조합한 프로덕션급 Laravel 개발 환경을 Docker로 구축하는 방법을 상세히 알아봅니다
2025년 8월 3일10 min read
Part 7: Docker로 Laravel & PHP 완벽한 개발 환경 구축하기
Laravel은 현대적인 PHP 프레임워크로, 복잡한 웹 애플리케이션을 우아하게 개발할 수 있게 해줍니다. 하지만 Laravel 프로젝트를 위한 개발 환경 구성은 복잡할 수 있습니다. 이번 포스트에서는 Docker를 활용해 프로덕션급 Laravel 개발 환경을 구축하는 방법을 알아보겠습니다.
Laravel 스택 이해하기
Laravel 애플리케이션을 위한 전형적인 스택 구성:
- Nginx: 웹 서버 및 리버스 프록시
- PHP-FPM: PHP FastCGI Process Manager
- MySQL: 관계형 데이터베이스
- Redis: 캐시 및 세션 저장소
- Composer: PHP 의존성 관리자
- Artisan: Laravel 명령줄 도구
프로젝트 구조 설계
plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
laravel-docker/
├── docker/
│ ├── nginx/
│ │ ├── Dockerfile
│ │ └── nginx.conf
│ ├── php/
│ │ ├── Dockerfile
│ │ └── php.ini
│ └── mysql/
│ └── my.cnf
├── src/ # Laravel 소스 코드
├── docker-compose.yml
├── docker-compose.prod.yml
└── .env.example
Step 1: Nginx 설정
nginx/Dockerfile
dockerfile
1
2
3
4
5
6
7
8
9
10
11
FROM nginx:alpine
# 설정 파일 복사
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 로그 디렉토리 생성
RUN mkdir -p /var/log/nginx
# 헬스체크
HEALTHCHECK \
CMD curl -f http://localhost/nginx-health || exit 1
nginx/nginx.conf
nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php index.html;
# 로그 설정
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# 최대 업로드 크기
client_max_body_size 100M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP 처리
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
# 버퍼 크기 설정
fastcgi_buffer_size 32k;
fastcgi_buffers 4 32k;
}
# 정적 파일 캐싱
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# .으로 시작하는 파일 접근 차단
location ~ /\. {
deny all;
}
# 헬스체크 엔드포인트
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
Step 2: PHP-FPM 설정
php/Dockerfile
dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
FROM php:8.1-fpm-alpine
# 필수 확장 설치
RUN apk add --no-cache \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
libzip-dev \
oniguruma-dev \
&& docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
&& docker-php-ext-install \
pdo \
pdo_mysql \
gd \
zip \
bcmath \
opcache \
mbstring
# Redis 확장 설치
RUN apk add --no-cache $PHPIZE_DEPS \
&& pecl install redis \
&& docker-php-ext-enable redis
# Composer 설치
COPY /usr/bin/composer /usr/bin/composer
# 사용자 설정
RUN addgroup -g 1000 laravel && \
adduser -u 1000 -G laravel -s /bin/sh -D laravel
# PHP 설정
COPY php.ini /usr/local/etc/php/conf.d/custom.ini
# 작업 디렉토리
WORKDIR /var/www/html
# 권한 설정
RUN chown -R laravel:laravel /var/www/html
USER laravel
php/php.ini
ini
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
; 메모리 제한
memory_limit = 256M
; 업로드 크기
upload_max_filesize = 100M
post_max_size = 100M
; 시간대
date.timezone = Asia/Seoul
; 에러 설정
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
; OPCache 설정 (프로덕션)
opcache.enable = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
opcache.revalidate_freq = 0
; 개발 환경에서는 OPCache 비활성화
; opcache.enable = 0
Step 3: Docker Compose 구성
docker-compose.yml (개발용)
yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
version: '3.8'
services:
# Nginx 웹 서버
nginx:
build:
context: ./docker/nginx
ports:
- "80:80"
volumes:
- ./src:/var/www/html
- ./docker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php
networks:
- laravel
# PHP-FPM
php:
build:
context: ./docker/php
volumes:
- ./src:/var/www/html
- ./docker/php/php.ini:/usr/local/etc/php/conf.d/custom.ini
environment:
- DB_HOST=mysql
- DB_PORT=3306
- DB_DATABASE=${DB_DATABASE:-laravel}
- DB_USERNAME=${DB_USERNAME:-laravel}
- DB_PASSWORD=${DB_PASSWORD:-secret}
- REDIS_HOST=redis
- REDIS_PORT=6379
depends_on:
- mysql
- redis
networks:
- laravel
# MySQL 데이터베이스
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
MYSQL_DATABASE: ${DB_DATABASE:-laravel}
MYSQL_USER: ${DB_USERNAME:-laravel}
MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
volumes:
- mysql-data:/var/lib/mysql
- ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
networks:
- laravel
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
# Redis 캐시
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- laravel
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
# Composer 유틸리티
composer:
build:
context: ./docker/php
volumes:
- ./src:/var/www/html
working_dir: /var/www/html
entrypoint: composer
networks:
- laravel
# Artisan 유틸리티
artisan:
build:
context: ./docker/php
volumes:
- ./src:/var/www/html
working_dir: /var/www/html
entrypoint: php artisan
networks:
- laravel
# Node.js (프론트엔드 빌드용)
npm:
image: node:16-alpine
volumes:
- ./src:/var/www/html
working_dir: /var/www/html
entrypoint: npm
networks:
- laravel
volumes:
mysql-data:
redis-data:
networks:
laravel:
driver: bridge
Step 4: Laravel 프로젝트 설정
새 Laravel 프로젝트 생성
bash
1
2
3
4
5
6
7
# Composer로 Laravel 설치
docker-compose run --rm composer create-project laravel/laravel .
# 권한 설정
sudo chown -R $USER:$USER src/
chmod -R 755 src/
chmod -R 777 src/storage src/bootstrap/cache
환경 설정 (.env)
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# src/.env
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
초기 설정 실행
bash
1
2
3
4
5
6
7
8
9
10
# 애플리케이션 키 생성
docker-compose run --rm artisan key:generate
# 데이터베이스 마이그레이션
docker-compose run --rm artisan migrate
# 캐시 정리
docker-compose run --rm artisan config:cache
docker-compose run --rm artisan route:cache
docker-compose run --rm artisan view:cache
Step 5: 개발 워크플로우
일반적인 개발 명령어
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 서비스 시작
docker-compose up -d
# 로그 확인
docker-compose logs -f php
# Composer 패키지 설치
docker-compose run --rm composer require laravel/sanctum
# 마이그레이션 생성 및 실행
docker-compose run --rm artisan make:migration create_posts_table
docker-compose run --rm artisan migrate
# 모델 생성
docker-compose run --rm artisan make:model Post -mc
# 컨트롤러 생성
docker-compose run --rm artisan make:controller PostController --resource
# 프론트엔드 에셋 빌드
docker-compose run --rm npm install
docker-compose run --rm npm run dev
큐 워커 실행
yaml
1
2
3
4
5
6
7
8
9
10
# docker-compose.yml에 추가
queue:
build:
context: ./docker/php
volumes:
- ./src:/var/www/html
command: php artisan queue:work --sleep=3 --tries=3
restart: unless-stopped
networks:
- laravel
스케줄러 실행
yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
# docker-compose.yml에 추가
scheduler:
build:
context: ./docker/php
volumes:
- ./src:/var/www/html
command: >
sh -c "while true; do
php artisan schedule:run --verbose --no-interaction &
sleep 60
done"
networks:
- laravel
Step 6: 프로덕션 최적화
docker-compose.prod.yml
yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
version: '3.8'
services:
nginx:
restart: always
volumes:
# 소스 코드는 이미지에 포함
- ./src/public:/var/www/html/public:ro
- ./src/storage/app/public:/var/www/html/storage:ro
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
php:
build:
context: .
dockerfile: ./docker/php/Dockerfile.prod
restart: always
volumes:
# 쓰기가 필요한 디렉토리만 마운트
- ./src/storage:/var/www/html/storage
- ./src/bootstrap/cache:/var/www/html/bootstrap/cache
environment:
APP_ENV: production
APP_DEBUG: false
mysql:
restart: always
ports: [] # 외부 포트 노출 제거
redis:
restart: always
ports: [] # 외부 포트 노출 제거
php/Dockerfile.prod
dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
FROM php:8.1-fpm-alpine AS base
# 확장 설치 (개발과 동일)
# ...
FROM base AS build
WORKDIR /var/www/html
# Composer 설치
COPY /usr/bin/composer /usr/bin/composer
# 의존성 설치
COPY src/composer.json src/composer.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader
# 소스 코드 복사
COPY src/ .
# Autoloader 최적화
RUN composer dump-autoload --optimize --no-dev
# 권한 설정
RUN chown -R www-data:www-data storage bootstrap/cache
FROM base AS production
# 빌드 단계에서 복사
COPY /var/www/html /var/www/html
# OPCache 프리로딩 (PHP 7.4+)
# RUN echo "opcache.preload=/var/www/html/preload.php" >> /usr/local/etc/php/conf.d/opcache.ini
USER www-data
EXPOSE 9000
성능 최적화 팁
1. OPCache 설정
ini
1
2
3
4
5
6
7
8
; 프로덕션 OPCache 설정
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.save_comments=1
opcache.fast_shutdown=1
2. MySQL 튜닝
ini
1
2
3
4
5
6
7
8
# docker/mysql/my.cnf
[mysqld]
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
query_cache_type = 1
query_cache_size = 128M
3. Redis 설정
bash
1
2
3
# Redis 영속성 설정
redis:
command: redis-server --appendonly yes --appendfsync everysec
트러블슈팅
1. 권한 문제
bash
1
2
# 컨테이너 내부에서 실행
docker-compose exec php chown -R www-data:www-data storage bootstrap/cache
2. 느린 파일 시스템 (macOS)
yaml
1
2
3
volumes:
# :delegated 옵션 추가
- ./src:/var/www/html:delegated
3. 메모리 부족
yaml
1
2
3
4
5
6
7
# docker-compose.yml
services:
mysql:
deploy:
resources:
limits:
memory: 1G
유용한 스크립트
Makefile
makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.PHONY: up down build fresh test
up:
docker-compose up -d
down:
docker-compose down
build:
docker-compose build
fresh:
docker-compose run --rm artisan migrate:fresh --seed
test:
docker-compose run --rm php ./vendor/bin/phpunit
shell:
docker-compose exec php sh
logs:
docker-compose logs -f
# 프로덕션 배포
deploy:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
마무리
Docker를 활용한 Laravel 개발 환경 구축은 처음에는 복잡해 보일 수 있지만, 한 번 설정하면 다음과 같은 이점을 얻을 수 있습니다:
- 일관된 개발 환경: 모든 팀원이 동일한 환경에서 작업
- 쉬운 온보딩: 새 개발자도 바로 개발 시작 가능
- 프로덕션 유사성: 개발 환경이 프로덕션과 거의 동일
- 확장성: 필요에 따라 서비스 추가/제거 용이
다음 포스트에서는 이렇게 구축한 컨테이너를 실제 프로덕션 환경에 배포하는 방법을 알아보겠습니다. AWS ECS, 멀티 스테이지 빌드, CI/CD 파이프라인 구축까지 다룰 예정입니다!
시리즈 네비게이션
- ← 이전: Part 6: 유틸리티 컨테이너
- → 다음: Part 8: 컨테이너 배포 전략