7.07.2013

Setup Shogi-server on AWS with Fabric

AWS: shogi-server を Fabric で構築する

目的

コンピュータ将棋のネット対局用サーバ shogi-server を AWS の EC2 インスタンス上に構築する。

このとき、全ての設定作業を Fabric で完結させたい。

 

前提

 

準備するもの

 

iptables

shogi-server (CSAプロトコル) で使う 4081 ポートを許可、ロギング設定など。

iptables
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 4081 -j ACCEPT
-N LOGGING
-A INPUT -j LOGGING
-A LOGGING -j LOG --log-level warning --log-prefix "DROP:" -m limit
-A LOGGING -j DROP
COMMIT

 

sudoers

sudo の際に環境変数 PATH を引き継ぐようにしないと、なぜか Ruby のインストールに失敗してしまう。
env_keep に PATH を追記した sudoers ファイルをあらかじめ用意しておく。

sudoers (オリジナルとの差分)
# diff -u ./sudoers.20130707 ./sudoers
--- ./sudoers.20130707  2013-05-10 03:53:29.636195742 +0900
+++ ./sudoers   2013-07-07 11:47:48.647961526 +0900
@@ -76,6 +76,7 @@
 Defaults    env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
 Defaults    env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
 Defaults    env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"
+Defaults    env_keep += "PATH"
  
 #
 # Adding HOME to env_keep may enable a user to run unrestricted

 

起動スクリプト

shogi-server をサービスとして登録し、起動/停止を行うためのスクリプト。
/etc/init.d/shogi-server として配備。

PIDファイルを /var/run、ログを /var/log/shogi-server 配下に作成している。

サービスは専用ユーザ shogi で実行される。

shogi-server.init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/bin/bash
#
# Start/Stop shogi-server daemon
#
# chkconfig: 2345 85 15
# description: shogi-server daemon mode
 
PROC_NAME=shogi-server
PROC_USER=shogi
PROC_GROUP=shogi
PROC_BIN=/opt/shogi-server/shogi-server
PID_FILE=/var/run/shogi-server.pid
DAEMON_LOG_DIR=/var/log/shogi-server
PLAYER_LOG_DIR=$DAEMON_LOG_DIR/player-logs
CONSOLE_LOG=$DAEMON_LOG_DIR/console.log
 
EVENT_NAME=event1
PORT_NUMBER=4081
 
# Sanity checks
[ -x $PROC_BIN ] || exit 1
 
# Source function library & LSB routines
. /etc/rc.d/init.d/functions
 
RETVAL=0
 
start()
{
    echo -n $"Starting $PROC_NAME: "
 
    mkdir -p $DAEMON_LOG_DIR $PLAYER_LOG_DIR
    touch $PID_FILE
    chown $PROC_USER:$PROC_GROUP $DAEMON_LOG_DIR $PLAYER_LOG_DIR $PID_FILE
 
    su - $PROC_USER -c \
        "ruby $PROC_BIN --daemon $DAEMON_LOG_DIR --pid-file $PID_FILE \
        --player-log-dir $PLAYER_LOG_DIR $EVENT_NAME $PORT_NUMBER \
        > $CONSOLE_LOG 2>&1"
    RETVAL=$?
    if [ $RETVAL -eq 0 ]; then
        success
    else
        rm -f $PID_FILE
        failure
    fi
    echo
}
 
stop()
{
    echo -n $"Stopping $PROC_NAME:"
    if [ -f $PID_FILE ]; then
        killproc -p $PID_FILE $PROC_NAME -TERM
        rm -f $PID_FILE
    fi
    RETVAL=$?
    [ $RETVAL -eq 0 ] && success || failure
    echo
}
 
case "$1" in
    start)
        start
        RETVAL=$?
        ;;
    stop)
        stop
        RETVAL=$?
        ;;
    status)
        status -p $PID_FILE $PROC_BIN
        RETVAL=$?
        ;;
    restart)
        stop
        start
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart}"
        RETVAL=2
        ;;
esac
 
exit $RETVAL

 

logrotate 定義ファイル

ログファイルの洗い替えは logrotate を利用する。
/etc/logrotate.d/shogi-server として配備。

shogi-server.logrotate
1
2
3
4
5
6
7
8
9
/var/log/shogi-server/shogi-server.log
/var/log/shogi-server/player-logs/*.log {
    weekly
    rotate 4
    missingok
    notifempty
    copytruncate
    compress
}

 

Fabric 用スクリプト

Fabric で使う Python スクリプト。
env.hosts はハードコーディングして使ってもよい。

標準APIの put 関数にも sudo 機能があるものの、SELinux 対応やバックアップ作成などが物足りなかったので
独自の _put という関数でラップしている。

EC2 のファイアウォール設定もこのスクリプトに含めているが、分離させた方がいいかもしれない。

shogi-server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# -*- coding: utf-8 -*-
"""
Installing shogi-server and its dependencies.
"""
 
import os
import json
from fabric.api import *
from fabric.decorators import runs_once, roles, task
 
env.user = 'ec2-user'
# env.hosts = []
env.use_ssh_config = True
 
WORK_DIR = os.path.dirname(os.path.abspath(__file__))
 
APL_USER = 'shogi'
APL_GROUP = 'shogi'
APL_HOME = '/home/%s' % APL_USER
 
GIT_SHOGI_SERVER = \
 
RUBY_VERSION = '1.9.3-p448'
 
EC2_SECURITY_GROUP = 'quick-start-1'
 
 
@task
def setup():
    """Setup tasks for shogi-server."""
 
    setup_os_user()
    setup_firewall()
    install_ruby()
    install_shogi_server()
 
 
@task
def setup_os_user():
    """Add 'shogi' user and 'shogi' group."""
 
    if sudo('id %s' % APL_USER, quiet=True).succeeded:
        return
 
    # Add user and group.
    sudo('groupadd -g 501 %s' % APL_GROUP)
    sudo('useradd -u 501 -g %s %s' % (APL_GROUP, APL_USER))
 
    # Copy sudoers.
    _put('%s/sudoers' % WORK_DIR, '/etc/sudoers', '440', 'root', 'root')
 
 
@task
def setup_firewall():
    """Configure linux and EC2 firewall.
        AWS CLI and its connection settings are required."""
 
    # Linux iptables
    _put('%s/iptables' % WORK_DIR, '/etc/sysconfig/iptables',
         '600', 'root', 'root')
    sudo('service iptables restart')
 
    # EC2 firewall
    port = 4081
    cidr = '0.0.0.0/0'
    old = local('aws ec2 describe-security-groups', capture=True)
    j = json.loads(old)
 
    found = (EC2_SECURITY_GROUP, port, cidr) in (
        (a['GroupName'], b['FromPort'], c['CidrIp'])
        for a in j['SecurityGroups']
        for b in a['IpPermissions']
        for c in b['IpRanges'])
 
    if not found:
        opts = '--group-name %s --ip-protocol tcp ' % EC2_SECURITY_GROUP
        opts += '--from-port %d --to-port %d --cidr-ip %s' % (port, port, cidr)
        local('aws ec2 authorize-security-group-ingress %s' % opts)
 
 
@task
def install_ruby():
    """Install git, rbenv, ruby-build and ruby."""
 
    # Install git.
    sudo('yum -y install git')
 
    # Install rbenv.
    with cd('/usr/local'):
        sudo('git clone %s %s' % (GIT_RBENV, 'rbenv'))
        sudo('mkdir rbenv/shims rbenv/versions')
 
    # Install ruby-build.
    with cd('/usr/local'):
        sudo('git clone %s %s' % (GIT_RUBY_BUILD, 'ruby-build'))
    with cd('/usr/local/ruby-build'):
        sudo('./install.sh')
 
    # Create system-level profile.
    profile = '/etc/profile.d/rbenv.sh'
    sudo("""echo 'export RBENV_ROOT="/usr/local/rbenv"' > %s""" % profile)
    sudo("""echo 'export PATH="/usr/local/rbenv/bin:$PATH"' >> %s""" % profile)
    sudo("""echo 'eval "$(rbenv init -)"' >> %s""" % profile)
 
    # Install requirements.
    sudo('yum -y groupinstall "Development Tools"')
    sudo('yum -y install openssl-devel readline-devel zlib-devel')
 
    # Install ruby.
    sudo('su - -c "rbenv install 1.9.3-p448"')
    sudo('su - -c "rbenv global 1.9.3-p448"')
    sudo('su - -c "rbenv rehash"')
 
 
@task
def install_shogi_server():
    """Install and start shogi-server as a service."""
 
    shogi_path = '/opt/shogi-server'
 
    if sudo('test -d %s' % shogi_path, quiet=True).succeeded:
        return
 
    # Clone from repository.
    sudo('git clone %s %s' % (GIT_SHOGI_SERVER, shogi_path))
    sudo('chown -R %s:%s %s' % (APL_USER, APL_GROUP, shogi_path))
 
    # Copy init script.
    _put('%s/shogi-server.init' % WORK_DIR, '/etc/init.d/shogi-server',
         '755', 'root', 'root')
 
    # Add and start service.
    sudo('chkconfig --add shogi-server')
    sudo('service shogi-server start')
 
    # Copy logrotate setting.
    _put('%s/shogi-server.logrotate' % WORK_DIR,
         '/etc/logrotate.d/shogi-server', '644', 'root', 'root')
 
 
def _put(src, dst, mode, owner, group):
    tmp = '/tmp/%s.tmp' % src.split('/')[-1]
    backup_opts = '--backup=simple --suffix=.`date +%Y%m%d`'
    permission_opts = '-m %s -o %s -g %s' % (mode, owner, group)
 
    put(local_path=src, remote_path=tmp)
    sudo('install %s %s %s %s' % (backup_opts, permission_opts, tmp, dst))
    sudo('rm -f %s' % tmp)

 

実行方法

fab コマンドで実行。Microインスタンスで20分弱で完了。

$ fab -f ./shogi-server.py --list
$ fab -f ./shogi-server.py -H 対象サーバ setup

0 件のコメント:

コメントを投稿