본문으로 건너뛰기

"sysadmin" 태그로 연결된 4개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 10분

개인 서버 운영의 역사 (TMI 대방출)

개인 서버를 운영해온지도 거의 10여년이 넘어가고 있다. 몇몇 취미로 운영하는 서비스를 돌리거나 RSS 리더, Mastodon 인스턴스 등 개인적인 서비스를 띄우는 데에 사용하고 있다. 무료 크레딧을 받거나 해서 저렴하게 운영할 수 있는 옵션이 생길 때마다 여러 호스팅 서비스를 옮겨다니곤 했다. (생각나는 것만: 카페24, DigitalOcean, Linode, GCE, EC2) 지금은 Lightsail에 정착했다.

이렇다보니 언제든 호스팅 서비스를 옮길 수 있게 해둘 필요를 느꼈다. 단순히 설정 방법을 기록해 두는 건 실수의 여지가 많고, 초기 설정 이후에 바꾼 내용 업데이트를 잊기 쉽다. 직접 서버에 들어가서 뭔가 수정하기보다는 형상 관리가 가능한 방식을 사용하고 싶어서 처음에는 당시에 익숙했던 Ansible로 관리를 했다. 하지만 시스템 전역에 설치한 패키지에 의존하도록 하다보니 OS 버전이 바뀌거나 하면 제대로 작동하지 않는 경우가 많았다.

결국 환경의 영향을 덜 받게 하려면 Docker를 사용해야겠다 생각했다. 물론 Ansible로 Docker 컨테이너를 관리하는 게 불가능하지는 않다. 그렇지만 중단 없이 서비스를 재시작(롤링 업데이트)하는 등 조금 복잡한 작업을 하기는 어렵다. 당시 Kubernetes는 단일 서버에서 사용하기에는 설정이 쉽지 않았기 때문에 Docker Swarm을 잠시 시도해봤으나 여러 가지로 아쉬운 점이 많아서 대충 방치해놓고 시간이 흘렀다.

그러던 중 k3s라는 가볍고 단일 서버에 설치 가능한 Kubernetes 배포판이 나왔다는 소식을 접하고 시험해봤는데 꽤나 만족스러웠다. 설치도 간단해서 좋았다. (명령어 하나면 충분: curl -sfL https://get.k3s.io | sh -) 대기 상태에서도 머신 자원을 생각보다 많이 사용하는 문제는 있지만 그 정도는 편리함과 타협할 수 있는 부분이라고 보고 지금까지 정착해서 사용하고 있다.

물론 직접 서버를 운영하는 게 Vercel이나 fly.io 같은 PaaS를 사용하는 것에 비해 효율이 떨어지는 일인 건 맞다. 그래도 인프라 공부도 되고 취미 생활로는 나쁘지 않은 것 같다.

인그레스 컨트롤러와 TLS 인증서

Kubernetes에서 웹 서비스는 일반적으로 인그레스로 외부에 노출하게 되고 인그레스 컨트롤러를 설치해야 한다. k3s에는 Traefik이 인그레스 컨트롤러로 기본 설치되는데 나는 익숙한 ingress-nginx를 대신 사용하고 있다. Traefik을 설치하지 않으려면 k3s을 처음 설치할 때 --disable traefik 옵션을 줘야 한다.

ingress-nginx는 LoadBalancer 타입의 서비스를 만들고 k3s의 ServiceLB에 의해 호스트 외부에서 접속할 수 있게 된다. 물론 Lightsail의 방화벽 설정에서도 80, 443 포트를 열어줘야 한다. Lightsail 인스턴스가 바뀌어도 IP가 그대로이도록 고정 IP를 할당하고, DNS는 직접 서버 IP를 가리키게 한다.

AWS의 로드 밸런서를 사용하지 않기 때문에 TLS 인증서는 직접 발급해야 한다. cert-manager를 이용해서 Let's Encrypt 인증서를 자동 발급, 자동 갱신하고 있다. 새 서브도메인에 대한 인증서가 필요하면 Ingress에 어노테이션만 달아주면 된다.

모니터링

처음에 별 생각 없이 익숙한 Prometheus Operator를 설치했다가 리소스 낭비가 심해서 삭제하고 그냥 살고 있었는데, 최근 실행하는 서비스가 많아지니 다시 필요성을 느꼈다.

회사 인프라에서 쓰는 걸 보고 알게 된 VictoriaMetrics를 한 번 깔아봤는데 가볍고 잘 작동한다! VictoriaMetrics도 오퍼레이터를 사용하는 게 권장하는 방식 같지만, 오퍼레이터 자체의 오버헤드도 줄이고 싶어서 단일 프로세스인 VictoriaMetrics Single을 띄우는 방향으로 해보았다.

VictoriaMetrics는 딱 Prometheus와 같이 지표 수집, 저장만 하는 역할이어서 지표를 보고 싶으면 Grafana도 설치해야 한다. 이 역시 튜토리얼 문서에 나온대로 하면 된다. 외부에서 Grafana에 접속하게 하려면 인증을 붙이든지 해야 하는데 귀찮아서 일단 Tailscale 프록시를 달아놓고 Tailscale VPN 내에서만 노출해 두었다.

로그도 오래 보관하면 좋긴 하겠지만 당장은 필요가 없어서 그냥 두고 있다. 만약 필요하다면 Loki를 써볼지도...

데이터베이스와 퍼시스턴트 볼륨

내가 돌리는 대부분의 서비스가 PostgreSQL에 의존하고 있기 때문에 k3s에 하나를 설치해서 공유하고 있다. 파드가 재시작되어도 데이터가 날아가면 안되니 퍼시스턴트 볼륨을 마운트해야 하는데, 이 또한 k3s에서 기본 제공하는 Local Storage Provider를 사용한다. (예전에는 기본 탑재가 아니었는데 언젠가부터 기본적으로 설치된다. 여기서 또 TMI, CPU를 많이 잡아먹는 버그를 수정한 적이 있다 😎)

호스트 머신 파일시스템의 /opt/local-path-provisioner 디렉토리 밑에 파일이 저장되는 매우 단순한 구조이므로 파드가 다른 머신으로 옮겨다닐 수 없는 문제가 있지만 어차피 노드를 한 개만 사용할 것이므로 전혀 문제가 되지 않는다. 노드를 추가하더라도 컨트롤 플레인이 있는 노드에서만 DB를 돌리면 오케이.

DB 백업은 별도로 하지 않고, Lightsail의 백업 기능을 켜두었다. 매일 디스크 스냅샷을 떠주고 일주일 간 유지한다.

인프라 형상 관리

위에서 설명한 시스템 구성 요소는 Helm 차트로 설치하고 있다. 개인적으로 Helm을 썩 좋아하진 않지만 (YAML을 템플리팅 할 미친 생각을 대체 누가 했을까?) Kubernetes 환경의 사실상 표준이기 때문에 어쩔 수 없이 타협을 해야 한다. 내가 직접 패키징해야 하는 경우는 거의 Kustomize를 사용하고 있다.

아무튼 여러 개의 Helm 차트를 설치해야 하고, 매번 helm 명령어를 입력할 수는 없기 때문에 Helmfile로 Helm 차트의 목록과 파라미터를 관리한다. 가능하면 직접 helm이나 kubectl로 조작하는 건 지양하고 Helmfile만으로 관리하려고 노력한다. 모든 설정은 나의 Git 저장소에 잘 올려두고 있다.

아직 깔끔하게 정리하지 못한 부분이 DB 암호 등 Helmfile에 주입할 비밀 정보를 관리하는 것이다. 일단은 Git에는 커밋하지 않는 내 로컬 머신에만 있는 파일에 넣어두고 있다. 만약 내 로컬 머신이 불의의 사고로 날아가더라도 k8s Secret에는 남아있어서 복구 가능하므로 큰 문제는 없을 것으로 생각한다.

· 약 9분

그동안은 파이썬 애플리케이션을 배포할 때 설정이 쉽다는 이유로 gunicorn을 주로 사용했다. (예전에 올린 관련 글) 이번에 우연히 uWSGI의 여러가지 기능에 대해 알게 되었는데, 호기심이 생겨 문서를 읽으면서 이것저것 적용해보았다. 공식 문서가 잘 쓰여있긴 하지만 워낙 기능이 방대해서 유용할만한 기능을 정리해본다.

혹시 uWSGI의 기본적인 설정 방법을 알고 싶다면 haruair님의 블로그 글을 추천한다.

설정 파일 변수 (placeholder)

이걸 고급 기능이라고 해야할 지 의문이지만... 설정 파일 안에서 반복되는 내용은 변수로 두고 한번에 치환할 수 있다.

# %(app_dir) -> /path/to/app
set-placeholder = app_dir=/path/to/app

# virtualenv = /path/to/app/env와 동일
virtualenv = %(app_dir)/env

부속 프로세스 관리

메인 웹 애플리케이션과 항상 같이 실행되는 부속 데몬/워커 프로세스가 있는 경우가 많다. 예를 들면 Celery 워커가 있다. 이런 프로세스들을 따로 시스템 서비스로 등록하지 않고 uWSGI가 관리하게 만들 수 있다. attach-daemon 옵션에 함께 실행할 명령을 지정하면 된다. 단, 이때 실행되는 명령어는 데몬화(daemonize)되면 안되고 foreground 모드로 실행되어야 한다.

attach-daemon = /path/to/worker/script

마스터 프로세스가 종료(리로드)되면 부속 프로세스도 함께 종료된다는 것에 주의해야 한다. 이것을 방지하려면 smart-attach-daemon 옵션을 사용해서 PID 파일 경로를 지정해 주어야 한다. 자세한 것은 문서를 보자.

정해진 시간에 명령 실행 (cron)

놀랍게도 cron도 uwsgi로 대체할 수 있다. 보통 crontab에 직접 등록해놓으면 애플리케이션 설정과 분리되어 있기 때문에 잊어버리거나 실수하기 쉬운데, uwsgi 설정에 넣으면 상당히 편리할 것이다. cron 옵션으로 지정하면 되는데, crontab의 설정과 거의 동일하다. 단, crontab에서 */10처럼 쓰던 것을 -10으로 써야 한다. *-1로 쓰면 된다.

# crontab: */5 * * * * /path/to/some/script
cron = -5 -1 -1 -1 -1 /path/to/some/script

cron 대신 unique-cron 옵션을 지정하면 정해진 시각이 되어도 이전에 실행되던 프로세스가 있으면 그 프로세스가 끝날때까지 대기해서 항상 하나의 프로세스만 실행된다. 자세한 것은 문서에서 더 볼 수 있다.

정적 파일 서빙

정적 파일을 uwsgi에서 전송하도록 할 수 있다. nginx에 맡기는 것에 비해 큰 장점이 있지는 않지만 애플리케이션에 대한 정보가 하나의 설정 파일에 들어있게 만들 수 있어서 좋은 것 같다. static-map 옵션으로 특정 문자열로 시작하는 경로를 가진 요청을 파일시스템 상의 특정 디렉토리에 매핑할 수 있다. 그리고 static-expires로 특정 패턴의 주소에 Expires 헤더를 설정할 수 있다. (자세한 설명)

# /static/a.jpg -> /path/to/static/a.jpg
static-map = /static=/path/to/static
# /static/build/로 시작하는 요청의 만료일을 요청 시각으로부터 2592000초 이후로 설정
static-expires-uri = /static/build/* 2592000

접속자가 없을 때 프로세스 종료 (cheap 모드)

어드민 툴 같은 서비스는 아주 자주 사용하지는 않기 때문에 항상 켜두면 메모리가 낭비된다. cheapidle 옵션을 사용하면 접속자가 일정 시간동안 없으면 프로세스를 종료해서 시스템 자원을 절약할 수 있다.

먼저 cheap 옵션은 첫번째 요청이 들어올 때까지 worker 프로세스를 실행하지 않도록 해준다. (master 프로세스는 항상 실행되어 있다) 또한 idle 옵션에 지정한 시간동안 아무런 접속이 없으면 프로세스가 cheap 모드로 들어간다.

# cheap 모드로 실행, 60초 동안 요청이 없으면 cheap 모드로 전환
cheap = true
idle = 60

Graceful reload

기능이라기보다는 작동 방식에 대한 설명인데, uWSGI는 Gunicorn과 리로드 동작 방식이 다르다. 먼저 uWSGI는 master 모드로 실행해야 graceful reload가 가능하다. 또한 Gunicorn은 HUP 시그널을 받으면 기존 프로세스는 그대로 둔 채 먼저 새로운 워커 프로세스를 실행하고 이전 워커를 하나씩 종료하는 방식인데 반해, uWSGI는 먼저 워커 프로세스를 모두 종료한 뒤 새로운 워커를 실행하는 방식이다. 따라서 uWSGI에서는 리로드되는 동안 들어오는 요청은 처리가 지연될 수 있다. (단, Gunicorn과 달리 리로드 되는 동안에도 메모리 사용량이 증가하지 않는다.)

다른 리로드 방식도 있으니 궁금하다면 문서를 읽어보자.

Emperor 모드: 여러 앱 관리

uWSGI 프로세스 자체를 관리하려면 upstart나 systemd 설정을 작성해서 시스템 서비스로 등록하는 것이 일반적이다. Emperor 모드를 사용하여 init 데몬과 별도로 uWSGI 프로세스를 관리할 수 있다. 가장 큰 장점은 배포판에서 사용하는 init 데몬의 종류에 상관 없이 서비스를 관리할 수 있다는 점일 듯 하다. 그리고 프로세스의 실행을 특정 포트에 접속하는 클라이언트가 나타날 때까지 지연하는 Socket Activation 기능을 사용할 수 있다. (물론 systemd에서도 가능하지만...)

먼저 설치는, 우분투 기준으로 uwsgi-emperor 패키지를 설치하면 된다. (구버전 우분투에서는 PPA 저장소를 사용하여 최신 버전을 설치할 수 있다.) 패키지를 설치하면 자동으로 emperor 프로세스가 실행된다. 설정 파일은 /etc/uwsgi-emperor/emperor.ini에 있다.

설정 파일에서 특별히 건드릴 부분은 없고, 마지막에 보면 emperor 옵션이 있을 것이다. 이 옵션에 지정된 디렉토리에 uWSGI 설정 파일을 새로 추가하면 그 설정대로 프로세스가 실행된다. (이때 관리되는 자식 프로세스를 vassal이라고 한다. 황제(emperor)와 신하(vassal)!) 설정 파일이 수정되거나 수정 시각이 변경(touch)되면 프로세스가 리로드된다. 디렉토리에서 설정 파일이 삭제되면 서비스가 종료된다. 물론 여러개의 디렉토리를 모니터링하게 할 수도 있으므로 나는 홈 디렉토리 밑의 설정 파일 디렉토리를 추가했다.

...
# vassals directory
emperor = /etc/uwsgi-emperor/vassals
emperor = /home/ditto/uwsgi-services

· 약 11분

파이썬 웹 애플리케이션을 배포할 때 고려할 점들이 많이 있습니다. 특히 선택할 수 있는 대안이 많으면 고민이 되는데요. 멋지게 '베스트 프랙티스'를 제시하면 좋겠지만 아직 그러기엔 많이 부족하고, 이 글에서는 제가 선택한 방식과 이유를 정리해서 공유해봅니다.

운영체제: Ubuntu

일반적으로 CentOS 아니면 Ubuntu를 사용하는 것 같습니다. Ubuntu를 선택한 가장 큰 이유는 '익숙해서'입니다. 그 밖에는 다양한 써드 파티 패키지(PPA)에 쉽게 접근할 수 있다는 점, 그리고 개발 환경으로 Ubuntu 데스크탑을 사용하면 배포할 때 환경 맞추기 훨씬 수월하다는 점이 있겠습니다. (CentOS를 데스크탑으로 쓰신다면야 말리지는 않겠습니다...) 버전은 굳이 LTS를 고집할 필요 없이 적당히 최신 버전을 쓰면 되는 것 같습니다.

WSGI 서버: Gunicorn

WSGI 서버로는 uWSGI 또는 Gunicorn이 많이 쓰입니다. uWSGI는 설정할 수 있는 파라미터도 많고 성능도 좋긴 하지만, Gunicorn은 성능이 크게 뒤지지 않으면서 훨씬 간단해서 관리하기가 수월하다고 생각합니다. 그리고 uWSGI는 graceful reload가 안돼서 재시작할 때 다운타임이 발생하는 것으로 보입니다. (해결 방법이 있다면 알려주세요.)

프로세스 관리: Upstart

대안이라면 supervisord가 있겠지만 Upstart는 Ubuntu에 기본으로 들어있어서 덜 귀찮죠. Upstart에 대해 자세히 알고 싶으시면 저번에 쓴 글을 참고하세요. 앞으로 Ubuntu가 systemd로 전환한다면 systemd를 사용하게 되겠죠.

웹 서버: nginx

이 선택은 딱히 논란의 여지가 없을 겁니다. 리버시 프록시와 정적 리소스 서빙에 사용합니다. 다만 반드시 Apache로 돌려야 하는 레거시가 있다면 예외입니다.

코드 배포: Git

애플리케이션 코드가 업데이트 되었을 때 어떻게 받아올 것인가 하는 이야기입니다. 정석대로(?) 하자면 Git 작업 사본과 실제 서비스되는 애플리케이션 코드를 분리하는 것이 좋습니다. 왜냐하면, git pull을 하는 동안 코드의 중간 상태가 노출될 수 있기 때문입니다. 예를 들어 이전 버전의 템플릿 파일과 새로운 버전의 템플릿 파일이 공존하게 된다면 서비스에 문제가 생길 수 있습니다. 하지만 대부분 아주 잠깐 발생하는 일이고, 템플릿 캐싱을 켜는 등 우회할 수 있는 방법이 있어서 큰 문제가 되지는 않습니다.

virtualenv나 설정 파일 등을 어디에 위치시킬 것인지도 고민하게 됩니다. 많은 경우 Git 작업 사본 안에 만들게 되는데, 그런 경우 .gitignore 파일에 확실히 추가하여 저장소에 들어가지 않도록 주의가 필요합니다.

돌아가는 서비스가 많을수록 헷갈리지 않도록 디렉토리 구조를 통일하면 도움이 됩니다. 애플리케이션 코드에서도 가급적 설정 파일 등의 위치나 파일명을 강제하지 말고 환경 변수에서 읽도록 하는 편이 좋습니다.

파이썬 패키지: virtualenv에 직접 설치

시스템에서 제공하는 바이너리 패키지를 설치할 수도 있지만 별로 권장하지 않습니다. 일단 virtualenv로 격리할 수도 없고, 최신 버전이 아닐 가능성이 매우 높고, 언제 업데이트가 될지도 알 수 없습니다. 다만 배포 대상 서버가 많다면 빌드 시간이 낭비되니까 직접 패키징하는 것을 고려해봐야 합니다.

추가로, 의존성을 setup.py로 관리할지 아니면 requirements.txt를 사용할지도 선택해야 하는데요. 이 쪽은 특별히 선호하는 방식이 있지는 않습니다. 하지만 많은 경우에 requirements.txt로만 관리해도 충분합니다. 어쨌든 관리가 되고 있다는 것이 중요합니다.

정적 리소스 배포

캐시 관리 전략: URL 기반

리소스가 갱신되었을 때 브라우저의 캐시를 어떻게 무효화 할 것인지 고려해봐야 합니다. 가장 확실한 방법은 리소스가 바뀔 때마다 URL을 바꾸는 것입니다. 아예 파일명을 바꾸거나 주소 뒤에 쿼리 문자열을 붙여서 (?v=3처럼) URL이 바뀌도록 해줍니다. 이런 일을 손으로 하면 실수할 가능성이 높으므로 가급적 자동으로 처리할 수 있는 시스템을 만드는 것이 좋습니다. 이렇게 하면 Expiry 헤더를 매우 길게 잡아 서버에 전혀 요청이 들어오지 않게 만들 수도 있어 성능상 이득이 있습니다.

내려주는 곳: 예산에 따라

서버가 한국에 있고, 접속자도 모두 한국 거주자라면 그냥 애플리케이션과 같은 서버에서 내려주면 되므로 특별히 고려할 것이 없습니다. 하지만 서버와 접속자의 위치가 다르고 충분한 예산이 있다면 CDN을 사용하는 것이 좋습니다.

CDN을 사용할 경우 S3와 같은 스토리지 서비스에 파일을 올릴지, 아니면 일반 웹 서버를 사용할지 선택해야 합니다. 스토리지 서비스를 사용할 경우에는 애플리케이션 배포 시 추가적인 리소스 배포 과정이 필요하지만 훨씬 안정적이라는 장점이 있습니다.

전처리: 상황에 따라

최근 웹 프론트엔드 개발에서는 SASS나 CoffeeScript처럼 전처리 과정이 필요한 언어를 사용하는 추세입니다. 크게 세 가지 방법이 있습니다.

  1. 배포하기 전에 미리 빌드: 배포할 서버 수가 많을 때 유리합니다. 혼자 개발하는 서비스라면 아예 Git 저장소에 같이 커밋할 수도 있습니다. 또한 스토리지 서비스를 통해 리소스를 내려줄 경우, 어차피 업로드하는 과정이 필요하므로 업로드하기 전에 해주면 됩니다.
  2. 서비스하는 서버에서 미리 오프라인 빌드: Git 훅 스크립트로 지정해두면 편하게 할 수 있습니다. 또한 애플리케이션 서버와 무관하므로 안전하다는 장점이 있습니다.
  3. 애플리케이션 서버에서 온라인 빌드: webassets 같은 라이브러리를 사용하여 애플리케이션 서버가 관리하도록 합니다. 서버 구동이 느려지거나 서비스에 영향을 미칠 수 있으므로 추천하는 방법은 아닙니다.

오류 추적: Sentry

실제 서비스를 시작하면 테스트할 때는 발견하지 못했던 예외가 발생할 수 있습니다. Sentry를 통해 오류를 수집하면 많은 도움이 됩니다. 또한 로거와 연동해서 치명적인 오류는 아니지만 예외적인 상황에 로그를 남기게 하면 비상시에 대처할 수 있어 좋습니다.

더 생각해볼 것들

  • 배포 자동화: Fabric으로 원격 배포를 자동화합니다.
  • 서버 설정 자동화: Chef, Puppet이나 Ansible 등을 사용해서 서버 세팅 과정을 형상 관리/자동화합니다.
  • 성능 모니터링: New Relic이 굉장히 좋지만, 가격이 만만치 않아서 아직 적절한 대안을 찾지 못했습니다.
  • 서버 접속 권한과 민감한 정보 관리
  • 업로드 된 파일 관리
  • 데이터베이스 / 메모리 캐시
  • 백그라운드 워커 (Celery) / 메시지 큐

· 약 7분

오래 돌아야 하는 서버 또는 워커를 어떻게 관리하고 계신가요? 설마 이렇게 하고 계신가요?

  • screen이나 tmux 안에 띄워놓고 잊어버리기
  • nohup으로 실행해두고 잊어버리기
  • 프로세스가 꺼졌는지 한참동안 모르고 있다가 당황하기
  • 시스템 재부팅 될 때마다 헬을 만나기

우분투에서 기본으로 제공되는 Upstart를 사용하면,

  • 시스템 부팅 시에 서비스 띄우기
  • 다른 서비스가 시작된 후에 서비스 띄우기
  • 프로세스가 오류로 꺼지면 자동으로 다시 띄우기
  • stdout/stderr를 로그 파일에 기록하기
  • 로그 파일이 커지면 쪼개기

와 같은 기능을 어렵지 않게 사용할 수 있습니다.

설정 파일 설치하기

Upstart 서비스 설정 파일은 /etc/init/에 모여있습니다. 따라서 /etc/init/ 디렉토리에 서비스명.conf 파일을 만들어 넣으면 됩니다.

심볼릭 링크로 설치하기

/etc/init/에는 시스템 서비스의 설정 파일도 모두 들어있기 때문에, 조금 더 관리를 편하게 하려면 별도의 디렉토리에 서비스 설정 파일을 모아두는 것도 좋은 선택입니다. 그러려면 /etc/init에 심볼릭 링크를 걸어야 합니다.

sudo ln -s /home/ubuntu/something.conf /etc/init/

주의! /etc/init/에 직접 들어있지 않고 심볼릭 링크로 들어있는 파일이 수정될 때는 Upstart가 변화를 감지하지 못합니다. 따라서 다음 명령어로 설정 파일을 다시 불러오게 해야 합니다.

sudo initctl reload-configuration

서비스 관리

설정 파일 작성법을 알아보기 전에 서비스 관리하는 방법을 먼저 알아둡시다.

  • 시작: sudo start 서비스명
  • 중단: sudo stop 서비스명
  • 재시작: sudo restart 서비스명 (주의: 서비스 설정 파일을 다시 읽어오지 않습니다. 설정 파일이 바뀌었으면 stop 후 start할 것)
  • 점잖은 재시작: sudo reload 서비스명 (정확히는, 프로세스에 HUP 시그널을 보냅니다)

설정 파일 작성하기

명령어 지정

가장 간단하게는 exec 뒤에 명령어를 쓰면 됩니다.

exec uname -a

좀 더 긴 쉘 스크립트가 필요할 경우 script 구문을 사용합니다.

script
sleep 5
uname -a
end script

어떤 명령어들은 실행하면 자동으로 백그라운드로 들어가는(detach/daemonize) 경우가 있습니다. 이를 방지하는 옵션이 있다면 켜는 것이 좋습니다. (보통 --foreground--nodetach)

포어그라운드 모드로 실행하는 옵션을 지원하지 않는 경우 Upstart 문서를 참고해서 설정하시기 바랍니다.

자동 시작/중단 조건

서비스를 특정 조건이 만족되었을 때 시작되거나 중단되게 할 수 있습니다.

가장 많이 사용하게 될 설정은 부팅 시 시작, 시스템 종료 시 중단이겠죠? 다음과 같이 적으면 됩니다.

start on runlevel [2345]
stop on runlevel [016]

또는 다른 서비스가 시작된 이후에 띄우고 싶을 수도 있습니다. A 서비스가 시작된 후를 조건으로 지정하려면,

start on started A

and 연산자로 여러 조건을 조합할 수도 있습니다.

start on started mysql and started nginx

mysql과 nginx 서비스가 시작된 뒤가 시작 조건이 됩니다.

실행 권한

프로세스는 기본적으로 root 권한을 가지고 실행됩니다. 보안을 위해 별도의 사용자/그룹 권한을 주고 실행하는 것을 권장합니다.

setuid username
setgid groupname

실행 환경 설정

  • 환경 변수 설정: env KEY=value (환경 변수는 exec/script 구문 안에서 $KEY로 참조할 수 있습니다)
  • 디렉토리 변경: chdir /path/to/current/dir

자동 재시작(respawn)

한 줄만 추가하면 프로세스가 예기치 않게 종료됐을 때 (종료 코드가 0이 아닐 때) 자동으로 다시 실행됩니다.

respawn

프로세스가 너무 빨리 되살아나는 것을 방지하기 위해 5초 동안 10번 재시작되면 더이상 재시작하지 않습니다. 이 제한은 respawn limit으로 바꿀 수 있습니다.

# 주의: respawn limit 구문이 있더라도 respawn 구문이 없으면 자동 재시작이 되지 않습니다
respawn
respawn limit COUNT INTERVAL # INTERVAL초 동안 COUNT번 재시작되면 포기합니다.
respawn limit unlimited # 제한을 없앱니다.

로그 파일

stdout/stderr로 출력된 내용은 /var/log/upstart/서비스명.log에 저장됩니다.

우분투 (14.04 기준)에서는 이 로그 파일이 하루에 한번 분할되고 최대 7개의 파일이 유지되는 것이 기본 설정입니다. /etc/logrotate.d/upstart에서 설정을 바꿀 수 있습니다.

설정 파일 예제

gunicorn으로 파이썬 웹 애플리케이션을 실행하는 예제입니다.

대안

  • 우분투 차기 (또는 차차기) 버전에서 Upstart가 systemd로 대체될 예정이라고 합니다. 데비안에서는 이미 systemd가 기본입니다.
  • 시스템과 독립적으로 작동하면서 Upstart 같은 기능을 제공하는 Supervisor도 많이 사용됩니다.