consul-template & supervisorでプロセスの可視化

こちらはフロムスクラッチ Advent Calendar 2017の9日目の記事です。

はじめに

ポプテピピック

もうすぐ、ポプテピピック始まりますね。
どうも、jkkitakitaです。

概要

掲題通り、consul + supervisordで
プロセス監視、管理に関して、可視化した話します。

きっかけ

どうしても、新規サービス構築や保守運用しはじめて
色々なバッチ処理等のdaemon・プロセスが数十個とかに増えてくると
↓のような悩みがでてくるのではないでしょうか。

  1. 一時的に、daemonをstopしたい
  2. daemonがゾンビになってて、再起動したい
  3. daemonが起動しなかった場合の、daemonのログを見る
  4. daemonが動いているのかどうか、ぱっとよくわからない。
  5. ぱっとわからないから、なんか不安。 :scream:

個人的には
5.は、結構感じます。笑
安心したいです。笑

ツールとその特徴・選定理由

簡単に本記事で取り扱うツールのバージョン・特徴と
今回ツールを選んだ選定理由を記載します。

ツール 特徴 選定理由
supervisor
v3.3.1
1. プロセス管理ツール
2. 2004年から使われており、他でよく使われているdaemon化ツール(upstart, systemd)と比較して、十分枯れている。
3. 柔軟な「プロセス管理」ができる。
4. APIを利用して、プロセスのstart/stop/restart…などが他から実行できる。
1.今までupstartを使っていたが、柔軟な「プロセス管理」ができなかったため。

※ upstartは「プロセス管理」よりかは、「起動設定」の印象。

consul
v1.0.1
1. サービスディスカバリ、ヘルスチェック、KVS etc…
2. その他特徴は、他の記事参照。
https://www.slideshare.net/ssuser07ce9c/consul-58146464
1. AutoScalingするサーバー・サービスの死活監視

2. 単純に使ってみたかった。(笑)

3. 本投稿のconsul-templateを利用に必要だったから(サービスディスカバリ)

consul-template
v0.19.4
1. サーバー上で、consul-templateのdaemonを起動して使用
2. consulから値を取得して、設定ファイルの書き換え等を行うためのサービス
ex.) AutoScalingGroupでスケールアウトされたwebサーバーのnginx.confの自動書き換え
1. ansibleのようなpush型の構成管理ツールだと、AutoScalingGroupを使った場合のサーバー内の設定ファイルの書き換えが難しい。

2. user-data/cloud-initを使えば実現できるが、コード/管理が煩雑になる。保守性が低い。

cesi
versionなし
1. supervisordのダッシュボードツール
2. supervisordで管理されているdaemonを画面から一限管理できる
3. 画面から、start/stop/restartができる
4. 簡易的なユーザー管理による権限制御ができる
1. とにかく画面がほしかった。

2. 自前でも作れるが、公式ドキュメントに載っていたから

3. 他にもいくつかOSSダッシュボードあったが、一番UIがすっきりしていたから。(笑)

実際にやってみた

上記ツールを使って
daemonを可視化するために必要な設定をしてみました。
本記事は、全て、ansibleを使って設定していて
基本的なroleは
ansible-galaxyで、juwaiさんのroleを
お借りしています。
https://galaxy.ansible.com/list#/roles?page=1&page_size=10&tags=amazon&users=juwai&autocomplete=consul

supervisor

クライアント側(実際に管理したいdaemonが起動するサーバー)

supervisord.conf
; Sample supervisor config file.
;
; For more information on the config file, please see:
; http://supervisord.org/configuration.html
;
; Notes:
;  - Shell expansion ("~" or "$HOME") is not supported.  Environment
;    variables can be expanded using this syntax: "%(ENV_HOME)s".
;  - Comments must have a leading space: "a=b ;comment" not "a=b;comment".

[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)
;chmod=0700                 ; socket file mode (default 0700)
;chown=nobody:nogroup       ; socket file uid:gid owner
;username=user              ; (default is no username (open server))
;password=123               ; (default is no password (open server))

[inet_http_server]         ; inet (TCP) server disabled by default
port=0.0.0.0:9001        ; (ip_address:port specifier, *:port for all iface)
username=hogehoge              ; (default is no username (open server))
password=fugafuga               ; (default is no password (open server))
;セキュリティ観点から、ここのportは絞る必要有。

[supervisord]
logfile=/tmp/supervisord.log        ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB               ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10                  ; (num of main logfile rotation backups;default 10)
loglevel=info                       ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid        ; (supervisord pidfile;default supervisord.pid)
nodaemon=false ; (start in foreground if true;default false)
minfds=1024                         ; (min. avail startup file descriptors;default 1024)
minprocs=200                        ; (min. avail process descriptors;default 200)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

[include]
files=/etc/supervisor.d/*.conf

/etc/supervisor.d/配下に
起動するdaemonを設定します。

daemon.conf
[group:daemon]
programs=<daemon-name>
priority=999

[program:<daemon-name>]
command=sudo -u ec2-user -i /bin/bash -c 'cd /opt/<service> && <実行コマンド>'
user=ec2-user
group=ec2-user
directory=/opt/<service>
autostart=true
autorestart=true
redirect_stdout=true
redirect_stderr=true
stopasgroup=true
stopsignal=QUIT
stdout_logfile=/var/log/<service>/daemon.stdout.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=10
stderr_logfile=/var/log/<service>/daemon.stderr.log
stderr_logfile_maxbytes=20MB
stderr_logfile_backups=10


[eventlistener:slack_notifier]
command=/usr/bin/process_state_event_listener.py
events=PROCESS_STATE
redirect_stderr=false
stopasgroup=true
stopsignal=QUIT
stdout_logfile=/var/log/<service>/event_listener.stdout.log
stdout_logfile_maxbytes=2MB
stdout_logfile_backups=10
stderr_logfile=/var/log/<service>/event_listener.stderr.log
stderr_logfile_maxbytes=2MB
stderr_logfile_backups=10
environment=SLACK_WEB_HOOK_URL="xxxxxxx"

eventlistener:slack_notifierは、下記投稿を参考に作成。
https://qiita.com/imunew/items/465521e30fae238cf7d0

[root@test02 ~]# supervisorctl status
daemon:<daemon-name>              RUNNING   pid 31513, uptime 13:19:20
slack_notifier                    RUNNING   pid 31511, uptime 13:19:20

server側(daemonの管理画面を表示するwebサーバー)

supervisord.conf
クライアント側と同様

consul

server側

[root@server01 consul_1.0.1]# pwd
/home/consul/consul_1.0.1

[root@server01 consul_1.0.1]# ll
total 16
drwxr-xr-x 2 consul consul 4096 Dec  3 04:49 bin
drwxr-xr-x 2 consul consul 4096 Dec  3 06:06 consul.d
drwxr-xr-x 4 consul consul 4096 Dec  3 04:50 data
drwxr-xr-x 2 consul consul 4096 Dec  3 04:50 logs

[root@server01 consul.d]# pwd
/home/consul/consul_1.0.1/consul.d

[root@server01 consul.d]# ll
total 16
-rw-r--r-- 1 consul consul 382 Dec  3 06:06 common.json
-rw-r--r-- 1 consul consul 117 Dec  3 04:49 connection.json
-rw-r--r-- 1 consul consul  84 Dec  3 04:49 server.json
-rw-r--r-- 1 consul consul 259 Dec  3 04:49 supervisord.json
/home/consul/consul_1.0.1/consul.d/common.json
{
  "datacenter": "dc1",
  "data_dir": "/home/consul/consul_1.0.1/data",
  "encrypt": "xxxxxxxxxxxxxxx", // consul keygenで発行した値を使用。
  "log_level": "info",
  "enable_syslog": true,
  "enable_debug": true,
  "node_name": "server01",
  "leave_on_terminate": false,
  "skip_leave_on_interrupt": true,
  "enable_script_checks": true, // ここtrueでないと、check script実行できない
  "rejoin_after_leave": true
}
/home/consul/consul_1.0.1/consul.d/connection.json
{
  "client_addr": "0.0.0.0",
  "bind_addr": "xxx.xxx.xxx.xxx", // 自身のprivate ip
  "ports": {
    "http": 8500,
    "server": 8300
  }
}
/home/consul/consul_1.0.1/consul.d/server.json
{
  "server": true, // server側なので、true
  "server_name": "server01",
  "bootstrap_expect": 1 // とりあえず、serverは1台クラスタにした
}
/home/consul/consul_1.0.1/consul.d/supervisord.json
{
  "services": [
    {
      "id": "supervisord-server01",
      "name": "supervisord",
      "tags" : [ "common" ],
      "checks": [{
        "script": "/etc/init.d/supervisord status | grep running",
        "interval": "10s"
      }]
    }
  ]
}

consul自体もsupervisordで起動します。

/etc/supervisor.d/consul.conf
[program:consul]
command=/home/consul/consul_1.0.1/bin/consul agent -config-dir=/home/consul/consul_1.0.1/consul.d -ui // -uiをつけて、uiも含めて起動。
user=consul
group=consul
autostart=true
autorestart=true
redirect_stdout=true
redirect_stderr=true
stdout_logfile=/home/consul/consul_1.0.1/logs/consul.stdout.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=10
stderr_logfile=/home/consul/consul_1.0.1/logs/consul.stderr.log
stderr_logfile_maxbytes=20MB
stderr_logfile_backups=10

agent側(管理したいdaemonが起動するサーバー側)

/home/consul/consul_1.0.1/consul.d/common.json
{
  "datacenter": "dc1",
  "data_dir": "/home/consul/consul_1.0.1/data",
  "encrypt": "xxxxxxxxxxxxxxx", // server側と同じencrypt
  "log_level": "info",
  "enable_syslog": true,
  "enable_debug": true,
  "node_name": "agent01",
  "leave_on_terminate": false,
  "skip_leave_on_interrupt": true,
  "enable_script_checks": true,
  "rejoin_after_leave": true,
  "retry_join": ["provider=aws tag_key=Service tag_value=consulserver region=us-west-2 access_key_id=xxxxxxxxxxxxxx secret_access_key=xxxxxxxxxxxxxxx"
  // retry joinでserver側と接続。serverのcluster化も考慮して、provider=awsで、tag_keyを指定。
]
  }
/home/consul/consul_1.0.1/consul.d/connection.json
{
  "client_addr": "0.0.0.0",
  "bind_addr": "xxx.xxx.xxx.xxx", // 自身のprivate ip
  "ports": {
    "http": 8500,
    "server": 8300
  }
}
/home/consul/consul_1.0.1/consul.d/daemon.json
{
  "services": [
        {
      "id": "<daemon-name>-agent01",
      "name": "<daemon-name>",
      "tags" : [ "daemon" ],
      "checks": [{
        "script": "supervisorctl status daemon:<daemon-name> | grep RUNNING",
        "interval": "10s"
      }]
    }
  ]
}
/home/consul/consul_1.0.1/consul.d/supervisord.json
{
  "services": [
    {
      "id": "supervisord-agent01",
      "name": "supervisord",
      "tags" : [ "common" ],
      "checks": [{
        "script": "/etc/init.d/supervisord status | grep running",
        "interval": "10s"
      }]
    }
  ]
}

agent側もsupervisordで管理

/etc/supervisor.d/consul.conf
[program:consul]
command=/home/consul/consul_1.0.1/bin/consul agent -config-dir=/home/consul/consul_1.0.1/consul.d // -uiは不要
user=consul
group=consul
autostart=true
autorestart=true
redirect_stdout=true
redirect_stderr=true
stdout_logfile=/home/consul/consul_1.0.1/logs/consul.stdout.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=10
stderr_logfile=/home/consul/consul_1.0.1/logs/consul.stderr.log
stderr_logfile_maxbytes=20MB
stderr_logfile_backups=10

cesi

image2.png

こちらのrepoから拝借させていただきました :bow:
基本的な設定は、README.mdに記載されている通り、セットアップします。

/etc/cesi.conf
[node:server01]
username = hogehoge
password = fugafuga
host = xxx.xxx.xxx.xxx // 対象nodeのprivate ip
port = 9001

[node:test01]
username = hogehoge
password = fugafuga
host = xxx.xxx.xxx.xxx // 対象nodeのprivate ip
port = 9001

[cesi]
database = /path/to/cesi-userinfo.db
activity_log = /path/to/cesi-activity.log
host = 0.0.0.0

(ansibleのroleにもしておく。)
cesiのコマンドも簡単にsupervisordで管理する様に設定します。

/etc/supervisor.d/cesi.conf
[program:cesi]
command=python /var/www/cesi/web.py
user=root
group=root
autostart=true
autorestart=true
redirect_stdout=true
redirect_stderr=true
stopasgroup=true
stopsignal=QUIT
stdout_logfile=/root/cesi.stdout.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=10
stderr_logfile=/root/cesi.stderr.log
stderr_logfile_maxbytes=20MB
stderr_logfile_backups=10

スクリーンショット 2017-12-10 1.51.12.png

うん、いい感じに画面でてますね。
ただ、この画面の欠点としてnodeが増えるたびに、
都度、 /etc/cesi.confを書き換えては
webサーバーを再起動しなければならない欠点がありました。
なので
今生きているサーバーは何があるのかを把握する必要がありました。
 → まさにサービスディスカバリ。
そこで、設定ファイルの書き方もある一定柔軟にテンプレート化できる
consul-tamplteの登場です。

consul-template

ここも同様にして、ansibleで導入します。
https://github.com/juwai/ansible-role-consul-template
あとは、いい感じに公式ドキュメントをみながら、templateを書けばok。

[root@agent01 config]# ll
total 8
-rwxr-xr-x 1 root   root    220 Dec  4 05:16 consul-template.cfg
/home/consul/consul-template/config/consul-template.cfg
consul = "127.0.0.1:8500"
wait = "10s"

template {
  source = "/home/consul/consul-template/templates/cesi.conf.tmpl"
  destination = "/etc/cesi.conf"
  command = "supervisorctl restart cesi"
  command_timeout = "60s"
}
/home/consul/consul-template/templates/cesi.conf.tmpl
{{range service "supervisord"}}
[node:{{.Node}}]
username = hogehoge
password = fugafuga
host = {{.Address}}
port = 9001

{{end}}

[cesi]
database = /path/to/cesi-userinfo.db
activity_log = /path/to/cesi-activity.log
host = 0.0.0.0

上記のように、consul-tamplateの中で
{{.Node}}という値を入れていれば
consulでsupervisordのnode追加・更新をトリガーとして
consul-templateが起動し

  1. /etc/cesi.confの設定ファイルの更新
  2. cesiのwebserverの再起動

が実現でき、ダッシュボードにて、supervisordが、管理できるようになります。

また
consul-templateは、daemonとして起動しておくものなので
consul-templateもまた、supervisordで管理します。

/etc/supervisor.d/consul-template.conf
[program:consul-template]
command=/home/consul/consul-template/bin/consul-template -config /home/consul/consul-template/config/consul-template.cfg
user=root
group=root
autostart=true
autorestart=true
redirect_stdout=true
redirect_stderr=true
stdout_logfile=/home/consul/consul-template/logs/stdout.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=10
stderr_logfile=/home/consul/consul-template/logs/stderr.log
stderr_logfile_maxbytes=20MB
stderr_logfile_backups=10

早速、実際サーバーを立ててみると…

スクリーンショット 2017-12-10 1.48.57.png

うん、いい感じにサーバーの台数が8->9台に増えてますね。
感覚的にも、増えるとほぼ同時に画面側も更新されてるので
結構いい感じです。(減らした時も同じ感じでした。)

めでたしめでたし。

やってみて、感じたこと

Good

  1. 各サーバーのプロセスの可視化できると確かに「なんか」安心する。
  2. サーバー入らずに、プロセスのstart/stop/restartできるのは、運用的にもセキュリティ的にも楽。
  3. supervisordは、探しても記事とかあまりない?気がするが、本当にプロセスを「管理」するのであれば、感覚的には、まぁまぁ使えるんじゃないかと感じた。
  4. consul-templateの柔軟性が高く、consulの設計次第でなんでもできる感じがよい。
  5. 遊び半分で作ってみたが、思ったより評判はよさげだった笑

Not Good

  1. supervisord自体のプロセス監視がうまいことできていない。
  2. まだまだsupervisordの設定周りを理解しきれていない。。。
     ※ ネットワーク/権限/セキュリティ周りのところが今後の課題。。usernameとかなんか一致してなくても、取れちゃってる・・・?笑
  3. consulもまだまだ使えていない。。。
  4. cesiもいい感じだが、挙動不審なところが若干ある。笑
    ※ 他のダッシュボードもレガシー感がすごくて、あまり、、、supervisordのもういい感じの画面がほしいな。
    http://supervisord.org/plugins.html#dashboards-and-tools-for-multiple-supervisor-instances

さいごに

プロセスって結構気づいたら落ちている気がしますが
(「いや、お前のツールに対する理解が浅いだけだろ!」っていうツッコミはやめてください笑)

単純にダッシュボードという形で
「可視化」して、人の目との接触回数が増えるだけでも
保守/運用性は高まる気がするので
やっぱりダッシュボード的なのはいいなと思いました^^

p.s.
色々と設定ファイルを記載していますが
「ん?ここおかしくないか?」というところがあれば
ぜひ、コメントお願いいたします :bow:

続きを読む

AWS 上に JobScheduler を構築した話

AWSには自前でジョブ実行用のサービスがあるので、こういうのはあまりやらないと思いますが、色々とハマったのでメモ。

インストール

JobScheduler インストール

  1. インストーラーをここからダウンロードします。

    スクリーンショット 2017-06-08 0.33.37.png

  2. 解凍した中にある jobscheduler_install.xml を以下の形に修正します。

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!--
    XML configuration file for JobScheduler setup
    
    The JobScheduler is available with a dual licensing model.
    - GNU GPL 2.0 License (see http://www.gnu.org/licenses/gpl-2.0.html)
    - JobScheduler Commercial License (see licence.txt)
    
    The setup asks you for the desired license model
    (see <entry key="licenceOptions" .../> below).
    
    If you call the setup with this XML file then you accept
    at the same time the terms of the chosen license agreement.
    -->
    <AutomatedInstallation langpack="eng">
    <com.izforge.izpack.panels.UserInputPanel id="home">
        <userInput/>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="licences">
        <userInput>
    
            <!-- Select the license model (GPL or Commercial) -->
            <entry key="licenceOptions" value="GPL"/>
    
            <!-- If you selected GPL as license model than the licence must be empty.
                 Otherwise please enter a license key if available.
                 It is also possible to modify the license key later. -->
            <entry key="licence" value=""/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.HTMLLicencePanel id="gpl_licence"/>
    <com.izforge.izpack.panels.HTMLLicencePanel id="commercial_licence"/>
    <com.izforge.izpack.panels.TargetPanel id="target">
    
        <!-- SELECT THE INSTALLATION PATH FOR THE BINARIES AND LIBRARIES
             The installation expands this path with the Scheduler ID as subdirectory.
             The path must be absolute!
             Default paths are
             /opt/sos-berlin.com/jobscheduler for Unix
             C:Program Filessos-berlin.comjobscheduler for Windows -->
        <installpath>/opt/sos-berlin.com/jobscheduler</installpath>
    
    </com.izforge.izpack.panels.TargetPanel>
    <com.izforge.izpack.panels.UserPathPanel id="userpath">
    
        <!-- SELECT THE DATA PATH FOR CONFIGURATION AND LOG FILES
             The installation expands this path with the Scheduler ID as subdirectory.
             The path must be absolute!
             Default paths are
             /home/[user]/sos-berlin.com/jobscheduler for Unix
             C:ProgramDatasos-berlin.comjobscheduler for Windows -->
        <UserPathPanelElement>/home/joc/jobscheduler</UserPathPanelElement>
    
    </com.izforge.izpack.panels.UserPathPanel>
    <com.izforge.izpack.panels.PacksPanel id="package">
    
        <!-- SELECT THE PACKS WHICH YOU WANT INSTALL -->
    
        <!-- Package: JobScheduler
             JobScheduler Basic Installation
             THIS PACK IS REQUIRED. IT MUST BE TRUE -->
        <pack index="0" name="Job Scheduler" selected="true"/>
    
        <!-- Package: Database Support
             Job history and log files can be stored in a database. Database support is
             available for MySQL, PostgreSQL, Oracle, SQL Server, DB2.
             THIS PACK IS REQUIRED. IT MUST BE TRUE -->
        <pack index="2" name="Database Support" selected="true"/>
    
        <!-- Package: Housekeeping Jobs
             Housekeeping Jobs are automatically launched by the Job Scheduler, e.g. to send
             buffered logs by mail, to remove temporary files or to restart the JobScheduler. -->
        <pack index="5" name="Housekeeping Jobs" selected="true"/>
    
    </com.izforge.izpack.panels.PacksPanel>
    <com.izforge.izpack.panels.UserInputPanel id="network">
        <userInput>
            <!-- Network Configuration -->
    
            <!-- Enter the name or ip address of the host on which the JobScheduler is operated -->
            <entry key="schedulerHost" value="localhost"/>
    
            <!-- Enter the port for TCP communication -->
            <entry key="schedulerPort" value="4444"/>
    
            <!-- Enter the port for HTTP communication -->
            <entry key="schedulerHTTPPort" value="40444"/>
    
            <!-- To enter a JobScheduler ID is required.
                 The IDs of multiple instances of the JobScheduler must be unique per server.
                 The JobScheduler ID expands the above installation paths as subdirectory.
                 Please omit special characters like: /  : ; * ? ! $ % & " < > ( ) | ^ -->
            <entry key="schedulerId" value="schedulerId_XXXX"/>
    
            <!-- It is recommended to enable TCP access for the host where the JobScheduler will install,
                 optionally enter additional host names or ip addresses. To enable all hosts in your
                 network to access the JobScheduler enter '0.0.0.0'. -->
            <entry key="schedulerAllowedHost" value="0.0.0.0"/>
    
            <!-- Choose (yes or no) wether the JobScheduler should be started at the end of the installation -->
            <entry key="launchScheduler" value="yes"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="cluster">
        <userInput>
            <!-- Cluster Configuration -->
    
            <!-- The JobScheduler can be installed independent of other possibly JobSchedulers,
                 as a primary JobScheduler in a backup system or as a backup JobScheduler.
                 Use '' for a standalone, '-exclusive' for a primary
                 or '-exclusive -backup' for a backup JobScheduler.
                 A database is required for a backup system. All JobSchedulers in a backup system
                 must have the same JobScheduler ID and the same database.
                 Further you can set '-distributed-orders' for a load balancing cluster.
                 For more information see
                 http://www.sos-berlin.com/doc/de/scheduler.doc/backupscheduler.xml
                 http://www.sos-berlin.com/doc/de/scheduler.doc/distributed_orders.xml -->
            <entry key="clusterOptions" value=""/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="smtp">
        <userInput>
            <!-- Mail Recipients Configuration / SMTP Authentication -->
    
            <!-- Enter the ip address or host name and port (default: 25) of your SMTP server -->
            <entry key="mailServer" value=""/>
            <entry key="mailPort" value="25"/>
    
            <!-- Configure the SMTP authentication if necessary. -->
            <entry key="smtpAccount" value=""/>
            <entry key="smtpPass" value=""/>
    
            <!-- Enter the addresses of recipients to which mails with log files are automatically
                 forwarded. Separate multiple recipients by commas -->
    
            <!-- Account from which mails are sent -->
            <entry key="mailFrom" value=""/>
    
            <!-- Recipients of mails -->
            <entry key="mailTo" value=""/>
    
            <!-- Recipients of carbon copies: -->
            <entry key="mailCc" value=""/>
    
            <!-- Recipients of blind carbon copies -->
            <entry key="mailBcc" value=""/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="email">
        <userInput>
            <!-- Mail Configuration / Event Handler -->
    
            <!-- Choose in which cases mails with log files are automatically forwarded. -->
            <entry key="mailOnError" value="no"/>
            <entry key="mailOnWarning" value="no"/>
            <entry key="mailOnSuccess" value="no"/>
    
            <!-- The Housekeeping package is required for configure JobScheduler as event handler
                 Choose this option if you intend to use JobScheduler Events and
                 - this JobScheduler instance is the only instance which processes Events
                 - this JobScheduler instance is a supervisor for other JobSchedulers which submit Events -->
            <entry key="jobEvents" value="off"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="database">
        <userInput>
            <!-- JobScheduler Database Configuration -->
    
            <!-- Choose the database management system. Supported values are 'mysql' for MySQL,
                 'oracle' for Oracle, 'mssql' for MS SQL Server, 'pgsql' for PostgreSQL,
                 'db2' for DB2 and 'sybase' for Sybase. -->
            <entry key="databaseDbms" value="mysql"/>
    
            <!-- You can choose between 'on' or 'off' to create the database tables.
                 If you have modified the initial data of an already existing installation,
                 then the modifications will be undone. Data added remains unchanged.
                 This entry should be only 'off', when you sure, that all tables are already created. -->
            <entry key="databaseCreate" value="on"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="dbconnection">
        <userInput>
            <!-- JobScheduler Database Configuration -->
    
            <!-- Enter the name or ip address of the database host -->
            <entry key="databaseHost" value="the host of RDS"/>
    
            <!-- Enter the port number for the database instance. Default ports are for MySQL 3306,
                 Oracle 1521, MS SQL Server 1433, postgreSQL 5432, DB2 50000, Sybase 5000. -->
            <entry key="databasePort" value="3306"/>
    
            <!-- Enter the schema -->
            <entry key="databaseSchema" value="jobscheduler_data"/>
    
            <!-- Enter the user name for database access -->
            <entry key="databaseUser" value="databaseUser"/>
    
            <!-- Enter the password for database access -->
            <entry key="databasePassword" value="databasePassword"/>
    
            <!-- You have to provide the MySQL, MS SQL Server, Sybase or DB2 JDBC driver respectively if you selected
                 corresponding DBMS type. For license reasons MySQL, Sybase and MS SQL Server JDBC Drivers are
                 not provided. Alternatively you can use the mariadb JDBC Driver for MySQL and
                 the jTDS JDBC Driver for MS SQL Server and Sybase which is provided. -->
    
            <!-- You can choose between 'yes' or 'no' for using the jTDS JDBC Driver
                 This entry affects only MS SQL Server or Sybase -->
            <entry key="connectorJTDS" value="yes"/>
    
            <!-- You can choose between 'yes' or 'no' for using the mariadb JDBC Driver
                 This entry affects only MySQL -->
            <entry key="connectorMaria" value="yes"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="jdbc">
        <userInput>
            <!-- JobScheduler Database Configuration -->
    
            <!-- Configuration for JDBC Driver
                 This entry is only necessary if you selected a DBMS type such as MySQL,
                 MS SQL Server, Sybase ot DB2 in the previous <userInput> element. -->
    
            <!-- You have to provide the MySQL, MS SQL Server, Sybase or DB2 JDBC driver respectively if you selected
                 corresponding DBMS type. For license reasons MySQL and MS SQL Server JDBC Drivers are
                 not provided. Specify the JDBC Driver source (e.g. mysql-connector-java-*.jar for MySQL,
                 sqljdbc.jar for MS SQL Server, jconn3.jar for Sybase). Alternatively you can use the mariadb
                 JDBC Driver for MySQL and the jTDS JDBC Driver for MS SQL Server and Sybase which is provided. -->
    
            <!-- Select the path to JDBC Driver -->
            <entry key="connector" value="/usr/share/java/mariadb-connector-java.jar"/>
    
            <!-- Only for DB2: Select the path to DB2 license file for JDBC Driver -->
            <entry key="connectorLicense" value=""/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="reportingDatabase">
        <userInput>
            <!-- Reporting Database Configuration
                 NOT SUPPORTED FOR SYBASE AND DB2 -->
    
            <!-- Set 'yes' if the JobScheduler and the Reporting database are the same.
                 If 'yes' then further Reporting database variables are ignored. -->
            <entry key="sameDbConnection" value="yes"/>
    
            <!-- Choose the database management system. Supported values are 'mysql' for MySQL,
                 'oracle' for Oracle, 'mssql' for MS SQL Server, 'pgsql' for PostgreSQL. -->
            <entry key="reporting.databaseDbms" value="mysql"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="reportingDbconnection">
        <userInput>
            <!-- Reporting Database Configuration
                 NOT SUPPORTED FOR SYBASE AND DB2 -->
    
            <!-- Enter the name or ip address of the database host -->
            <entry key="reporting.databaseHost" value="qnet-jobscheduler.c58hqrvwigfw.ap-northeast-1.rds.amazonaws.com"/>
    
            <!-- Enter the port number for the database instance. Default ports are for MySQL 3306,
                 Oracle 1521, MS SQL Server 1433, postgreSQL 5432. -->
            <entry key="reporting.databasePort" value="3306"/>
    
            <!-- Enter the schema -->
            <entry key="reporting.databaseSchema" value="jobscheduler_data"/>
    
            <!-- Enter the user name for database access -->
            <entry key="reporting.databaseUser" value="reporting.databaseUser"/>
    
            <!-- Enter the password for database access -->
            <entry key="reporting.databasePassword" value="reporting.databasePassword"/>
    
            <!-- You have to provide the MySQL or MS SQL Server JDBC driver respectively if you selected
                 corresponding DBMS type. For license reasons MySQL and MS SQL Server JDBC Drivers
                 are not provided. Alternatively you can use the mariadb JDBC Driver for MySQL and the
                 jTDS JDBC Driver for MS SQL Server which is provided. -->
    
            <!-- You can choose between 'yes' or 'no' for using the jTDS JDBC Driver
                 This entry affects only MS SQL Server -->
            <entry key="reporting.connectorJTDS" value="yes"/>
    
            <!-- You can choose between 'yes' or 'no' for using the mariadb JDBC Driver
                 This entry affects only MySQL -->
            <entry key="reporting.connectorMaria" value="yes"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="reportingJdbc">
        <userInput>
            <!-- Reporting Database Configuration
                 NOT SUPPORTED FOR SYBASE AND DB2 -->
    
            <!-- Configuration for JDBC Driver
                 This entry is only necessary if the package 'Database Support' is chosen and you
                 selected a DBMS type MySQL or MS SQL Server in the previous
                 <userInput> element. -->
    
            <!-- You have to provide the MySQL or MS SQL Server JDBC driver respectively if you selected
                 corresponding DBMS type. For license reasons MySQL and MS SQL Server JDBC Drivers are
                 not provided. Specify the JDBC Driver source (e.g. mysql-connector-java-*.jar for MySQL,
                 sqljdbc.jar for MS SQL Server). Alternatively you can use the mariadb JDBC Driver for
                 MySQL and the jTDS JDBC Driver for MS SQL Server which is provided. -->
    
            <!-- Select the path to JDBC Driver -->
            <entry key="reporting.connector" value="/usr/share/java/mariadb-connector-java.jar"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="end">
        <userInput/>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.InstallPanel id="install"/>
    <com.izforge.izpack.panels.ProcessPanel id="process"/>
    <com.izforge.izpack.panels.FinishPanel id="finish"/>
    </AutomatedInstallation>
    
    
  3. setup.sh を実行します

    ./setup.sh jobscheduler_install.xml
    

JOC インストール

  1. JobScheduler と同様に JOC をダウンロードします。

    スクリーンショット 2017-06-08 0.35.42.png

  2. joc_install.xml を修正します。

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!--
    XML configuration file for JOC
    
    If you call the installer with this XML file then
    you accept at the same time the terms of the
    licence agreement under GNU GPL 2.0 License
    (see http://www.gnu.org/licenses/gpl-2.0.html)
    -->
    <AutomatedInstallation langpack="eng">
    <com.izforge.izpack.panels.UserInputPanel id="home">
        <userInput/>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.HTMLLicencePanel id="gpl_licence"/>
    <com.izforge.izpack.panels.TargetPanel id="target">
    
        <!-- SELECT THE INSTALLATION PATH
             It must be absolute!
             For example:
             /opt/sos-berlin.com/joc on Linux
             C:Program Filessos-berlin.comjoc on Windows -->
        <installpath>/opt/sos-berlin.com/joc</installpath>
    
    </com.izforge.izpack.panels.TargetPanel>
    <com.izforge.izpack.panels.UserInputPanel id="jetty">
        <userInput>
    
            <!-- JOC requires a servlet container such as Jetty.
                 If a servlet container already installed then you can use it.
                 Otherwise a Jetty will be installed in addition if withJettyInstall=yes.
                 You need root permissions to install JOC with Jetty. -->
            <entry key="withJettyInstall" value="yes"/>
            <entry key="jettyPort" value="4446"/>
            <!-- Only necessary for Windows -->
            <entry key="jettyStopPort" value="40446"/>
            <!-- Only necessary for Unix (root permissions required) -->
            <entry key="withJocInstallAsDaemon" value="yes"/>
            <!-- Path to Jetty base directory
                 For example:
                 /homer/[user]/sos-berlin.com/joc on Linux
                 C:ProgramDatasos-berlin.comjoc on Windows -->
            <entry key="jettyBaseDir" value="/home/joc/jetty"/>
    
            <!-- Java options for Jetty. -->
            <!-- Initial memory pool (-Xms) in MB -->
            <entry key="jettyOptionXms" value="128"/>
            <!-- Maximum memory pool (-Xmx) in MB -->
            <entry key="jettyOptionXmx" value="512"/>
            <!-- Thread stack size (-Xss) in KB -->
            <entry key="jettyOptionXss" value="4000"/>
            <!-- Further Java options -->
            <entry key="jettyOptions" value=""/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="reportingDatabase">
        <userInput>
            <!-- Reporting Database Configuration -->
    
            <!-- Choose the database management system. Supported values are 'mysql' for MySQL,
                 'oracle' for Oracle, 'mssql' for MS SQL Server, 'pgsql' for PostgreSQL. -->
            <entry key="reporting.databaseDbms" value="mysql"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="reportingDbconnection">
        <userInput>
            <!-- Reporting Database Configuration -->
    
            <!-- Enter the name or ip address of the database host -->
            <entry key="reporting.databaseHost" value="qnet-jobscheduler.c58hqrvwigfw.ap-northeast-1.rds.amazonaws.com"/>
    
            <!-- Enter the port number for the database instance. Default ports are for MySQL 3306,
                 Oracle 1521, MS SQL Server 1433, postgreSQL 5432. -->
            <entry key="reporting.databasePort" value="3306"/>
    
            <!-- Enter the schema -->
            <entry key="reporting.databaseSchema" value="jobscheduler_data"/>
    
            <!-- Enter the user name for database access -->
            <entry key="reporting.databaseUser" value="qnet_admin"/>
    
            <!-- Enter the password for database access -->
            <entry key="reporting.databasePassword" value="7sYne7aFEsFSK7xh"/>
    
            <!-- You have to provide the MySQL or MS SQL Server JDBC driver respectively if you selected
                 corresponding DBMS type. For license reasons MySQL and MS SQL Server JDBC Drivers are
                 not provided. Alternatively you can use the mariadb JDBC Driver for MySQL and
                 the jTDS JDBC Driver for MS SQL Server which is provided. -->
    
            <!-- You can choose between 'yes' or 'no' for using the jTDS JDBC Driver
                 This entry affects only MS SQL Server -->
            <entry key="reporting.connectorJTDS" value="yes"/>
    
            <!-- You can choose between 'yes' or 'no' for using the mariadb JDBC Driver
                 This entry affects only MySQL -->
            <entry key="reporting.connectorMaria" value="yes"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="reportingJdbc">
        <userInput>
            <!-- Reporting Database Configuration -->
    
            <!-- Configuration for JDBC Driver
                 This entry is only necessary if you selected a DBMS type such as MySQL and
                 MS SQL Server in the previous <userInput> element. -->
    
            <!-- You have to provide the MySQL or MS SQL Server JDBC driver respectively if you selected
                 corresponding DBMS type. For license reasons MySQL and MS SQL Server JDBC Drivers are
                 not provided. Specify the JDBC Driver source (e.g. mysql-connector-java-*.jar for MySQL,
                 sqljdbc.jar for MS SQL Server). Alternatively you can use the mariadb
                 JDBC Driver for MySQL and the jTDS JDBC Driver for MS SQL Server which is provided. -->
    
            <!-- Select the path to JDBC Driver -->
            <entry key="reporting.connector" value="/usr/share/java/mariadb-connector-java.jar"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="database">
        <userInput>
            <!-- JobScheduler Database Configuration -->
    
            <!-- Set 'yes' if the Reporting and the JobScheduler database are the same.
                 If 'yes' then further JobScheduler database variables are ignored. -->
            <entry key="sameDbConnection" value="yes"/>
    
            <!-- Choose the database management system. Supported values are 'mysql' for MySQL,
                 'oracle' for Oracle, 'mssql' for MS SQL Server, 'pgsql' for PostgreSQL,
                 'db2' for DB2 and 'sybase' for Sybase. -->
            <entry key="databaseDbms" value=""/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="dbconnection">
        <userInput>
            <!-- JobScheduler Database Configuration -->
    
            <!-- Enter the name or ip address of the database host -->
            <entry key="databaseHost" value="qnet-jobscheduler.c58hqrvwigfw.ap-northeast-1.rds.amazonaws.com"/>
    
            <!-- Enter the port number for the database instance. Default ports are for MySQL 3306,
                 Oracle 1521, MS SQL Server 1433, postgreSQL 5432, DB2 50000, Sybase 5000. -->
            <entry key="databasePort" value="3306"/>
    
            <!-- Enter the schema -->
            <entry key="databaseSchema" value="jobscheduler_data"/>
    
            <!-- Enter the user name for database access -->
            <entry key="databaseUser" value="qnet_admin"/>
    
            <!-- Enter the password for database access -->
            <entry key="databasePassword" value="7sYne7aFEsFSK7xh"/>
    
            <!-- You have to provide the MySQL, MS SQL Server, Sybase or DB2 JDBC driver respectively if you selected
                 corresponding DBMS type. For license reasons MySQL, Sybase and MS SQL Server JDBC Drivers are
                 not provided. Alternatively you can use the mariadb JDBC Driver for MySQL and
                 the jTDS JDBC Driver for MS SQL Server and Sybase which is provided. -->
    
            <!-- You can choose between 'yes' or 'no' for using the jTDS JDBC Driver
                 This entry affects only MS SQL Server or Sybase -->
            <entry key="connectorJTDS" value="yes"/>
    
            <!-- You can choose between 'yes' or 'no' for using the mariadb JDBC Driver
                 This entry affects only MySQL -->
            <entry key="connectorMaria" value="yes"/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="jdbc">
        <userInput>
            <!-- JobScheduler Database Configuration -->
    
            <!-- Configuration for JDBC Driver
                 This entry is only necessary if you selected a DBMS type such as MySQL,
                 MS SQL Server, Sybase ot DB2 in the previous <userInput> element. -->
    
            <!-- You have to provide the MySQL, MS SQL Server, Sybase or DB2 JDBC driver respectively if you selected
                 corresponding DBMS type. For license reasons MySQL and MS SQL Server JDBC Drivers are
                 not provided. Specify the JDBC Driver source (e.g. mysql-connector-java-*.jar for MySQL,
                 sqljdbc.jar for MS SQL Server, jconn3.jar for Sybase). Alternatively you can use the mariadb
                 JDBC Driver for MySQL and the jTDS JDBC Driver for MS SQL Server and Sybase which is provided. -->
    
            <!-- Select the path to JDBC Driver -->
            <entry key="connector" value="/usr/share/java/mariadb-connector-java.jar"/>
    
            <!-- Only for DB2: Select the path to DB2 license file for JDBC Driver -->
            <entry key="connectorLicense" value=""/>
    
        </userInput>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.UserInputPanel id="end">
        <userInput/>
    </com.izforge.izpack.panels.UserInputPanel>
    <com.izforge.izpack.panels.InstallPanel id="install"/>
    <com.izforge.izpack.panels.ProcessPanel id="process"/>
    <com.izforge.izpack.panels.FinishPanel id="finish"/>
    </AutomatedInstallation>
    
  3. setup.sh を実行します

    ./setup.sh joc_install.xml
    

続きを読む

Amazon FreeRTOS のAPI一覧

Amazon FreeRTOS V1.0.0 の各APIについて一言で説明してみました。

FreeRTOS kernel

タスクとスケジューラ

API Comment
portSWITCH_TO_USER_MODE() Supervisor モードから User モードへ切り替える
vTaskAllocateMPURegions() タスクにMemory Protection Unit (MPU) regionsを割り当てる
xTaskAbortDelay() タスクを Blocked state に遷移させる
xTaskCallApplicationTaskHook() タスクにtaskhookを関連付ける。
xTaskCheckForTimeOut() タスクにタイムアウトを設定、expireしたら Blocked state に遷移させる
xTaskCreate() タスクを生成する
xTaskCreateStatic() 静的なメモリを使ってタスクを生成する
xTaskCreateRestricted() Memory Protection Unit (MPU) restricted task を生成する
vTaskDelay() タスクを指定tickだけ Blocked statee に入れる
vTaskDelayUntil() タスクをある絶対時刻まで Blocked statee に入れる
vTaskDelete() タスクを削除する
taskDISABLE_INTERRUPTS() 割り込むを無効にする
taskENABLE_INTERRUPTS() 割り込みを有効にする
taskENTER_CRITICAL() クリティカルセクションに入る
taskENTER_CRITICAL_FROM_ISR() クリティカルセクションに入る
taskEXIT_CRITICAL() クリティカルセクションから出る
taskEXIT_CRITICAL_FROM_ISR() クリティカルセクションから出る
xTaskGetApplicationTaskTag() タスクのタグを取得する
xTaskGetCurrentTaskHandle() Running Stateにいるタスクのハンドルを取得する
xTaskGetIdleTaskHandle() Idle Stateにいるタスクのハンドルを取得する
xTaskGetHandle() 名前を指定して るタスクのハンドルを取得する
uxTaskGetNumberOfTasks() タスク数を取得する
vTaskGetRunTimeStats() タスクの統計情報を取得する (タスク名、CPU時間、CPU時間の割合)
xTaskGetSchedulerState() スケジューラの状態を取得する (開始していない、実行中、サスペンド中)
uxTaskGetStackHighWaterMark() タスクの残りスタック量を取得する
eTaskGetState() タスクの enumerated type な状態を取得する
uxTaskGetSystemState() 複数タスクの詳細な状態を取得する。
vTaskGetTaskInfo() ある一つのタスクの詳細な状態を取得する。
pvTaskGetThreadLocalStoragePointer() タスクの Thread local storage のポインタを取得する
pcTaskGetName() タスクの名前を取得する
xTaskGetTickCount() タスクが消費したtickcountを取得する
xTaskGetTickCountFromISR() タスクが消費したtickcountを取得する
vTaskList() タスクの状態を読みやすい形式で表示する
xTaskNotify() タスクにNotify (32bitの値)を送る
xTaskNotifyAndQuery() タスクにNotify (32bitの値)を送る 以前の値を使う
xTaskNotifyAndQueryFromISR() タスクにNotify (32bitの値)を送る 以前の値を使う
xTaskNotifyFromISR() タスクにNotify (32bitの値)を送る
xTaskNotifyGive() タスクにNotifyを送る 値はインクリメントする
vTaskNotifyGiveFromISR() タスクにNotifyを送る 値はインクリメントする
xTaskNotifyStateClear() タスクのpending notificationをクリアする。 notification valueはクリアしない
ulTaskNotifyTake() タスクにNotifyを送る 値はデクリメントする
xTaskNotifyWait() タスクに指定tickcout後にNotifyを送る
uxTaskPriorityGet() タスクのプライオリティを取得する
vTaskPrioritySet() タスクのプライオリティを設定する
vTaskResume() タスクの状態をSuspendからReadyに復帰する
xTaskResumeAll() すべてのSuspendのタスクを Activeに復帰する
xTaskResumeFromISR() タスクの状態をSuspendからReadyに復帰する
vTaskSetApplicationTaskTag() タスクにタグを割り当てる
vTaskSetThreadLocalStoragePointer() タスクの Thread local storage のポインタを設定する
vTaskSetTimeOutState() タイムアウトの構造体を設定する
vTaskStartScheduler() スケジューラを開始する
vTaskStepTick() tiklessの場合にtickを進ませる。
vTaskSuspend() タスクをsuspendする
vTaskSuspendAll() すべてのタスクをsuspendする
taskYIELD() タスクのコンテキストスイッチを強制する

キュー

API Comment
vQueueAddToRegistry() キューに読みやすい名前を割り当てる
xQueueAddToSet() キューセットにキューまたはセマフォを追加する
xQueueCreate() キューを生成する
xQueueCreateSet() キューセットを生成する
xQueueCreateStatic() 静的なメモリを使ってキューを生成する
vQueueDelete() キューを削除する
pcQueueGetName() キューの名前を取得する
xQueueIsQueueEmptyFromISR() キューが空かどうか確認する
xQueueIsQueueFullFromISR() キューが満杯かどうか確認する
uxQueueMessagesWaiting() キューが保持しているアイテム数を取得する
uxQueueMessagesWaitingFromISR() キューが保持しているアイテム数を取得する
xQueueOverwrite() キューが満杯でも、値を詰め込み上書きする。
xQueueOverwriteFromISR() キューが満杯でも、アイテムを詰め込み上書きする
xQueuePeek() キューからアイテムを取り出す、取り出したアイテムはキューにそのまま残す
xQueuePeekFromISR() キューからアイテムを取り出す、取り出したアイテムはキューにそのまま残す
xQueueReceive() キューからアイテムを取り出す、取り出したアイテムはキューから削除する
xQueueReceiveFromISR() キューからアイテムを取り出す、取り出したアイテムはキューから削除する
xQueueRemoveFromSet() キューセットからキューまたはセマフォを削除する
xQueueReset() キューを空の状態にリセットする
xQueueSelectFromSet() キューセットから利用可能なキューまたはセマフォをselectする
xQueueSelectFromSetFromISR() キューセットから利用可能なキューまたはセマフォをselectする
xQueueSend() キューの最後にアイテムを追加する。
xQueueSendToFront() キューの先頭にアイテムを追加する。
xQueueSendToBack() キューの最後にアイテムを追加する。
xQueueSendFromISR() キューの最後にアイテムを追加する。
xQueueSendToFrontFromISR() キューの先頭にアイテムを追加する。
xQueueSendToBackFromISR() キューの最後にアイテムを追加する。
uxQueueSpacesAvailable() キューの空きサイズを取得する

セマフォ

API Comment
vSemaphoreCreateBinary() バイナリセマフォを生成する [非推奨]
xSemaphoreCreateBinary() バイナリセマフォを生成する
xSemaphoreCreateBinaryStatic() 静的なメモリを使ってバイナリセマフォを生成する
xSemaphoreCreateCounting() カウンティングセマフォを生成する
xSemaphoreCreateCountingStatic() 静的なメモリを使ってカウンティングセマフォを生成する
xSemaphoreCreateMutex() ミューテックスを生成する
xSemaphoreCreateMutexStatic() 静的なメモリを使ってミューテックスを生成する
xSemaphoreCreateRecursiveMutex() 再帰ミューテックスを生成する
xSemaphoreCreateRecursiveMutexStatic() 静的なメモリを使って再帰ミューテックスを生成する
vSemaphoreDelete() セマフォ、ミューテックスを削除する
uxSemaphoreGetCount() セマフォのカウント値を取得する
xSemaphoreGetMutexHolder() ミューテックスを保持するタスクのハンドルを取得する
xSemaphoreGetMutexHolderFromISR() ミューテックスを保持するタスクのハンドルを取得する
xSemaphoreGive() セマフォを解放する
xSemaphoreGiveFromISR() セマフォを解放する
xSemaphoreGiveRecursive() 再帰ミューテックスを解放する
xSemaphoreTake() セマフォを獲得する
xSemaphoreTakeFromISR() セマフォを獲得する
xSemaphoreTakeRecursive() ミューテックスを獲得する

ソフトウェアタイマ

API Comment
xTimerChangePeriod() タイマの周期を設定する
xTimerChangePeriodFromISR() タイマの周期を設定する
xTimerCreate() タイマを生成する
xTimerCreateStatic() 静的なメモリを使ってタイマを生成する
xTimerDelete() タイマを削除する
xTimerGetExpiryTime() タイマがexpireする時刻 を取得する
pcTimerGetName() タイマの名前を取得する
xTimerGetPeriod() タイマの周期を取得する
xTimerGetTimerDaemonTaskHandle() タイマデーモンタスクのハンドルを取得する
pvTimerGetTimerID() タイマのIDを取得する
xTimerIsTimerActive() タイマを有効にする
xTimerPendFunctionCall() タイマのファンクションコールを設定
xTimerPendFunctionCallFromISR() タイマのファンクションコールを設定
xTimerReset() タイマをリスタートする
xTimerResetFromISR() タイマをリスタートする
vTimerSetTimerID() タイマのIDを設定する 複数のタイマで同じコールバック関数を呼ぶ場合に使う
xTimerStart() タイマをスタートする
xTimerStartFromISR() タイマをスタートする
xTimerStop() タイマをストップする
xTimerStopFromISR() タイマをストップする

event group

API Comment
xEventGroupClearBits() イベントグループのbitsをクリアする
xEventGroupClearBitsFromISR() イベントグループのbitsをクリアする
xEventGroupCreate() イベントグループを生成する
xEventGroupCreateStatic() 静的なメモリを使ってイベントグループを生成する
vEventGroupDelete() イベントグループを削除する
xEventGroupGetBits() イベントグループの値を取得する
xEventGroupGetBitsFromISR() イベントグループの値を取得する
xEventGroupSetBits() イベントグループの値を設定する
xEventGroupSetBitsFromISR() イベントグループの値を設定する
xEventGroupSync() ある値になるまでかタイムアウトを待って、イベントグループに値を設定する。
xEventGroupWaitBits() イベントグループがある値になるかタイムアウトまで待つ

Stream Buffer

API Comment
xStreamBufferCreate() ストリームバッファを生成する
xStreamBufferCreateStatic() 静的メモリを使ってストリームバッファを生成する
xStreamBufferSend() ストリームバッファにデータを書き込む
xStreamBufferSendFromISR() ストリームバッファにデータを書き込む
xStreamBufferReceive() ストリームバッファからデータを読み込む
xStreamBufferReceiveFromISR() ストリームバッファからデータを読み込む
vStreamBufferDelete() ストリームバッファを削除する
xStreamBufferIsFull() ストリームバッファが満杯かどうか確認する
xStreamBufferIsEmpty() ストリームバッファが空かどうか確認する
xStreamBufferReset() ストリームバッファを空にリセットする
xStreamBufferSpacesAvailable() ストリームバッファの空きサイズを取得する
xStreamBufferBytesAvailable() ストリームバッファが保持しているデータサイズを取得する
xStreamBufferSetTriggerLevel() ストリームバッファのトリガレベルを設定する

Message Buffer

API Comment
xMessageBufferCreate() メッセージバッファを生成する
xMessageBufferCreateStatic() 静的メモリを使ってッセージバッファを生成する
xMessageBufferSend() メッセージバッファにデータを書き込む
xMessageBufferSendFromISR() メッセージバッファにデータを書き込む
xMessageBufferReceive() メッセージバッファからデータを読み込む
xMessageBufferReceiveFromISR() メッセージバッファからデータを読み込む
vMessageBufferDelete() メッセージバッファを削除する
xMessageBufferIsFull() メッセージバッファが満杯かどうか確認する
xMessageBufferIsEmpty() メッセージバッファが空かどうか確認する
xMessageBufferReset() メッセージバッファを空にリセットする
xMessageBufferSpaceAvailable() メッセージバッファの空きサイズを取得する

AWS

aws_crypto

API Comment
CRYPTO_ConfigureHeap() 暗号処理用のHeapを確保する
CRYPTO_SignatureVerificationStart() 署名の検証を開始する
CRYPTO_SignatureVerificationUpdate() 署名の追加データを検証する
CRYPTO_SignatureVerificationFinal() 署名の検証を終了する

aws_greengrass_discovery

API Comment
GGD_GetGGCIPandCertificate() green grass に接続する
GGD_JSONRequestStart() クラウドからJSONファイルの取得を開始する
GGD_JSONRequestGetSize() クラウドからJSONファイルのサイズを取得する
GGD_JSONRequestGetFile() クラウドからJSONファイルを取得する
GGD_JSONRequestAbort() クラウドからJSONファイルの取得を中止する
GGD_GetIPandCertificateFromJSON() JSONファイルからホストIPと証明書を取得する

aws_mqtt_lib

API Comment
MQTT_Init() MQTTを初期化する
MQTT_Connect() MQTT Connectを送信する
MQTT_Disconnect() MQTT Disonnectを送信する
MQTT_Subscribe() MQTT Subscribeを送信する
MQTT_Unsubscribe() MQTT Unsubscribeを送信する
MQTT_Publish() MQTT Publishを送信する
MQTT_ParseReceivedData() 受信したMQTT メッセージを復号する
MQTT_ReturnBuffer() コールバックで提供されたバッファを解放する
MQTT_Periodic() タイムアウトとキープアライブを管理する

aws_secure_socket

API Comment
lib_initDECLARE_LIB_INIT() Secure Socketsライブラリを初期化する
SOCKETS_Socket() TCPソケットを生成する
SOCKETS_Connect() ソケットを指定したIPアドレスとポートに接続する
SOCKETS_Recv() TCPソケットからデータを受信する
SOCKETS_Send() リモートソケットにデータを送信する
SOCKETS_Shutdown() ソケットの全二重通信を閉じる
SOCKETS_Close() ソケットを解放する
SOCKETS_SetSockOpt() ソケットオプションを設定する
SOCKETS_GetHostByName() DNSを使ってホスト名を解決する

aws_shadow

API Comment
SHADOW_ClientCreate() Shadowクライアントを生成する
SHADOW_ClientConnect() Shadowサービスに接続する
SHADOW_ClientDisconnect() Shadowサービスから切断する
SHADOW_ClientDelete() Shadowクライアントを削除する
SHADOW_Update() Thing Shadowをアップデートする
SHADOW_Get() クラウドからThing Shadowを取得する
SHADOW_Delete() クラウドのThing Shadowを削除する
SHADOW_RegisterCallbacks() コールバック関数の登録
SHADOW_ReturnMQTTBuffer() MQTT Bufferを返却する

aws_system_init

API Comment
SYSTEM_Init() システムを初期化する

aws_tls

API Comment
TLS_Init() TLSを初期化する
TLS_Connect() TLSで接続する
TLS_Recv() TLSで受信する
TLS_Send() TLSで送信する
TLS_Cleanup() TLSを削除する

aws_wifi

API Comment
WIFI_On() Wi-FiをONにする
WIFI_Off() Wi-FiをOffにする
WIFI_ConnectAP() APに接続する
WIFI_Disconnect() AP接続を切断する
WIFI_Reset() Wi-Fiモジュールをリセットする
WIFI_SetMode() Wi-Fiのモードを設定する
WIFI_GetMode() Wi-Fiのモードを設定する
WIFI_NetworkAdd () ネットワークの設定を追加する
WIFI_NetworkGet() ネットワークの設定を取得する
WIFI_NetworkDelete() ネットワークの設定を削除する
WIFI_Ping() pingを行う
WIFI_GetIP () Wi-FiのIPアドレスを取得する
WIFI_GetMAC() Wi-FiのMACアドレスを取得する
WIFI_GetHostIP() 指定したホストのIPアドレスを取得する
WIFI_Scan() Wi-Fiをscanする
WIFI_StartAP() APをスタートする
WIFI_StopAP() APをストップする
WIFI_ConfigureAP () APの設定をする
WIFI_SetPMMode() 省電力モードの設定をする
WIFI_GetPMMode() 省電力モードの設定を取得する

続きを読む

RedashをAMIから起動してみた

公式AMIからインスタンスの起動

https://redash.io/help-onpremise/setup/setting-up-redash-instance.html

から AMI を選択します。Region ap-northeast-1 の AMI リンクをクリックするとインスタンスタイプを選択する画面に遷移するので、適当なインスタンスタイプを選択します。

1年間の無料枠を使っているので無料で使える t2.micro にしました。

キーペアを設定して起動します。

アクセス設定

マネジメントコンソールからインスタンスのセキュリティーグループのインバウンドで HTTP アクセスを適切に許可します。

これでブラウザからインスタンスのIPにアクセスすればページが表示されます。

internal_server_error.png

Internal Server Error(500エラー)ですね。サービス側に問題がありそうなので確認します。

サーバ側設定

起動したインスタンスに SSH ログインします。

とりあえずサービスを再起動してみます。

$ sudo supervisorctl restart all
redash_server: stopped
redash_celery_scheduled: stopped
redash_celery: stopped
redash_celery: started
redash_server: started
redash_celery_scheduled: started

再度ブラウザをリロードしてみますが、Internal Server Error に変化はありません。

supervisord のログを確認します。

$ less /var/log/supervisor/supervisord.log

が、怪しそうなログは出ていません。Redash が使っているサービスの起動状態をひとつひとつ確認します。

PostgreSQL のプロセスを確認します。

$ ps aux | grep -i postgres
ubuntu   21547  0.0  0.0  12948   932 pts/0    S+   13:47   0:00 grep --color=auto -i postgres

見つからないので、PostgreSQL が起動していない事が原因のようです。ログを確認します。

$ less /var/log/postgresql/postgresql-9.5-main.log
2017-10-16 13:32:30 UTC [1352-1] LOG:  database system was shut down at 2017-08-13 12:39:56 UTC
2017-10-16 13:32:30 UTC [1352-2] LOG:  MultiXact member wraparound protections are now enabled
2017-10-16 13:32:30 UTC [1351-1] LOG:  database system is ready to accept connections
2017-10-16 13:32:30 UTC [1356-1] LOG:  autovacuum launcher started
2017-10-16 13:32:31 UTC [1361-1] [unknown]@[unknown] LOG:  incomplete startup packet
2017-10-16 13:34:33 UTC [1351-2] LOG:  received fast shutdown request
2017-10-16 13:34:33 UTC [1351-3] LOG:  aborting any active transactions
2017-10-16 13:34:33 UTC [1705-1] redash@redash FATAL:  terminating connection due to administrator command
2017-10-16 13:34:33 UTC [1704-1] redash@redash FATAL:  terminating connection due to administrator command
2017-10-16 13:34:33 UTC [1356-2] LOG:  autovacuum launcher shutting down
2017-10-16 13:34:34 UTC [1353-1] LOG:  shutting down
2017-10-16 13:34:34 UTC [1353-2] LOG:  database system is shut down
2017-10-16 13:34:53 UTC [19851-1] FATAL:  could not map anonymous shared memory: Cannot allocate memory
2017-10-16 13:34:53 UTC [19851-2] HINT:  This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages. To reduce the request size (currently 148488192 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.

FATAL エラーが出てますね。その前にログの時刻が見づらいので、タイムゾーンを日本時間に変更します。

$ sudo timedatectl set-timezone Asia/Tokyo
$ date
Mon Oct 16 22:53:17 JST 2017

JST に変わりました。

先ほどの PostgreSQL のエラーは割り当てられたメモリサイズが大きいというもののようです。

$ sudo vi /etc/postgresql/9.5/main/postgresql.conf

#max_connections = 100
max_connections = 50

#shared_buffers = 128MB
shared_buffers = 32MB

少ないですね。再起動します。

$ sudo /etc/init.d/postgresql restart
Restarting postgresql (via systemctl): postgresql.service.

プロセスを確認します。

$ ps aux | grep -i postgres
postgres 21785  0.0  1.5 186840 15880 ?        S    23:02   0:00 /usr/lib/postgresql/9.5/bin/postgres -D /var/lib/postgresql/9.5/main -c config_file=/etc/postgresql/9.5/main/postgresql.conf
postgres 21787  0.0  0.3 186840  3832 ?        Ss   23:02   0:00 postgres: checkpointer process
postgres 21788  0.0  0.3 186840  3832 ?        Ss   23:02   0:00 postgres: writer process
postgres 21789  0.0  0.3 186840  3832 ?        Ss   23:02   0:00 postgres: wal writer process
postgres 21790  0.0  0.6 187244  6104 ?        Ss   23:02   0:00 postgres: autovacuum launcher process
postgres 21791  0.0  0.3 148544  3128 ?        Ss   23:02   0:00 postgres: stats collector process
ubuntu   21808  0.0  0.1  12948  1092 pts/0    S+   23:02   0:00 grep --color=auto -i postgres

起動した!

ブラウザからアクセスすると。

welcome_redash.png

表示された!

Admin ユーザを登録すればとりあえず使えるようになります。

このままだとメール設定などが出来ていなので、必要であればヘルプ

https://redash.io/help/

などを参考にします。

ちなみに

最初に試した時は、次のような en_US.UTF-8 ロケールが無いというエラーが出ていました。

2017-10-09 08:38:28 UTC [2801-1] redash@redash FATAL:  database locale is incompatible with operating system
2017-10-09 08:38:28 UTC [2801-2] redash@redash DETAIL:  The database was initialized with LC_COLLATE "en_US.UTF-8",  which is not recognized by setlocale().
2017-10-09 08:38:28 UTC [2801-3] redash@redash HINT:  Recreate the database with another locale or install the missing locale.

確認すると確かに無い。

$ locale -a
C
C.UTF-8  # en_US.utf8 がない
POSIX

そのため、次のようにして en_US.UTF-8 をインストールして起動しました。

$ sudo locale-gen en_US.UTF-8
$ locale -a
C
C.UTF-8
en_US.utf8
POSIX

この記事を書くために最初から通して試してみたら en_US.utf8 ローカルが入っていて、再現しませんでした。

Issue を立てたのですが、勘違いだった可能性が高いのでそっと閉じました。

続きを読む

Djangoの既存プロジェクトをec2にデプロイ

概要

詰まりまくったのでメモ。Dockerで開発していたのでそのままデプロイしたろ!と思っていたが意味わからなすぎて断念。(おそらく?)一般的な方法でデプロイした。もっと良い方法があれば教えて欲しい限りです。

環境

OS: Amazon Linux AMI release 2017.09
ローカル: Docker
Python: 3.6.2
Django: 1.11.5
Gunicorn: 19.7.1
Nginx: 1.12.1

AWSの設定

インスタンスの作成

AWSの設定はAmazon Web Services 基礎からのネットワーク&サーバー構築を参考にした。

AWSに登録してコンソール > EC2からインスタンスを作成する。全部デフォルト。t2microなら無料。
VPC、サブネット、ルートテーブル、ゲートウェイ、セキュリティグループやらが作成される(はず)。なかったら作ってVPCに紐付ける。sshキーをダウンロードまたは登録しておく。

ポートの開放

EC2 > セキュリティグループからポートが開放できる。セキュリティグループを選択 -> インバウンド -> 編集 -> ルールの追加で80番ポート、8000番ポート(確認用、あとで閉じる)を開く。タイプはカスタムTCP、ソースは0.0.0.0/0で良い。
ここで EC2 > インスタンス から作ったインスタンスの詳細が確認できる。右側のパブリックDNSでドメイン、IPv4パブリックIPでIPが確認できる。

nginxのインストール

AWSにsshでログイン。キーペアをダウンロードした場合は~/.sshに置いて別のキー名を指定する。

# ssh -i ~/.ssh/id_rsa ec2-user@(インスタンスのIP)

以下ではrootで作業する。

$ sudo su -

以下、EC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイするを参考にnginxをインストール。

nginx入れる。

$ yum install nginx

nginxを起動する。

$ nginx

nginxの自動起動設定

$ chkconfig --add nginx
$ chkconfig nginx on

自動起動設定できているか確認する。

$ chkconfig | grep nginx
nginx           0:off   1:off   2:on    3:on    4:on    5:on    6:off

また、http://(パブリックDNS)を確認してnginxが起動しているか確認する。

Djangoプロジェクトの起動

AWSにプロジェクトを送る。

scpでzipで固めたDjangoプロジェクトを送る。また送る前にrequirements.txtは用意しておく。

# pip freeze > requirements.txt
# scp -i ~/.ssh/id_rsa ~/path/to/project.zip ec2-user@yourIP:home/ec2-user/

送ったものがhome/ec2-userに落ちているはず。解凍する

$ unzip project.zip

ほんとはgitで落とせばいいんだろうけどプライベートリポジトリにしてるので新しいuser登録して新しくssh登録してってしないといけないのかな。誰か教えてください。

pythonとかを入れる

色々考えるのがめんどくさかったのでEC2サーバにPython3環境構築を参考にした。

gitとpyenv入れる。-yが全部yesって答えるオプションらしい。初めて知った。

$ yum install git -y
$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv

path通す。

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

コマンド通るか確認。

$ pyenv -V

依存関係を入れる。

$ sudo yum install gcc zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel -y

本体を入れる。

$ pyenv install 3.6.2

pythonを切り替える。これはrootのpythonなので、sudo su -後でないとデフォルトのpythonに戻ってしまうので注意。

$ pyenv global 3.6.2
$ pyenv rehash
$ python --version
Python 3.6.2

Django、その他諸々のプロジェクトに必要なライブラリをインストールする。

$ pip install --upgrade -r project/requirements.txt

requirements.txtにGunicornが入ってなければGunicornを入れる。
Gunicornとはwsgiサーバーのことで、nginxとDjangoを繋ぐものみたいなイメージ。

$ pip install gunicorn

manage.pyの上でDjangoを起動させる。

$ gunicorn your_project.wsgi --bind=0.0.0.0:8000

http://(パブリックDNS):8000を確認すると、ALLOWED_HOSTSに追加してね!と出るので追加する。

/your_project/settings.py
# 中略
ALLOWED_HOSTS = ['(パブリックDNS)']
# 以下略

もう一回確認してプロジェクトが見えれば成功。

Nginxの設定の変更

再びEC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイするを丸パクリ。本当にありがとうございました
/etc/nginx.confとあるが、Amazon Linuxでは/etc/nginx/nginx.confにあった。
以下引用

/etc/nginx.confを以下の通り編集する

/etc/nginx.conf
〜中略〜

http {
   〜中略〜

   upstream app_server {
       server 127.0.0.1:8000 fail_timeout=0;
    }

   server {
        #以下4行はコメントアウト
        #listen       80 default_server;
        #listen       [::]:80 default_server;
        #server_name  localhost;
        #root         /usr/share/nginx/html;

       # 以下3行を追加
        listen    80;
        server_name     IPアドレス or ドメイン;
        client_max_body_size    4G;

       # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

       location / {
            # 以下4行を追加
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass   http://app_server;
        }

   〜以下略〜

nginxの再起動

$ service nginx restart
Stopping nginx:                                            [  OK  ]
Starting nginx:                                            [  OK  ]

Djangoを再びGunicornで立ち上げる

$ gunicorn your_project.wsgi --bind=0.0.0.0:8000

http://(パブリックDNS)で確認できたら成功。

daemon化

今はコマンドからGunicornを起動しているだけなので、ログアウトしたら終了してしまう。そこでGunicornをデーモン化して常駐するようにしたい。
ただ色々な所を見ると、Surpervisorというツールでそれが可能なようだが、SurpervisorはPython3系に対応していないので2系の環境をもう一つ作れとのこと。んなアホな。
もうちょっと調べてみると、普通に-Dというオプションをつけるだけでデーモンとして起動できるらしい。

起動

$ gunicorn your_project.wsgi --bind=0.0.0.0:8000 -D

終了するときは

$ ps -ef | grep gunicorn
root     17419     1  0 Oct07 ?        00:00:08 /root/.pyenv/versions/3.6.2/bin/python3.6 /root/.pyenv/versions/3.6.2/bin/gunicorn your_project.wsgi --bind=0.0.0.0:8000 -D
root     17422 17419  0 Oct07 ?        00:00:01 /root/.pyenv/versions/3.6.2/bin/python3.6 /root/.pyenv/versions/3.6.2/bin/gunicorn your_project.wsgi --bind=0.0.0.0:8000 -D
root     21686 21594  0 08:05 pts/0    00:00:00 grep --color=auto gunicorn
$ kill 17419

スクリプトで起動

ただこのままだと毎回めんどくさいので、シェルスクリプトでサーバーを起動、停止するにあるスクリプトを使わせてもらう。

Flaskとかで作ったちょっとしたサーバーの起動/停止用のシェルスクリプト。
gunicornでデーモン状態にしている。
startで起動、stopで終了、restartでstop+start。

your_project.sh
#!/bin/sh
PROGNAME=`basename $0`
BASEDIR=`dirname $0`
PIDFILE=$BASEDIR/$PROGNAME.pid

start() {
  echo "Starting server..."
  cd $BASEDIR
  gunicorn flaskhello:app -p $PIDFILE -D
}

stop() {
  echo "Stopping server..."
  kill -TERM `cat $PIDFILE`
  rm -f $PIDFILE
}

usage() {
  echo "usage: $PROGNAME start|stop|restart"
}

if [ $# -lt 1 ];  then
  usage
  exit 255
fi

case $1 in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart)
    stop
    start
    ;;
esac

以下のgunicorn flaskhello:app -p $PIDFILE -D
gunicorn your_project.wsgi --bind=0.0.0.0:8000 -Dに書き換える。

$ sh your_project.sh start   #起動
$ sh your_project.sh stop    #終了
$ sh your_project.sh restart #再起動

sshを切ってもhttp://(パブリックDNS)が見えたら成功。ひとまず見えるようになった。

staticファイルが見つからない

ただここまで追ってもcss,jsなどstaticファイルは見つかってないはず。以下で見えるようにする。
setting.pyのSTATIC_URL、STATIC_PATHは設定済みでローカルでは見える状態とする。
詳しい解説 -> Django での static files の扱い方まとめ

staticファイルのコピー

manage.pyの上でstaticファイルを指定したディレクトリにコピー

$ python manage.py collectstatic

nginx.confをいじる

nginx.confにstaticファイルのディレクトリを登録する。

/etc/nginx/nginx.conf
server{
    # 〜中略〜
    location /static {
         alias /home/ec2-user/your_project/static;
         #settings.pyで設定したのと同じ場所を記述
    }
    # 〜以下略〜
}

パーミッション変更

ここでnginxとgunicornを再起動する。

$ sh your_project.sh stop
Stopping server...
$ service nginx restart
Stopping nginx:                                            [  OK  ]
Starting nginx:                                            [  OK  ]
$ sh your_project.sh start
Starting server...

するととpermission deniedが出た。これはec2-userにotherからの権限がないせいだった。
参考によると、このディレクトリに移動する権限が必要らしい。
/home/でec2-userに権限を与える。

$ ls -l
drwx------  5 ec2-user ec2-user 4096 Oct  6 04:19 ec2-user
$ chmod o+x ec2-user
$ ls -l
drwx-----x  5 ec2-user ec2-user 4096 Oct  6 04:19 ec2-user

gunicornを再起動すると無事にstaticファイルにアクセスできて終了。

参考

EC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイする
EC2サーバにPython3環境構築
シェルスクリプトでサーバーを起動、停止する
Django での static files の扱い方まとめ
Nginxでつくる、どシンプルな静的コンテンツサーバ

続きを読む

AWS Greengrassを利用する際の「センサー制御部」と「新しいLambda関数のデプロイ」について

9/26のJAWS-UG IoT専門支部主催「AWS Greengrass Handson」を受講し終わったばかりの松下です

受講目的

  1. AWS Greengrass Coreからセンサー制御ってどうするの?
  2. Lambda関数の更新時の手順は?

この2点が前から気になっていました。運用するのに必須だからさ。

結論

AWS Greengrass についてわかった事 (たぶん)

AWS Greengrass Core (エッジデバイス上で動くコンテナプロセス、以下ggc) は、AWS IoT に対するMQTT Proxyサーバ的役割と、Lambda実行環境を持っている。

そのため

  • ggcを起動すると 8883 ポートでMQTT接続を待ち受ける
  • オフラインとなったとしても、センサー制御部プログラム側から見ると、AWS IoTに直接接続しているかの如く動作し続けることができる

AWS Greengrass Coreとセンサー制御部の関係

ggcやその上で動くLambdaからOSが持っているローカルリソース、例えば /dev/ttyUSB0 をRead/Writeすることはできない。(と推測される)

  • そのため、「センサーからReadする→AWS Greengrass SDKを使って)ggcにデータを投げる」というプログラムが必要。また、このプログラムが動くようにする環境が必須
  • また、上記プログラムの活動制御はggcは行ってくれないので、systemdのUnitを作る※、そのプログラム自体の更新の仕組みを考える、等が必須
    • ※ggc上のLambdaを更新する際、ggcのMQTT接続が切れる模様。そうなると、センサー制御部のプログラムは再接続の仕組みを持たせるか、supervisor的なところから再起動してもらう必要がある

Lambda関数の更新時の手順

ggc上で動くLambdaのイメージ

ハンズオンでの構成を再現すると

ThingShadowSensor.py
  (publishするように書く)
     ↓
(8883:sensing/data/Sensor-01)
     ↑
  (subscribeするように設定)
Lambda:cpuUsageChecker-01:VERSION
  (publishするように書く)
     ↓
(8883:$aws/things/Alert-01/shadow/#)
     ↑
  (subscribeするように書く)
ThingAlertSensor.py

わかりづらくてゴメン。何が言いたいかというと、Lambda関数だ~とか言わず、結局のところMQTTのトピックを介してパイプ処理をしている。そして、ggc内はスクリプトじゃなくてLambda関数を実行できるよ、という解釈で大丈夫。(たぶん)

そのため、この行先(=サブスクリプション)に、たとえば Lambda:cpuUsageChecker-01:NEW-VERSION が含まれるエントリー(トピックの組み合わせ)を作ってあげれば NEW-VERSION な Lambda関数が起動する。

手順は以下の通り;

  1. AWS Lambda関数を更新、発行する (新しいバージョンができる)
  2. クラウド側AWS Greengrassの「Lambda」メニューで、新しいバージョンのLambda関数を「追加」する (複数のバージョンが存在することになる)
  3. クラウド側AWS Greengrassの「サブスクリプション」メニューで、新しいバージョンのLambda関数を「ソース」や「ターゲット」とするエントリーを作成し、古いバージョンのLambda関数を利用しているエントリーを消す
    • 消さなくても良い。その代わり、トピックを調整する必要がある
  4. クラウド側AWS Greengrassの「グループ」メニューから、デプロイする

まだわからないこと

  • ggcに対してすっぴんのMQTTクライアント接続が可能なのか? (AWS Greengrass SDKはデカすぎる。センサー制御プログラムは極力小さくしたいはずだ!)

あとがき

市川さんはじめ、全国のJAWS-UG IoT 専門支部の方々、お疲れ様でしたー!!!

続きを読む

Predicting Red Hat Business Value

I. Definition

Project Overview

The history of Open Source project is not so new, however, the faster new technologies are developing and the world is getting globalized, the more people engage in open source projects and the communities are growing. Thanks to the Internet, you are not necessary to be near around the communities to work with them. New innovative ideas are stimulated by people’s interaction across the world and developed as open source projects, e.g. OpenStack, Hadoop and TensorFlow.
But on the other hand, adapting open source products to business operation is not so easy. The development of open source products is very agile and engineers all over the world are always updating the source code. Because of the agility, open source products are always kind of beta version and have some bugs. As a result, there are many companies which offer professional services such as implementation, consulting and maintenance to help businesses utilize open source products.

Today, there are many companies packaging open source products as their service, e.g. Tera Data, HortonWorks, Mirantis and Red Hat. However, they are struggling to scale their business [1]. Even Red Hat, which has been one of the most successful Open Source Platform Business companies since they released their own Linux distribution called “Red Hat Linux” in 1994, the amount of earning is small comparing to other tech giants. For example, Amazon, which started a new cloud service “AWS” a decade ago, earned 12 billion dollars revenue only from AWS in 2016 [2], on the other hand, Red Hat earns just 2 billion dollars revenue from their total business in 2016 [3]. (But the amount is still much bigger than other open source business model based companies).

Problem Statement

Therefore, in order to find ways to maximize their business opportunities, a prediction model is to be created to “accurately identify which customers have the most potential business value for Red Hat” based on their characteristics and activities.”

In the data sets of this project, there are feature characteristics and a corresponding target variable “outcome” which is defined as yes/no binary flag. (It means, a customer’s activity has business value if the outcome of his/her activity is “yes”). Thus, it is necessary to create a binary prediction model which predicts what activities result in positive business outcome or not. As most of the features are categorical data, however, the data should be encoded into numerical values at first. Then, as supervisor data which has several features corresponding with the target “outcome” variable, the data set is passed to binary classification algorithm such as Decision Tree, Logistic Regression and Random Forest. Now, we have no idea which one should be used for this problem, as a result, these algorithms are going to be compared and the most appropriate one is chosen as a base model for the prediction model. When comparing the algorithms, the same metrics would be used to appropriately evaluate their result. After that, by using grid search method, the parameters for the algorithm will be optimized. Then, the prediction model is evaluated again and finally the optimized model is presented.

Metrics

To measure and evaluate the performance of the prediction results, Area under ROC Curve [4] is going to be used as this is a binary classification problem. In ROC curve, true positive rates are plotted against false positive rates. The closer AUC of a model is getting to 1, the better the model is. It means, a model with higher AUC is preferred over those with lower AUC.

II. Analysis

Data Exploration

Data set used in this project contains following data (You can find the data set at Kaggle project page [5]:

  • people.csv: This file contains people list. Each person in the list is unique identified by people_id and has his/her own corresponding characteristics.
  • act_train.csv: This activity file contains unique activities which are identified activity_id. Each activity is corresponding to its own activity characteristics. In the activity file, business value outcome is defined as yes/no for each activity.

Here, there are 8 types of activities. Type 1 activities are different from type 2-7 activities. Type 1 activities are associated with 9 characteristics, on the other hand, type 2-7 activities have only one associated characteristic. And also, in both csv files, except for “date” and “char_38” columns, all data type is categorical so it should be encoded before being processed by binary classification algorithms. Also, as the data sets are separated into 2 csv files, these files should be combined into a merged data set. After the merge, the shape of the training data set is (2197291, 55); 54 predictor features plus 1 target variable.  
Exploratory Visualization
The following chart shows how many activities are performed in each month. According to the chart, “2022/10” has the maximum over 350,000 activities, on the other hand, “2022/07” has the minimum around 50,000 activities. Even though there are some difference in the number of activities among months, it shows no sign of upward trend.

image

Fig. 1 Number of activities per month

The following chart shows how many people joined the community in each month. Even though there are some difference in the number of people join among months, it shows no sign of upward trend. However, as it shows the number of new people join at each month, it means, the number of community member is increasing month by month. According to the data set, the number of people in the data set is 189118 and the average number of people join per month is around 9000. Therefore, this community has more than 50% YoY growth in the number of community member. (It is amazing given the fact most of open source communities are struggling to gather people, however, it is also reasonable to think the similar number of people leave the community as well. But the data set includes no data to investigate it.)

image

Fig. 2 Number of people join per month

The following chart is a very simple overview of the outcome of activities. It shows how many activities result in positive business value (“outcome” = 1) or not. As a result, 44% of activities has positive business impact.

image

Fig. 3 Number of activities per outcome

The following chart is the histogram of community members based on the average outcome of their activities. Maximum is 1, minimum is 0 and the interval is 0.1. What is interesting here is the distribution is completely divided into 2 groups. Some people contribute to the community no matter what activities they do, but on the other hand, some people hardly contribute to the community. Therefore, finding people who has business value for the community is very critical for the prosperity of the community.

image

Fig. 4 Distribution of average outcome by people

In sum, there are 2 things I would like to mention here regarding to the charts. First, this community is a very active community in which constantly new members take part and many activities are performed, moreover, the numbers are really big. So it is reasonable to assume the samples in the dataset are not biased and represent the distribution of Red Hat’s community. Second, the above chart shows whether Red Hat community gains business value or not depends on who performs an activity. Therefore, the issue of this project stated in “Problem Statement” section is an appropriate target to be solved and creating a prediction model is a relevant approach.

Algorithms and Techniques

In order to predict which customers are more valuable for Red Hat business, a binary classification model should be created based on supervised algorithms such as decision tree, logistic regression and random forest tree by using training dataset which includes feature characteristics and a corresponding target variable “outcome” which is defined as yes/no binary flag.

In this project, 3 classification algorithms, Decision Tree [6], Logistic Regression [7] and Random Forest [8] are going to be compared at first. Each of them has its pros and cons, however, all of them can learn much faster than other supervise classification algorithms such as Support Vector Classifier [9]. The dataset of this project has more than 2 million samples with 50+ features, furthermore, 3 algorithms will be compared and grid search method will be used as well in the later phase of this project to optimize parameter. Therefore, choosing CPU and time efficient algorithms is reasonable choice.

These 3 algorithms are widely used for binary classification problem. Logistic Regression gives you a single linear decision boundary which divide your data samples. The pros of this algorithms are low variance and provides probabilities for outcomes, but as the con, it tends to be highly biased. On the other hand, Decision Tree gives you a non-linear decision boundary which divides the feature space into axis-parallel rectangles. This algorithm is good at handling categorical features and also visually intuitive how decision boundary divides your data but likely overfitting. Random Forest is actually an ensemble of decision trees which intends to reduce overfitting. It calculates a large number of decision trees from subsets of the data and combine the trees through a voting process. As a result, Random Forest is less overfitting models but makes it difficult to visually interpret.

Benchmark

This paper is based on the Kaggle project [5] and they set 50% prediction as benchmark. So in this paper, the benchmark is used as well.

III. Methodology

Implementation

The following flow chart shows the overview of the implementation process for creating a binary classification model. In case for reproducing this project, follow the below workflow and refer the code snippets which are cited on this paper.

image

Fig. 5 Overview of implementation flow

Data Preprocessing

As the data sets prepared for this project are separated into 2 csv files, these files should be combined into a merged data set. The action csv file contains all unique activities and each unique activity has a corresponding people ID which shows who performed the activity. Thus, the relation between activity and people ID is one-to-many so that left join method is used for merging datasets on people ID by setting action csv as the left table. The below code is showing very brief overview of this merge implementation.

image

Fig. 6 Code Snippet: Merge people data to activity data

Then, since the most of columns in the dataset is categorical data as described in Data Exploration section, these categorical data should be encoded using label encoding algorithm [10]. Note that the samples in Fig. 7 contains just a few of columns in the dataset. As the dataset actually contains more than 50 features, most of columns are omitted intentionally.

image

Fig. 7 Code Snippet: Encode dataset

image

Fig. 8 Encoded date set samples

Finally, the dataset will be splitted into training and test sets in random order. In this project, 80% of the data will be used for training and 20% for testing.

image

Fig. 9 Code Snippet: Split dataset

Model Selection

In the model selection phase, all parameters of the algorithms compared are set as default. In addition, in order to reduce the time to train algorithms, the training sample size is set to 100,000 samples.

For fitting algorithms and evaluating their predictions, the code below are implemented. Note that, where “classification_algorithm” represents supervised algorithm modules. Also, (features_train, outcome_train, features_test, outcome_test) are corresponding to the variables in Fig. 8 Code Snippet: Splitting dataset.

image

Fig. 10 Code Snippet: Fit and evaluate algorithm

IV. Results

Model Evaluation and Validation

For evaluation, Area under the ROC Curve [11] is used. AUC [4] is the size of area under the plotted curve. The closer AUC of a prediction model is getting to 1, the better the model is. Fig. 10 shows the code snippet for plotting ROC curve based on the prediction results by supervised algorithms. Note that, here “predictions_test” contains prediction results and “outcome_test” is the target score.

image

Fig. 11 Code Snippet: Plot ROC Curve

image

Fig. 12 ROC Curve and AUC of each algorithm 

Refinement

Based on the result of the model selection phase, the most desirable prediction algorithm for this binary classification model is Random Forest, whose AUC value is 0.93.

Next, using grid search method [12], the binary classification model is going to be optimized further. In order to reduce the time for Grid Search, the training sample size is set to 100,000 samples. As optimized parameters, “n_estimator” and “min_sample_leaf” are chosen.

n_estimator: Grid Search parameter set 12, 24, 36, 48, 60
“n_esimator” is the number of trees to calculate. The higher number of trees, the better performance you get, but it makes the model learn slower.

min_samples_leaf: Grid Search parameter set 1, 2, 4, 8, 16
“min_samples_leaf” is the minimum number of samples required to be at a leaf node. The more leaf at a node, the less the algorithm catches noises.

image

Fig. 13 Code Snippet: Grid Search Optimization

image

Fig. 14 Heat Map: Grid Search AUC Score

As a result of Grid Search, the best parameter for the model with the dataset is (“n_estimator”: 60, “min_samples_leaf”: 1). As you can see in Fig. 13 Heat Map, the more “n_estimator” gets bigger, the more the predictions are improved. On the other hand, the increase of “min_samples_leaf” are not effective, or rather, it decreases the prediction performance.

Justification

Finally, there is the optimized parameter set. Therefore, by training the model with the parameter sets and with the all 1,757,832 sample data, the best prediction result should be returned. As a result, the optimized model returned 0.993 AUC score and it is significantly greater than the benchmark (0.5).
In addition, to investigate if the model is robust and generalized enough (it means not overfitting), a new set of prediction results using the classification model was submitted to Kaggle competition (as this paper is based on Kaggle Project). For the submission, another dataset “act_test.csv” provided by Kaggle for the competition is used. Note that “act_test.csv” contains different data from “act_train.csv” which is used for model fitting. As a result, Kaggle evaluated the prediction made by the classification model and returned 0.863 AUC score. Therefore, the binary classification model is generalized enough to make good predictions based on unseen data.

V. Conclusion

Summary

The purpose of this project has been to build a binary classification prediction model for “identify which customers have the most potential business value for Red Hat”. Through the comparison of supervised classification algorithms and the optimization using Grid Search, finally, the best classification models, which is based on Random Forest Classifier, was built and scored 0.993 AUC score which is much higher than the benchmark score 0.5. In sum, using the prediction model, Red Hat can accurately predict which activities performed by its community members likely result in its positive business value. The prediction must be useful for planning its business strategies to maximize their return on investment.
In addition, as the code snippets for implementation is clearly stated throughout this paper, not just for reproducing the model for this specific issue but also it can be applied to similar binary classification issue with some code modification.

VI. Reference

[1] “https://techcrunch.com/2014/02/13/please-dont-tell-me-you-want-to-be-the-next-red-hat/”

[2] “http://phx.corporate-ir.net/phoenix.zhtml?c=97664&p=irol-reportsannual”
[3] “https://investors.redhat.com/news-and-events/press-releases/2016/03-22-2016-201734069”
[4] “http://scikit-learn.org/stable/modules/generated/sklearn.metrics.roc_auc_score.html”
[5] “https://www.kaggle.com/c/predicting-red-hat-business-value/data”
[6] “http://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html”
[7] “http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html”
[8] “http://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestRegressor.html”
[9] “http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html”
[10] “http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html”

[11] “http://scikit-learn.org/stable/auto_examples/model_selection/plot_roc.html”
[12] “http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html”

続きを読む

EC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイする

Nginx + Gunicorn + Supervisorの組み合わせでDjangoアプリケーションを立ち上げたので手順のメモ
今回はOSに何も入っていない状態から始めていきます

環境

OS: Amazon Linux AMI
Python: 3.6.1
Django: 1.11.4
Nginx: 1.10.3
Gunicorn: 19.7.1
Supervisor: 3.3.3

Nginxのインストール

nginxのインストール

$ sudo yum install nginx

nginx起動する

$ sudo nginx

nginx自動起動設定

$ sudo chkconfig --add nginx
$ sudo chkconfig nginx o

自動起動設定確認
以下のようになっていればok

$ chkconfig | grep nginx
nginx           0:off   1:off   2:on    3:on    4:on    5:on    6:off

http://ipアドレスにアクセスしちゃんと起動しているか確認する
以下の通りになっていればOK
スクリーンショット 2017-08-03 13.40.56.png

Python環境の構築

今回はAnacondaで構築した
こちらからPython 3.6 versionをダウンロードする
ダウンロードしたパッケージをCyberduckなどのFTPツールで/home/ec2-userにアップロードする

アップロード完了したら下記コマンドでAnacondaインストールする

$ bash Anaconda3-4.4.0-Linux-x86_64.sh

インストール完了後、Anacondaのコマンドが使えるようにPATHを通す

$ export PATH="$PATH:/home/ec2-user/anaconda3/bin"

condaのコマンドを打って確認

$ conda info -e
# conda environments:
#
root                  *  /home/ec2-user/anaconda3

良さげです

pythonも3.6になっている

$ python --version
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)

Djangoプロジェクトの作成

今回は直接EC2上でプロジェクトを作成します
本来はローカルで開発したDjangoアプリケーションをgit cloneすべき
また、DBもデフォルトのSQliteを使用しますが、実際のサービスを公開するにはPostgresqlやMariaDBを使う

まずはDjangoのインストール
root環境で動かすかどうかは少し議論の分かれるところで、別に環境を作ってDjangoを動かした方がいいんじゃないか、と思ったりもしますが、とりあえず今回はroot環境でインストールしてしまいます

$ pip install django

問題なければプロジェクトを作成

$ django-admin startproject test_project

プロジェクトが作られていることを確認

$ ls -ltr
total 511032
-rw-rw-r--  1 ec2-user ec2-user 523283080 Aug  3 04:50 Anaconda3-4.4.0-Linux-x86_64.sh
drwxrwxr-x 20 ec2-user ec2-user      4096 Aug  3 04:53 anaconda3
drwxrwxr-x  3 ec2-user ec2-user      4096 Aug  3 05:05 test_project

/test_project/test_project/settings.pyのALLOW HOSTを下記の通り編集しておく

settings.py
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ["サーバのIPアドレス"]

下記のコマンドでDjangoを起動
デフォルトだと127.0.0.1:8000がbindアドレスとして使用されているため、オプションで0.0.0.0:8000を追加する必要があります
また、事前にAWSのセキュリティグループで8000番ポートを解放しておく必要があります

$ cd test_project
$ python manage.py runserver 0.0.0.0:8000

そして、http://IPアドレス:8000にアクセスすると以下の通りDjangoアプリケーションにアクセスできる
スクリーンショット 2017-08-03 15.23.25.png

Gunicornのインストール

GunicornはPython製のWSGIサーバ
WSGIサーバというのはWebサーバとWebアプリケーションをつなぐサーバのこと
なので、Nginx <-> Gunicorn <-> Djangoというような構成をイメージしていただければと

まずはGunicornのインストールをやっていく

$ pip install gunicorn

インストールされたらGunicornでDjangoを起動させる

$ gunicorn test_project.wsgi --bind=0.0.0.0:8000

先程と同様、http://IPアドレス:8000にアクセスするとDjangoアプリケーションに接続できる

Nginxの設定の変更

/etc/nginx.confを以下の通り編集する

/etc/nginx.conf

〜中略〜

http {
    〜中略〜

    upstream app_server {
        server 127.0.0.1:8000 fail_timeout=0;
    }

    server {
        #以下4行はコメントアウト
        #listen       80 default_server;
        #listen       [::]:80 default_server;
        #server_name  localhost;
        #root         /usr/share/nginx/html;

        # 以下3行を追加
        listen    80;
        server_name     IPアドレス or ドメイン;
        client_max_body_size    4G;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
            # 以下4行を追加
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass   http://app_server;
        }

    〜以下略〜

編集したら下記コマンドでnginxを再起動する

$ sudo service nginx restart
Stopping nginx:                                            [  OK  ]
Starting nginx:                                            [  OK  ]

これでNginxでのリバースプロキシの設定は完了
今回はnginx.confを直接編集したけれど、設定ファイルをどこか別のところに書いてそれを読み込ませる、という方法でもOK

その後、DjangoをGunicornで立ち上げる

$ gunicorn test_project.wsgi --bind=0.0.0.0:8000

次はhttp://IPアドレスにアクセスするとDjangoの画面が表示されるはず

Supervisorでプロセスをデーモン化する

今の状態だとGunicornのコマンドを中止したり、サーバからログアウトするとアプリケーションが停止してしまう
これを解消するためにSupervisorでGunicornのプロセスをデーモン化する

早速、Supervisorをインストール、としたいところだけれど、SupervisorはPython2系でしか動作しない
そのためAnacondaでPython2系の仮想環境を構築し、その環境にSupervisorをインストールしていく

まずは下記コマンドでSupervisor用のPython2系の仮想環境を作る

$ conda create -n supervisor python=2.7

python2系の環境に切り替えてpipでsupervisorをインストール

$ source activate supervisor
$ pip install supervisor

問題なくインストールできたら、supervisorの設定ファイルを作成し、それを/etc配下に配置する

$ echo_supervisord_conf > supervisord.conf
$ sudo mv supervisord.conf /etc

次にsupervisorの設定を行うため、supervisord.confを下記の通り編集

supervisord.conf
〜中略〜
[supervisord]
logfile=/var/log/supervisord.log ; ログの場所を変更
;logfile=/tmp/supervisord.log ; main log file; default $CWD/supervisord.log #コメントアウト
logfile_maxbytes=50MB        ; max main logfile bytes b4 rotation; default 50MB
logfile_backups=10           ; # of main logfile backups; 0 means none, default 10
loglevel=info                ; log level; default info; others: debug,warn,trace
pidfile=/var/run/supervisord.pid ; 追記
;pidfile=/tmp/supervisord.pid ; supervisord pidfile; default supervisord.pid #コメントアウト

〜中略〜
# includeはコメントアウトされているのでコメント外す
[include]
files = supervisord.d/*.conf ; 起動するプロセスのconfファイルの配置場所
;files = relative/directory/*.ini

ログファイルは作っておき、パーミッションも設定しておく

$ sudo touch /var/log/supervisord.log
$ sudo chown ec2-user /var/log/supervisord.log
$ sudo chgrp ec2-user /var/log/supervisord.log
$ sudo chmod 774 /var/log/supervisord.log

あと、ログローテションも設定しておく

$ sudo sh -c "echo '/var/log/supervisord.log {
       missingok
       weekly
       notifempty
       nocompress
}' > /etc/logrotate.d/supervisor"

次にデーモン化するプロセスのコマンドを記載したファイルを作っていく
まずはそれらのファイルを配置するディレクトリを作成

$ sudo mkdir /etc/supervisord.d

/etc/supervisord.d配下にdjango_app.confを作成
ここに、Gunicornのプロセスをデーモン化するための設定を以下のように書く

django_app.conf
[program:django_app]
directory=/home/ec2-user/test_project
command=gunicorn test_project.wsgi --bind=0.0.0.0:8000
numprocs=1
autostart=true
autorestart=true
user=ec2-user
redirect_stderr=true

directoryに実行するディレクトリを指定、commandのところにプロセスを起動するためのコマンドを指定する

ここまでできたら下記のコマンドでsupervisorを立ち上げる

$ supervisord

次に、confファイルを読み込ませる
こちらは仮にconfを修正などする場合は必ず実行する

$ supervisorctl reread

なお、下記のコマンドでデーモンを再起動し、そのタイミングでもconfが読み込まれたりする

$ supervisorctl reload

下記のコマンドでGunicornのプロセスをデーモン化する

$ supervisorctl start django_app

仮にdjango_app: ERROR (already started)というメッセージが出た場合は、以下のコマンドでプロセスの再起動をしたり、停止をしてからstartしたりする

$ supervisorctl stop django_app # 停止
$ supervisorctl restart django_app # 再起動

さて、この状態でサーバからログアウトしてみる
そして、http://IPアドレスにアクセスすると、Djangoの画面が表示される
GunicornのプロセスがSupervisorによりデーモン化されていることになる

よかったですね

続きを読む

EC2(AMI)にnginx + uWSGI + supervisorでDjangoアプリケーションをデプロイする

これがベストプラクティスかどうなのかわかりませんが、おれはこうやりました、って話
もし、これでうまくいかなかったらコメントください

環境

  • Python:3.6.0(anacondaで構築)
  • Django:1.10.2
  • nginx:1.11.10
  • Amazon Linux AMI

今回はAMIでやりましたが、多分CentOSでも大丈夫なはず

大雑把な流れ

1.uWSGIのインストール & uWSGIでDjangoアプリケーションを立ち上げる
2.nginx + uWSGIでアプリケーションを立ち上げる
3.supervisorでプロセスをdaemon化する

uWSGIのインストール & uWSGIでDjangoアプリケーションを立ち上げる

まずはuWSGIをpipでインストール

$ pip install uwsgi

もしもインストールがうまくいなかったりしたらuwsgiをEC2インスタンスにインストールしようとして失敗した話 – Qiitaを参考にしてみましょう

uWSGIからDjangoアプリケーションを立ち上げる

インストールができたら、下記コマンドでuWSGIからDjangoアプリケーションを立ち上げてみる
(実行するディレクトリはDjangoプロジェクトのルートディレクトリで)

$ uwsgi --http :8000 --module プロジェクト名.wsgi

この状態でhttp://[EC2のパブリックIP]:8000にブラウザからアクセスすれば、アプリケーションに接続できるはず

nginx + uWSGIでアプリケーションを立ち上げる

nginxのuwsgiモジュール用の設定を行う

Djangoプロジェクト直下にuwsgi_paramsというファイルを作成する
ファイルの中身は下記の通り

uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

nginx用のconfファイルを作成

Djangoプロジェクト直下にnginx用のconfファイルを作成
今回はmyweb_nginx.confという名前にしています

django_project/myweb_nginx.conf
upstream django {
    server 127.0.0.1:8000;
}

server { 
    listen      80;
    server_name EC2のパブリックIP or ドメイン;
    charset     utf-8;
    client_max_body_size 100M;

    location /static {
        alias /django_projectのディレクトリ(フルパス)/static;
    }

    location / {
        uwsgi_pass  django;
        include    /django_projectのディレクトリ(フルパス)/uwsgi_params;
    }

}

myweb_nginx.confを/etc/nginx/nginx.confに読み込ませる

$ sudo vi /etc/nginx/nginx.confhttp {}内を編集
include /etc/nginx/sites-enabled/*;を追加する

/etc/nginx/nginx.conf
http {
    ...中略...
    include /etc/nginx/sites-enabled/*; #ここを追加

    server {
    ...中略...

sites-enabledにシンボリックリンクを貼る

$ mkidr /etc/nginx/sites-enabledでsites-enabledを作成
さらに下記コマンドでmyweb_nginx.confを/etc/nginx/sites-enabled/にシンボリックを貼る

$ sudo ln -s /django_projectのディレクトリ(フルパス)/myweb_nginx.conf /etc/nginx/sites-enabled/

nginxを再起動し、Djangoアプリケーションを立ち上げる

$sudo /etc/init.d/nginx restartでnginxを再起動
下記のコマンドをDjangoプロジェクト直下で実行する

$ uwsgi --socket :8000 --module プロジェクト名.wsgi

この状態でhttp://[EC2のパブリックIP]にブラウザからアクセスすれば、アプリケーションに接続できるはず

補足:静的ファイルの読み込み

上記の状態でアプリケーションに接続した場合、staticディレクトリに配置しているcssなどが適用されていないはず
settings.pyにSTATC_ROOTを指定する必要がある

settings.py
STATIC_ROOT = os.path.join(BASE_DIR, "static/")

下記コマンドでstaticファイルをSATIC_ROOTで指定したディレクトリにまとめる

$ python manage.py collectstatic

supervisorでプロセスをdaemon化する

python2系への切り替え

supervisorはpython3系では動いてくれないので、anacondaでpython2.7の仮想環境を構築していく

$ conda create -n supervisor python=2.7 anaconda

仮想環境への切り替えは

$ source activate supervisor

でおこない、抜ける場合は

$ source deactivate

でおこなう

supervisorのインストール

python2系の状態でsupervisorをインストールする

$pip install supervisor

supervisord.confの生成

ホームディレクトリで$echo_supervisord_conf > supervisord.confを実行
さらに$ sudo mv supervisord.conf /etcを実行し、supervisord.confを/etcに配置する

supervisord.dディレクトリの作成

/etc配下にsupervisord.dを作成する

$ sudo mkdir /etc/supervisord.d

supervisorログの設定

/var/logにログを吐かせるため、supervisord.confを編集
/etc/supervisord.conf
[supervisord]
;logfile=/tmp/supervisord.log
logfile=/var/log/supervisor/supervisord.log

ディレクトリの作成とlogファイルの作成

$ sudo mkdir /var/log/supervisor
$ sudo vi /var/log/supervisor/supervisord.log

また、下記のコマンドでログローテションを設定する

$ sudo sh -c "echo '/var/log/supervisor/*.log {
       missingok
       weekly
       notifempty
       nocompress
}' > /etc/logrotate.d/supervisor"

pid,includeの設定

supervisord.confを編集する

/etc/supervisord.conf
[supervisord]
...中略...
# pidファイルの配置場所の指定
;pidfile=/tmp/supervisord.pid
pidfile=/var/run/supervisord.pid

# ...中略

# includeセクションがコメントアウトされているので、コメントインして下記の用に修正。
[include]
files = supervisord.d/*.conf

supervisord.d/*.confの作成

supervisord.d配下のconfファイルにデーモン化するプロセスを指定する
今回はuWSGI + nginxでのDjangoアプリケーションの立ち上げをデーモン化する

/etc/supervisord.d/django.conf
[program:django_app] ;
directory=/djangoアプリケーションのディレクトリ(フルパス)/;
command=/home/ec2-user/anaconda3/bin/uwsgi --socket :8000 --module プロジェクト名.wsgi ;
numprocs=1 ;
autostart=true ;
autorestart=true  ;
user=ec2-user  ;
redirect_stderr=true  ;

supervisorの再起動->サービス立ち上げ

$ supervisordでsupervisorのサービスを起動
$ supervisord rereadで設定ファイルの再読み込み
$ supervisord reloadでsupervisorの再起動
$ supervisord start django_appでDjangoアプリケーションの立ち上げるプロセスがデーモン化される

この状態でhttp://[EC2のパブリックIP]にブラウザからアクセスすれば、アプリケーションに接続でき、且つサーバからログアウトした状態でもプロセスが死なない

参考リンク

Supervisorで簡単にデーモン化 – Qiita
EC2上で Django + Nginx + uWSGI を試す – Qiita

続きを読む

redashでAWS Athena使おうとしたら、DataSourceに出てこなくてはまったので解決策

redashでAWS Athena使おうとしたら、はまったので解決策

使うぞって思ってたら、
Data SourceのType欄に出てこなかったので
解決策を記載しておきます:smiley:

※すべて2017/5/18現在の事象です

場面

今回は以下の場面です

  • AWSのEC2のAMIを利用
  • ソースをダウンロードしてインストール

Hostedでは、問題なく選択できました

redashのインストール

redashのインストール方法を記載しておきます

パターン1. AWS EC2でredashのAMIを利用する

  • EC2インスタンス作成時に、redashで提供されているAMIを選択する

Setting up a Redash Instance

※現時点(2017/5/18)では、1.0.1+b2833 がインストールされます

パターン2. ソースを持ってきてインストールする

  • EC2インスタンス作成時に、OSは、ubuntuを選択
  • 以下を実行

ソースゲット

cd /usr/local/src/

wget https://raw.githubusercontent.com/getredash/redash/master/setup/ubuntu/bootstrap.sh

スクリプト実行

sh bootstrap.sh

アップグレード

cd /opt/redash/current
sudo bin/upgrade

確認

ls -l /opt/redash/

※現時点(2017/5/18)では、1.0.3.b2850 にアップグレードされます

本題のAWS Athenaへの接続

現象

上記2つの方法で設定すると以下の画像のように
AWS Athenaが出てきません:cry:


redash01.png


調査

/opt/redash/redash.1.0.3.b2850/redash/query_runner/athena.py

ファイルあるしなぁと調査したり

解決策

公式のサーポートに書いてました:joy:

Support for AWS Athena

方法

/opt/redash/.env

に以下を追加

export REDASH_ADDITIONAL_QUERY_RUNNERS="redash.query_runner.athena"

再起動して、反映

/etc/init.d/supervisor reload

するとredash02.png

出てきました!:tiger::tiger::tiger:

あとは、
AWS AthenaへのKey等を設定してください~

※注意※

redashのバージョンが、0.12以下の場合はエラーになるので注意!

続きを読む