AWSでRDSを利用せずに永続化データベースを利用するWEBアプリケーションサンプル

最近知ったのですが、EC2にS3のバケットをマウントすることが出来るようです。
つまり、S3にRDBMSのファイルを置いて、そこに対してシステムがアクセスするようにすればRDSを利用せずに永続化したデータを利用することが出来るようになります。

# S3をマウントする

[s3fs-fuse](https://github.com/s3fs-fuse/s3fs-fuse)というライブラリを利用することで、S3のバケットをEC2にマウントすることが出来るようになります。

ubuntu14.04を対象にインストールからセットアップの手順を記載します。
（GithubのReadmeの内容がほぼそのまま書いているだけです。）

## インストール

```
sudo su root

apt-get update
apt-get install automake autotools-dev g++ git libcurl4-gnutls-dev libfuse-dev libssl-dev libxml2-dev make pkg-config
cd /usr/local/src
git clone https://github.com/s3fs-fuse/s3fs-fuse.git
cd s3fs-fuse
./autogen.sh
./configure
make
sudo make install
```

## セットアップ

S3上に、__s3-mount__という名前のバケットを作成してあること、
バケットの下に__test__というフォルダが作成してあること、
また、IAMユーザーでS3にのみアクセス出来るユーザーを作成し、アクセストークンとシークレットキーを取得していることを前提とします。

```
sudo su root

echo '[AWS_ACCESS_TOKEN]:[AWS_ACCESS_SECRET]' > /etc/passwd-s3fs
mkdir -p /mnt/s3fs
s3fs s3-mount:/test /mnt/s3fs -o allow_other
```

ここまで、行えばs3のバケットをマウントできているはずです。

```
df -h |grep s3fs
s3fs            256T     0  256T   0% /mnt/s3fs
```

256TBもある！

# DBをS3に作成して、アプリケーションから接続する

## sqlite3でDB作成

MySQLやPostgresqlでもdataを置く場所をS3にしておけば動くんじゃないかなぁと思いますが、試してはいません。
今回は、簡単に利用出来るsqlite3でDBを作成しました。

```
# インストール
sudo apt-get install sqlite3

# DB、テーブル作成
sqlite3 /mnt/s3fs/sample.db
sqlite> create table users(name text, age integer);
sqlite> .table
users
sqlite> .exit
```

## sinatraで簡易的なWEBアプリケーションを作成

rubyのインストールについては割愛します。

```
# 利用するgemをインストール
gem install sinatra unicorn sqlite3 slim --no-ri --no-rdoc
```

アプリケーションのディレクトリ構成は下記のようにしました。
アプリケーションサーバーには安定性重視で、１プロセスが１リクエストしか対応しないUnicornを選んでいます。

```
/var/www/app
  logs/
  pids/
  sockets/
  config.ru
  unicorn.rb
  main.rb
```

```rb:unicorn.rb
worker_processes 1
timeout 180

root_dir = Dir.pwd
listen      File.join(root_dir, 'sockets', 'app.sock')
pid         File.join(root_dir, 'pids', 'app.pid')
stderr_path File.join(root_dir, 'logs', 'app.log')
```

```rb:config.ru
require 'rubygems'
require 'sqlite3'
require 'sinatra'
require './main.rb'

run Sinatra::Application
```

```rb:main.rb
require 'slim'

before do
  @db = SQLite3::Database.new('/mnt/s3fs/sample.db')
end

after do
  @db.close
end

get '/users' do
  @users = @db.execute('SELECT * FROM users')

  slim <<-SLIM
  h1 Users
  div
    a href="/users/new" Create
  - if @users.empty?
    p No data.
  - else
    table
      tr
       th name
       th age
      - @users.each do |user|
        tr
          td= user[0]
          td= user[1]
  SLIM
end

get '/users/new' do
  slim <<-SLIM
　div
    a href="/users" List
  form method="POST" action="/users"
    div
      label for="name" Name
      input type="text" id="name" name="name"
    div
      label for="age" Age
      input type="number" id="number" name="age" minimum="0"
    div
      button type="submit" Submit
  SLIM
end

post '/users' do
  @db.execute(
    'INSERT INTO users(name, age) VALUES(?, ?)',
    [params['name'], params['age']]
  )
  redirect to('/users')
end
```

こんな感じで、コードを準備したら、

```
unicorn -c unicorn.rb
```

とやればアプリケーションサーバーが起動します。

# WEBサーバーを用意する

nginxでアプリケーションサーバーにプロキシしてあげれば完成です。

```
sudo apt-get install nginx
```

```diff:/etc/nginx.conf
  user www-data;
- worker_processes 4;
+ worker_processes auto;
  pid /run/nginx.pid;
    .
    .
    .
  ##
  # Virtual Host Configs
  ##

  include /etc/nginx/conf.d/*.conf;
- include /etc/nginx/sites-enabled/*;
+ include /etc/nginx/sites-enabled/*.conf;
    .
    .
    .
```

```nginx:/etc/nginx/sites-enabled/app.conf
upstream unicorn {
    server unix:/var/www/app/sockets/app.sock fail_timeout=0;
}

server {
    listen 80;
    server_name loalhost;
    root /var/www/app/;

    location / {
        try_files @uri @uri/ @app;
    }

    location @app {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://unicorn;
    }

    error_page 404 /404.html;
    error_page 422 /422.html;
    error_page 400 401 403 500 502 503 504 /500.html;
}
```

Nginxの設定を作成したら再起動します。

```
sudo nginx -t && sudo service nginx reload
```

ここまでできたら、EC2に付与されたパブリックDNSなり、パブリックIPなりでシステムにアクセス出来るようになります。

# まとめ

* S3にデータを置くことで、バックアップなどの運用を気にせずにDBを持つことが出来るようになります。
* RDSを利用しないので、発生する料金はS3の使用量と、データ転送量のみで割安です。
* S3とEC2との間の通信には結構な時間がかかるので、処理時間は遅いです。
* 身内で公開するレベルの小さなシステムであれば、非常に手軽に作成できて、運用費もそこまでかからないので、このような構成を選択するのは全然ありだなぁという感想でした。