Chef12に対応したAWS OpsWorksにRailsアプリをデプロイする①
昨日の社内年度末ローカルミーティングでニックネームが「マニラ」になった前田です。
弊社ではAWSでアプリケーションをデプロイする際、インフラ構築の自動化&省力化の為のDevOpsツールとして主にAWSのOpsWorksを利用しています。
昨年12月にAmazonからアナウンスがあった通り、AWS OpsWorksでChef12を利用することが出来るようになりました。
将来的にChef11サポートが終了することを見越して、Chef12に対応したOpsWorksで動くRailsアプリケーションCookbookの骨組みを作成する、というミッションを与えられましたので今回取り組みました。
Chef12になり大きく変わる点は、まずはbuilt-inで用意されていたLayerが無くなる、ということで、自分達でCookbookを一から用意する必要があるということです。
また、スタック情報やデータバッグの取得方法が変わった為、opsworks-cookbooksをそのまま使用することなどは出来ません。
全く一から作り直す、ということになります。
まずはCookbookをどのようにOpsWorksに配置するか、ということから考えました。
OpsWorksへCookbook配備設計
Chef12バージョンのOpsWorksでは、Chef11バージョンの時にあったManage Berkshelf
の項目が無くなりました。
Chef11バージョン | Chef12バージョン |
---|---|
今まではBerkshelfレシピ使う時は、Berksfileに使いたいレシピを記載し、アップロードするだけでOpsworks上で使用できたのですが、これからはローカルでレシピをインストールしてアップロードしなければなりません。
Berkshelfとカスタムで使用するレシピを上手く統合して使いたいので、AWSドキュメントのベストプラクティスを参考に下記のような手順でクックブックをアップロードする設計にしました。
ポイントは、ローカルでBerkshelfレシピをインストールせず、CirclCIでBerksehlfレシピインストール・カスタムCookbookとの統合、などを実行することと、Custom jsonをレポジトリ内で管理して、合わせてアップデートする、というところです。
サービス毎にCookbookレポジトリを作成する、という方針で設計しているので、Stackに設定するCustom Jsonは、Cookbooksレポジトリの中に入れてしまおう、ということです。
Stack作成
あらかじめVPCやSSH_key、S3レポジトリ、S3レポジトリのGET権限を持つIAMを作成しておきます。
OpsWorksの画面で真ん中の Chef 12 Stack を選択し、作成していきます。
Use custom Chef cookbooks
で、Repository type
をS3 Archive
にします。
Cookbookレポジトリのディレクトリ構成
Cookbookのディレクトリ構成は下記のようにしました。
├── Berksfile
├── berks_cookbooks
│ └── Berksfile
├── custom_cookbooks
│ └── 自分で作成するカスタムレシピ郡
├── config
│ └── custom_json.yml
├── circle.yml
└── circleci
├── .aws
│ └── credentials
└── scripts
└── install_berks_cookbooks.sh
└── update_stack.rb
Berksfile
berks package cookbooks.tar.gz
コマンドで、各Cookbookを圧縮するための読込先パスを書きます。
source 'https://supermarket.chef.io'
# Berkshelf Cookbooks
cookbook 'yum-epel', path: "./berks_cookbooks/cookbooks/yum-epel"
cookbook 'ruby_build', path: "./berks_cookbooks/cookbooks/ruby_build"
cookbook 'mysql', path: "./berks_cookbooks/cookbooks/mysql"
cookbook 'nginx', path: "./berks_cookbooks/cookbooks/nginx"
# Custome Cookbooks
cookbook 'time-zone', path: "./custom_cookbooks/time-zone"
cookbook 'rbenv', path: "./custom_cookbooks/rbenv"
cookbook 'ruby', path: "./custom_cookbooks/ruby"
cookbook 'setup', path: "./custom_cookbooks/setup"
cookbook 'deploy', path: "./custom_cookbooks/deploy"
berks_cookbooks/Berksfile
berks install
コマンドで、Berksehlf Cookbookをダウンロードする為のBerksfileです。
source 'https://supermarket.chef.io'
cookbook 'yum-epel'
cookbook 'ruby_build'
cookbook 'rbenv'
cookbook 'ruby'
cookbook 'nginx'
cookbook 'mysql', '~> 5.3.6'
circle.yml
CircleCI上で、BerkshelfコミュニティCookbookインストール、Custom CookbookとコミュニティCookbookを圧縮、S3にアップロード、インスタンスにCookbookアップデート、StackのCustom Jsonアップデートを実行します。
machine:
ruby:
version: 2.0.0
general:
branches:
ignore:
- master
deployment:
master
branch: master
commands:
- gem install berkshelf
- bash ./circleci/scripts/install_berks_cookbooks.sh
- sudo pip install awscli
- mv ./circleci/.aws ~/
- echo '[opsworks_iam]' >> ~/.aws/credentials
- echo 'aws_access_key_id = '$AWS_OPS_WORKS_ACCESS_KEY_ID >> ~/.aws/credentials
- echo 'aws_secret_access_key = '$AWS_OPS_WORKS_SECRET_ACCESS_KEY >> ~/.aws/credentials
- echo '[s3_iam]' >> ~/.aws/credentials
- echo 'aws_access_key_id = '$AWS_S3_ACCESS_KEY_ID >> ~/.aws/credentials
- echo 'aws_secret_access_key = '$AWS_S3_SECRET_ACCESS_KEY >> ~/.aws/credentials
- berks package cookbooks.tar.gz
- aws --profile s3_iam s3 cp cookbooks.tar.gz s3://rails-application-cookbooks/
- aws --profile opsworks_iam opsworks --region us-east-1 create-deployment --stack-id $STACK_ID --command "{"Name":"update_custom_cookbooks"}"
- ruby ./circleci/scripts/update_stack.rb
circleci/scripts/install_berks_cookbooks.sh
BerkshelfコミュニティCookbookインストールスクリプト。
#!/bin/bash
echo "install berkshelf cookbooks."
cd berks_cookbooks
berks vendor cookbooks
circleci/scripts/update_stack.sh
config/custom_json.yml
をjsonに変換してStackのCustom Jsonにアップデートするスクリプト。
require 'json'
require 'yaml'
puts 'update stack custom json.'
custom_json = "'#{YAML.load_file('config/custom_json.yml').to_json}'"
system("aws --profile opsworks_iam opsworks --region us-east-1 update-stack --stack-id #{ENV['STACK_ID']} --custom-json #{custom_json}")
config/custom_json.yml
Json形式でも良いかもしれませんが、yaml形式のほうが見やすいかと思い、yamlからJsonに変換するようにしました。
Custom Jsonは、アプリ固有の値などを設定し、カスタムレシピ内でCustom Jsonにセットした値を使用する設計です。
例えばAPIサーバではCORS対応、CMSサーバではimagemagickをインストールする、などです。
Custom Jsonの値だけを修正すれば別のRailsアプリケーションが配備されたスタックでもこのCookbookをコピーするだけで使うことが出来るようにしました。
---
stack:
stack_name: "rails_application_stack"
layers:
-
layer_name: "web-server"
deploy_layer_name: "deploy-server"
app_name: "web_application"
ruby:
versions:
-
version: "2.3.0"
global: "true"
-
layer_name: "cms-server"
deploy_layer_name: "deploy-server"
app_name: "api_application"
nginx:
cors: true
ruby:
versions:
-
version: "2.3.0"
global: "true"
imagemagick: true
-
layer_name: "deploy-server"
ruby:
versions:
-
version: "2.3.0"
global: "true"
CircleCIに環境変数設定
AWS_OPS_WORKS_ACCESS_KEY_ID
AWS_OPS_WORKS_SECRET_ACCESS_KEY
AWS_S3_ACCESS_KEY_ID
AWS_S3_SECRET_ACCESS_KEY
S3のIAMユーザー、OpsWorksのIAMユーザを作成しておき、上記にセットします。
以上でGitHubにプッシュし、OpsWorksにCookbookを配備するところまでが出来ました。
次回へ続く
今回はCookbookをOpsWorks上に配置するところまでをつらつらと書きました。
全体設計やレシピを書く前にやったことなどを次回書きたいと思います。
AWSを活用したスケーラブルなサービス構築をお考えの際は、是非MMMにお声がけ下さい!
追記 : 記事をアップしました。
-> Chef12に対応したAWS OpsWorksにRailsアプリをデプロイする②