[AWS][Python3]SESでIAM認証情報を変換してSMTP 認証情報を取得する

SESをSMTPを利用して送信したい

SESを利用するならばboto3を利用するのが一番簡単かと思いますが,
ローカル開発環境ではMailHog等の開発用SMTPサーバを用いて動作確認を行なっている環境で
汎用的なsmtplibをテスト・本番で利用したいというのがそもそものはじまり

SMTPを利用するには

通常のsmtp同様以下のような形で行えます

email.py
def send_mail(self, host, user, password, msg):
  smtp_con = smtplib.SMTP_SSL(host=host)
  smtp_con.ehlo()
  smtp_con.login(user, password)
  result = smtp_con.send_message(msg=msg)
  smtp_con.close()

が、ここで渡すpasswordが曲者

以下のリンクを見るとわかりますが
通常boto3などのAPI経由であれば、パスワードにはIAMのAWS_SECRET_ACCESS_KEYを用いるが
SMTP認証情報にはAWS_SECRET_ACCESS_KEY を Amazon SES SMTPパスワードに変換したものを利用する必要があります
Amazon SES SMTP 認証情報の取得

新たにSMTP認証情報を持つIAMユーザを作る方法もありますが
今回はAWS_SECRET_ACCESS_KEY->SMTPCredentialsの変換を行います

Amazon SES SMTP Credentialsを python3 で生成する

実際に利用したコードがこちら
keyにAWS_SECRET_ACCESS_KEYを渡してあげればSMTP認証に利用できるパスワードが返却される

aws_ses_hash.py
def hash_smtp_pass_from_secret_key(self, key):
    message = "SendRawEmail"
    sig_bytes = bytearray(b'x02')

    h = hmac.new(key.encode(), message.encode(), digestmod=hashlib.sha256)
    digest = h.hexdigest()

    sig_bytes.extend(bytearray.fromhex(digest))

    return base64.b64encode(sig_bytes).decode()

ちなみにpython2であれば以下が参考になります
■[tips][aws][python][ruby] Amazon SES SMTP Credentialsをpythonやrubyで作ってみる

続きを読む

何となくEC2(Amazone Linux)にMariaDBを入れてSpiderで水平Shardingしてみた

雑記と言うか備忘録と言うか、そんな感じでEC2にMariaDBの10.0系をyum経由で導入し、水平Sharding環境を構築しました

#RDS使えばいいんですけど、高いんですよね。それなりにお試しでならEC2を3台構成にすれば水平Shardingは遊べますし。

※構成例

MariaDB:USER / PASS = root / Spider
SERVER:三台 Spiderサーバ:1台 DATANODE:2台
※ IPは仮として
SpiderNode:192.168.10.10
Datanode1:192.168.10.11
Datanode2:192.168.10.12
としますので読み替えてご活用下さい
DB:example_db
table:books
使うエンジン:Spider&Innodb

下準備

yum -y update && shutdown -r now

1.MariaDBインストール
●MariaDBのレポジトリ追加

echo '# MariaDB 10.0 CentOS repository list - created 2014-04-02 07:21 UTC' >> /etc/yum.repos.d/mariadb.repo
echo '# http://mariadb.org/mariadb/repositories/' >> /etc/yum.repos.d/mariadb.repo
echo '[mariadb]' >> /etc/yum.repos.d/mariadb.repo
echo 'name = MariaDB' >> /etc/yum.repos.d/mariadb.repo
echo 'baseurl = http://yum.mariadb.org/10.0/centos6-amd64' >> /etc/yum.repos.d/mariadb.repo
echo 'gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB' >> /etc/yum.repos.d/mariadb.repo
echo 'gpgcheck=1' >> /etc/yum.repos.d/mariadb.repo
echo 'enable=1' >> /etc/yum.repos.d/mariadb.repo

●MariaDBの起動

yum install -y MariaDB-server MariaDB-client

●MariaDB起動

chkconfig mysql on
/etc/rc.d/init.d/mysql start

2.MariaDBの初期設定
●設定適用(対象:全サーバ)


/usr/bin/mysqladmin -u root password 'Spider'
mysql -u 'root' --password='Spider' -e 'CREATE DATABASE example_db;GRANT ALL PRIVILEGES ON *.* TO "root"@"192.168.10.%" IDENTIFIED BY "Spider";FLUSH PRIVILEGES;'

●設定適用(対象:Spiderサーバのみ)
▼MariaDBへのspiderエンジンのインストール

mysql -u 'root' --password='Spider' -e 'source /usr/share/mysql/install_spider.sql'

確認は mysql -u 'root' --password='Spider' -e 'show engines;' にて

●データノードのspiderサーバへの登録(対象:Spiderサーバのみ)

mysql -u 'root' --password='Spider' -e 'CREATE SERVER db1 FOREIGN DATA WRAPPER mysql OPTIONS (USER "root", PASSWORD "Spider", HOST "192.168.10.11", PORT 3306);'
mysql -u 'root' --password='Spider' -e 'CREATE SERVER db2 FOREIGN DATA WRAPPER mysql OPTIONS (USER "root", PASSWORD "Spider", HOST "192.168.10.12", PORT 3306);'

こちらも確認は mysql -u 'root' --password='Spider' -e 'select * from servers;' mysql にて

●スキーマの登録(対象:SpiderNodeのみ)

    CREATE TABLE books
    (
        id int AUTO_INCREMENT NOT NULL,
        name VARCHAR(255) NOT NULL,
        price int(11) NOT NULL default 0,
        created_at DATETIME NOT NULL,
        updated_at DATETIME,
        lock_version int(11) NOT NULL default 0,
        PRIMARY KEY (id)
    ) ENGINE = SPIDER DEFAULT CHARSET=utf8
    PARTITION BY HASH(id) (
      PARTITION p1 comment 'server "db1", table "books"',
      PARTITION p2 comment 'server "db2", table "books"'
    );

●スキーマの登録(対象:db1,db2)

    CREATE TABLE books
    (
        id int AUTO_INCREMENT NOT NULL,
        name VARCHAR(255) NOT NULL,
        price int(11) NOT NULL default 0,
        created_at DATETIME NOT NULL,
        updated_at DATETIME,
        lock_version int(11) NOT NULL default 0,
        PRIMARY KEY (id)
    ) ENGINE = SPIDER DEFAULT CHARSET=utf8
    ;

●Spiderサーバの「my.cnf」設定変更(対象:Spiderサーバのみ)

echo '[mysqld]' >> /etc/my.cnf
echo 'spider_internal_sql_log_off = ON' >> /etc/my.cnf
echo 'spider_remote_sql_log_off   = 1' >> /etc/my.cnf

●Spiderサーバの「my.cnf」設定変更(対象:Spiderサーバのみ)

/etc/rc.d/init.d/mysql restart

お仕舞。

テスト:以下10行のクエリを発行する
※クエリの発行はSpiderサーバのexample_dbで実施

    INSERT INTO books(name, price, created_at) VALUES ('3日で分かるJava', 2500, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('3日で分かるRuby', 2300, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('独習仮想化',      5000, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('Java入門',        2000, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('入門Ruby',        2800, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('Effective Ruby',  4200, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('すごいRuby',      5800, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('Ruby徹底入門',    3000, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('RubyからJavaへ',  1800, NOW());
    INSERT INTO books(name, price, created_at) VALUES ('クラウド大全',    6000, NOW());

●Spiderノード上で全件Select

  MariaDB [example_db]> select * from books order by id;
  +----+----------------------+-------+---------------------+------------+--------------+
  | id | name                 | price | created_at          | updated_at | lock_version |
  +----+----------------------+-------+---------------------+------------+--------------+
  |  1 | 3日で分かるJava      |  2500 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  2 | 3日で分かるRuby      |  2300 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  3 | 独習仮想化           |  5000 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  4 | Java入門             |  2000 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  5 | 入門Ruby             |  2800 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  6 | Effective Ruby       |  4200 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  7 | すごいRuby           |  5800 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  8 | Ruby徹底入門         |  3000 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  9 | RubyからJavaへ       |  1800 | 2017-09-13 00:06:51 | NULL       |            0 |
  | 10 | クラウド大全         |  6000 | 2017-09-13 00:06:51 | NULL       |            0 |
  +----+----------------------+-------+---------------------+------------+--------------+
  10 rows in set (0.01 sec

●db1ノード上で全件Select

  MariaDB [example_db]> select * from books order by id;
  +----+----------------------+-------+---------------------+------------+--------------+
  | id | name                 | price | created_at          | updated_at | lock_version |
  +----+----------------------+-------+---------------------+------------+--------------+
  |  2 | 3日で分かるRuby      |  2300 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  4 | Java入門             |  2000 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  6 | Effective Ruby       |  4200 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  8 | Ruby徹底入門         |  3000 | 2017-09-13 00:06:51 | NULL       |            0 |
  | 10 | クラウド大全         |  6000 | 2017-09-13 00:06:51 | NULL       |            0 |
  +----+----------------------+-------+---------------------+------------+--------------+
  5 rows in set (0.00 sec)

●db2ノード上で全件Select

  MariaDB [example_db]> select * from books order by id;
  +----+----------------------+-------+---------------------+------------+--------------+
  | id | name                 | price | created_at          | updated_at | lock_version |
  +----+----------------------+-------+---------------------+------------+--------------+
  |  1 | 3日で分かるJava      |  2500 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  3 | 独習仮想化           |  5000 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  5 | 入門Ruby             |  2800 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  7 | すごいRuby           |  5800 | 2017-09-13 00:06:51 | NULL       |            0 |
  |  9 | RubyからJavaへ       |  1800 | 2017-09-13 00:06:51 | NULL       |            0 |
  +----+----------------------+-------+---------------------+------------+--------------+
  5 rows in set (0.00 sec)

出来てますね

続きを読む

開発用サーバーを作る on AWS(Ruby on Rails 5)

はじめに

まぁそのまんま。
こちらのRubyonRails編。

Ruby 2.4.2
Ruby on Rails 5.1.4
なり。

Amazon Linux AMI+Nginx+Unicorn
やで。

インストールなど

sudo yum update
sudo yum -y install git
sudo yum install nodejs --enablerepo=epel
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
sudo yum install gcc openssl-devel readline-devel sqlite-devel
rbenv install 2.4.2
rbenv global 2.4.2
gem update --system
gem install --no-ri --no-rdoc rails
gem install bundler
gem install sqlite3
gem install unicorn
rbenv rehash
rails -v
rails new /var/www/html/sample
sudo mkdir /var/run/unicorn
sudo chmod 777 /var/run/unicorn
sudo chown -R nginx:ec2-user /var/www/html
sudo chmod 2777 /var/www -R

まぁ、だいぶ時間がかかりまっせ。

sample/config/unicorn.rbを作成

application = 'sample'

worker_processes 2
timeout 15

pid "/var/run/unicorn/unicorn_#{application}.pid"
listen "/var/run/unicorn/unicorn_#{application}.sock"

preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
  puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
  Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
  ActiveRecord::Base.connection.disconnect!end

after_fork do |server, worker|
  Signal.trap 'TERM' do
  puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
  ActiveRecord::Base.establish_connection
end

stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])

起動

bundle exec unicorn_rails -c config/unicorn.rb -E development -D

OSの再起動対応

~/.bashrcに追記

sudo mkdir -p /var/run/unicorn && sudo chmod 777 /var/run/unicorn

/etc/nginx/conf.d/sample.conf作成

upstream unicorn {
  server unix:/var/run/unicorn/unicorn_sample.sock;
}
server {
    listen 8080;
    server_name localhost;
    root /var/www/html/sample;

    access_log /var/log/nginx/myapp_access.log;
    error_log /var/log/nginx/myapp_error.log;

    try_files $uri/index.html $uri @unicorn;
    location @unicorn {
        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;
    }
}

PHPを80で動かしてる都合上8080でごわす。

Nginx再起動

sudo service nginx restart

いじょー
Security Groupで8080はあけておきましょうね。

by 株式会社Arrvis

続きを読む

awsで`yum install sqlite-devel`できない

gem install sqlite3 -- --with-sqlite3-dir=/opt/localを実行しようとしたら以下のようなエラーメッセージが。。。

Building native extensions with: '--with-sqlite3-dir=/opt/local'
This could take a while...
ERROR:  Error installing sqlite3:
    ERROR: Failed to build gem native extension.

    current directory: /home/test_user/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/sqlite3-1.3.13/ext/sqlite3
/home/test_user/.rbenv/versions/2.4.0/bin/ruby -r ./siteconf20171006-4484-7fx7vj.rb extconf.rb --with-sqlite3-dir=/opt/local
checking for sqlite3.h... no
sqlite3.h is missing. Try 'brew install sqlite3',
'yum install sqlite-devel' or 'apt-get install libsqlite3-dev'
and check your shared library search path (the
location where your sqlite3 shared library is located).
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/home/test_user/.rbenv/versions/2.4.0/bin/$(RUBY_BASE_NAME)
    --with-sqlite3-config
    --without-sqlite3-config
    --with-pkg-config
    --without-pkg-config
    --with-sqlite3-dir
    --with-sqlite3-include
    --without-sqlite3-include=${sqlite3-dir}/include
    --with-sqlite3-lib
    --without-sqlite3-lib=${sqlite3-dir}/lib

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /home/test_user/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/extensions/x86_64-linux/2.4.0-static/sqlite3-1.3.13/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /home/test_user/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/sqlite3-1.3.13 for inspection.
Results logged to /home/test_user/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/extensions/x86_64-linux/2.4.0-static/sqlite3-1.3.13/gem_make.out

yum install sqlite-develを実行したところ

Loaded plugins: priorities, update-motd, upgrade-helper
You need to be root to perform this command.

と怒られたのでHow Can I Enable Root SSH Access in An Amazon EC2 Instance?を参考にsudo suを実行してyum install sqlite-develを実行。問題解決!

続きを読む

[Ruby] aws-sdk v3.0.1 で S3 にファイルアップロード

本題

aws-sdk を使ってS3にファイルアップロードする。
version 3.0 を使っている日本語記事が見つからなかったので書く。

install

Gemfileに以下を追加
gem 'aws-sdk'
bundle install
$ bundle install

How To Use

require 'aws-sdk'
class Caws
  def initialize
    @bucket_name = 'bucketname'

    Aws.config.update({
        region: 'ap-northeast-1',
        credentials: Aws::Credentials.new('ACCESS-KEY', 'SECRET-KEY')
    })

    s3 = Aws::S3::Resource.new
    @bucket = s3.bucket(@bucket_name)
  end

  def send(s3_path,upd_path,file_name)
    o = @bucket.object(s3_path + '/' + file_name)
    o.upload_file(upd_path + '/' + file_name)
  end
end

ACCESS-KEY とか SECRET-KEY はここで取得できた。
https://console.aws.amazon.com/iam/home?#/security_credential

How to use send

  • s3_path : s3 の指定したbucket上のpath
  • upd_path : ローカルにあるファイルパス
  • file_name : ファイルネーム

Test

class CawsTest < Test::Unit::TestCase

  def setup
    @aws = Caws.new
  end

  def test_upd
    @aws.send("s3/file/path","/local/file/path", "file.txt")
  end
end

Reference

http://docs.aws.amazon.com/sdk-for-ruby/v3/api/index.html

続きを読む

AWS Elasticsearch Serviceが律儀にgzipを返すようになって死んだ [解決済み]

エラー内容

今朝起きたら、 Elasticsearch + Rails にこんなエラーが起こっていた。

MultiJson::ParseError: unexpected character at line 1, column 1 [parse.c:664]

なんと、いままで Accept-Encoding を無視していた AWS Elasticsearch Service が、急に仕事をしだして、 gzip 形式で返すようになった模様。

追記: なお、 elasticsearch-rails で利用されている faraday は、 Accept-Encoding: gzip がデフォなようです。

バージョン

追記しました

elasticsearch (5.0.0)
elasticsearch-api (5.0.0)
elasticsearch-transport (5.0.0)
elasticsearch-dsl (0.1.4)
elasticsearch-model (0.1.9)
elasticsearch-rails (0.1.9)
elasticsearch-transport (5.0.0)
faraday (0.9.2)
faraday_middleware (0.12.2)
faraday_middleware-aws-signers-v4 (0.1.5)

最新版からは若干古いです。

解決法

faraday_middleware gemを使って、gzipを受け取れるように変更する。

Gemfile

...
gem 'faraday_middleware'
...

config/initializers/aws.rb


Elasticsearch::Model.client = Elasticsearch::Client.new(
  host: ENV.fetch('ELASTICSEARCH_ENDPOINT'),
  port: 80
) do |faraday|
  faraday.use FaradayMiddleware::Gzip # ここを追加
  faraday.request :aws_signers_v4,
                  credentials: Aws::Credentials.new(ENV.fetch('AWS_KEY_ID'), ENV.fetch('AWS_ACCESS_KEY')),
                  service_name: 'es',
                  region: 'ap-northeast-1'
  faraday.adapter Faraday.default_adapter
end

被害者の会

他にも被害者はいる模様。
https://forums.aws.amazon.com/thread.jspa?threadID=223784

Posted on: Sep 26, 2017 10:56 AM

Just my personal experience –
Without any client changes, one day the ES responses stopped working. When I inspected the response, it was gzipped. The client library I was using requested gzip. Since I didn’t make any changes to my client code, I am guessing that the following things are true, but none of them are really proven:

a) the client library had always been requesting gzipped responses ( evidence: https://github.com/elastic/elasticsearch-ruby/issues/457#issuecomment-326204925 )
b) the client library was not equipped to receive gzipped responses ( evidence: my code doesn’t work )
c) the AWS ES service used to ignore the request for gzipped response, and now it honors it. ( evidence: my code used to work )

I was not able to figure out how to get my client library to stop requesting gzipped responses (seems it would require modifying the faraday library), but by changing my code to look like https://github.com/elastic/elasticsearch-ruby/issues/457#issuecomment-326020618 , I was able to get my client library to understand the gzipped response.

Hope those data points help.

ただ、時系列がずれているので、AWSが順次アップデートをしていったのだろうか?
詳細はAWSにコンタクトする予定。

こちらは、ほぼ同時刻の被害者の方
https://qiita.com/s_nakamura/items/49b19f2544aa61229bbc

コメント

gzip対応してないのにAccept-encodingでgzip投げてた利用者側が悪いのでは?

仰る通りです。。
OSSのライブラリを使っている以上、そのライブラリの挙動の責任は利用者にありますね。この記事のタイトルから「AWSやべー」という声が聞こえてきそうですが、どちらかと言うと、「いままでgzip無視しててごめんね、今朝直しといたから」と言った感じですね。

Amazon Elasticsearchのgzipの件、全ノードでgzipが返ってくるわけじゃなくてあるノードでだけgzip返ってくる罠
挙動が戻ってまたエラーになったら…

レスポンスがgzipでない場合、何もせず通過させるので、この変更を入れておけば、仮に挙動がもとに戻っても大丈夫なはずです。

https://github.com/lostisland/faraday_middleware/blob/master/lib/faraday_middleware/gzip.rb#L25

追記 10/4 11:15 p.m. JST

とここまで書いてて気づいたが、 faraday_middleware によると、 Faraday.default_adapter == :net_http を使うと、Gzipは自動解凍されるよう。。。なんだこれ

  # This middleware is NOT necessary when these adapters are used:
  # - net_http on Ruby 1.9+
  # - net_http_persistent on Ruby 2.0+
  # - em_http

追記 10/5 12:10 a.m. JST

真の原因は、これかな?(未検証)

https://github.com/elastic/elasticsearch-ruby/issues/457#issuecomment-326204925
faraday_middleware-aws-signers-v4 は IAM Role から Elasticsearch Service の認証通す middleware

Can this be the reason why compression is enabled by default ?
https://github.com/winebarrel/faraday_middleware-aws-signers-v4/blob/588b8c1086a72e2762ea3834bfff3d9f71b66024/lib/faraday_middleware/request/aws_signers_v4.rb#L70

if Net::HTTP::HAVE_ZLIB
  env.request_headers['Accept-Encoding'] ||= 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3'
end

続きを読む

EC2サーバにRuby環境構築

やりたいこと

AWSのEC2サーバ上で、rbenv + Ruby最新版が動く環境を作る

インスタンス情報

  • インスタンスタイプ t2.micro
  • Amazon Linux AMI 2017.03.1.20170812 x86_64 HVM

rbenvインストール

githubのリポジトリからクローンしてきます
gitが入ってない場合は先にyumでgitをインストールします

$ sudo yum install git -y

まずは本体をインストール

$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
Cloning into '/home/ec2-user/.rbenv'...
remote: Counting objects: 2629, done.
remote: Total 2629 (delta 0), reused 0 (delta 0), pack-reused 2629
Receiving objects: 100% (2629/2629), 488.96 KiB | 1.14 MiB/s, done.
Resolving deltas: 100% (1645/1645), done.

リポジトリをクローンしてきたらパスを通してコマンドが打てるようにします

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

ちゃんとコマンドが通るか確認

$ rbenv -v
rbenv 1.1.1-6-g2d7cefe

これでインストール完了っぽく見えますがまだインストールコマンドは通りません

$ rbenv install -l
rbenv: no such command `install'

ruby-buildのインストール

ruby-buildはRubyをインストールするためのrbenvのプラグインです。
これをインストールすることで、「rbenv install {バージョン}」でRubyをインストール出来るようになります。

$ git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
Cloning into '/home/ec2-user/.rbenv/plugins/ruby-build'...
remote: Counting objects: 7936, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 7936 (delta 0), reused 1 (delta 0), pack-reused 7934
Receiving objects: 100% (7936/7936), 1.67 MiB | 1.15 MiB/s, done.
Resolving deltas: 100% (4877/4877), done.

インストールスクリプトの実行

$ cd ~/.rbenv/plugins/ruby-build
$ sudo ./install.sh

これで、以下のコマンドからインストール可能なRubyのバージョン一覧が見れるはず
$ rbenv install -l
現時点でのRubyの最新バージョンは2.4.2みたい

Rubyインストール

$ rbenv install 2.4.2
Downloading ruby-2.4.2.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.2.tar.bz2
Installing ruby-2.4.2...

BUILD FAILED (Amazon Linux AMI 2017.03 using ruby-build 20170914-2-ge40cd1f)

Inspect or clean up the working tree at /tmp/ruby-build.20170930203342.4180
Results logged to /tmp/ruby-build.20170930203342.4180.log

Last 10 log lines:
The Ruby openssl extension was not compiled.
The Ruby readline extension was not compiled.
The Ruby zlib extension was not compiled.
ERROR: Ruby install aborted due to missing extensions
Try running `yum install -y openssl-devel readline-devel zlib-devel` to fetch missing dependencies.

Configure options used:
  --prefix=/home/ec2-user/.rbenv/versions/2.4.2
  LDFLAGS=-L/home/ec2-user/.rbenv/versions/2.4.2/lib 
  CPPFLAGS=-I/home/ec2-user/.rbenv/versions/2.4.2/include 

怒られました。
エラーメッセージの中に
「Try running yum install -y openssl-devel readline-devel zlib-devel to fetch missing dependencies.」
とあるのでその通りにしてみます。

$ sudo yum install -y openssl-devel readline-devel zlib-devel

もう一度先程のコマンドをトライ

$ rbenv install 2.4.2
Downloading ruby-2.4.2.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.2.tar.bz2
Installing ruby-2.4.2...
Installed ruby-2.4.2 to /home/ec2-user/.rbenv/versions/2.4.2

成功しました
以下のコマンドでこのOSで使用するRubyのバージョンを2.4.2と宣言
$ rbenv global 2.4.2

ちゃんと切り替わってるか確認

$ ruby -v
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-linux]
ちなみに

以前まではrbenvを使って新しくRubyのバージョンをインストールした時やバージョンを切り替えた時は
$ rbenv rehash
というコマンドを打つお約束がありましたが、いつの間にか必要なくなっていたそうで。

参考URL
rbenvでrehashがいらなくなった

とりあえずこれでRubyが動くようになりました
最終的にはrailsアプリをEC2上で動かしてインターネットから閲覧出来るようにしたいのですがそれについてはまたいつか。

続きを読む

serverless frameworkを使って本格的なAPIサーバーを構築(Express編)

serverless frameworkを使って本格的なAPIサーバーを構築(Express編)

最近、推しメンの serverless framework の記事の第3弾です。
保守メンテが楽になりつつも、実戦で速攻で構築ができます。

目次

framework_repo.png

前回まで、serverless frameworkのハンズオンの記事まで書きました。
今回は、serverless frameworkで 「 lambda + APIGateway + DynamoDB 」 の構成ですが、Expressを導入しローカルで実行できるようにします。

この記事でできるようになること

  • Lambda上でExpressを動かす
  • ローカルでAPIを試す環境を構築する

Expressを使う旨味

  • サーバレスでサービス全体を作るのはしんどい
  • ローカルで開発+テストできるようになる
  • Expressは、RubyのSinatraのようなシンプルなMVCフレームワークなのでとっつきやすい
  • packege.jsonをfunctionごとに用意しなくて良くなる!!

前準備

  • expressをinstallしておく
  • Lambda上でExpressを動かすツールとして、「aws-serverless-express(公式)」と「express-on-serverless」というものがあります。本来公式のものを使うべきですが、今回はexpress-on-serverlessを使用しました。
$ yarn add express
$ yarn add express-on-serverless

まずは、単純にJSONを返してみる(Express)

  • serverless.ymlを編集します

    • /配下をAnyですべてExpressに渡します
serverless.yml
functions:
  app:
    handler: functions/api/api.handler
    events:
      - http:
          path: /{proxy+}
          method: any
          cors: ${self:custom.cors_enabled.${opt:stage, self:provider.stage}}
  • api.jsでは、/hello がきたらJSONを返すようにしておきます。
functions/api/api.js
'use strict';

const express = require('express');
const app = express();
const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({
  extended: true
}));
app.use(bodyParser.json());

const response = {
  statusCode: 200,
  body: JSON.stringify({
    message: 'Go Serverless v1.0! Your function executed successfully!'
  }),
};

app.get("/hello", function(req, res, next){
  res.json(response);
});
exports.handler = require('express-on-serverless')(app);

ローカルで動かしてみる

  • 先ほどの、シンプルなアプリケーションをローカルで動かして見ましょう
  • functions/api/api.jsに以下を追記してください。
functions/api/api.js
// 追記する
if (process.env.NODE_ENV === 'dev') {
  app.listen(3000,  () => {
    console.log("Node.js is listening to PORT: 3000");
  });
}

そしてサーバーを起動します $ node functions/api/api.js

すると、http://localhost:3000/hello で先ほどのAPIにアクセスすることができます!!

  • これで、deployしても、前回と同じようなAPIを作ることができます!!
$ sls deploy -v -s dev

デプロイが成功し、APIにアクセスすると
前回Expressを使わずに作ったAPIと同じレスポンスになると思いますが、ローカルで開発できるようになったので、開発スピードは5000兆倍になります!!

まとめ

  • serverlessにExpressを乗せて使うと、開発スピードが5000兆倍になりました!!
  • 今まで通り、serverlessの旨味を生かしたイベントのフックでの発動+APIの構築ができるようになり利益ありありです。

次回

  • serverless frameworkを使って本格的なAPIサーバーを構築(テストコード編)です!!

続きを読む