環境構築メモ(VPC/EC2/RDS/ELB/Route53)
AWSの勉強がてらAWS上にRailsで作ったサービスの環境構築したメモ。AWS初心者なのでGUIでポチポチやってるけど楽しい。ただGUIなのでメモが取りにくくて困る。まぁ最終的な設定値は画面で見れるので困りはしないのだけど。慣れてくると何度もやる操作はCLIにしたいところ。キャプるほど今更張り切って解説記事書くほどでもないので自分用テキストメモです。超長文です。そこんとこよろしく。
- VPC関連
-
- VPC作成
Services->VPC->Your VPCs->Create VPCのメニューから作成。
-
-
- Name tag: vpc-xxxxとか適当な名前を付ける。この単位でプライベートアドレスを管理できる。
- CIDR block: プライベートアドレス範囲を192.168.0.0/24とかで指定
- Tenancy: Defaultは共有、Dedicatedにするとハードウェアを専有する。別途費用かかる。
-
-
- サブネット作成
Services->VPC->Subnets->Create Subnetsのメニューから作成。
とりあえずAP用に1つとDB用に2つサブネット作る。DB用はなぜだかMultiAZしない場合でも、後の方でDB Subnet Groupを作るために最低2つのAZに属するサブネットを作っておく必要があった。
-
- インターネットゲートウェイ作成
Services->VPC->Internet Gateways->Create Internet Gatewayのメニューから作成。
-
-
- Name tag: igw-xxxxとか適当な名前を付ける。VPCで1つあればよい。
-
-
- ルーティングテーブル設定
Services->VPC->Route Tables->VPC作るとできたやつ選択。
Routes->Editで0.0.0.0/0にigw-xxxxのルールを追加してデフォGWをさっき作ったインターネットGWにする。
-
- セキュリティグループ作成
Services->VPC->Security Groups->Create Security Groupのメニューから作成。
AP用とDB用で必要なポートが違うので別に作る。できたSecurity Groupを選択してInbound Rulesに必要なポート番号を追加する。最終的にELB用にも1ついるのだけど、それはELB作るときに作る。
- EC2関連
APサーバ用のEC2のインスタンス作る。
-
- EC2インスタンス作成
EC2->Instances->Launch Instanceのメニューから作成。
-
-
- Choose an Amazon Machine Image (AMI): AMIのイメージ選ぶ。ローカルと合わせてCentOSにしようかと悩んだけど、Amazon Linuxだとデフォルトでawsコマンド使えたり便利なのでやっぱりAmazonLinuxにしておいた。微妙なOSの違いでハマったらそれも勉強ということで。HVMとPVの違いは仮想化方式の違いで、カーネルイメージのロードの仕方とかが違うようなのだけど、HVMの方がより純粋な仮想マシンに近いので、基本的にHVMが使えるならHVMのイメージの方がよいんじゃないかと。
- Choose an Instance Type: とりあえず一番しょぼいt2.microで。性能見て問題あればあとから上げる。
- Configure Instance Details
- Number of instances: 作成するインスタンス数
- Purchasing option: スポットインスタンス使いたい場合。インスタンス価格が相場で決まります。特に今のところ不要。
- Network: 先ほど作ったVPCを指定する。
- Subnet: 先ほど作ったサブネットを指定する。
- Auto-assign Public IP: パブリックIP付与するか。SSHでログインしたりとかするのでEnable。
- IAM role: ロールに紐づけてキーの配置とか自動でしたい場合に使う。とりえずNone。
- Shutdown behavior: インスタンス停止時に自動でインスタンス破棄するか。しないのでStop。
- Enable termination protection: 事故防止でインスタンス破棄をロックする。特に不要なのでOFF。
- Monitoring: CloudWatchのモニタリングを詳細モードにするか。別途お金かかるのでOFF。
- Tenancy: ハードウェア専有するか。別途お金かかるのでShared。
- Advanced Details: User data渡してブートストラップ時に自動設定したり作り込みするときとかに使う。とりあえず今のところなし。
- Add Storage: ルートデバイスのディスクサイズとディスクの種類を指定する。General Purpose(SSD)というのがオススメっぽい。
- Tag Instance: xxxx-ap01とか適当な名前を付けておく。
- Configure Security Group: 先ほど作ったセキュリティグループを指定する。
-
-
- キーペア作成
インスタンス設定ウィザードの最後でSSH用のキーペア作成できるので作っておく。秘密鍵はローカルにダウンロードしてパーミション400に変える。
-
- Elastic IPの付与
EC2->Elastic IPs->Allocate New Addressメニューから作成。
できた固定IPをAssociate Addressでインスタンス指定して割り当てる。
固定IPとっておかないとインスタンス再起動の度にIP変わってめんどい。ちなみにインスタンスに割当済みの固定IPは無料だけど、未割り当てIPを放置すると課金されるので注意。IPv4アドレス潤沢に持ってるのすげーなと思う。
-
- SSH設定
手元のPCの~/.ssh/configにできたインスタンスのIPとキーペア指定して名前でログインできるようにしておく。だいたいこんなかんじ。
Host xxxx HostName xxx.xxx.xxx.xxx User ec2-user Port 22 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile ~/work/aws/aws-login.pem IdentitiesOnly yes LogLevel FATAL
-
- Chefでプロビジョニング
OS起動してSSHできるようになればもはやAWS関係ないのだけどchefでプロビジョニングしていく。
開発環境用のノード定義がAPとDBでrun_listが混ざってたのでこの機会にロールを使って分離した。ノード定義とロール定義で微妙にattributesの書き方が違って若干悩んだ。具体的にはこんなかんじ。
例えばroles/ap.jsonというファイル作って、
{ "name": "ap", "chef_type": "role", "json_class": "Chef::Role", "default_attributes": { "rvm": { "user": "root", "default_ruby": "ruby-2.2", "user_default_ruby": "ruby-2.2", "rubies": ["ruby-2.2"] } }, "override_attributes": {}, "run_list": [ "recipe[build-essential]", "recipe[git]", "recipe[rvm::system]", "recipe[sqlite]", "recipe[nodejs]", "recipe[mysql::client]" ] }
ロールの場合はdefault_attributesというセクション作ってそこに書くらしい。ちなみにoverride_attributesというのは優先順位で低いレベルで定義されてるattributesを上書きしたい場合に使うらしい。
環境依存設定はenvironments/production.jsonとか作って、例えばこんなかんじで定義できる。
{ "name": "production", "description": "Production environment", "chef_type": "environment", "json_class": "Chef::Environment", "default_attributes": { "mysql": { "port": "3306" } }, "override_attributes": {} }
この例の場合だとポートとか指定しても意味ないけど、開発環境用の定義とかはmysqlの設定とかここにいろいろ書けばよいと思う。
で、実際のノード定義の方はnodes/xxxx-ap01.jsonの中にこんなかんじにしておけばよい。
{ "environment": "production", "run_list": [ "role[ap]" ] }
で、準備できたらknife-soloでcookする。やっぱsshできればセットアップできるのよいわー。
$ knife solo cook xxxx-ap01
ちなみに余談でローカルのvagrant開発環境のノンデグレ確認するのに、saharaというプラグイン入れたらvagrantのスナップショットが取れるようになって捗った。
$ vagrant plugin install sahara $ vagrant sandbox on $ vagrant sandbox rollback $ vagrant sandbox commit $ vagrant sandbox off
使い方とかは以下を参照。
Vagrant に sahara プラグインをインストール | EasyRamble
-
- AWS用のキー設定
あとでデータの受け渡しにawsコマンドからS3使うのにaws configure でキー設定しておく。アプリの稼働に必要なわけではないのだけど、地味にこーゆーキー周りの扱いとかを自動化するのはもうちょっと調べ物が必要そう。
$ aws configure
- RDS関連
RDSでDB作ってく。EC2ローカルにmysql入れるでも十分動きそうなんだけど、まぁ何事も勉強だということで、RDS使ってみる。
-
- DBサブネットグループ作成
RDSのインスタンス作る前にDBサブネットグループというのを作る必要がある。RDSはMultiAZでも動かせるので、AZを跨いだサブネットを管理する必要があるという理解。VPC関連の準備のところでDB用に作ったサブネット2つを使う。
RDS->Subnet Groups->Create DB Subnet Groupメニューから作成。
-
- RDSインスタンス作成
RDS->Instances->Launch DB Instanceメニューから作成。
-
-
- Select Engine: DBに使うソフトを選ぶ。今回はMySQL使う。
- Do you plan to use this database for production purposes?: 商用の場合はMultiAZがオススメだぜ、ただし無料ユーザは使えないけどな。ということでNo。MultiAZはあとで試してみたい。
- Specify DB Details
- License Model: mysqlの場合はgeneral-public-licenseの1択。
- DB Engine Version: 使いたいバージョン選ぶ。MySQLだと5.1/5.5/5.6系が使える。使える一覧からマイナーバージョンまで指定する必要あり。
- DB Instance Class: インスタンス性能。とりあえず一番小さいdb.t2.microで。
- Multi-AZ Deployment: MultiAZ使う場合。今回はとりあえずNo。
- Storage Type: 普通のGeneral Purpose(SSD)にしておく。IOPSの要求が高いアプリならProvisioned IOPS(SSD)とかにするとよいんじゃないかと。
- Allocated Storage: ストレージのサイズを指定する。
- DB Instance Identifier: DBインスタンスの名前。xxxx-db01とか適当な名前を付ける。エンドポイントURLの一部に使われる。
- Master Username: DB管理ユーザ名。初期化時に作成される。
- Master Password: パスワード
- Configure Advanced Settings
- VPC: VPCを指定する。
- Subnet Group: 先ほど作ったDBサブネットグループを指定する。
- Publicly Accessible: DBのエンドポイントにAWS外からアクセスする必要がある場合に使うよう。今回はNo。
- Availability Zone: 配置先のAZを指定する。
- VPC Security Group(s): DB用に作っておいたセキュリティグループを指定する。
- Database Name: MySQLで言うところのCREATE DATABASEで作成する名前。初期化時に作成される。
- Database Port: ポート番号指定する。MySQLデフォルトだと3306。
- DB Parameter Group: my.cnfに設定する初期パラなどがカスタマイズできるけど、一旦デフォルトで作って、あとで変更する。
- Option Group: DBのオプション機能使う場合。オプション機能というのは例えばOracleTDEとかそーゆーの。MySQLだとMEMCACHEDだけ使えるよう。特に使わないのでデフォルトで。
- Enable Encryption: 暗号化したい場合。特にないのでNo。
- Backup Retention Period: バックアップの保持期限。デフォルト7日。
- Backup Window: バックアップ処理の曜日と時間帯が指定できる。
- Auto Minor Version Upgrade: マイナーアップグレードを許可するか。
- Maintenance Window: メンテ時間帯の曜日と時間帯が指定できる。バックアップと重ねるのは不可。
-
-
- DBユーザ作成
とりあえずこれでDBのインスタンスできたのでAPサーバから普通にmysqlコマンドでログインできるようになった。つながらない場合はセキュリティグループの設定とかあってるか確認してくれ。
接続に必要なエンドポイントのURLはRDS->Instancesでインスタンス選択すると表示される。接続はAP用サーバからだいたいこんなかんじで直接つなげる。
$ mysql -h xxxx.xxxx.ap-northeast-1.rds.amazonaws.com -P 3306 -u ユーザ名 -p
あと、アプリで使用するユーザを作っておく。MySQLだとリモート接続できるユーザ@"%"で指定しないといかん。
mysql> GRANT ALL PRIVILEGES ON データベース名.* TO ユーザ名@localhost IDENTIFIED BY 'パスワード' WITH GRANT OPTION; mysql> GRANT ALL PRIVILEGES ON データベース名.* TO ユーザ名@"%" IDENTIFIED BY 'パスワード' WITH GRANT OPTION; mysql> SELECT user,host FROM mysql.user;
-
- DB文字コードの変更
デフォルトだと文字コードがlatin1とかなってるのでutf8mb4に変更する。
mysql> ALTER DATABASE db_production CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql> show variables like 'character_set%';
-
- DBパラメータグループ作成&適用
character_set_database以外の文字コードも変えておいた方がよいかなと思い直して、DBパラメータグループというので初期パラを指定できるようなのでそれを使う。
RDS->Parameter Groups->Create Parameter Groupメニューから作成。
-
-
- Parameter Group Family: パラメータグループの種類。mysql5.6を指定。
- Group Name: pg-xxxxとか適当な名前を付ける。
- Description: 適当な説明文を記載。
-
RDS->Parameter Groups->(パラメータグループ選択)->Edit Parametersでパラメータ指定。
character_setでfilterして、以下の項目を"utf8mb4"にする。
-
-
- character_set_database
- character_set_client
- character_set_connection
- character_set_results
- character_set_server
-
ちなみにcharacter_set_filesystemはファイル名に関するもののようなので、engine-default=binaryのままにしておく。
変更できたらSave changesボタンで保存するんだけど、これだけでは反映されない。
RDS->Instances->(インスタンス名選択)->Instance Actions->Modifyメニューから
DB Parameter Groupの設定を先ほど作成したパラメータグループを指定して、Apply Immediatelyにチェックして変更を適用する。おそらく修正するパラメータによるのかもしれないけど、インスタンスのステータス画面で、ParameterGroupステータスがpending-rebootになると、リブート反映待ち状態になる。Instance Actionsから手動でリブートして反映させる。
文字コード設定はこのへん参考にした。
日本人ならRDSを使う際にかならずやらなければならないこと - リア充爆発日記
- Railsアプリのデプロイ関連
Railsアプリをproduction環境で動かせるようにする。
-
- ビルド
まずはgit cloneしてbundle installしておく。
$ git clone https://github.com/xxxx/xxxx $ cd xxxx/ $ bundle install --path vendor/bundle
-
- DB接続設定
次にDB接続設定をするのだけど、パスワードとか埋めるのどうすればよいんだろうとググってみたところ、database.ymlに環境変数埋められるっぽい。
設定ファイル内のパスワード等を環境変数で隠すための How to 色々 - Qiita
config/database.ymlをこんなかんじで環境変数から読ませるようにする。
production: <<: *default host: <%= ENV['DB_HOST'] %> port: <%= ENV['DB_PORT'] %> database: <%= ENV['DB_DATABASE'] %> username: <%= ENV['DB_USERNAME'] %> password: <%= ENV['DB_PASSWORD'] %>
環境変数はconfig/environments/production.envとか適当なファイル作って、
export DB_HOST=xxxx export DB_PORT=xxxx export DB_DATABASE=xxxx export DB_USERNAME=xxxx export DB_PASSWORD=xxxx
railsとかrakeコマンド打つ前に読み込みしておく。
$ source config/environments/production.env
envファイルはパスワード書いてあるので.gitignoreに入れておく。
DB接続設定終わったらマイグレーションする。
$ bundle exec rake db:migrate RAILS_ENV=production
いちおうテーブルとインデックスがちゃんと想定通りできたか念のため見ておく。
mysql> show create table xxxx; mysql> show index from xxxx;
-
- データ投入
初期データはあらかじめS3にまとめてupしておいたので取得する。
$ aws s3 ls s3://minamijoyo/xxxx/ $ aws s3 cp s3://minamijoyo/xxxx/xxxx.gz db/ $ gzip -dc db/xxxx.gz > db/xxxx.txt
データロードにそれなりに時間かかるので、バックグランドで動かす。RAILS_ENV=production指定を忘れずに。
$ nohup bundle exec rake db:seed RAILS_ENV=production > output.log 2>&1 &
ロードが終わったらいちおう件数だけ確認しとく。
$ wc -l db/xxxx.txt
mysql> select count(*) from xxxx;
-
- Railsのシークレットキーの設定
データ投入が終わったので、Railsサーバを起動してみる。
$ bundle exec rails s -b 0.0.0.0 -e production
とやってアクセスしてみるとInternal Server Errorが出る。
ログを見てみると以下のような文字列が出ている
Missing `secret_token` and `secret_key_base` for 'production' environment, set these values in `config/secrets.yml` WEBrick/1.3.1 (Ruby/2.2.0/2014-12-25) at xxx.xxx.xxx.xxx:3000
ググってみたところ、セッション保護に必要なシークレットキーというのを設定しないといけないらしい。
secret_key_baseあたりのメモ - Qiita
[Rails]production環境で動かす - Qiita
rake secretで長い文字列が生成されるので、
$ bundle exec rake secret
これを先ほど作ったconfig/environments/production.envに環境変数として追加しておく。
export SECRET_KEY_BASE=xxxx
読み込み。
$ source config/environments/production.env $ bundle exec rails s -b 0.0.0.0 -e production
-
- アセットのプリコンパイル
エラーはでなくなったけど、なぜだかデザインがCSS反映されてないっぽい。ググったところアセットの設定が必要らしい。
RailsをローカルでProductionモードで起動させる方法 - Rails Webook
Rails4.2だと以下の環境変数をエクスポートしておく必要があるよう。さっき作った環境変数ファイルconfig/environments/production.envに以下の行を追加。
export RAILS_SERVE_STATIC_FILES=true
あと、RAILS_ENVも毎回してするのがめんどくなってきたので、ついでにエクスポートしとく。
export RAILS_ENV=production
$ source config/environments/production.env
ちなみにRails4.1だと環境変数ではなくconfig/environments/production.rbに
config.serve_static_assets = true
と書いておけばよいらしいです。
準備できたらアセットをプリコンパイルする。
$ bundle exec rake assets:precompile
-
- WEBrickの起動
今度こそWEBrick起動したらちゃんと画面出た。
$ nohup bundle exec rails s -b 0.0.0.0 -e production &
起動しっぱなしにするんにとりあえずnohupでバックグランドで上げておいたがこれが正しい作法なのかはわかんないです。production環境だと、WEBrickじゃなくてnginx+Unicornみたいな構成が多いよう。性能が必要になったら調べる。
何度もデプロイするようになるとcapistranoとかでデプロイも自動化するのがよいらしい。デプロイがめんどくなってきたら調べる。
まぁとりあえずこれでAPサーバのグローバルIP直で動くようにはなった。
- ELB関連
APサーバ1台ならIP直でもよくねという気もするけど、何事も試してみないと勉強ならんのでELBから振り分けできるようにする。
EC2->Load Balancers->Create Load Balancerメニューから作成。
-
- Configure Health Check
- Ping Protocol: HTTPとかヘルスチェック用のプロコトルを指定する。
- Ping Port: 3000とかヘルスチェック用のポート番号を指定する。
- Ping Path: /とかヘルスチェック用のパスを指定する。
- Response Timeout: タイムアウト調整したい場合は指定できる。デフォルト5秒。
- Health Check Interval: ヘルスチェック間隔調整したい場合は指定できる。デフォルト30秒。
- Unhealthy Threshold: ヘルスチェックNGで切り離し判定するしきい値。デフォルト2回。
- Healthy Threshold: ヘルスチェックOKで組み込み判定するしきい値。デフォルト10回。間隔30秒だとちょっと多いので2回に変更しとく。
- Configure Health Check
-
- Select Subnets: 振り分け先のサブネットを指定する。
- Assign Security Groups: ELB用のセキュリティグループ作成する。サービス用のポートだけ開ければよいのでELB用に1つ作ればよいと思う。
- Add Instances to Load Balancer: 振り分け先のEC2インスタンスを指定する。
- Add Tags: 適当なタグをつける。Nameタグぐらい付けておいたらよいんじゃないですかね。
これでELB経由でアクセスできるようになった。
- Route53関連
ELBのURL長いのでもちろん独自ドメインでアクセスできるようにしたいよねー。ということで勢いでドメイン取った。
Route53->Registered Domains->Register Domainメニューから作成。
TLDによって値段が違うので好きなのを選ぶ。現時点のTLDごとの値段の一覧は以下。
https://d32ze2gidvkk54.cloudfront.net/Amazon_Route_53_Domain_Registration_Pricing_20140731.pdf
.ioドメインとかかっこよいのだけど高いので普通に.comにしておいた。
あと本格的なサービスであればサービスごとに専用ドメイン取るべきなんだけど、特に収益化するつもりのない個人的に趣味で作ってるツールにそれぞれ専用のドメイン取ると維持費用もバカにならないので、汎用の個人ドメインを取得して、細々したものはその中で運用する方針にした。
-
- Contact Details
- My Registrant, Administrative and Technical Contacts are all the same: 登録者、管理者、技術者は全部同じなのでYes。
名前とか住所とか入れるけど、個人で取得する場合のポイントは以下。
申請したらRoute53->Registered Domains->Pending Requests のところで登録状況のステータスが見れる。申請直後はステータスがDomain registration in progressになってた。
ちょっと休憩して後で見たらPendingの欄から消えて、Registered Domain の方に表示されてた。見たの2時間後ぐらいだけど、登録完了メールが30分後ぐらいに来てたので、たぶんそれぐらいで登録されたんだと思う。メールアドレス認証のメールも来るのでリンク踏んで認証しておく。15日以内にメールアドレス認証しないとドメインが凍結されるっぽいので必須。
登録終わったらDNSのレコード作る。
Route53->Hosted Zones見ると既にゾーンはできていて、ドメイン選択してGo to Record Sets見るとSOAとNSレコードは既に登録されてる状態になってた。
じゃあ次はサービス用に新規にCNAMEレコード作ってELBに向ける。っと思ったけど、調べるとAWSのELBに割り当てるならCNAMEよりAlias使った方がよいっぽい。
Amazon Route 53のALIASレコード利用のススメ | DevelopersIO
AliasとはCNAME風に振る舞うAWS独自機能で、AWS内で名前解決してDNSプロトコルレベルでは直接Aレコードを返す。何がうれしいかというと、CNAME返されるともっかいAレコード引かないといけないけど、Aliasなら直接Aが返ってくるのでDNSのリクエスト数が減って名前解決が速くなる。あとドメイン名自体をサイトのURLにしたい場合に、Zone ApexはCNAME使えないけど、Aレコードなら使えるので、Aliasで返せば対応できるという使い方もある。
Route53->Hosted Zones->(ドメイン名)->Go to Record Sets->Create Record SetsからAlias作る。
登録完了したらドメイン名で画面出た。めでたい。
最低限動くものできたけど、まだいろいろ中身弄りたいのでURLはまた今度。