서버 관리 Part 4: 서버 배포

iFun Engine으로 작성한 게임 서버는 서비스 설정을 지정하고, 이를 패키지로 묶어서 개별 서버에 설치/업그레이드 할 수 있습니다. 이 문서에서는 그 과정을 설명합니다. 현재 지원하는 운영체제는 아래와 같습니다. * Ubuntu 16.04 64bit(Xenial) * Ubuntu 18.04 64bit(Bionic) * Ubuntu 20.04 64bit(Focal) * CentOS 7 64bit * CentOS 8 64bit

Important

Ubutu 14.04 LTS (Trusty Tahr) 에 대한 지원은 1.0.0-3986 Stable 버전을 끝으로 종료되었습니다.

Note

아이펀 엔진 윈도우 버전의 배포 방법은 프로젝트 배포하기 을 참고해주세요.

서비스 설정하기

Note

Ubuntu 16.04 버전과 CentOS 7 버전부터는 운영체제가 서비스를 관리하기 위해 systemd 를 공통적으로 사용합니다.

Systemd

프로젝트 소스 디렉터리의 etc/systemd/{{project-name}}.service 파일을 패키지 생성 시에 포함해서 배포합니다.

설정 파일 내용과 관련한 세부 사항은 Systemd 페이지를 확인해주세요.

Note

Flavor: 역할에 따라 서버 구분하기 에 언급된 flavor 를 사용할 경우, 파일 이름은 etc/systemd/{{project-name}}.{{flavor}}.service 입니다.

예제: 서비스를 실행할 유저 변경하기

보안 상의 이유로, root 대신에 다른 유저를 생성하고, 해당 유저 계정으로 서비스를 실행하는 것을 권장합니다.

아래는 centos 라는 유저와 그룹으로 서비스를 실행하게끔 수정한 예시입니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[Service]
LimitNOFILE=999999

# It is strongly recommended that one should use uid:gid other than the root
# NOTE: Please update User=, Group=, and ExecStartPre=
User=centos
Group=centos

# Makes the daemon automatically restart
Type=simple
Restart=on-failure
RestartSec=5s

# create directories, and change permissions (with root privileges)
PermissionsStartOnly=true
ExecStartPre=/usr/bin/mkdir -p /var/log/funapi/example/glog \
  /var/log/funapi/example/activity \
  /var/crash/funapi/example
# NOTE: change root:root to service's uid:gid
ExecStartPre=/usr/bin/chown -R centos:centos /var/log/funapi/example/glog \
  /var/log/funapi/example/activity \
  /var/crash/funapi/example

ExecStart=/usr/bin/example-launcher

패키지 생성

패키지 생성에 대해서는 서버 관리 Part 3: 서버 패키징 을 참고해주세요.

패키지 설치/업그레이드

Ubuntu 16.04 또는 18.04 또는 20.04

서버 관리 Part 3: 서버 패키징 에서 설명된 대로 deb 파일을 생성합니다. 이 파일을 서버에 복사한 후 dpkg 명령으로 설치합니다.

가령 example 이라는 이름의 게임인 경우 다음과 같습니다.

$ sudo dpkg -i example_0.0.1_install.deb
[sudo] password for ubuntu:
Selecting previously unselected package example.
(Reading database ... 424079 files and directories currently installed.)
Preparing to unpack example_0.0.1_install.deb ...
Unpacking example (0.0.1) ...
Setting up example (0.0.1) ...
Processing triggers for ureadahead (0.100.0-16) ...
ureadahead will be reprofiled on next reboot

Important

만약 funapi1-runtime 이 설치되지 않은 경우 패키지 설치 오류 메시지가 나옵니다. 이런 경우에는 다음 명령을 실행하면 자동으로 필요한 패키지와 서버 패키지가 설치됩니다.

$ sudo apt-get install -f

Centos 7 또는 Centos 8

서버 관리 Part 3: 서버 패키징 에서 설명된 대로 rpm 파일을 생성합니다. 이 파일을 서버에 복사한 후 rpm 명령으로 설치합니다.

가령 example 이라는 이름의 게임인 경우 다음과 같습니다.

$ sudo rpm -i example_0.0.1_install.rpm

Important

만약 funapi1-runtime 이 설치되지 않은 경우 패키지 설치 오류 메시지가 나옵니다. 이런 경우에는 다음 명령을 실행해서 funapi1-runtime 을 먼저 설치합니다.

$ sudo yum install funapi1-runtime

서비스 시작/종료하기

Note

Ubuntu 16.04 버전과 CentOS 7 버전부터는 운영체제가 서비스를 관리하기 위해 systemd 를 공통적으로 사용합니다.

Systemd

systemctl 명령을 이용해서 서비스를 활성화 하고 시작, 종료할 수 있습니다.

가령 example 이라는 서버를 설치한 경우 다음과 같이 실행합니다.

서비스 활성화

서버 재시작 할 때 게임 서비스가 자동으로 뜨게 하기 위해서는 다음 명령으로 서비스를 활성화 합니다.

$ sudo systemctl enable example

서비스 시작

$ sudo systemctl start example

서비스 종료

$ sudo systemctl stop example

서비스 인스턴스 여러개 실행하기

하나의 호스트에 같은 서버 프로세스를 여러 개 띄우고자 할 때에는 서비스 유닛 파일을 수정해서 systemctl 명령을 실행할 때, 실행 인자로 구별자를 전달할 수 있습니다.

앞에서 설치한 exmaple 라는 서비스를 01, 02 라는 인스턴스로 구별해서 실행하는 방법을 순서에 따라 설명하겠습니다.

Note

아이펀 엔진 프로젝트인 exmaple 패키지가 설치되어 있다고 가정하겠습니다.

설명에서 예시로 사용하는 example 은 프로젝트명으로 Flavor 를 사용하는 경우에는 다른 형식으로 표현됩니다. 01, 02 프로세스를 구별하기 위한 구별자 예시로 사용자 여러분이 원하는 이름으로 변경할 수 있습니다.

준비하기

먼저, 서비스 실행과 관련한 파일들이 실행 인자를 처리할 수 있도록 변경해야 합니다.

패키지를 설치할 때 생성된 원본 파일들은 보존하기 위해서 복사해서 사용하겠습니다.

# 실행 인자를 처리할 수 있는 서비스 유닛 파일 생성
$ sudo cp /lib/systemd/system/example.service /lib/systemd/system/example@.service

# 서버 런처 스크립트 생성
$ sudo cp /usr/bin/example-launcher /usr/bin/example-launcher.tmpl

# 개별 서버 프로세스에 적용될 MANIFST 파일을 비어있는 상태로 생성
$ sudo mkdir /etc/example
$ sudo touch /etc/example/MANIFEST-01.override.json
$ sudo touch /etc/example/MANIFEST-02.override.json

서비스 유닛 파일 수정

앞서 생성한 /lib/systemd/system/example@.service 파일을 열어서 다음과 같이 수정해 주세요.

%i 문자열이 서비스를 실행할 때 전달하는 인스턴스 구분자로 치환됩니다.

# Systemd service configuration for example template
[Unit]
Description=iFun Engine game - example %i
After=syslog.target network-online.target

... 중략 ...

# create directories, and change permissions (with root privileges)
PermissionsStartOnly=true
ExecStartPre=/usr/bin/mkdir -p /var/log/funapi/example-%i/glog \
  /var/log/funapi/example-%i/activity \
  /var/log/funapi/example-%i/counter \
  /var/crash/funapi/example
# NOTE: change root:root to service's uid:gid
ExecStartPre=/usr/bin/chown -R root:root /var/log/funapi/example-%i/glog \
  /var/log/funapi/example-%i/activity \
  /var/log/funapi/example-%i/counter \
  /var/crash/funapi/example

EnvironmentFile=/etc/default/example
ExecStart=/bin/bash -e /usr/bin/example-launcher.tmpl %i

서버 런처 파일 수정

마찬가지로 /usr/bin/example-launcher.tmpl 파일을 열어서 다음과 같이 수정해 주세요.

런처 스크립트의 실행 인자($1)로 인스턴스 구분자를 전달 받아서 사용합니다.

#!/bin/bash -e
# vim: fileencoding=utf-8 tabstop=2 softtabstop=2 shiftwidth=2 expandtab

... 중략 ...

# You can override by using 'export VAR="value"' before running this script.
# If you are running the script through upstart or systemd, set the variable
#   in /etc/default/<your_game_name>.
################################################################################
# consume instance name from command line
INSTANCE=$1
shift

... 중략 ...

################################################################################
# YOUR GAME LOCATIONS.
################################################################################
debugging_symbol_dir="$GAME_ROOT_DIR/usr/share/example/default/symbols"
resource_root_dir="$GAME_ROOT_DIR/usr/share/example/default/resources"
component_library_dir="$GAME_ROOT_DIR/usr/lib/example/default"
component_manifest_dir="$GAME_ROOT_DIR/usr/share/example/default/manifests"
component_override_file="${GAME_MANIFEST_OVERRIDE:-/etc/example/MANIFEST-${INSTANCE}.override.json}"
log_root_dir="${GAME_LOG_ROOT_DIR:-/var/log/funapi/example-${INSTANCE}}"
crashdump_root_dir="${GAME_CRASHDUMP_ROOT_DIR:-/var/crash/funapi/example}"
glog_options="${GAME_GLOG_OPTIONS:- --max_log_size=10 --stop_logging_if_full_disk}"
contact_email_address="${GAME_CONTACT_EMAIL_ADDRESS:-}"


################################################################################
# Sources the implementation to run the executable.
################################################################################
source ${FUNAPI_DATA_DIR}/bash/launcher-common.sh

설정 오버라이드 하기

서버 프로세스들은 서비스에 사용할 포트 번호가 중복되면 안되기 때문에 서비스 각자 MANIFEST override 파일을 생성해서 개별적으로 설정해야 합니다.

미리 생성해 둔 /etc/example/MANIFEST-01.override.json, /etc/example/MANIFEST-02.override.json 파일을 열어서 아래와 같이 각자 포트가 중복되지 않도록 설정합니다.

기본 프로젝트에서 사용하는 포트들만 변경했으며, RPC 기능 등을 사용한다면 관련 포트들도 모두 설정해야 합니다. 자세한 내용은 MANIFEST.json 오버라이드하기 를 참고해 주시기 바랍니다.

// /etc/example/MANIFEST-01.override.json

{
  "override": {
    "ApiService": {
      "api_service_port": 8014
    },
    "SessionService": {
      "tcp_json_port": 8012,
      "http_json_port": 8018
    }
  }
}

// /etc/example/MANIFEST-02.override.json

{
  "override": {
    "ApiService": {
      "api_service_port": 9014
    },
    "SessionService": {
      "tcp_json_port": 9012,
      "http_json_port": 9018
    }
  }
}

서비스 실행하기

이제 example@{인스턴스명} 과 같은 형식으로 각 서비스를 구별할 수 있습니다.

서비스를 실행하기에 앞서 다음 명령을 실행해서 서비스를 활성화합니다.

sudo systemctl daemon-reload
sudo systemctl enable hello@01
sudo systemctl enable hello@02

자, 이제 서비스를 실행해 보겠습니다.

sudo systemctl start hello@01
sudo systemctl start hello@02

서비스 실행 로그 보기

Ubuntu 16.04 버전과 CentOS 7 보다 이후에 출시된 리눅스 배포판들은 운영체제에서 실행되는 서비스를 관리하는 데에 systemd 를 공통적으로 사용합니다.

systemd 는 서버가 실행되거나 종료될 때 별도의 로그를 생성하는데, 이 로그를 통해서 서비스가 원활히 실행되고 있는지 확인할 수 있습니다.

Note

게임 서버 자체의 로그는 프로그래밍 Part1: 디버그용 로그 을 참고해주세요.

Note

게임 서버의 coredump 에 대해서는 디버깅 및 프로파일링 지원 을 참고해주세요.

Systemd

systemd 로 실행한 경우, 서비스 로그는 journalctl -x -u {{project-name}} 명령으로 확인할 수 있습니다.

만일 example 이라는 서버의 경우 다음과 같이 로그를 볼 수 있습니다.

$ sudo journalctl -x -u example

패키지 버전 고정하기

라이브 환경에서는 사용하는 funapi1-runtime 나 에이전트, 혹은 개발하는데 사용한 외부 라이브러리의 버전을 고정해야하는 경우가 있습니다. 아래와 같은 방법을 이용하시면 됩니다.

Ubuntu 환경에서 버전 고정하기

버전 고정:

funapi1-runtime 의 버전을 고정한다고 하면, 다음과 같이 명령을 입력하면 됩니다.

$ sudo apt-mark hold funapi1-runtime

해당 명령 후에는 apt-get upgrade 등에서 해당 패키지를 제외하고 업데이트하게 됩니다.

버전이 고정된 패키지 확인:

$ sudo apt-mark showhold
funapi1-runtime

위에서 입력한 패키지가 버전이 고정된 것을 확인하실 수 있습니다.

버전 고정 해제:

다음 명령으로 패키지 버전이 고정된 것을 풀 수 있습니다.

$ sudo apt-mark unhold funapi1-runtime

이 이후에는 패키지를 업그레이드할 수 있습니다.

CentOS 환경에서 버전 고정하기

CentOS 환경에서 버전을 고정하려면 추가 패키지가 필요합니다. 다음 명령으로 설치해주시면 됩니다.

$ sudo yum install yum-versionlock

yum-versionlock 패키지를 설치한 후에 아래 명령들을 실행할 수 있습니다.

버전 고정:

funapi1-runtime 의 버전을 고정한다고 하면, 다음과 같이 명령을 입력하면 됩니다.

$ sudo yum versionlock funapi1-runtime

해당 명령 후에는 yum update 등에서 해당 패키지를 제외하고 업데이트하게 됩니다.

버전이 고정된 패키지 확인:

$ sudo yum -q versionlock list
0:funapi1-runtime-1.0.0-1680centos7.*

위에서 입력한 패키지가 버전이 고정된 것을 확인하실 수 있습니다.

버전 고정 해제:

다음 명령으로 패키지 버전이 고정된 것을 풀 수 있습니다.

$ sudo yum versionlock delete funapi1-runtime

이 이후에는 패키지를 업그레이드할 수 있습니다.

Important

CentOS 6에서는 sudo yum versionlock delete '*:funapi1-runtime*' 처럼 입력하셔야 합니다.

비표준 위치에 게임 서버 설치하기

비표준 위치에 게임 서버 패키지 설치하기

앞에서는 DEBRPM 형태의 파일을 만들어서 배포하는 것을 설명했습니다. 이런 패키지 파일은 미리 정해진 위치에 게임 서버를 설치하고, root 권한도 필요로 합니다.

그러나 경우에 따라서는 root 권한을 취득할 수 없는 경우도 있을 수 있고, 특정 디렉토리 아래 게임 서버 내용을 모두 설치해야될 수도 있습니다. 예를 들어, 퍼블리셔에게 게임 서버를 전달하면 퍼블리셔가 사용하는 Linux 계정 아래 게임 패키지가 그대로 설치되길 바라는 경우를 생각해볼 수 있습니다. 이런 경우는 설치 위치를 강제하는 DEBRPM 으로는 곤란하고 tar.gz 로 패키지를 생성해서 퍼블리셔에게 전달하고 퍼블리셔가 원하는 위치에 압축을 풀게 하는 것이 좋습니다. (tar.gz 의 생성에 관련해서는 생성 가능한 패키지 타입 을 참고하세요.)

편의상 우리가 만드는 게임의 이름을 hello 라고 하고, lobby flavor 를 사용했다고 하겠습니다. 그러면 생성되는 패키지는 hello_0.0.1_install-lobby.tar.gz 가 될 것입니다. 이제 퍼블리셔가 관리하는 리눅스 서버의 계정이 zeus 라고 가정했을 때, 퍼블리셔는 저 패키지를 /home/zeus 아래에서 풀었을 것입니다.

$ cd /home/zeus
$ tar zxf hello_0.0.1_install-lobby.tar.gz

이렇게 하면 /home/zeus 아래에 DEBRPM 으로 설치했을 때 / 디렉토리 아래 설치되었을 내용들이 그대로 복사된 것을 확인하실 수 있을 겁니다.

비표준 위치에 게임 서버 패키지 설치하고 서비스로 띄우기

그런데, 이 방법은 파일은 복사하지만, upstartsystemd 처럼 게임 서버를 서비스로 (즉, 데몬으로) 실행시키는 것을 자동으로 해줄 수는 없습니다. 따라서 이런 설정은 수작업으로 해줘야됩니다.

또한 서비스를 등록하더라도 / 가 아니라 우리가 지정한 /home/zeus 에서 게임 서버를 찾을 수 있게끔 경로를 지정하는 작업도 필요합니다. 이 작업들은 게임 서버 OS를 세팅할 때 처음 1회만 하면 됩니다.

아래는 Upstart 와 Systemd 의 경우에 대해서 서비스를 등록하는 방법을 설명합니다. 두 가지 방법 모두, 환경 변수를 지정해주고 스크립트를 등록해주는 작업을 하게 됩니다.

서비스 설정 파일에 지정 가능한 환경 변수

  • GAME_ROOT_DIR: 게임 서버가 설치된 경로를 지정합니다. 지정하지 않으면 / 가 기본값으로 사용됩니다.

  • GAME_MANIFEST_OVERRIDE: MANIFEST.json 오버라이드하기 에 기술된 MANIFEST.json 오버라이드 파일 위치를 지정합니다. 지정하지 않으면 /etc/{{project}}/MANIFEST.override.json 으로 기본 지정됩니다.

  • GAME_LOG_ROOT_DIR: 로그 파일들이 생성될 디렉토리를 지정합니다. 지정하지 않으면 기본값으로 /var/log/funapi/게임이름 이 사용됩니다.

  • GAME_CRASHDUMP_ROOT_DIR: 코어덤프 디렉토리를 지정합니다. 지정하지 않으면 기본값으로 /var/crash/funapi/게임이름 이 사용됩니다.

  • GAME_GLOG_OPTIONS: Google logging 의 추가 옵션을 지정합니다. 지정하지 않으면 기본값으로 –max_log_size=10 –stop_logging_if_full_disk 가 사용됩니다.

  • GAME_CONTACT_EMAIL_ADDRESS: 게임 서버가 죽는 경우 알림을 받을 이메일 주소를 지정합니다. 지정하지 않으면 이메일을 보내지 않습니다.

Tip

GAME_CONTACT_EMAIL_ADDRESS 를 세팅하면 게임 서버가 죽을 때마다 알림 메일을 받을 수 있어서 편리합니다.

systemd 를 사용하는 경우 수작업으로 서비스 등록하기

비표준 위치에 설치한 게임 서버를 서비스로 등록하려면 패키지 생성 전 작업이 필요합니다.

먼저 소스 디렉터리로 이동한 후 CMakeLists.txt 에 주석처리 되어 있는 set(WANT_SYSTEMD false) 의 주석을 풀고 true 로 설정해 줍니다.

set(WANT_SYSTEMD true)

그리고 etc/systemd/hello.lobby 파일에 systemd 가 우리가 게임을 설치한 /home/zeus 에서 프로그램을 찾게끔 다음처럼 써줍니다. 서비스 설정 파일에 지정 가능한 환경 변수 에 나열된 다른 변수들도 개행하여 여러개 쓸 수 있습니다.

GAME_ROOT_DIR=/home/zeus

Important

export 를 쓰지 않습니다.

그 후 etc/systemd/hello.lobby.service 파일을 열어서 2 개의 ExecStartPre 와 맨 아래줄에 있는 ExecStart 의 경로의 /usr 앞에 게임을 설치할 home/zeus 를 추가해 줍니다.

ExecStartPre=/bin/mkdir -p /home/zeus/var/log/funapi/example/glog \
  /home/zeus/var/log/funapi/example/activity \
  /home/zeus/var/log/funapi/example/counter \
  /home/zeus/var/crash/funapi/example
ExecStartPre=/bin/chown -R root:root /home/zeus/var/log/funapi/example/glog \
  /home/zeus/var/log/funapi/example/activity \
  /home/zeus/var/log/funapi/example/counter \
  /home/zeus/var/crash/funapi/example

...

ExecStart=/home/zeus/usr/bin/hello.lobby-launcher

마지막으로 예제: 서비스를 실행할 유저 변경하기 의 설명에 따라 서버를 실행할 유저를 변경합니다.

서비스 등록을 위한 사전 작업을 완료했습니다. TGZ 로 패키징 합니다.

서버를 설치할 머신에서 패키징 한 파일의 압축을 풀면 etc/default/lib/systemd/system/ 이라는 디렉터리가 있을 것입니다. 이 디렉터리들 아래에는 각각 hello.lobby 파일과 hello.lobby.service 라는 systemd 서비스 설정 파일이 존재할 것입니다.

$ ls etc/default
hello.lobby

$ ls lib/systemd/system
hello.lobby.service

이 파일들을 다음처럼 복사합니다.

$ sudo cp etc/default/hello.lobby /etc/default/
$ sudo cp lib/systemd/system/hello.lobby.service /lib/systemd/system/

이제 다음처럼 게임 서버를 실행할 수 있습니다.

$ sudo systemctl start hello.lobby