Rackの基礎
こんにちは!! エンジニアとしてインターンしている村田です。
今回はズバリRackについて書いていきたいと思います。
Rackに関しては日本語の資料もあまり多くないため、特にRailsの初心者の方はよく分からないまま見過ごしていることも多いかもしれません。
私も初心者なので探すのに苦労しました。。
そこで自分もまだまだではありますが、これを読んで自分と同じような初心者の方々がRackのぼんやりとしたイメージをつかんでいただけるように書いていきたいと思います。
それでは早速ですがやっていきましょう!
Rackが誕生した背景
まず何故Rackが必要なのか? ここがわからないと、話が頭に入ってこないですよね。
これはPSGIというPythonのインターフェースの仕様に影響を受けました。
以前のPythonフレームワーク界は、様々フレームワークが開発されたものの、そのフレームワークが特定のWebサーバーに依存した状態でした。 ただし、これではサーバーの乗り換え時の負担が大きかったりして、開発者も頭を悩ませることが多かったようです。 そこで、ある特定のルールを定めて(インターフェース)、そのルールのもとフレームワークとWebサーバーの開発を進めることにしました。
これがPSGIの正体です。
RubyのフレームワークもRailsとSinatraを筆頭に多様性に富みますし、WebサーバーもWebrickをはじめとして様々な独自のデプロイ方法を持っています。
すると、当然PSGIの代わりとなるシステムが必要になりまして、そうやって検討されていった結果現在のRackが誕生していくことになります。
Rackってなに??
さていよいよ本題にいきましょう。
Rackとは、rackupというコマンドを打つことで指定したファイルを参照しながらWebサーバーを立ち上げられるようにしているライブラリ(gem)です。 もう少し機能自体の説明をするならば、HTTPの送受信処理を担当するモジュールです。
指定したファイル(デフォルトではconfig.ru。ちなみにruはruckupの略。)には独自のRuby DSLで様々なミドルウェアやアプリケーションを参照するように書かれています。 これをもとにWebサーバーを立ち上げていくことになります。
ビジュアル的に説明をするとこんな感じになっています。

このようにミドルウェアを介して、RailsなどのRackアプリケーションに対してWebサーバーから来たHTTPをパースしたりしています。
試しにbundle exec rake middlewareと打ってみましょう。 例えばこのような感じになって出てくると思います。
use Raven::Rack use ActionDispatch::Static use Rack::Lock use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fd239f96d58> use Rack::Runtime use Rack::MethodOverride use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use BetterErrors::Middleware use ActionDispatch::RemoteIp use ActionDispatch::Reloader use ActionDispatch::Callbacks use ActiveRecord::Migration::CheckPending use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CacheStore use ActionDispatch::Flash use ActionDispatch::ParamsParser use Remotipart::Middleware use Rack::Head use Rack::ConditionalGet use Rack::ETag use Warden::Manager use ExceptionNotification::Rack use Bullet::Rack use OmniAuth::Strategies::Facebook run Rails::Application.routes
ここに書いてあるuseの対象がRackミドルウェアです。
そして最後に書いてあるrunの対象はRackアプリケーション(Rails)になります。
ちなみにuseやrunはRack DSLなのですが、今回は詳しく触れませんので興味がある方は調べてみてください。
ruckupの正体
こちらをご覧いただければお分かり頂けると思いますが、実はrackupコマンドがやっているのはRack::Server.startをしているだけなんですよね。
と言われてもRack::Serverとかよく知らないって方がこの記事を読んでくださっていると思いますので、説明します。
Railsで開発をしているとrails s、省略しないで書くと、rails serverというコマンドを打つことがあると思います。 このrails serverというコマンドも実は省略形でして、正確にはRails::Server.startというコマンドを打っています。
何か先ほどとても似ているコマンドを見ましたよね? そうRack::Server.startです。
もちろんこの二つは関係がないわけではなく、Rails::ServerはRack::Serverのサブクラスになっているのです。
継承する中で、ポートを3000番にするなど様々な違いを作っているようですが基本的な動きの部分は引き継いでいることがわかっていただけたと思います。
Rackミドルウェアの話
Rackミドルウェアはもちろん自分で追加することもできますが、そのためには簡単なルールを幾つか覚えなくてはなりません。
1つのHashオブジェクトを引数に取る「call」メソッドが実装されていること callメソッドは「ステータスコード」、「ヘッダーを表現したHash」、「eachに反応するオブジェクト」の3つの要素を持った配列を返すこと
上にある2つがそのルールになります。
また慣習的にcallメソッドの引数にはenvを用いることも覚えておくと得をするかもしれません。
詳しく話を始めるとDSLの説明やその他いろいろな話を書かないといけないので割愛しますが、Railsのようなアプリケーションも最後はcallで呼ばれるので
lamda do |env| [200, {"Content-Type" => "text/html"}, ["Hello, world!"]] end
簡単に言ってしまえばこのような形で動いていることになります。 これは先ほどの条件を満たしていますよね。
最後に
私の説明では物足りなかった部分も多かったと思います。 でもこれをきっかけにしてRackのイメージを掴めた方が一人でもいらっしゃればとても嬉しいです。
私もわからないことだらけなのでもっと勉強していこうと思いますが、最後にこの記事を作る上で参考にさせていただいた記事を載せておこうと思います。
それでは失礼いたします。
AWS Lambdaを使用し、CloudfrontのInvalidationを走らせる
こんにちは。エンジニアの志村です。
先日assets on S3についての実装を行いました。
その際の記事は下記になります。
今回はasset_syncにより、S3にassetファイルがアップロードされた段階でCloudfrontのInvalidationをLambdaを使用して走らせるという処理を実装したいと思います!
Invalidationとは
Cloudfrontのキャッシュを明示的に消す機能です。
クラスメソッドさんの上記の記事が非常に分かりやすいかと思います。
キャッシュを使用するのに重要なポイントは適切なキャッシュ期間を設けることです。それを行わないと古いファイルがいつまで経っても配信され続けてしまいます。
特にassets on S3を使用する場合には、明示的にファイルをInvalidationしないといつまで経っても最新のassetsファイルがユーザーに配信されなくなります。
このような状況を防ぐために、S3にファイルが転送された段階で、assetsのキャッシュを消去する必要があります。
Lambdaの実装
ではLambdaの実装を始めていきます。
AWSコンソールからLambdaを選択します
「Create Lambda Function」を選択します

今回はBlueprintは使用しないので「Skip」を選択します

S3を選択します。

| 項目 | 説明 |
|---|---|
| Bucket | assets用のBucketを選択 |
| Event Type | S3にassetsファイルがCreateされた段階でInvalidationを走らせたいので「Object Created(All)」を選択 |
| Enable Trigger | チェックを入れると、設定したイベントが走るようになります(production等で設定する場合は注意!) |
※Prefix, Suffixは必要であれば入力して下さい。
LambdaFunctionを作成します

| 項目 | 説明 |
|---|---|
| Name | 適当な名前を入力 |
| Description | 分かりやすい説明を入力 |
| Runtime | 今回はPythonにします |
コードは下記のコードが非常に分かりやすかったのでそれを使用します。
※DistributionIDは自身のCloudfrontディストリビューションのIDを入力して下さい
from __future__ import print_function import boto3 import time def lambda_handler(event, context): for items in event["Records"]: path = "/" + items["s3"]["object"]["key"] print(path) client = boto3.client('cloudfront') invalidation = client.create_invalidation(DistributionId='ディストリビューションID', InvalidationBatch={ 'Paths': { 'Quantity': 1, 'Items': [path] }, 'CallerReference': str(time.time()) })
因みにですが、boto3はPython用のAWS SDKです。 Lambda FunctionをPythonで書く際には必ずと言って良いほど良く使用します。 ドキュメントも充実しているので非常に使い勝手が良いと思います。
Boto 3 Documentation — Boto 3 Docs 1.4.0 documentation
ロールやその他の設定をします
「Lambda function handler and role」のRole→「Create a custom role」を選択します。

そうすると下記のような画面が現れるので、下記のCloudfrontのInvalidationを許可するロールを作成します。

下記のようなポリシードキュメントになります。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"cloudfront:CreateInvalidation"
],
"Resource": [
"*"
]
}
]
}
最後に「Create function」を押下すれば完成です!
使い方
asset_syncでデプロイすると勝手にInvalidationが走ってくれます。 下記の画面で確認が出来ます。
AWSコンソールからCloudfrontを選択します
該当するディストリビューションのIDを選択します

「Invalidations」タブを選択すると、Invalidationsの状態が閲覧出来ます。ここでデプロイ直後にStatusがProgressとなっていればトリガーがきちんと走っています。

以上になります。 キャッシュの扱いって本当難しいなーと感じますが、この様に自動化してしまえば特に意識することも少なくなって、よりCloudfrontが使いやすくなりますね。