si.mpli.st dev

Caddy 웹 서버로 쉽게 PHP 웹사이트 운영하기

솔직히 말해서 PHP 사이트를 운영하는 경험은 썩 유쾌하진 않습니다. Apache를 사용한다면 htaccess 파일을, NGINX를 사용하면 자체 설정 파일을 이리저리 수정해가며 “왜 이게 안되지?” 라고 물어보며 머리를 쥐어뜯게 되는 경우가 종종 있죠. “간단하게 워드프레스 블로그나 설치해서 써야지” 라고 말하다가 어느새 journalctl -u nginx -f 의 출력 결과를 반쯤 감긴 눈으로 멍하니 쳐다보고 있을지도 모릅니다.

최근에 지인들의 워드프레스 웹사이트를 다른 서버로 옮길 일이 있었습니다. 개인적으로 운영하는 사이트 중에는 PHP를 쓰는 사이트가 없지만, 그렇다고 워드프레스를 Ruby로 포팅할 수야 없으니 PHP를 돌리긴 해야했죠. 보통은 NGINX와 php-fpm을 이용해 적당히 설정하고 HTTPS를 위해 certbot을 붙였겠지만, 이왕 새로 설치하는 거 더 모-던하고 간단하게 설정하고 싶었습니다.

마침 서버 설정을 할때쯤 해커뉴스를 통해 Caddy 웹 서버의 새로운 버전인 2.0이 나왔다는 이야기를 들었던 터라, 한번 써보고 싶어졌습니다. 상업 서비스를 운영하는 입장이었다면 익숙한 NGINX를 썼겠지만, 심심풀이로 돌리는 서버는 모험이 빠지면 섭섭하니까요.

만능 도구상자 Caddy

Caddy의 공식 웹사이트

Caddy의 공식 웹사이트

Caddy는 Go로 작성된 웹 서버입니다. 2015년에 나왔으니 이제 막 5살을 넘겼죠. Caddy가 아직까지도 다른 웹 서버와 가장 크게 차별화되는 점은 Automatic HTTPS 기능인데, 도메인 이름만 지정하면 HTTPS를 켜고, 모든 HTTP 요청을 HTTPS로 리디렉트하고, Let’s Encrypt로 인증서까지 발급해줍니다. 여기에 덧붙여 HTTP/2 지원, 정적 파일 서버, 리버스 프록시, 룰 기반 리디렉트 설정 등 웹 서버가 갖춰야 할 기능은 모두 갖추고 있지만, 라이브러리 의존성이 아예 없어 단일 바이너리로 실행이 됩니다. 심지어 libc 의존성도 없어 Alpine Linux와 같은 환경에서도 바로 돌릴 수 있죠!

설정도 무척 간단합니다. NGINX에서 정적 파일을 호스팅하려면 이런 식으로 설정을 하는데…

# NGINX
server {
	listen 80;
	listen 443 ssl http2;

	// IPv6 연결 설정
	listen [::]:80;
	listen [::]:443 ssl http2;

	// HTTPS 인증서 설정 (발급 별도 필요)
	ssl_certificate /path/to/ssl/public.pem;
	ssl_certificate_key /path/to/ssl/private.pem;

	server_name example.com;

	root /home/sites/example.com;

	// HTTPS 연결 강제 (HSTS)
	if ($scheme != "https") {
		return 301 https://$host$request_uri;
	}
	add_header Strict-Transport-Security "max-age=31536000";
}

Caddy에서는 암시적인 기본값이 있기 때문에 짧은 설정이면 됩니다. 설정에서는 나타나지 않는 인증서 발급도 알아서 해결해줍니다.

# Caddy
example.com {
	root * /home/sites/example.com
	file_server
}

이렇게 쉽고 간단한 Caddy를 리눅스 서버에 설치하고, PHP를 연동하는 과정을 살펴보도록 하겠습니다.

PHP 웹사이트를 위해 Caddy 설정하기

공식 웹사이트에서 Caddy를 설치하는 다양한 방식을 확인할 수 있는데, 저는 Ubuntu 18.04 LTS에 APT 패키지 관리자로 설치해 보겠습니다. 먼저 Caddy의 패키지 저장소를 추가하고 설치해줍니다.

$ echo "deb [trusted=yes] https://apt.fury.io/caddy/ /" \
    | sudo tee -a /etc/apt/sources.list.d/caddy-fury.list
$ sudo apt update
$ sudo apt install caddy
APT 출력

패키지를 설치하면 Caddy가 자동으로 켜지는걸 확인할 수 있습니다.

$ sudo systemctl status caddy
Caddy 실행 중

다음으로는 php-fpm을 설치합니다. 필요에 맞게 php-imagick과 같은 확장 기능도 설치해줍니다.

$ sudo apt install php7.2-fpm php-imagick ...

설치가 완료되면 fpm 역시 자동으로 실행이 됩니다.

php-fpm도 켜졌다.

Caddy 설정으로 넘어가기 이전에, php-fpm의 설정을 살짝 수정하겠습니다. Ubuntu 패키지로 설치했다면 /etc/php/7.2/fpm/pool.d/www.conf 파일을 수정하면 됩니다.

기본적으로 우분투의 웹 서버 패키지(Apache, NGINX 등…)는 www-data 라는 사용자와 그룹을 사용하도록 통일이 되어 있습니다. Caddy는 공식 저장소가 아닌 외부 저장소를 통해 받았으므로, 자체적으로 caddy라는 사용자와 그룹을 사용합니다. 이대로 사용하는것도 가능은 하지만, 웹 서버와 PHP 런타임의 권한을 통일해주는 것이 역시 정신건강에 좋겠죠. www-data를 모두 caddy로 바꿔줍니다.

$ cd /etc/php/7.2/fpm/pool.d
$ sudo sed -i 's/www-data/caddy/g' www.conf
$ sudo systemctl restart php7.2-fpm
sed를 이용하여 모두 찾아 바꾸기

sed를 이용하여 모든 www-data를 caddy로 변환했다. 익숙하다면 vim으로 해도 된다.


마지막은 Caddyfile 설정입니다. /etc/caddy/Caddyfile 을 열고, 다음 설정을 추가해주세요.

mydomain.com {
	root * /home/sites/mydomain.com
	php_fastcgi unix//var/run/php/php7.2-fpm.sock
	file_server
}

이후 sudo systemctl reload caddy 명령어로 Caddy 설정을 다시 읽어오면 됩니다. Caddy가 새로운 설정을 읽고, 지정한 도메인을 위한 인증서를 자동으로 받아옵니다. 설정한 도메인을 들어가면 Caddy의 기본 인덱스 페이지가 반겨주네요.

Caddy의 기본 페이지

기본 페이지가 삐뚤어져있는 건 어서 제대로 된 콘텐츠로 바꾸라는 무언의 압박인 듯..

지정한 루트 폴더에 phpinfo 파일을 만들면, php도 정상 작동하는 것을 확인할 수 있습니다.

정겨운 phpinfo 화면

아니, 설정이 어떻게 저리 짧죠?

php_fastcgi 디렉티브 하나만 썼는데, /wp-admin으로 온 요청을 알아서 연결해 준다거나 하는 rewrite도 작동하는게 이상하게 느껴질 수도 있습니다. 사실 이 디렉티브 뒤에 특별한 흑마법이 숨겨져 있는 건 아니고, 대부분의 현대적인 PHP 애플리케이션에서 사용하는 설정을 간단하게 쓸 수 있도록 해 놓은 것입니다.

php_fastcgi directive

설정을 자세히 보면 Caddy에 내장되어 있는 리버스 프록시 디렉티브를 사용하여 구현이 되어 있습니다. 만약 민감한 디렉터리 등을 막고 싶다거나 php_fastcgi 디렉티브에 없는 특수한 설정을 하고 싶다면, rewrite와 같은 여러 가지 디렉티브를 추가할 수 있습니다. 아래는 Textcube의 썸네일 관련 rewrite를 구현한 예시입니다.

example.com {
  matcher a {
    path_regexp tc_thumb ^(thumbnail)/([0-9]+/.+)$
  }
  rewrite match:a cache/{http.matchers.path_regexp.tc_thumb.1}/{http.matchers.path_regexp.tc_thumb.2}

  php_fastcgi unix//var/run/php/php7.2-fpm.sock
  ...
}

WordPress, Laravel을 비롯한 대부분의 애플리케이션은 php_fastcgi 디렉티브만으로 동작하기 때문에 아마 대부분의 사용 사례에는 위와 같은 세부 설정을 건드릴 필요는 없을 것입니다.

마치며

Caddy는 비교적 역사가 짧은 웹 서버인만큼, 다른 오래된 서버와 다르게 프로덕션에서 검증이 많이 되지는 않았습니다. Go로 짜여진만큼 C++ 기반의 다른 소프트웨어만큼 신뢰를 받지 못할수도 있죠. 저도 이런 이유로 업무에서 Caddy를 사용하지는 않았지만, 이번에 새로 배포된 버전 2를 써보면서 이런 고민을 상쇄할만큼 사용하기 편리하다는 인상을 받았습니다. 여러 가지 구성 요소로 이루어진 복잡한 서비스를 운영한다면 한계에 다다를 수 있겠지만, 간단한 리버스 프록시와 정적 파일 서버를 찾는다면 Caddy가 가장 쉽고 간편한 해결책이 될 수 있을 것 같습니다.