Nginx 설정 형상관리하기
이전의 리뷰미는 nginx 설정을 바꾸기 위해 직접 인스턴스에 접속해야 했습니다.
또, 개발 서버와 운영 서버와의 nginx 설정도 서로 달랐습니다.
현재는 GitHub에 .conf
와 docker-compose.yml
파일을 관리하고 있고, 해당 부분의 변경 사항이 인스턴스에 전파됩니다.
이 글에서는 서로 다른 nginx 설정 정보를 어떻게 통합했는지에 대해서 다룹니다.
🤨 서로 다른 환경 설정
운영 서버와 개발 서버로 분리되는 순간 관리해야 하는 부분이 하나 더 생기게 됩니다. 인스턴스가 최소 한 개 추가되기도 하고, 여러 이유들로 인해 인프라에 작업을 해 두었다면 더욱 그렇겠죠. 리뷰미도 https 통신을 위해 nginx를 사용하고 있었고, 개발 서버와 운영 서버에 각각 설치돼 있었습니다.
관리 포인트가 많아진다는 것은 결국 한 번의 수정이 곧 두 번의 수정이라는 의미입니다. 개발 서버에서 충분한 테스트 후 운영 서버로 반영해야 할 때에는, 어떤 부분이 수정되었는지 다시 비교해보아야 해요. 이 과정에서 실수할 수 있는 것은 분명하거니와 실수하더라도 빠르게 바로잡을 수 없었습니다.
가만 봅시다. 이거 어디에서 많이 본 상황 아닌가요?
처음 프로젝트를 시작하고 얼마 안 돼 같은 일을 겪었습니다.
빌드하고, .jar
파일을 만들고, 이를 인스턴스로 옮겨 java -jar
명령어를 통해 서버를 실행하기까지.
불편함은 오류를 회복하는 시간을 더디게 만들었고 효율적인 업무에 방해가 될 뿐이었습니다.
이를 해결하기 위해 CD 자동화를 진행했어요. 실행 파일을 빌드하는 것부터 실제 서버에 업로드 및 실행하는 것을 빠르게 확인할 수 있었습니다.
일련의 과정들을 인프라에도 적용할 수 있지 않을까요?
🕸️ nginx를 Git으로 관리하라고요?
네, 이제는 nginx의 설정 파일을 Git으로 관리하려고 합니다.
실제로 nginx에서 우리의 설정은 nginx.conf
와 /conf.d
하위의 파일들만 관리되고 있었어요.
추가적으로 Https를 위한 인증서 키가 /cert
디렉토리에 존재합니다만, 인증서는 이 글에서는 다루지 않습니다.
그렇다면 우리가 관리해야 하는 것은 nginx.conf
, /conf.d
하위 파일들, 그리고 docker-compose.yml
이 되겠습니다. 도커를 활용한 것은 운영-개발 서버의 환경 차이를 최대한 줄이기 위함이었지만, 개발 서버에서 이것저것 실험해 보면서 운영 서버와는 다른 환경이 되어버렸어요. 이 차이를 줄이기 위해서라도 docker-compose.yml
또한 버전 관리를 해야할 필요성을 느꼈습니다.
docker-compose.yml
에서는 포트 바인딩, 모니터링을 위한 exporter, 네트워크 정보가 포함돼 있습니다. 내부 설정에 필요한 부분과 로깅은 모두 호스트와 볼륨 매핑돼 있으므로, down
, up
을 진행하더라도 중요한 데이터는 손실되지 않습니다. 이 파일은 그대로 Git에 업로드되어도 괜찮겠네요!
기본적으로 Private repository에 업로드되는 것을 원칙으로 합니다.
Public에 업로드하는 경우 Actions secret으로 대부분의 정보를 공개하지 않을 것을 권장합니다.
services:
nginx:
container_name: nginx
image: nginx:stable-alpine3.20
restart: always
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./logs:/var/log/nginx
- ./conf.d:/etc/nginx/conf.d
- ./cert:/etc/nginx/cert
ports:
# ...
nginx_exporter:
container_name: nginx_exporter
depends_on:
- nginx
image: nginx/nginx-prometheus-exporter:1.3.0
# ...
networks:
# ...
nginx 설정 파일에는 운영 서버와 개발 서버가 서로 다르게 적혀야하는 부분이 있습니다. IP/Port와 같은 upstream
정보와 server_name
이예요. app-dev.conf
, app-prod.conf
와 같이 두 개의 파일로 관리해야 할까요? 저희는 관리 포인트가 늘어나는 것이 싫어서 여러 방법을 찾아 보았어요.
upstream app {
server 10.1.2.3:8080; # 개발 서버와 운영 서버와의 IP와 포트가 다릅니다
}
server {
listen 80;
server_name your-domain.here.com;
# https 리다이렉션..
}
server {
listen 443 ssl;
server_name your-domain.here.com;
# 인증서, 리버스 프록싱 설정..
}
리눅스에서는 envsubst
라는 프로그램을 제공합니다. environment variable substitution
, 환경 변수 치환이예요. 문자열을 제공한 뒤, 리눅스 내 환경 변수가 존재한다면 해당 내용으로 치환합니다. 이때, ${VAR_NAME}
와 같은 형태로 제공해야 해요.
그렇다면 환경 변수에 서버와 도메인 정보를 미리 넣어둔 뒤, 설정 파일에 치환할 변수를 추가한다면 어떨까요? 아래와 같은 모습이 되겠습니다. 환경 변수를 어떻게 설정할지는 GitHub Actions에서 다시 설명드리도록 할게요!
upstream app {
server ${UPSTREAM_URL};
}
server {
listen 80;
server_name ${SERVER_URL};
}
server {
listen 443 ssl;
server_name ${SERVER_URL};
}