한준후블로그

서버 한 대로 사이트 14개 굴리기 — nginx · PM2 · certbot 구성

·2분 읽기 ·1,081자

#서버#인프라#운영

지금 서버 한 대에서 사이트 14개가 돌아가고 있다. 정적 사이트도 있고 Node 앱도 있고, 도메인은 .io, .co.kr, 서브도메인까지 섞여 있다. 특별한 기술은 없고, 규칙을 하나 정해두고 그것만 지킨 것이 전부다.

구성

사양은 4 vCPU / 15GB RAM / 969GB. 지금 디스크 사용률은 2%다. 개인 사이트 수십 개는 이 정도면 남는다.

  • nginx — 모든 요청의 입구. 정적 파일은 여기서 바로 주고, 동적 요청만 뒤로 넘긴다
  • PM2 — Node 앱 프로세스 관리. 죽으면 살리고, 재부팅해도 복구
  • MySQL — 앱별로 DB와 계정을 따로 판다
  • certbot — Let's Encrypt 인증서 발급과 자동 갱신

규칙 1 — 사이트 하나에 설정 파일 하나

/etc/nginx/sites-available/도메인으로 파일을 만들고 sites-enabled에 심볼릭 링크를 건다. 한 파일에 여러 도메인을 몰아넣지 않는다. 사이트를 내릴 때 링크만 지우면 되고, 실수로 다른 사이트를 건드릴 일이 없다.

공통 보안 헤더처럼 반복되는 건 snippets/에 빼두고 include한다.

server {
    server_name node9.io www.node9.io;
    root /var/www/node9.io;
    include snippets/common-headers.conf;

    location ~* \.(svg|png|jpg|webp|ico|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }

    location / {
        try_files $uri $uri.html $uri/index.html =404;
    }
}

try_files $uri $uri.html를 넣어두면 /about.html/about으로도 열 수 있다. 정적 사이트에서 주소를 깔끔하게 유지하는 가장 싼 방법이다.

규칙 2 — Node 앱은 포트를 겹치지 않게 미리 나눠둔다

앱마다 포트를 하나씩 정하고 127.0.0.1에만 바인딩한다. 외부에서 포트로 직접 접근할 수 없게 하고, 무조건 nginx를 거치게 한다.

app.listen(PORT, '127.0.0.1');

새 앱을 올릴 때는 먼저 빈 포트를 확인한다.

sudo ss -ltn | grep -oE '127.0.0.1:[0-9]+' | sort -u

nginx 쪽은 이렇게 넘긴다. HostX-Forwarded-Proto를 빼먹으면 앱이 자기 주소를 http://localhost로 착각해서 리다이렉트가 깨진다.

location / {
    proxy_pass http://127.0.0.1:4040;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Express에서는 app.set('trust proxy', 1)을 켜야 req.ipsecure 쿠키가 제대로 동작한다.

규칙 3 — PM2는 반드시 save까지

PM2 문서의 startup 항목에 나와 있듯, 앱을 띄우는 것까지는 다들 하는데, pm2 savepm2 startup을 빼먹으면 서버가 재부팅되는 순간 전부 내려간다. 이건 한 번 당해봐야 안 잊는다.

pm2 start ecosystem.config.js
pm2 save
pm2 startup        # 출력되는 명령을 sudo로 한 번 실행

환경변수는 코드에 넣지 않고 .env에 두고 권한을 600으로 잠근다. ecosystem 파일에서 읽어서 넘긴다.

규칙 4 — 인증서는 발급하고 잊는다

certbot을 쓰면 도메인을 추가할 때마다 한 줄이면 끝난다. certbot이 nginx 설정까지 알아서 고쳐주고, 갱신 타이머도 등록해둔다.

sudo certbot --nginx -d blog.example.com --redirect

와일드카드 DNS(*.example.com)를 미리 걸어두면 서브도메인을 새로 팔 때 DNS 전파를 기다릴 필요가 없다. 도메인 등록기관 설정 한 번으로 이후가 편해진다.

안 하는 것

  • Docker를 쓰지 않는다. 앱이 전부 Node 한 가지라 런타임을 격리할 이유가 없었다. 메모리와 운영 복잡도만 늘었을 것이다
  • 포트를 외부에 열지 않는다. 22, 80, 443 외에는 방화벽에서 막는다
  • DB 계정을 공유하지 않는다. 앱마다 계정을 파고 SELECT, INSERT, UPDATE, DELETE 정도만 준다. 하나가 뚫려도 옆으로 못 번진다

지금 상태

이 구성으로 정적 사이트 8개, Node 앱 6개가 돌고 있고 메모리는 절반쯤 쓴다. 사이트를 하나 더 올리는 데 걸리는 시간은 설정 파일 하나 쓰고 certbot 한 줄, 5분이면 된다. 규칙을 단순하게 잡아두면 늘어나도 관리 비용이 거의 안 는다.

한준후개발자 겸 창업가. 크리프트 대표, 주식회사 위트 최대주주.

소개 ↗

함께 읽기