AmazonLinuxでAWSのLocalStackインストール手順

リンク: https://github.com/atlassian/localstack

Dockerインストール

確認する:

$ sudo yum search docker
Loaded plugins: priorities, update-motd, upgrade-helper
========================================================================================= N/S matched: docker ==========================================================================================
docker-storage-setup.noarch : A simple service to setup docker storage devices
docker.x86_64 : Automates deployment of containerized applications
docker-devel.noarch : A golang registry for global request variables (source libraries)
docker-pkg-devel.noarch : A golang registry for global request variables (source libraries)

  Name and summary matches only, use "search all" for everything.


$ sudo yum info docker

Loaded plugins: priorities, update-motd, upgrade-helper
Available Packages
Name        : docker
Arch        : x86_64
Version     : 17.03.1ce
Release     : 1.50.amzn1
Size        : 22 M
Repo        : amzn-updates/latest
Summary     : Automates deployment of containerized applications
URL         : http://www.docker.com
License     : ASL 2.0 and MIT and BSD and MPLv2.0 and WTFPL
Description : Docker is an open-source engine that automates the deployment of any
            : application as a lightweight, portable, self-sufficient container that will
            : run virtually anywhere.
            :
            : Docker containers can encapsulate any payload, and will run consistently on
            : and between virtually any server. The same container that a developer builds
            : and tests on a laptop will run at scale, in production*, on VMs, bare-metal
            : servers, OpenStack clusters, public instances, or combinations of the above.

インストール:

$ sudo yum install -y docker

サービス起動

$ service docker start

LocalStackインストール

プロジェクトをクローンする

$ git clone https://github.com/atlassian/localstack.git

起動する:

make docker-run

# Or using docker-compose:

docker-compose up

続きを読む

AWS ソリューションアーキテクトプロフェッショナル 受けてきました&勉強法(2017/6版)

前書き

image.png

 前回 の続きです。ソリューションアーキテクトアソシエイト(SAA)に受かったので今度はプロフェッショナル(SAP)に挑戦しました。

AWS の資格試験はわかりにくい

ワークショップについて

 公式サイトから読み取れないので補足。以下のクラスがありますが、

レベル/分野 アーキテクト 開発 運用
アソシエイト取得済み Advanced Architecting on AWS DevOps Engineering on AWS 同左
アソシエイト未取得向け Architecting on AWS Developing on AWS Systems Operations on AWS
AWS 未経験者向け Amazon Web Services 実践入門 1 / 2 同左 同左

 前提事項は記載ありるものの、いきなり Advanced などを受けてもいいし、受けずに受験に挑んでも大丈夫です。アソシエイト未取得向けのを2つ受ける必要はなさそうなので、Architecting 受けてから Developing 受けたりするのは、けっこう内容の重複がある ので、お金がもったいない。
 その他のクラスは試験に出てくる頻度が低いので、興味があれば受ければいいやつです。

 ソリューションアーキテクトアソシエイト(SAA) だけは試験対策として半日のワークショップがありますので、時間とお金があれば受けるといいかと。

認定試験について

 おさらいですが。
image.png

 ワークショップと同じく、範囲がかぶっているので アソシエイト取れたらほかのアソシエイトもいけそう なので、資格の数を増やすにはよさそうです、プロフェッショナルはそう簡単にいかないですが。英語版の資格として Advanced Networking 、Big Dataが追加となっています。Security(Beta) もあった気がしますが、 Beta が取れていないのかな。いずれさらに上の階級にある Master が提供されるかもしれないという噂があったりします。

試験概要

 概要をざっくり記載します。アソシエイトの倍の時間でついでに受験料も倍です。合格ラインはやはり65%という噂です。1問あたり2分ちょっとで解き、3時間集中し続ける というなかなかヘビーな内容です。

TOPIC DATA
試験時間 170分間
問題数 80問
合格ライン 非公開
受験料 ¥32,400

出題範囲

 出題範囲を確認すると、アソシエイトでは高可用性がほとんどでしたが、Pro では求められるものが変わっていることがわかります。そのほか試験ガイドをざっと読みました。

TOPIC 出題率
高可用性および事業継続性 15%
原価計算 5%
デプロイメントマネージメント 10%
ネットワーク設計 10%
データストレージ 15%
セキュリティ 20%
拡張性と伸縮自在性 15%
クラウド移行およびハイブリッドなアーキテクチャ 10%

学習

セミナーを受講する

 前述の Advanced Architecting on AWS を受講しました。
 セミナーには AWS 社員の方や、AWS をすでに実務で利用されているかたが多くいて、私のような素人も半分くらいいる印象でした。私は実業務で AWS に触れたことが実はほとんどなく、本気の AWS 運用に必要な知識を持ち合わせていなかったので次のような観点が非常に勉強になりました。

  • ユーザ管理:IAM と ADやフェデレーションでの連携を考慮し、大規模運用を考える
  • コスト観点:一括請求やスポットインスタンスの選び方で、大幅なコスト削減を考える
  • 運用・監査:KMSを使用した暗号化、CloudLogs でのロギング、ClodWatch でのモニタリングで運用・監査への対応を考える
  • 可用性・セキュリティ:大規模&複数VPCのデザイン、MultiAZ、MultiRegion で可用性向上を実現する、DDoS からインフラを保護する

AWS 活用資料集(BalckBelt)、WhitePaper を読む

 アップデートもあるので、改めて時間を見つけて読みました。時間があるときは Webinar にも参加しました。 Webinar だと日々疑問に思っていることの質問を AWS の SA に直接質問できるので、すごくおトクです。

  • BlackBelt

    • Amazon EC2
    • Elastic Load Balancing
    • Auto Scaling
    • Amazon EC2 Container Service
    • AWS Elastic Beanstalk
    • AWS Lambda
    • Amazon EBS
    • Amazon S3
    • Amazon CloudFront
    • Amazon Glacier
    • AWS Storage Gateway
    • Amazon Elastic File System
    • AWS CloudTrail & AWS Config
    • AWS Support & Trusted Advisor
    • AWS Directory Service
  • WhitePaper

    • セキュリティのベストプラクティス
    • セキュリティプロセスの概要
    • リスクとコンプライアンス
    • AWS を用いた故障耐障害性の高いアプリケーションの構築
    • 新しいリージョンへの AWS リソースの移行
    • 災害対策目的での AWS の使用

本読む

 AWS 事例全集 といった AWS が配っている冊子もありますが数がひたすら多いので、勉強用としてこちらの本で一般的なアーキテクチャを学びました。

image
Amazon Web Services 定番業務システム12パターン 設計ガイド

試験に向けて

 以下、結果もあわせて書いていきます。

サンプル問題集にチャレンジ

ソリューションアーキテクト プロフェッショナル

 サンプル問題を解いた結果、微妙でした。アソシエイトは本試験とくらべて非常に簡単だったので、先が思いやられます。

結果

4/6問 正解

ソリューションアーキテクト以外もやってみる

 SysOpsアソシエイト、DevOpsアソシエイトのサンプル問題もやってみました。ソリューションアーキテクトとはちょっと毛色の違うサービス(CloudFormationとかSQSとか)が出てくるので復習になります。Pro は回答が書いていなかったのでやっていません。

SysOps結果

6/10問 正解

DevOps結果

6/9問 正解

模擬試験

 データ書いておきます。なお、アソシエイト合格者はバウチャーコードが利用できますので活用しましょう。問題文はコピーできます。

TOPIC DATA
試験時間 90分間
問題数 40問
合格ライン 明記なし
受験料 ¥4,320

結果

得点: 27%
結果: 不合格

 時間足りなさすぎました。結果を分析しますが全体的に悪すぎて手のうちようがない感じでした。。。

TOPIC 出題率 正解率(模擬結果) 模擬出題数(予測) 誤答数(予想)
高可用性および事業継続性 15% 33% 6 4
原価計算 5% 0% 2 2
デプロイメントマネージメント 10% 0% 4 4
ネットワーク設計 10% 50% 4 2
データストレージ 15% 50% 6 3
セキュリティ 20% 25% 8 6
拡張性と伸縮自在性 15% 16% 6 5
クラウド移行およびハイブリッドなアーキテクチャ 10% 25% 4 3

出題傾向を抑える

 ベストプラクティスが複数書いてあって、どれも正解だけどよりベストなものを選ぶという問題や、コスト/可用性/移行期間/既存システムとの連携 のどれが求められているのかといった、真の目的を把握しないと答えられない問題が多くありました。
 また、昔の AWS 構成を最新のサービスで置き換えるような問題もあるため、古くなった知識はアップデートが必要です。

模擬試験の復習と弱点克服

 CloudHSM とかの暗号化系、DirectConnect/StorageGateway などの低レイヤー系、TrustedAdvisor など課金系、STS/IAMロールなどの認証系、StepFunctions や SWF などフロー系、Redshift や Kinesis などのBigData系 は、アソシエイトでも踏み込んだ問題が出なかったので個人的に苦手でした。これらは BlackBeltで復習しました。
 また、コピーした模試の問題はを1つづつ調べ、答え合わせをしました。模擬試験については本試験でも出ることがあるので暗記するつもりで復習しました。英語で回答を解説しているサイトがあるので大いに活用しました。

本試験

結果

合格!

image.png

内訳

総合スコア: 66%

トピックレベルのスコア:
1.0 High Availability and Business Continuity: 91%
2.0 Costing: 75%
3.0 Deployment Management: 62%
4.0 Network Design: 37%
5.0 Data Storage: 91%
6.0 Security: 56%
7.0 Scalability & Elasticity: 69%
8.0 Cloud Migration & Hybrid Architecture: 28%

所感

 なんとかギリギリセーフでした。本試験、すごい疲れます。。。
 実務半年程度のペーペーなので試験勉強と実務知識が比例するわけじゃなく、試験としては問題数こなすのが一番という気がしました。本試験のほうが模試より簡単だったので絶望せずがんばりましょう。

 次は DevOps に挑戦したいです。

続きを読む

〇FLAGの中の人に憧れてMastodon×AWSでモンストドン作ってみた

Mastodon立ち上げたらいい会社に入れると聞いて、邪な気持ちで。。。いや、Mastodonとモンストって相性よさそうだなぁと思いたち、少し乗り遅れた感をかもしだしながら、フルにAWSを使って規模拡大しても大丈夫な構成で作ってみた。

モンストドン (https://monstdn.com)

構成

monstdn.png

最小構成のざっくり料金($1=113円、1ヶ月30日計算)

サービス 単価 月額料金
ALB 1台 × $0.0243/1H + データ転送的なの  約2000円 + α
EC2 2台(t2.nano) × $0.008/1H + データ転送的なの 約1300円 + α
RDS 1台(db.t2.micro シングルAZ) $0.028/1H + データ転送的なの 約2300円 + α
ElasticCache 1台(cache.t2.micro) $0.026/1H + データ転送的なの 約2100円 + α
S3Bucket $0.025/GB + リクエスト数的なの + α
SES $0.10/1,000通あたり + データ転送的なの + α
合計     ( 約7700円 + α なので ) ざっくり1万ぐらい

※無料枠があるので1年目はもう少しやすくできそう

やったこと

  • AWSのアカウント作成
  • IAMの作成とアカウントの初期設定(二段階認証とか、パスワードポリシーとか)
  • Route53でドメインを買う
  • SESでメール設定と制限解除申請
  • ACMの取得(無料でHTTPS通信)
  • S3バケット作成(画像とかのアップロードファイルの配信用)
  • VPCとセキュリティグループの作成
  • SES、S3へアクセスする為のIAMユーザの作成
  • ElasticCacheでRedisの作成
  • RDSでPostgreSQLの作成
  • EC2でCentOSを使ってMastodonの構築(下に詳細)とイメージ(AMI)の作成
  • AutoScallingの設定
  • ALB(ApplicationLoadBalancer)の作成(ACMをつける)
  • Route53でHostZoneのレコード設定

CentOSでのMastdon構築(20170517現在)

sudo su -
yum -y update
yum -y install vim

localectl set-locale LANG=ja_JP.utf8
localectl set-keymap jp106
localectl status

timedatectl set-timezone Asia/Tokyo
timedatectl status

dd if=/dev/zero of=/mnt/swapfile bs=1M count=2560
mkswap /mnt/swapfile
swapon /mnt/swapfile
chmod 0644 /mnt/swapfile
echo "/mnt/swapfile                             swap                    swap    defaults                0 0" >> /etc/fstab
free

vim /etc/sysconfig/selinux
 SELINUX=enforcing
 ↓
 SELINUX=disabled

systemctl disable postfix
systemctl disable auditd.service

yum -y install libxml2-devel ImageMagick libxslt-devel git curl nodejs file
yum -y install epel-release
rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
yum -y install ffmpeg ffmpeg-devel

yum -y group install "Development tools"
curl -sL https://rpm.nodesource.com/setup_4.x | sudo bash -

yum -y install nodejs
npm -g install yarn

yum -y install postgresql postgresql-contrib postgresql-devel
yum install -y openssl-devel readline-devel

useradd mastodon
passwd mastodon
su - mastodon
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src && cd ~
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile && source ~/.bash_profile
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
rbenv install 2.4.1 && rbenv global $_ && rbenv rehash

# 確認
ruby -v

cd ~
git clone https://github.com/tootsuite/mastodon.git live
cd live
git checkout $(git tag | tail -n 1)

gem install bundler
bundle install --deployment --without development test
yarn install --pure-lockfile

cp .env.production.sample .env.production
sed -i "/^PAPERCLIP_SECRET=$/ s/$/`rake secret`/" .env.production
sed -i "/^SECRET_KEY_BASE=$/ s/$/`rake secret`/" .env.production
sed -i "/^OTP_SECRET=$/ s/$/`rake secret`/" .env.production

vim .env.production
#Redis,Postgresql,言語,SMTP,S3の設定

RAILS_ENV=production bundle exec rails db:setup
RAILS_ENV=production bundle exec rails assets:precompile

exit

cat << "_EOF_" > /etc/systemd/system/mastodon-web.service
[Unit]
Description=mastodon-web
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_


cat << "_EOF_" > /etc/systemd/system/mastodon-sidekiq.service
[Unit]
Description=mastodon-sidekiq
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_

cat << "_EOF_" > /etc/systemd/system/mastodon-streaming.service
[Unit]
Description=mastodon-streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/usr/bin/npm run start
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
_EOF_

systemctl enable mastodon-{web,sidekiq,streaming}
systemctl start mastodon-{web,sidekiq,streaming}

cat << "_EOF_" | crontab -
RAILS_ENV=production
@daily cd /home/mastodon/live && /home/mastodon/.rbenv/shims/bundle exec rake mastodon:daily > /dev/null
_EOF_

yum -y install nginx

cat << "_EOF_" > /etc/nginx/conf.d/mastodon.conf
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name {domainName};

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /home/mastodon/live/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  location / {
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";

    proxy_pass http://localhost:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}
_EOF_

systemctl enable nginx
systemctl start nginx

# ユーザ登録後 admin設定
RAILS_ENV=production bundle exec rails mastodon:make_admin USERNAME={UserName}

メモ

EC2のDiskはSSDで(swapで使う)
ロードバランサーはApplicationの方じゃないとwebSocketがうまくいかない
コミュニティベースのシステムだからCloudFrontはあまり必要性感じなかったので使わなかった
(日本向けだしS3のバケット東京リージョンにあるし、S3もかなり性能いいし)
もしCloudFrontを使うなら、websocketできないからS3の前に置く感じ
今回CloudFrontの利点があるとすれば”ドメイン”が自分の使えることぐらいかな
CentOSじゃなくてAmazonLinux使いたかったけど、ffmpeg入れるのにやたら時間かかったからやめた。一応動いたけど(純正AWSが。。。)
DockerはDeployまで楽そうだけど、効率よくなさそうだったのでやめた
AWSでDocker使うならECSでやってみたいけど、Mastodonはすんなりできるのかなー
セキュリティ的にはロードバランサーからの80番ポートしか受け付けないように制御してるから大丈夫かな。
sshでのログインは同じVPC内に踏み台サーバ立ててと。

最後に

ここまで読んで頂きありがとうございます。
技術的なことを投稿するのはこれが初めてですが、だれかのお役にたてれたら嬉しいです。
普段はPHPとAWS少しいじる程度なのでいい勉強になりました。
ほとんど公開されている文献をもとにプラモデル感覚で作りましたので、ご指摘等あればコメント頂ければと思います。
個人でのサイト運用となりますので、落ちたらごめんなさい。

続きを読む

Rails4+Capistrano3+Nginx+Unicorn EC2へのデプロイ < 後編 >

インフラ勉強中の者がAWSにrailsアプリをEC2にデプロイしたので備忘録として残します。
< 後編 >です。

【イメージ】
スクリーンショット 2017-05-09 11.04.30.png

< 前編 >Unicorn の設定までを実施しました。

デプロイチェック

ローカル
$ bundle exec cap production deploy:check

デプロイチェックをすると下記のようなエラーが出るはずです。

~ 省略 ~
00:02 deploy:check:linked_files
ERROR linked file /var/www/deployApp/shared/config/database.yml does not exist on YOUR_IP_ADDRESS

/var/www/deployApp/shared/config/database.yml が存在しないためのエラーです。

shared ディレクトリは、Capistranoが自動生成するディレクトリです。
EC2の qiitaApp の配下を確認します。

EC2
[myname@ip-10-0-1-86 qiitaApp]$ ls -la
drwxrwxr-x 4 ec2-user ec2-user 4096  5  9 04:08 .
drwxrwxrwx 3 root     root     4096  5  9 04:06 ..
-rw-rw-r-- 1 ec2-user ec2-user    6  5  9 03:33 .ruby-version
drwxrwxr-x 2 ec2-user ec2-user 4096  5  9 04:08 releases ← 自動生成
drwxrwxr-x 7 ec2-user ec2-user 4096  5  9 04:08 shared ← 自動生成

config 配下に database.yml を生成すればエラーは出なくなるのですが、次に同じようにsecrets.ymlが存在しないというエラーが出ますので、共に生成して変更します。

EC2上にdatabase.ymlの生成の前にローカルの database.yml を変更します。

ローカル
$ vi config/database.yml 

======================= database.yml ================================

~ 省略 ~
#production:
#  <<: *default
#  database: db/production.sqlite3
   ↓↓↓
production:
  <<: *default
  adapter: mysql2
  database: qiita_db
  host: qiita_db.xxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com                                                                                                                                                     
  username: qiita_user
  password: YOUR_DB_PASS
  encoding: utf8

====================================================================

変更したら database.yml 全体をコピーして保存します。

EC2
[ec2-user@ip-10-0-1-86 qiitaApp]$ cd shared/config/
[ec2-user@ip-10-0-1-86 config]$ touch database.yml
[ec2-user@ip-10-0-1-86 config]$ vi database.yml

======================= database.yml ===============================

 ローカルでコピーしたdatabase.ymlを貼り付けます。

====================================================================

同様に secrets.yml もローカルの中身をコピーして(変更はなし)、configの配下に secrets.yml を生成して貼り付けます。

EC2
[ec2-user@ip-10-0-1-86 config]$ touch secrets.yml
[ec2-user@ip-10-0-1-86 config]$ vi secrets.yml

再度、デプロイチェックを実施します。

ローカル
$ bundle exec cap production deploy:check

エラーが出なければdeploy:checkは完了です。

デプロイ

ローカル
$ bundle exec cap production deploy --trace #traceをつけることでバグの箇所を発見しやすくします

エラーが出なければデプロイ完了です。

今回は、scaffoldで簡単なUserアプリを生成しているので、 直接、http://IP_ADDRESS/users/ を叩いて確認してみます。

…おそらく500エラーが返ってきてしまって確認できないと思います。

Unicornの状態とエラーログを確認します。

EC2
[ec2-user@ip-10-0-1-86 config]$ ps ax | grep unicorn
30256 ?        Sl     0:01 unicorn master -c /var/www/qiitaApp/current/config/unicorn.rb -E deployment -D                                             
30311 ?        Sl     0:00 unicorn worker[0] -c /var/www/qiitaApp/current/config/unicorn.rb -E deployment -D                                          
30313 ?        Sl     0:00 unicorn worker[1] -c /var/www/qiitaApp/current/config/unicorn.rb -E deployment -D                                          
30316 ?        Sl     0:00 unicorn worker[2] -c /var/www/qiitaApp/current/config/unicorn.rb -E deployment -D                                          
30334 pts/1    S+     0:00 grep --color=auto unicorn

Unicornは稼働していることが確認できます。
次はエラーログです。

EC2
[ec2-user@ip-10-0-1-86 config]$ cd ../..
[ec2-user@ip-10-0-1-86 qiitaApp]$ tail -f current/log/unicorn_error.log 

この状態で再度ローカルからデプロイします。

ローカル
[ec2-user@ip-10-0-1-86 qiitaApp]$ bundle exec cap production deploy
EC2
I, [2017-05-09T06:57:08.516736 #30256]  INFO -- : executing ["/var/www/qiitaApp/shared/bundle/ruby/2.3.0/bin/unicorn", "-c", "/var/www/qiitaApp/current/config/unicorn.rb", "-E", "deployment", "-D", {8=>#<Kgio::UNIXServer:fd 8>}] (in /var/www/qiitaApp/releases/20170509065649)
I, [2017-05-09T06:57:08.735844 #30256]  INFO -- : inherited addr=/tmp/unicorn.sock fd=8
I, [2017-05-09T06:57:08.736108 #30256]  INFO -- : Refreshing Gem list
I, [2017-05-09T06:57:09.978667 #30311]  INFO -- : worker=0 ready
I, [2017-05-09T06:57:09.981219 #30256]  INFO -- : master process ready
I, [2017-05-09T06:57:09.982336 #30313]  INFO -- : worker=1 ready
I, [2017-05-09T06:57:09.985331 #30316]  INFO -- : worker=2 ready
I, [2017-05-09T06:57:10.155786 #29111]  INFO -- : reaped #<Process::Status: pid 29166 exit 0> worker=0
I, [2017-05-09T06:57:10.155874 #29111]  INFO -- : reaped #<Process::Status: pid 29168 exit 0> worker=1
I, [2017-05-09T06:57:10.155945 #29111]  INFO -- : reaped #<Process::Status: pid 29171 exit 0> worker=2
I, [2017-05-09T06:57:10.155973 #29111]  INFO -- : master complete

この時点ではエラーらしきものが出ていません。

再度 http://IP_ADDRESS/users/ を叩いて確認してみます。

EC2
E, [2017-05-09T06:59:27.508190 #30316] ERROR -- : app error: Missing `secret_token` and `secret_key_base` for 'production' environment, set these values in `config/secrets.yml` (RuntimeError)

Missing secret_token and secret_key_base とあります。
EC2上の qiitaApp/current/config/secrets.yml の secret_key_base:の環境変数(SECRET_KEY_BASE)が設定されていないことが原因です。

EC2
[ec2-user@ip-10-0-1-86 qiitaApp]$ cd current
[ec2-user@ip-10-0-1-86 current]$ bundle exec rake secret
1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz ←コピーする
[ec2-user@ip-10-0-1-86 current]$ vi config/secrets.yml 

============================== secrets.yml ==================================

~ 省略 ~
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
          ↓↓↓
  secret_key_base: 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz

=============================================================================

本来であれば、直接入力するのではなく、

SECRET_KEY_BASE=1234567890abcd...
export SECRET_KEY_BASE

とかで環境変数のまま使用すべきです。
が、何回実施してもググってもうまくいかなかったので、取り急ぎ直接入力で対応します。
dotenvというgemがあるようなので、それで対応してもいいと思います。

再度、ローカルからデプロイして、urlを叩けば下記の画面が表示されると思います。
スクリーンショット 2017-05-09 16.28.33.png

大変参考にさせていただきました。
capistranoを使ってrailsをnginx+unicorn+mysqlの環境にデプロイする
ローカルで開発したRailsアプリをCapistrano3でEC2にデプロイする
Capistrano3でUnicorn+Nginxな環境にRailsをデプロイする:初心者向け
Railsプロダクション環境 Unicornでsecret_key_baseが設定されていないとエラーが出る

続きを読む

Setup Rails on unicorn on nginx on AWS EC2 linux

How to install Unicorn

1.Modify Gemfile contents

$ sudo vim ~/(sampleapp)/Gemfile

Add gem 'unicorn'

2. Install unicorn at the Gemfile’s directory

$ bundle install

3. Check installation of unicorn

$ bundle show unicorn
/home/ec2-user/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/unicorn-5.3.0

4. Make “unicorn.rb” on /(sampleAppDirectory)/config and write following contents

unicorn.rb
  worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
  timeout 15
  preload_app true

  listen '/home/ec2-user/testApp/tmp/unicorn.sock' #{Railsアプリケーションのあるディレクトリ}/tmp/unicorn.sock
  pid    '/home/ec2-user/testApp/tmp/unicorn.pid' #{Railsアプリケーションのあるディレクトリ}/tmp/unicorn.pid

  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'])

5. Execute following code and create rake file at your app directory

$ rails g task unicorn 
Running via Spring preloader in process 13140
      create  lib/tasks/unicorn.rake

6. Start unicorn and check unicorn starting

$ bundle exec unicorn_rails -c config/unicorn.rb 

*↑production環境かdeployment環境は選べる。Unicorn commands一覧参照

$ ps -ef | grep unicorn
ec2-user 12631 11576  0 08:09 pts/1    00:00:00 vim unicorn.rb
ec2-user 13547 13151  0 08:37 pts/5    00:00:01 unicorn master -c config/unicorn.rb                                                                                            
ec2-user 13582 13547  0 08:37 pts/5    00:00:00 unicorn worker[0] -c config/unicorn.rb                                                                                         
ec2-user 13584 13547  0 08:37 pts/5    00:00:00 unicorn worker[1] -c config/unicorn.rb                                                                                         
ec2-user 13587 13547  0 08:37 pts/5    00:00:00 unicorn worker[2] -c config/unicorn.rb                                                                                         
ec2-user 14665 13960  0 08:51 pts/3    00:00:00 grep --color=auto unicorn

Modify the setting of Nginx

1. Modify nginx.conf to rails.cof

nginx/1.10.2時点ではnginx.conf($ nginx -tでconfigファイルを確認できる)。nginx.confに以下のように書き換える。

nginx.conf
# For mor# e information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user ec2-user;
worker_processes 1;

events {
    worker_connections 1024;
}

http {
#    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
#                     '$status $body_bytes_sent "$http_referer" '
#                      '"$http_user_agent" "$http_x_forwarded_for"';

#   access_log  /home/ec2-user/testApp/access.log  main;

#    sendfile            on;
#    tcp_nopush          on;
#    tcp_nodelay         on;
#    keepalive_timeout   65;
#    types_hash_max_size 2048;

#    include             /etc/nginx/mime.types;
#    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
#   include /etc/nginx/conf.d/*.conf;

#   index   index.html index.htm;

    upstream unicorn {
      server  unix:/home/ec2-user/testApp/tmp/unicorn.sock; #/home/{ユーザ名}/{Railsアプリケーション>名}/tmp/unicorn.sock
    }

    server {
        listen       xx; #HTTP = 80
        server_name  xxx.xxx.xxx.xxx; #Your server ip-address or domain name

        access_log  /var/log/access.log;
        error_log   /var/log/error.log;

        root    /home/ec2-user/testApp/public; #/home/{ユーザ名}/{Railsアプリケーション名}/public
#       index   index.html;
        client_max_body_size 4G; #100m;
        error_page  404              /404.html;
        error_page  500 502 503 504  /500.html;
        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;
                }

        }

2. Reload nginx

$ sudo service nginx restart

3. Access your server address!!!

ここからはunicornのnginxに対するpermission deniedが出た時の処理

2017/04/23 06:45:40 [crit] 29912#0: *1 open()
"/var/lib/nginx/tmp/proxy/1/00/0000000001" failed (13: Permission
denied) while reading upstream, client: (clientserver), server:
(my-server), request: "GET / HTTP/1.1", upstream:
"http://unix:/pathTo/tmp/unicorn.sock:/", host:
"(my-server)"

上記エラーではnginx, tmpにおけるアクセス権限がnginxのみしかないので、アクセス権を解放する(下記アクセスは777としているが修正する必要あり)

$ sudo chmod 777 nginx
$ sudo chmod 777 tmp

続きを読む

lambda入門(Node)③ – API Gatewayを使ってslackからのリクエストをlambdaで受けられるようにする

第3回になりました。

過去のはこちら。

今回は、ようやくやりたいことに近づいて来まして
slackからのリクエストをlambdaで受けられるようにしたいと思います。

何か調べていくと、どうもAPI Gatewayを使うと良い感じぽい。
まずはAPI Gatewayについて予習を。

API Gateway

どんな役割をしてくれるのか

APIのエンドポイントとして待ち構える玄関として使える
現在はhoge/*のようなパスを/hoge/{proxy+}として設定できる
プロキシリソースなるものらしい
post, putの振り分けなどが簡単になった

課金体系

受信した API 呼び出しと、送出したデータ量に対して発生

serverless frameworkでの設定

serverless.yml
functions:
  bookStore:
    handler: books/store.store
    events:
      - http:
          path: books
          method: get
          cors: true

これだけ。
デプロイ実行すると

$ serverless deploy -v --stage dev
Serverless: Packaging service...
・
・
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1490662361327
・
・
Service Information
service: testProject
stage: dev
region: ap-northeast-1
api keys:
  None
endpoints:
  GET - https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/books
functions:
  bookStore: testProject-dev-bookStore

エンドポイントが作られた。。!

レスポンスを送れるか試してみる

$ curl https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/books
{"message":"ok",...........}

うん、okそう、すごい。ymlに書くだけで何でも設定してくれちゃう。

Slackから応答させるようにする

ここで今回の本題。
作成したエンドポイントに対してスラックがリクエストを送って
slack上にメッセージが返ってくるようにする。

全体像

スクリーンショット 2017-04-29 15.17.57.png

やることとしては、リクエストパラメータを解析してデータを保存。
保存した結果を返す

book.js

'use strict';

const slackAuthorizer = require('../authorizer/slackAuthorizer');
const parser = require('../service/queryParser');

const bookSave = require('../useCase/book/save.js');

module.exports.book = (event, context, callback) => {
  const queryParser = new parser(event.body);
  const authorizer = new slackAuthorizer(queryParser.parseToken());

  /**
   * 認証
   */
  if (!authorizer.authorize()) {
    context.done('Unauthorized');
  }

  /**
   * @Todo ここで lambda function の振り分けを行いたい
   */
  bookSave(event, (error, result) => {});

  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'ok',
    }),
  };

  callback(null, response);
};

同期で処理を行いたいので asyncを使って処理をブロックごとに実行する

save.js

'use strict';

const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
const bookTable = process.env.bookTable;

const uuidV1 = require('uuid/v1');

const webhookUrl = process.env.slack_webhook_url;
const request = require('request');

const dateTime = require('node-datetime');
const dt = dateTime.create();
const insertDate = dt.format('Y-m-d H:M:S');

const async = require('async');

const parser = require('../../service/queryParser');

module.exports = (event, callback) => {

  const queryParser = new parser(event.body);

  const key = uuidV1();

  async.series([
    function(callback) {

      /**
       * データを保存
       */
      dynamoDB.put({
        'TableName': bookTable,
        'Item': {
          'id': key,
          'title': queryParser.parseText(),
          'insert_date': insertDate,
        },
      }, function(err, data) {
        callback(null, "saved");
      });
    },
    function(callback) {

      /**
       * 保存したものを取り出して
       * 結果を返す
       */
      dynamoDB.get({
        TableName: bookTable,
        Key: {
          id: key,
        },
      }, function(err, data) {
        if (!err) {
          const response = {
            text: ``${data.Item.title}` is Saved !!`,
          };
        
          /**
           * webhook でチャンネルにメッセージを返す
           */
          request.post(webhookUrl, {
            form: {
              payload: JSON.stringify(response),
            },
          }, (err, response, body) => {
            callback(null, 'getData');
          });
        }
      });
    },
  ], function(err, results) {
    if (err) {
      throw err;
    }
  });
};

slash commandから送られてきたパラメータをパースするために
query-stringモジュールを使い、ラップした

queryParser.js
'use strict';

const queryStringParser = require('query-string');

module.exports = class queryParser {

  constructor (queryString) {
    this.queryString = queryString;
  }

  parseToken () {
    return queryStringParser.parse(this.queryString).token;
  }

  parseText () {
    return queryStringParser.parse(this.queryString).text;
  }
};

slashコマンドの設定は省略します。
最初の api gatewayで設定されたエンドポイントのURLを
設定してあげればいいので

実際に動かすとこんな感じです。

4b4b9687608f58545ef2b4536a7166c2.gif

ソースは汚いかもしれませんが、saverlessフレームワークで
エンドポイント作成やdb、外部サービスの連携が簡単に出来ました

もっとキレイに書けるようにjsの筋力をつけていかねば。。笑

続きを読む

JAWS DAYS 2017 ワークショップ Docker on Elastic Beanstalk 〜Days after tomorrow〜 (2)

この記事では、複数のDockerコンテナで構成される環境を構築するための設定例を具体的に解説します。ワークショップのフォローアップではありますが、一般的な事柄を扱いますので、ワークショップに参加されていない方にもお読み頂ける内容です。

前回の記事: JAWS DAYS 2017 ワークショップ Docker on Elastic Beanstalk 〜Days after tomorrow〜 (1)

春ですね

こんにちは。Emotion Techの子安です。ようやく暖かくなってきましたね。やっぱり春といえば桜ですかね!

sakura.jpg

恐縮です。

前回のつづき

さて、前回はテーマとする環境の構成を説明し、利用するコンテナ定義ファイル docker-compose.yml の枠組みを解説しました。更に web app db コンテナのうち、 web db コンテナの設定を見ました。今回はアプリケーションそのものである app コンテナを見ていきます。

各コンテナの設定 2

appコンテナ

app:
  image: phusion/passenger-ruby23:0.9.20
  environment:
    APPLICATION_ENV: development
    APPLICATION_ROLE: app
    PASSENGER_APP_ENV: development
  labels:
    eb.workshop.role: app
  networks:
    - eb.workshop
  depends_on:
    - db
  volumes:
    - ./app/init/40_setup.sh:/etc/my_init.d/40_setup.sh:ro
    - ./app/passenger/passenger.conf:/etc/nginx/sites-available/default:ro
    - ./app/rails-app:/var/www/rails-app

Ruby on Railsのアプリを実行するappコンテナです。phusion/passenger-dockerイメージを利用しています。このイメージはさまざまな機能を持っていますが、まずは設定内容を見ましょう。

  • image: phusion/passenger-dockerイメージを指定しています。
  • environment: イメージ側で PASSENGER_APP_ENV 環境変数を読んでRailsの環境を切り替えてくれる機能があります。
  • volumes: ここで3つのマウントを指定しています。コンテナ側のパスを見てください。

    • /etc/my_init.d/40_setup.sh:ro: /etc/my_init.d 配下に置いたスクリプトを初期化時に実行してくれる機能があります。実行権限を忘れずにつけるようにします。 :ro は読み取り専用の意味です。
    • /etc/nginx/sites-available/default:ro: ここにnginxの設定ファイルが置かれています。
    • /var/www/rails-app: Railsのアプリケーションをここに置きます。これは任意の場所で構いません。上記のnginx設定ファイルに記述しています。

phusion/passenger-docker

phusion/passenger-dockerは、Phusion社がメンテナンスしているDockerイメージです。このイメージは、さらに別のイメージ phusion/baseimage-docker をベースとして作られています。このphusion/baseimage-dockerには、いくつかの有用な機能が含まれています。

  • 正しいinitプロセス: /sbin/my_init にinitプロセスを内蔵しています。本来initプロセスには、親プロセスのいなくなった子プロセス(孤児)を里親として引き受ける役割があります。加えて SIGTERM を受け取った際に、他のサービスを終了させる機能を持ちます。
  • スーパーバイザ: 軽量なスーパーバイザ runit を内蔵しています。
  • ログマネージャ: syslogデーモン syslog-ng を内蔵しています。logrotateも設定済みです。
  • cron: cronデーモンを内蔵しています。

重要なことは、これらの機能が協調動作するように設定されていることです。 /sbin/my_init がrunitを立ち上げ、runitがsyslog-ngやcronを管理します。デフォルトのコンテナ起動コマンドが /sbin/my_init に設定されていますので、

docker run phusion/baseimage

として起動することで、最小限のコンテナ化されたLinux(Ubuntu)環境を手に入れることができます。他にも、今回も利用している /etc/my_init.d 配下のスクリプトを初期化時に実行する機能、root以外でコマンドを実行するための setuser コマンドなど、様々な機能や仕組みを備えています。詳しくはWebページを参照ください。

phusion/passenger-dockerは、このphusion/baseimage-dockerの上に、nginxやrubyのアプリケーションサーバである Phusion Passenger をインストールしてあります。nginxはrunitの配下に設定されていますので、すぐにスーパーバイズされたデーモンとして稼働させることができます。

コンテナ初期化スクリプト

可能なことはコンテナ自身に判断させるのが良い設計です。そのためにコンテナ起動時に実行するスクリプトを組み込むことがよく行われます。今回 app コンテナで設定している初期化スクリプトの内容を見てみましょう。 /app/init/40_setup.sh を参照ください。

このスクリプトの主な役割は4つです。

  • nginxを起動可能にする
  • Railsアプリのセットアップ
  • 環境変数の引き渡し
  • DBマイグレーション

ハンズオンのため、本来はコンテナ初期化スクリプトに記述すべきでない処理もここに入っていますが、続けて詳しく説明します。

/app/init/40_setup.sh
rm -f /etc/service/nginx/down

これはrunitの機能です。サービスを定義するディレクトリ(今回は /etc/service/nginx )の直下に down という名前のファイルがあると、自動起動しません。phusion/passenger-dockerイメージ側で配置されているものですが、自動起動してほしいのでファイルを削除しています。

/app/init/40_setup.sh
chown -R app:app /var/www
cd /var/www/rails-app
setuser app bundle install --path=../vendor/bundle
if [ "$PASSENGER_APP_ENV" = 'production' ]; then
  setuser app bin/rails assets:precompile
  setuser app bundle install --path=../vendor/bundle --without test development --deployment --clean
fi

このあたりは実環境であればCIで、ビルドの一処理として適切に実施すべきものです。アプリケーションのファイルパーミッションを適切に設定し、ライブラリのインストールを行います。更にproductionモードであれば、アセットのプリコンパイルと不要なライブラリの削除を行います。

/app/init/40_setup.sh
echo "passenger_env_var 'SECRET_KEY_BASE' '$SECRET_KEY_BASE';" >> /etc/nginx/conf.d/10_setenv.conf
echo "passenger_env_var 'RDS_HOSTNAME' '$RDS_HOSTNAME';" >> /etc/nginx/conf.d/10_setenv.conf

ここは環境変数をpassengerへ引き渡すための処理です。コンテナでは環境変数を扱う機会が多くなりますので、利用するミドルウェアに合わせて設定が必要です。

/app/init/40_setup.sh
RAILS_ENV=$PASSENGER_APP_ENV setuser app bin/rails db:create db:migrate

こちらはDBのマイグレーションです。この処理も実環境であればコンテナ初期化スクリプトではなく、デプロイの手順の一貫として実施すべきものです。

次回へ

ここまでが app コンテナの設定でした。長くなりましたので、つづきは次回にしたいと思います。次回はElastic Beanstalkの設定ファイル、Dockerrun.aws.jsonを解説する予定です。

続きを読む

Mastodonでgit pullしたらビルドエラー

概要

EC2で稼働させているMastodongit pullした後にdocker-compose buildしたら下記エラーが出てハマった。

環境

  • AWS EC2(t2.medium)
  • Ubuntu Server 16.04 LTS

エラー内容

ubuntu@ip-***-***-**-**:~/mastodon$ docker-compose build
redis uses an image, skipping
db uses an image, skipping
Building streaming
Step 1/9 : FROM ruby:2.4.1-alpine
 ---> 5eadd5d1419a
Step 2/9 : LABEL maintainer "https://github.com/tootsuite/mastodon" description "A GNU Social-compatible microblogging server"
 ---> Using cache
 ---> 95a4b711ef32
Step 3/9 : ENV RAILS_ENV production NODE_ENV production
 ---> Using cache
 ---> 499e95f00e13
Step 4/9 : EXPOSE 3000 4000
 ---> Using cache
 ---> 167a91f421f4
Step 5/9 : WORKDIR /mastodon
 ---> Using cache
 ---> e185ae07f027
Step 6/9 : COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
 ---> Using cache
 ---> 460d49537428
Step 7/9 : RUN BUILD_DEPS="     postgresql-dev     libxml2-dev     libxslt-dev     build-base"  && apk -U upgrade && apk add     $BUILD_DEPS     nodejs     libpq     libxml2     libxslt     ffmpeg     file     imagemagick  && npm install -g npm@3 && npm install -g yarn  && bundle install --deployment --without test development  && yarn --ignore-optional  && yarn cache clean  && npm -g cache clean  && apk del $BUILD_DEPS  && rm -rf /tmp/* /var/cache/apk/*
 ---> Running in 68a8d45af6e8
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/main/x86_64/APKINDEX.tar.gz
ERROR: http://dl-cdn.alpinelinux.org/alpine/v3.4/main: No such file or directory
WARNING: Ignoring APKINDEX.167438ca.tar.gz: No such file or directory
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/community/x86_64/APKINDEX.tar.gz
ERROR: http://dl-cdn.alpinelinux.org/alpine/v3.4/community: No such file or directory
WARNING: Ignoring APKINDEX.a2e6dac0.tar.gz: No such file or directory
OK: 21 MiB in 29 packages
WARNING: Ignoring APKINDEX.167438ca.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.a2e6dac0.tar.gz: No such file or directory
ERROR: unsatisfiable constraints:
  build-base (missing):
    required by: world[build-base]
  ffmpeg (missing):
    required by: world[ffmpeg]
  file (missing):
    required by: world[file]
  imagemagick (missing):
    required by: world[imagemagick]
  libpq (missing):
    required by: world[libpq]
  libxml2 (missing):
    required by: world[libxml2]
  libxml2-dev (missing):
    required by: world[libxml2-dev]
  libxslt (missing):
    required by: world[libxslt]
  libxslt-dev (missing):
    required by: world[libxslt-dev]
  nodejs (missing):
    required by: world[nodejs]
  postgresql-dev (missing):
    required by: world[postgresql-dev]
ERROR: Service 'streaming' failed to build: The command '/bin/sh -c BUILD_DEPS="     postgresql-dev     libxml2-dev     libxslt-dev     build-base"  && apk -U upgrade && apk add     $BUILD_DEPS     nodejs     libpq     libxml2     libxslt     ffmpeg     file     imagemagick  && npm install -g npm@3 && npm install -g yarn  && bundle install --deployment --without test development  && yarn --ignore-optional  && yarn cache clean  && npm -g cache clean  && apk del $BUILD_DEPS  && rm -rf /tmp/* /var/cache/apk/*' returned a non-zero code: 11

解決方法

$ git checkout $(git describe --tags `git rev-list --tags --max-count=1`)

ハマってる最中にREADMEに安定バージョン使ってねって追記されてましたとさ。。

追記

v1.2にしたところ下記のエラーが再現。調査中。

ubuntu@ip-***-**-**-***:~/mastodon$ docker-compose build
redis uses an image, skipping
db uses an image, skipping
Building streaming
Step 1/9 : FROM ruby:2.4.1-alpine
 ---> 5eadd5d1419a
Step 2/9 : LABEL maintainer "https://github.com/tootsuite/mastodon" description "A GNU Social-compatible microblogging server"
 ---> Using cache
 ---> 95a4b711ef32
Step 3/9 : ENV RAILS_ENV production NODE_ENV production
 ---> Using cache
 ---> 499e95f00e13
Step 4/9 : EXPOSE 3000 4000
 ---> Using cache
 ---> 167a91f421f4
Step 5/9 : WORKDIR /mastodon
 ---> Using cache
 ---> e185ae07f027
Step 6/9 : COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
 ---> Using cache
 ---> 06b33c54cfd4
Step 7/9 : RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories  && BUILD_DEPS="     postgresql-dev     libxml2-dev     libxslt-dev     build-base"  && apk -U upgrade && apk add     $BUILD_DEPS     nodejs@edge     nodejs-npm@edge     libpq     libxml2     libxslt     ffmpeg     file     imagemagick@edge  && npm install -g npm@3 && npm install -g yarn  && bundle install --deployment --without test development  && yarn --ignore-optional  && yarn cache clean  && npm -g cache clean  && apk del $BUILD_DEPS  && rm -rf /tmp/* /var/cache/apk/*
 ---> Running in 0b9005a839d9
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/main/x86_64/APKINDEX.tar.gz
ERROR: http://dl-cdn.alpinelinux.org/alpine/v3.4/main: No such file or directory
WARNING: Ignoring APKINDEX.167438ca.tar.gz: No such file or directory
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/community/x86_64/APKINDEX.tar.gz
ERROR: http://dl-cdn.alpinelinux.org/alpine/v3.4/community: No such file or directory
WARNING: Ignoring APKINDEX.a2e6dac0.tar.gz: No such file or directory
fetch https://nl.alpinelinux.org/alpine/edge/main/x86_64/APKINDEX.tar.gz
140162439957356:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:s23_clnt.c:794:
ERROR: https://nl.alpinelinux.org/alpine/edge/main: Permission denied
WARNING: Ignoring APKINDEX.65bdaf85.tar.gz: No such file or directory
OK: 21 MiB in 29 packages
WARNING: Ignoring APKINDEX.167438ca.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.a2e6dac0.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.65bdaf85.tar.gz: No such file or directory
WARNING: The repository tag for world dependency 'nodejs@edge' does not exist
WARNING: The repository tag for world dependency 'nodejs-npm@edge' does not exist
WARNING: The repository tag for world dependency 'imagemagick@edge' does not exist
ERROR: Not committing changes due to missing repository tags. Use --force to override.
ERROR: Service 'streaming' failed to build: The command '/bin/sh -c echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories  && BUILD_DEPS="     postgresql-dev     libxml2-dev     libxslt-dev     build-base"  && apk -U upgrade && apk add     $BUILD_DEPS     nodejs@edge     nodejs-npm@edge     libpq     libxml2     libxslt     ffmpeg     file     imagemagick@edge  && npm install -g npm@3 && npm install -g yarn  && bundle install --deployment --without test development  && yarn --ignore-optional  && yarn cache clean  && npm -g cache clean  && apk del $BUILD_DEPS  && rm -rf /tmp/* /var/cache/apk/*' returned a non-zero code: 255

続きを読む

AWS CodeDeploy 導入調査 アプリ側編

AWS CodeDeploy 導入調査 AWS 設定編 の続きになります

前回のあらすじ

  • EC2 インスタンスを作った
  • コードアップロード用の S3 Bucket を作った
  • デプロイ用の IAM ロールを作った
  • CodeDeploy アプリケーションを作った

今回はアプリ側の作業です.

デプロイ先アプリ構成

Nginx – uWsgi を sudpervisord でプロセス監視
Nginx とか supervisord とかは割とどうでもいいのですが
サーバー構成はあらかじめ一通り作成しておきます.

さらに CodeDeloy-Agent をインストールしておきます.
http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install.html

とりあえずデプロイしてみる

ローカル -> S3 のアップロードは AWS-CLI を使用します.
ローカルプロジェクトのトップディレクトリで,

$ aws deploy push --application-name <<app-name>> --s3-location s3://<<s3bucket-name>>/<<app-name>>.zip --source ./ --region <<region-name>>

を実行します.
<<app-name>> は CodeDeploy で作成したアップケーション名です.

  • –ignore-hidden-files オプションで隠しファイルを無視します.
  • S3 にアップロードするファイル名は本当はなんでもいいです.
  • 圧縮時の拡張子は tar.gz でもいけるはず.

アップロードに成功すると以下のように出力されます.

To deploy with this revision, run:
aws deploy create-deployment --application-name <<app-name>> --s3-location bucket=<<s3bucket-name>>,key=<<app-name>>.zip,bundleType=zip,eTag=<<eTag>>--deployment-group-name <deployment-group-name> --deployment-config-name <deployment-config-name> --description <description>

ただし今回は成功しないと思います.

appspec.yml の作成

appspec.yml は CodeDeploy によるデプロイ作業を記述したものになります.
まずローカルプロジェクトのトップディレクトリに appspec.yml ファイルを作成し,

version: 0.0
os: linux

まで記述します. ここで,
– version は appspec のシステムバージョンで2017/04現在 0.0 のみ有効です
– os はデプロイ先 EC2 インスタンスの OS 種類で windows か linux かを設定します. linux ディストリビューションまで指定する必要はありません.

次に,

files:
   - source: /
     destination: /usr/local/src

でファイル配置を設定します.
source はコードパスで appspec.yml があるディレクリの相対パスになります.
なので source: / とした場合はディレクリ構成そのままで EC2 インスタンスの
destination に配置されます.
特定のディレクリ/ファイルのみ配置先を変えたい場合は – source: destination の記述を追加することができます.

さらに, 今回は以下の設定を追加しました.

hooks:
  AfterInstall:
    - location: copy_settings.sh
      timeout: 180
      runas: root
    - location: pip_install.sh
      timeout: 180
      runas: root
  ApplicationStart:
    - location: reload_uwsgi.sh
      timeout: 180
      runas: root

hooks 以下では CodeDeploy ライフサイクルの各処理にフックしてスクリプトを実行させることができます.
http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html

CodeDeploy-Agent は Ruby で記述されており, Ruby コード上で Shell スクリプトを実行します.
今回追加した処理は

  • コード配置後に EC2インスタンス内に仕込んでおいた追加設定をコピー
  • プロジェクト内の requirements.txt の内容で pip install
  • アプリスタート時に uwsgi のリスタート

です. runas は各スクリプトの実行ユーザーです.
今回は面倒だったので全て root にしました.

デプロイに再度チャレンジ

ふたたび ローカル -> S3 のアップロード をやってみると今度は成功するはずです.
アップロードが成功したら次は AWS コンソールの CodeDeploy に移動します.

  1. ダッシュボードでアプリケーションを選択して “アクション” -> “新しいリビジョンのデプロイ”
  2. “アプリケーション”, “デプロイグループ” はそのまま, “リビジョンタイプ” は Amazon S3
  3. “リビジョンの場所” でアップロードしたソースコードを指定します.
  4. その他はそのままで “デプロイ” でデプロイ開始します.

フックスクリプトでエラーにならなければデプロイは成功するはずです.

フックスクリプトでエラーになったので解決した内容は次回

続きを読む