J-한솔넷

Nginx 기반 Docker 개발환경 만들기, 삽질기 본문

프로그래밍/PHP

Nginx 기반 Docker 개발환경 만들기, 삽질기

jhansol 2024. 6. 18. 21:07

이번에는 기존에 자가 사용하고 있는 도커 개발환경(Apache 기반)을 두고, 새롭게 Nginx 기반으로 개발환경을 구성해봤습니다. 새롭게 환경을 구성하게된 동기, 구성 과정, 구성후 느낌 정도를 포스팅하고자 합니다.

구성 동기

최근(아니 최근 1년)에는 Lravel Blade 기반의 프론트엔드부분을 개발할 일이 없어 젼혀 모르고 있다가, 사이드 프로젝트를 진행하려고 준비를 하는 과정에 CSS와 Blade 파일을 수정했는데 핫 리로드 기능이 동작하지 않는 것을 확인했습니다. 이 기능이 없었을 때는 어래 그려려니 했지만 막상 사용하다 않되니 너무 불편했습니다.
원래는 아래와 같이 CSS나 Blade 파일을 수정하면 수정을 감지하고 페이지를 리로드해줘야 합니다.

7:56:26 PM [vite] page reload resources/views/test.blade.php

그리고 Docker 개발환경에서 웹사이트 응답속도가 느려도 너무나 느린 것입니다. 이참에 Apache 기반이 아닌 Nginx 기반 환경으로 바꿔보고 싶은 것도 하나의 동기입니다.

구성 과정

AI의 도움을 받다.

서버 구성을 자주 하는 것도 아니고, 기억이 안나는 문제도 있었지만 Nginx의 경우 익숙하지 않은 것이라 ChatGPT에게 도움을 요청했습니다.

nginx의 가상 호스트 설정에서 도매인 주소의 host name을 기빈으로 디렉토리를 지정하는 방법이 없을까?

ChatGPT는 맵을 이용한 멀티 도매인 제안을 해주었습니다.

http {
    map $host $root_path {
        hostnames;
        example.com   /var/www/example.com/public_html;
        example.org   /var/www/example.org/public_html;
        default       /var/www/default/public_html;
    }

    server {
        listen 80;
        server_name example.com www.example.com example.org www.example.org;

        root $root_path;

        location / {
            try_files $uri $uri/ =404;
        }
    }
}

하지만 저의 경우에는 "http://public.web.wd"와 같이 주소가 주어졌을 때 "/DevHome/web/public" 형태로 도큐먼트 루타가 결정되는 형태로 되어야 하는데, 저와는 맞지 않았습니다. 그래서...

map을 이용하지 않고 일정한 규칙에 따라 디렉토리가 결정되면 좋을 것 같은데, 예를 들어 a.sites.wd의 경우 /dev/a/public_html, b.sites.wd의 경우 /dev/b/public_html 로 연결되었으면 해.

server {
    listen 80;
    server_name ~^(?<subdomain>\w+)\.sites\.wd$;

    location / {
        root /dev;
        alias /dev/$subdomain/public_html;
        try_files $uri $uri/ =404;
    }
}

위 내용을 적용해보니 Nginx가 오류를 표시하고는 실행이 안되길래 다시 물었습니다.

"alias" directive is duplicate, "root" directive was specified earlier in /etc/nginx/sites-enabled/default:17
이 메시지에 대해 설명해줘

nginx 설정 파일에서 root 지시어와 alias 지시어는 서로 다르게 동작하며, 
같은 location 블록 내에서 동시에 사용할 수 없습니다. 이 오류 메시지는 
root와 alias를 같은 location 블록에 지정하려고 했기 때문에 발생합니다.

server {
    listen 80;
    server_name ~^(?<subdomain>\w+)\.sites\.wd$;

    location / {
        set $subdir /dev/$subdomain/public_html;
        root $subdir;
        try_files $uri $uri/ =404;
    }
}

Docker 기반 환경 구성시 가장 불편한 점

제가 Docker 기반 환경을 구성하기 위해 수많은 삽질을 했습니다. 삽질 과정에서 가장 불편한 점이 위와 같은 구성 파일의 오류를 알수 없다는 것입니다. Docker용 linux 이미지의 경우 각종 서비스가 systemd를 기반으로 등작하지 않습니다. 결국 journalctl과 같은 기능을 사용할 수 없어 어떤 오류가 발생하여 서비스가 실행되지 않는지 알수 없게됩니다.
하는 수 없이 VirtualBox에 Ubuntu를 설치하고, 개발환경 전반을 테스트하고 Docker 이미지에 적용하는 과정을 거쳐야 했습니다.

시행착오 끝에 만든 Nginx 환경설정 파일

위 과정을 거쳐 Nginx 설정 파일을 아래와 같이 만들었습니다. 이 글을 쓰기 전에 최종 테스트도 통과를 했습니다.

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    index index.php index.html index.htm index.nginx-debian.html;

    server_name localhost admin.sites.wd;

    root /var/www/html;

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

    location ~ \.php$ {
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        include fastcgi.conf;
        fastcgi_hide_header X-Powered-By;
    }
}

server {
    listen 80;
    listen [::]:80;

    listen 443 ssl;
    listen [::]:443 ssl;

    ssl_certificate /etc/ssl/private/dev.crt;
    ssl_certificate_key /etc/ssl/private/dev.key;

    index index.php index.html index.htm index.nginx-debian.html;

    server_name ~^(?<host_name>[\w\-]+)\.(?<organization>[\w\-]+)\.wd$;

    set $site /DevHome/$organization/$host_name;
    root $site;

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

    location ~ \.php$ {
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        include fastcgi.conf;
        fastcgi_hide_header X-Powered-By;
    }
}

DockerFile 군살빼기

기존에 구성했던 DockerFile은 레이어도 너무 많고, 불필요한 PHP 모듈도 많아 대폭 정리를 했습니다. DockerFile 내용은 아래와 같습니다. 그리고 저의 깃허브 레포지토리에도 해당 내용이 올라가 있습니다. 전체 파일이나 내용을 보시려면 레포지토리를 방문해서 확인해보세요.

FROM ubuntu:24.04
ENV DEBIAN_FRONTEND noninteractive
ENV TZ Asia/Seoul
ENV LC_ALL C.UTF-8

WORKDIR /DevHome

RUN mkdir -p /DevHome/sites/admin; ln -snf /usr/share/zoneinfo/$TZ /etc/localtime; echo $TZ > /etc/timezone

RUN apt-get update; \
    apt-get -y upgrade; \
    mkdir -p /etc/apt/keyrings; \
    apt-get install -y gnupg curl ca-certificates zip unzip git libpng-dev python3 dnsutils librsvg2-bin fswatch ffmpeg nano nginx mysql-client; \
    curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null; \
    echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu noble main" > /etc/apt/sources.list.d/ppa_ondrej_php.list; \
    apt-get update; \
    apt-get install -y php8.2-bcmath php8.2-cli php8.2-fpm php8.2-curl php8.2-dev php8.2-gd php8.2-intl php8.2-ldap php8.2-mbstring php8.2-mysql php8.2-opcache php8.2-readline \
    php8.2-soap php8.2-xml php8.2-xsl php8.2-zip php8.2-xdebug; \
    curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer; \
    curl https://deb.nodesource.com/setup_lts.x | bash -; \
    apt-get update; \
    apt-get install -y nodejs; \
    npm install -g pnpm; \
    npm install -g bun; \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null; \
    echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list; \
    apt-get update; \
    apt-get install -y yarn; \
    apt-get -y autoremove; \
    apt-get clean; \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

RUN sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 200M/g' /etc/php/8.2/fpm/php.ini; \
    sed -i 's/memory_limit = 128M/memory_limit = 512M/g' /etc/php/8.2/fpm/php.ini; \
    sed -i 's/post_max_size = 8M/post_max_size = 250M/g' /etc/php/8.2/fpm/php.ini; \
    sed -i 's/short_open_tag = Off/short_open_tag = On/g' /etc/php/8.2/fpm/php.ini; \
    echo "xdebug.mode = develop,debug" >> /etc/php/8.2/mods-available/xdebug.ini; \
    echo "xdebug.client_host=host.docker.internal" >> /etc/php/8.2/mods-available/xdebug.ini; \
    echo "xdebug.client_port = 9000" >> /etc/php/8.2/mods-available/xdebug.ini

COPY index.php /var/www/html
COPY ssl_key/* /etc/ssl/private
COPY default /etc/nginx/sites-available
ADD myadmin /var/www/html/myadmin
COPY docker_entrypoint.sh /usr/bin/docker_entrypoint.sh

RUN chmod 0755 /usr/bin/docker_entrypoint.sh; \
    rm -r /var/lib/apt/lists /var/cache/apt/archives

VOLUME [ "/DevHome" ]

EXPOSE 80 443 5173 8080

ENTRYPOINT [ "docker_entrypoint.sh" ]

CMD ["tail","-f","/var/log/nginx/error.log"]

docker_entrypoint.sh

PHP fpm과 Nginx 기반으로 변경되다 보니 이 파일도 아래와 같이 변경했습니다.

#!/bin/bash

service php8.2-fpm start
service nginx start

exec "$@"

구성 후 느낀 점

개발환경을 구성하고 다시 테스트를 해봤는데, 응답속도는 기존과 동일한 것 같습니다. 속도의 문제는 Apache나 Nginx나 별로 차이가 없어 보입니다. 구글링을 해보니 WSL이 Windows의 파일 시스템에 접근할 때 속도가 느려진다는 이야기가 있습니다. 찾고 보니 Vite의 공식 문서에서도 언급이 되어 있네요.

Host bind volume과 docker volume의 차이

저의 개발환경에서 프로젝트 폴더는 Bind Volume으로 연결하고, 데이터베이스(Docker Volume)와 PHPMyAdmin과 관리용 PHP프로그램은 이미지 내부에 포함되어 있습니다. 지금에 와서 생각해보니 PHPMyAdmin은 응답속도가 그렇게 느리다는 생각을 못했습니다. 하지만 프로젝트 폴더의 웹사이트는 느려도 너무 느려 도저히 못 쓰겠다는 지경까지 왔습니다. 지금 생각해보면 이 두 방식의 차이로 생기는 속도차이로 보입니다. WSL 기반의 Docker 환경에서는 Bind Volume은 피하는 것이 좋겠습니다. 안된다면 WSL을 쓰지 않는 것도 고려를 해봐야 할 것 같습니다.

Vite Hot Reload가 동작하지 않는 문제

이 글을 쓰기 전에 막 Ubuntu를 노트북에 설치하고, 개발환경 구성을 마쳤습니다. 노트북의 개발환경에서는 Hot Reload 기능이 너무나 잘 동작합니다. 물론 속도도 빠르구요. 이건 운영체제의 문제나 Docker의 문제로 어렴풋이 생각은 하고 있었습니다만 구체적으로 검증을 하지 목한 터라 결론을 못내리고 있었습니다. 마지막으로 구글링을 해보니 WSL의 파일시스템 문제로 파일의 변경 감지가 않된다는 말도 있고, Vite 공식 문저에도 아래와 같은 내용이 있었네요. 이걸 알았으면 이번 삽질도 하지 않았을텐데 ㅠㅠ

Using Vite on Windows Subsystem for Linux (WSL) 2

When running Vite on WSL2, file system watching does not work when a file is edited by Windows applications (non-WSL2 process). This is due to a WSL2 limitation. This also applies to running on Docker with a WSL2 backend.

To fix it, you could either:

Recommended: Use WSL2 applications to edit your files.  
It is also recommended to move the project folder outside of a Windows filesystem. Accessing Windows filesystem from WSL2 is slow.  
Removing that overhead will improve performance.  
Set { usePolling: true }.  
Note that usePolling leads to high CPU utilization.

마치며

Docker 개발환경에서 WSL을 사용하지 않는것을 심각하게 고민하고 있습니다.

'프로그래밍 > PHP' 카테고리의 다른 글

Laravel 용 패키지 만들기, 삽질기  (0) 2024.06.02
삽질의 연속  (0) 2024.04.20
메모리 프로파일링?  (0) 2023.11.09
ChatGPT를 이용한 코딩?  (0) 2023.10.26
WSL2를 쓰야하나!!!  (0) 2023.09.14