inlee's blog

PHP Dockerfile 만들기 (with Laravel, Xdebug, vscode)

· inlee

PHP docker 공식 이미지는 PHP만 제공하기 때문에 추가로 필요한 패키지들은 별도로 설치하여 사용해야 한다. 그리고 Laravel과 같이 특정 라이브러리들을 필수로 하는 프레임워크도 있기 때문에 대부분의 사용자들은 패키지들을 추가한 Dockerfile을 정의하여 사용한다.

이 글은 PHP docker 공식 이미지에 일반 PHP와 Laravel 기반의 어플리케이션 개발/운영 환경을 포함하는 Dockerfile을 만든 내용이다.이를 위해 PHP에서 필수라 생각되는 패키지와 Laravel에서 요구하는 필수 패키지 설치와 실행 설정을 하였다. 추가로 PHP 코드 디버깅(Debugging)을 위한 Xdebug 설정과 Visual Studio Code 에서의 설정을 하였다.

개요

PHP의 경우 일반적으로 Document Root는 소스코드 저장경로로 하여 실행한다. Wordpress, Codeigniter가 대표적이다. 그러나 Laravel과 같이 Document Root를 소스코드 내 특정 디렉터리(/public)로 설정하여 실행하기도 한다.

Docker를 이용하는 경우 전자라면 컨테이너(Container)에 소스코드 전체를 공유하여 별 다른 설정 없이 사용할 수 있지만 후자의 경우라면 소스코드 전체를 컨테이너에 공유한 후 Document Root를 별도로 설정해 주어야 한다. Apache 기반의 docker PHP를 이용한다면 .htaccess 파일의 설정을 통해 쉽게 해결이 가능하겠지만 이는 NginX와 연결하여 사용할 경우라면 근본적인 해결책이 되지 않는다.

이 글에서는 앞서 언급한 두가지 경우, 일반 PHP와 Laravel 프로젝트에 대해 모두 사용할 수 있도록 Docker 기반에서 소스코드 저장경로와 Laravel을 실행하는 /public 디렉터리 모두 Document Root로 하는 Docker 환경설정을 구성한 내용에 대해 서술하였다. 또한 NginX와 사용하기 위해 php-fpm 기반에서 설정을 진행했다. 추가로 PHP 코드 디버깅(Debugging)을 위한 Xdebug 설정과 Visual Studo Code에서의 설정을 한 내용에 대해 서술하고자 한다.

구성

이 글에서 언급하는 Dockerfile과 관련 항목의 구성은 PHP docker 공식 이미지 중 php-fpm을 기반으로 하였으며 일반적인 PHP 어플리케이션과 Laravel로 구현된 어플리케이션의 동작을 모두 가능하게 하였다.

Base: php-fpm

이 글의 Dockerfile 구현은 운영(Production) 모드에서 Reverse Proxy 혹은 웹 서버로 사용하는 NginX와 사용하고자 php-fpm을 기반으로 하였다. 참고로 Apache는 내장 php 모듈이 있어 php 파일을 자체적으로 처리하지만, NginX는 없기 때문에 php-fpm을 통해 php 파일을 처리해야 한다.

개발(Development) 모드에서는 PHP 내장 서버(Built-in web server)를 이용하도록 하였다.

Packages

설치하여 사용하는 패키지는 Laravel에서 필수로 요구하는 패키지와 이미지를 다루는 gd, PHP 패키지 매니저인 composer, git, curl, wget, openssl, zip, conv, opcache 그리고 개발 모드에서 사용할 Xdebug를 사용하도록 하였다.

Ports

Port는 80번과 9000번 2가지를 사용하였다. 그런데 운영 모드에서 사용하는 php-fpm의 기본 Port와 Xdebug의 Port가 같은 9000번을 사용하여 혼란을 줄 수 있어 php-fpm에서 사용하는 Port를 80 번으로 변경하였다. 자세한 내용은 아래와 같다.

  • 80번: development, production 웹 서버용으로 사용.
    • development: php 명령어를 이용해 PHP 내장 서버 실행.
    • production: php-fpm 명령어 실행. php-fpm 에서 사용하는 기본 Port는 9000번이나 xdebug에서 사용하는 Port 번호와 혼동되지 않도록 80번으로 강제 변경하여 사용하도록 설정.. 즉 개발/운영 모드에서 사용하는 Port를 동일한 80번으로 통일.
  • 9000번: Xdebug 용으로 사용

Volumes

아래와 같이 2개의 Volume을 사용하도록 하였다.

  • /var/www/html: 웹 서버 Root.
  • /usr/local/etc/php/conf.d/php.ini: PHP 환경설정 파일.

환경변수

동작 환경에 사용되는 환경 변수는 development / production 모드 설정과 Laravel 사용 여부이며 내용은 아래와 같다.

  • DOCKER_ENV: 운영 모드. development / production 중 1이며 기본값은 development.
  • DOCKER_LARAVEL: Laravel 프레임워크 사용 여부. true / false 중 1이며 기본값은 false.

실행

docker run 명령어로 실행 시 설정된 환경변수를 이용해 아래와 같이 실행하도록 구현하였다.

  1. DOCKER_ENV 값이 development일 경우, 개발모드로 실행.
  • Xdebug 활성화.
  • PHP 내장 서버(built-in web server) 실행.
  1. DOCKER_ENV 값이 production일 경우, 운영모드로 실행.
  • Xdebug 활성화 하지 않음.
  • php-fpm 명령어 실행.

PHP 내장 서버는 개발 모드에서 사용이 되고 PHP 파일을 자체적으로 처리하며 CLI로 동작한다. 하나의 프로세스로 동작하기 때문에 요청(Request)이 Block될 경우에는 프로그램이 중지된다.

development / production 모드에 상관없이 DOCKER_LARAVEL 값이 true인 경우, Laravel 프로젝트의 실행 디렉터리인 /public 디렉터리의 파일을 실행한다. 이는 Laravel 여부에 상관없이 /public 디렉터리의 파일을 실행하는 프로젝트라면 true로 설정하여 사용 가능하다.

Xdebug 설정

docker run 명령 실행 시 DOCKER_ENV 환경변수의 값이 development(개발모드)일 경우 IDE에서의 디버깅을 할 수 있도록 Remote 설정을 포함한 Xdebug 활성화를 하였다. 그리고 사용하는 개발도구인 Visual Studio Code의 설정은 아래와 같이 하였다.

Xdebug 활성화

Container를 개발모드로 실행 시 Xdebug가 아래 Docker 에서 제공하는 명령어로 활성화 된다.

docker-php-ext-enable xdebug

이 명령어는 php 설정 파일이 저장되는 디렉터리인 /usr/local/etc/php/conf.d/ 디렉터리에 docker-php-ext-xdebug.ini 파일을 생성한 후 아래와 같이 Xdebug 설정이 기록되어 실행된다.

# docker-php-ext-xdebug.ini
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so

설정이 완료되면, 페이지 로딩 시 오류가 발생하면 Xdebug가 오류를 출력해준다.

Remote 설정

Xdebug는 페이지 로딩 시 오류가 있을 경우 관련 내용을 출력하지만, 특정 시점에서의 변수값, Call Stack 등을 확인을 하고자 할 때에는 Xdebug의 remote 관련 옵션을 이용해 IDE와 연동하여 사용할 수 있다. 이 기능을 사용하려면 아래와 같이 Xdebug 설정 파일에 remote 관련 설정을 해 주어야 한다.

# docker-php-ext-xdebug.ini
xdebug.default_enable=1
xdebug.remote_enable=1
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.remote_connect_back=0
xdebug.remote_host=host.docker.internal
xdebug.idekey=VSCODE
xdebug.remote_autostart=1
xdebug.remote_log=/usr/local/etc/php/xdebug.log

Xdebug의 remote 설정과 관련된 주요 내용은 아래와 같다1.

  • xdebug.remote_enable: Remote 디버깅 사용 여부
  • xdebug.remote_port : Remote와의 통신에 사용할 port 번호. Xdebug 기본값인 9000 번으로 설정. 이 글의 구현에서는 9000 포트가 php-fpm과 중복되기 때문에, php-fpm의 동작 port를 80으로 변경.
  • xdebug.remote_hosts: Remote와의 통신에 사용할 IP 주소. Docker를 사용할 경우 host.docker.internal 입력.
  • xdebug.remote_autostart: Xdebug 사용 시 GET/POST 등으로 remote debugging 사용을 알려야 하는데 이를 활성화하면 사용 알림 필요 없이 자동으로 시작.

여기서 host.docker.internal은 Docker 버전 18.03 이후부터 제공하는 Docker 컨테이너 내부에서 Host를 호출할 때 사용하는 DNS name 이다2 3 4. 즉 Host의 IP 주소이며 컨테이너는 remote_host와 remote_port를 이용해 Host와 통신한다.

이 글의 구현에서는 추후 IDE와 사용될 것을 고려하여 Container 실행 시 Xdebug 활성화와 Remote 설정을 동시에 진행한다.

Visual Studio Code 설정

Container에 Xdebug 활성화와 remote 설정이 완료되면 Visual Studio Code에서 접근할 수 있도록 아래와 같이 launch.json 파일을 생성5한 후 아래와 같이 Xdebug 연결을 위한 설정을 해 주어야 한다.

// launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
        "name": "Listen for XDebug",
        "type": "php",
        "request": "launch",
        "port": 9000,
        "log": true,
        "externalConsole": false,
        "pathMappings": {
            "/var/www/html": "${workspaceRoot}",
        },
        "ignore": [
            "**/vendor/**/*.php"
        ]
    }]
}

launch.json 파일의 주요 설정은 아래와 같다.

  • port: Container의 xdebug.remote_port와 같은 port 설정
  • pathMappings: 좌측에는 Container의 working directory 이며 우측에는 Visual Studio Code에서 Open한 코드의 디렉터리로 “${workspaceRoot}” 입력.
  • ignore: 디버깅을 무시할 파일 목록. 위의 예시에서는 composer로 설치된 패키지 전부를 무시하도록 설정.

구현 결과

구현 결과와 실행 방법은 아래 Repository에 업로드 하였다.

마치며

이 글은 PHP를 이용해 일반적인 어플리케이션과 Laravel을 동시에 사용하는 Dockerfile을 구현한 것에 대해 정리한 내용이다.

구현하게 된 계기는 Laravel로 개발 환경을 전환하면서 이에 맞는 Docker 설정이 필요했고 더불어 기존 개발된 프로젝트도 대응할 수 있도록 하는 데에 목적을 두었다. Laravel은 laradock이라는 개발 환경을 제공하는 오픈 소스 프로젝트가 있다. 그러나 많은 Docker Image를 실행할 수 있도록 설정을 포함하고 있어 처음 사용하는 사용자들에게는 복잡하다고 판단되었고 또한 기존 구현되었던 프로젝트 개발/운영에는 맞지 않다고 판단되어 직접 구현하게 되었다. 또한 Xdebug를 추가함으로써 특정 시점의 변수 값이나 Call Stack을 확인하여 효율적인 개발이 가능하게 하고자 하였다.

관련 내용은 많이 공개되어 있으며 구현은 어렵지 않았다. 때문에 관련 내용을 알고 있으면 이 글의 내용은 어렵지 않을 것이라 생각된다.

Appendix. php-fpm

fpm은 FastCGI Process Manager의 약자로 직역하면 CGI를 빠르게 처리하는 프로세스 관리자 라는 뜻이다. CGI는 웹 서버가 처리할 수 없는 데이터의 처리를 하는 외부 프로그램을 말하며 Fast가 붙은 것은 CGI 처리를 빠르게 한 것을 말한다6. php-fpm은 PHP 파일을 처리하는 FastCGI 이며 주로 NginX와 같이 사용된다.


  1. 자세한 내용은 XDebug docs(https://xdebug.org/docs/all_settings), 생활코딩 Xdebug(https://opentutorials.org/course/692/3758) 참조. ↩︎

  2. https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds, 2020.04.16 검색. ↩︎

  3. https://docs.docker.com/docker-for-windows/networking/, 2020.04.16 검색. ↩︎

  4. Docker에서 macOS 호스트 포트에 접근하기, SEORENN NOTEBOOK, https://seorenn.tistory.com/20, 2020.04.16 검색. ↩︎

  5. launch.json 파일 생성은 https://code.visualstudio.com/docs/editor/debugging 참조. ↩︎

  6. 여러개의 Process를 사전에 생성한 후 Process pool 생성. 이후 요청이 들어오면 Pool로부터 처리할 process를 가져와 처리한 후 완료되면 다시 pool에 사용 가능한 상태로 복귀. ↩︎