개발한 서버를 ec2로 배포하는 방식은 다양하다.

 

elastic beanstalk을 써서 aws한테 "해 줘"를 시전할 수도 있고, docker image로 만들어서 컨테이너로 배포할 수도 있는 등 매우 다양하다.

docker로 띄운다면 eks를 쓸 수도 있을거고 ecs를 쓸 수도 있을거고 docker compose를 사용할 수도 있을거고..

 

더 나열하면 끝도 없으니 이 쯤하고 오늘의 주제로 넘어가자.

 

오늘은 ec2에 배포한 패키지를 service로 등록하여 systemd가 모니터링 하게 하고, systemctl로 start/stop 하는 과정을 다뤄보려 한다. ec2에 배포는 깃에서 클론을 받을 수도 있고 whl 패키지로 만들어 인스톨 할 수도 있는데, 이번 포스트에서는 다루지 않으려고 한다.

 

Service Script 작성

systemd 가 service를 모니터링하게 하기 위해서는 .service script(이하 유닛 파일)를 작성해야 하고, script 의 각 옵션에 대한 부분은 공식 문서에서 확인할 수 있다.

[Unit]
Description=Main Service
After=network.target

[Service]
Type=notify
User=ubuntu
Group=ubuntu
RuntimeDirectory=gunicorn
WorkingDirectory=/opt/main
ExecStart=/opt/main/venv/bin/gunicorn --workers=8 --worker-class=egg:meinheld#gunicorn_worker maf_core.site.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
Restart=on-failure
RestartSec=3

[Install]
Wnated=multi-user.target

편의상 service 명은 main으로 명명했고, 패키지를 다운로드하는 디렉토리역시 /opt/main 으로 했다.

 

유닛 파일들의 옵션들을 살펴보자.

 

[Unit]

스크립트의 확장자가 .service 이긴 하지만 통상적으로 unit file이라고 부른다. systemd가 모니터링 하고 있는 서비스들의 목록을 볼 때 systemctl list-units 를 사용하는 것도 같은 이유이다.

 

Description

unit file 에 대한 설명이다.

 

After

서비스의 의존성을 나타내는 부분이며, 아래의 표와 같이 After 말고도 다양한 선택지가 있다. 자세히 알고 싶다면 공식 문서를 참고하자.

예제에서는 .network.target 이 시작됐는지 체크한다.

여기서 .target 이라는 부분이 조금 생소한데, .target공식문서 에 따르면 linux가 시작되면서 유닛들을 grouping 하는 데에 사용되는 systemd 의 target uni 들이라고 한다.

 

해당 부분에는 target 이 아닌 service 를 줄 수도 있는데, 쉽게 말해 group unit 이 아닌 개별 유닛 과 의존 관계를 맺을 수도 있다는 뜻이다.

 

아래와 같이 명령어를 통해 target unit list 를 전부 조회할 수 있다. target unit은 시스템의 run level 과도 관련이 있는데, 추후에 다뤄보려 한다.

systemctl list-units --type=target

  UNIT                   LOAD   ACTIVE SUB    DESCRIPTION
  basic.target           loaded active active Basic System
  cloud-config.target    loaded active active Cloud-config availability
  cloud-init.target      loaded active active Cloud-init target
  cryptsetup.target      loaded active active Local Encrypted Volumes
  getty.target           loaded active active Login Prompts
  graphical.target       loaded active active Graphical Interface
  local-fs-pre.target    loaded active active Local File Systems (Pre)
  local-fs.target        loaded active active Local File Systems
  multi-user.target      loaded active active Multi-User System
  network-online.target  loaded active active Network is Online
  network-pre.target     loaded active active Network (Pre)
  network.target         loaded active active Network
  nss-lookup.target      loaded active active Host and Network Name Lookups
  nss-user-lookup.target loaded active active User and Group Name Lookups
  paths.target           loaded active active Paths
  remote-fs-pre.target   loaded active active Remote File Systems (Pre)
  remote-fs.target       loaded active active Remote File Systems
  slices.target          loaded active active Slices
  sockets.target         loaded active active Sockets
  sound.target           loaded active active Sound Card
  swap.target            loaded active active Swap
  sysinit.target         loaded active active System Initialization
  time-set.target        loaded active active System Time Set
  time-sync.target       loaded active active System Time Synchronized
  timers.target          loaded active active Timers

 

마찬가지로, 아래와 같이 등록된 service 들도 조회할 수 있다.

systemctl list-units --type=service


UNIT                           LOAD   ACTIVE SUB     DESCRIPTION                                           >
accounts-daemon.service     loaded active running Accounts Service                                       >
apparmor.service            loaded active exited  Load AppArmor profiles                                 >
apport.service              loaded active exited  LSB: automatic crash report generation                 >
atd.service                 loaded active running Deferred execution scheduler                           >
blk-availability.service    loaded active exited  Availability of block devices                          >
cloud-config.service        loaded active exited  Apply the settings specified in cloud-config           >
cloud-final.service         loaded active exited  Execute cloud user/final scripts                       >
cloud-init-local.service    loaded active exited  Initial cloud-init job (pre-networking)                >
cloud-init.service          loaded active exited  Initial cloud-init job (metadata service crawler)      >
console-setup.service       loaded active exited  Set console font and keymap
...

 

[Service]

Type

유닛 타입을 선언한다.

  • simple (기본값): 유닛이 시작된 경우 즉시 systemd 는 유닛의 시작이 완료되었다고 판단한다. 다른 유닛과 통신하기 위해 소켓을 사용하는 경우 이러한 설정을 사용하면 안된다.
  • forking: 자식 프로세스를 생성이 완료되는 단계까지를 systemd 가 시작이 완료되었다고 판단하게 된다. 부모 프로세스를 추적할 수 있도록 PIDFile= 필드에 PID 파일을 선언해 주어야 한다.
  • oneshot: simple 과 다소 유사하지만 단일 작업을 수행하는데 적합한 타입이다. 또한 실행 이후 해당 실행이 종료되더라도 RemainAfterExit=yes 옵션을 통해 유닛이 활성화 상태로 간주할 수 있다.
  • notify: simple 과 동일하다. 다만 유닛이 구동되면 systemd 에 시그널을 보낸다. 이때 시그널에 대한 내용은  libsystemd-daemon.so 에 선언 되어 있다.
  • dbus: DBUS 에 지정된 BusName 이 준비될때까지 대기한다. 즉 DBUS 준비가 완료된 이후 유닛이 시작되었다고 간주한다.

 

User

해당 서비스를 등록할 유닉스 유저이다. root 유저로 등록하겠다면 root로 명시하면 된다. 포스팅의 예제에서는 ubuntu os 의 기본 유저인 ubuntu를 사용했다. aws linux 2 를 썼다면 기본 유저가 ec2-user 였을 것이다.

 

Group

유저의 그룹을 명시한다. 사실상 유닉스에서 그룹은 권한과 직결되어 있기 때문에, 서비스를 수행할 권한으로 이해해도 될 듯 하다.

 

RemainAfterExit [yes|no]
유닛이 종료 이후에도 유닛이 활성화 상태로 판단한다.
 
GuessMainPID [yes|no]
이 옵션은 Type=forking 가 설정되어 있고 PIDFile= 설정이 되어 있지 않은 경우에 작동한다. systemd 가 유닛이 정상적으로 시작되었는지 판단이 명확하지 않는 경우 사용된다. 만일 여러개의 데몬으로 구성된 유닛의 경우 잘못된 PID 추측이 발생할 수 있다. 그로인해 오류 검출이나 자동 재시작 등의 작업이 불가능 할 수 있다. 이 옵션은 이러한 문제를 방지하는 기능을 한다.
 
PIDFile
PID 파일을 지정한다. 만일 유닛 타입이 forking 이라면 해당 설정을 추가해 주어야 한다. (절대 경로 사용)
 
BusName
D-Bus 의 버스 이름을 지정한다. Type=dbus 인 경우 필수 사항이다. 다른 Type 의 경우라도 D-Bus 버스 이름을 다르게 사용하는 경우 별도로 설정을 해주는 것이 좋다.

 

Environment
해당 유닛 에서 사용할 환경 변수를 선언한다. 또한 반드시 “Exec*=” 옵션보다 상단에 위치해야 한다.
ex) Environment="ONE=one" 'TWO=two two'
 
EnvironmentFile
해당 유닛에서 사용할 환경 변수 파일을 선언한다. 환경 변수 파일에서 "#' 와 ";" 로 시작되는 라인은 주석으로 처리된다. "Environment=" 와 같이 사용하는 경우 "Environment=" 옵션값이 먹게 된다. 또한 반드시 “Exec*=” 옵션보다 상단에 위치해야 한다.
 
RuntimeDirectory
서비스에 쓰기가 가능한 runtime 디렉토리를 할당하고, 명시한 user/group 의 소유가 되며 unit file이 삭제되면 해당 디렉토리도 자동으로 삭제된다.

 

WorkingDirectory

앱이 구동후 작업이 실행되는 영역의 위치, path

별도의 지정이 없으면 / 디렉토리를 작업 디렉토리로 사용한다.

 

ExecStart

systemctl start 시 실행할 구문으로, 이 때 uwsgi, gunicorn 처럼 설치된 명령어를 직접 사용하는 경우 설치된 절대 경로를 필요로 하기 때문에 주의해야 한다.

다중 명령어를 지원한다.

ex) ExecStart = command1; commnad2; commnad3

 

ExecReload

systemctl reload 시 실행할 구문으로, ExecStart와 마찬가지로 절대 경로를 필요로 한다.

 

ExecStop

systemctl stop 시 실행할 구문이다. 중지 방식은 KilllMode 로 지정한다.

 

RestartSec
재시작 명령을 수행할때 중지 이후 다시 시작하는데 대기(sleep)하는 시간을 설정한다. 기본값은 100ms 이다. 각각 min, s, ms 단위로 설정한다. 해당 설정은 Restart= 옵션이 있는 경우에만 적용된다.

TimeoutStartSec
유닛이 시작하는데 대기하는 시간을 설정한다. 기본값은 90초(90s)이다. 만일 Type=oneshot 인 경우 해당 설정이 해당 설정이 적용되지 않는다. 만일 시작 시간을 대기하지 않고 무한정 리턴값을 기다리게 설정할려면 TimeoutStartSec=0 으로 설정해 주면 된다.

TimeoutStopSec
옵션을 중지하는데 대기하는 시간을 설정한다. 기본값은 90초(90s)로 위의 TimeoutStartSec= 옵션과 동일하게 TimeoutStopSec=0 으로 설정하면 무한정 리턴값을 기다리게 된다. TimeoutStopSec 옵션에 설정된 값 안에 종료되지 않으면 SIGKILL 시그널을 보내서 강제로 종료하게 된다.

TimeoutSec
TimeoutStartSec 와 TimeoutStopSec 을 동시에 설정한다.

WatchdogSec
유닛이 시작된 이후 유닛 상태 감시(keep-alive ping)할때의 상태 값을 리턴하는데 대기하는 시간을 설정한다. 

Restart 옵션이 on-failure, always 인 경우 유닛을 자동으로 재시작하게되고 이때 WatchdogSec 설정을 해주어야 한다. 기본값은 "0" 으로 유닛 상태 감시를 사용하지 않는다.



Restart
유닛이 죽었을때나 혹은 WatchdogSec 만큼의 시간 동안 응답이 없는 경우 재시작한다.

  • no (기본값): 유닛을 다시 시작하지 않는다.
  • on-success: 는 유닛이 정상적으로 종료되었을 때만 재시작한다. 종료시에 0 을 리턴하여 종료되었거나 SIGHUP, SIGINT, SIGTERM, SIGPIPE 등과 같은 시그널 또는 SuccessExitStatus 설정에서 지정된 리턴 코드 목록에 따른 시그널에 대해서 모두 성공으로 인식해 재시작을 하게 된다.
  • on-failure: 유닛이 비정상적으로 종료되었을때 재시작한다. 리턴값이 "0" 이 아닌 경우, core dump 와 같이 비정상적인 시그널을 받고 종료된 경우, 타임 아웃값내 응답이 없는 경우 등일때 재시작 하게 된다.
  • on-watchdog: WatchdogSec 에 설정된 시간내 응답이 없는 경우에만 재시작 한다.
  • on-abort: 지정되지 않은 리턴값을 받은 경우 재시작을 한다.
  • always: 종료 상태 등과 무관하게 무조건 재시작한다.

여담이지만, Restart 옵션은 잘 생각해서 줘야 한다. ec2 내부에서 vpn 서비스 설정을 잘못 주어 인스턴스 접속이 불가했던 적이 있는데, Restart=always 로 옵션을 줘버려서 인스턴스를 재시작 해도 접속을 할 수가 없었다...

 

[Install]

 

Alias
유닛의 별칭을 지정한다. systemctl enable 명령어를 통해서 별칭으로 생성할 수 있다. 별칭은 유닛 파일 확장자(유닛 타입)를 가지고 있어야 한다.
ex) httpd.service 의 Alias=apache.service

WantedBy, RequiredBy
systemctl enable 명령어로 유닛을 등록할때 등록에 필요한 유닛을 지정한다. 해당 유닛을 등록하기위한 종속성 검사 단계로 보면 된다. 따라서 해당 설정은 [Unit] 섹션의 Wants 와 Requires 옵션과 관계 있다.

Also
systemctl enable, systemctl disable 로 유닛을 등록하거나 해제할때 다른 유닛 또한 같이 등록, 해제를 하도록 구성할 수 있다.

 

References

https://www.freedesktop.org/software/systemd/man/systemd.service.html

 

systemd.service

Similarly to the oneshot services, there are sometimes units that need to execute a program to set up something and then execute another to shut it down, but no process remains active while they are considered "started". Network configuration can sometimes

www.freedesktop.org

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=forioso&logNo=220890354096 

 

[systemctl] 시스템서비스

systemctl 은  /etc/systemd/system/ 폴더에 서비스 설정파일이 생성된다. systemctl daemon-reload s...

blog.naver.com

https://fmd1225.tistory.com/93

 

systemd unit 등록 관련 옵션 정리

- RHEL 7 의 OS 적인 가장 큰 변화는 3.x 커널을 사용한다는 점(물론 2.6.x 커널에서 큰 차이가 있는 것은 아니다.) 그리고 정통적인 init 데몬에서 systemd 데몬으로 변경이 되었다는 점이다. systemd 에 대

fmd1225.tistory.com

 

+ Recent posts