Rails5アプリケーションのAWSによるネットワーク構築 Nginx+Puma+Capistranoな環境とAWS構築(VPC EC2 RDS CloudFlont Route53 etc)

はじめに

数ヶ月前に書いたRails5のAWS構築をもう少し項目を増やしてから公開しようと思っていたのですが、なかなか出来ないのでもう公開します。(ELBとかCloudFlontとかもっとちゃんと書けそう、、)
項目の追加や修正は適宜入れると思います。

Railsは5.0を使っています。(記事を書いたときの最新)

4系は以前書きました↓

構築する環境

  • VPC
  • EC2
    • Nginx + Puma
    • Capistrano
  • (S3)
  • ELB
  • RDS
  • CloudFlont
  • Route53
  • (CloudWatch)

VPC、EC2のインスタンスの作成

AWSのデザインが多少変更はありますが、以下を参考に作成出来ます。

AWS VPCによるネットワーク構築とEC2によるサーバー構築

これに追加でElastic IPの設定もしておきました。

EC2内の環境構築

作成したEC2にログインして環境を構築します。
ec2-userでまずログインしましょう。

ユーザーの作成

デフォルトではec2-userなので新たなユーザーを追加し、そのユーザーでsshログイン出来るように設定します。

$ sudo adduser shizuma #ユーザーの追加 「shizuma」の部分は好きなユーザー名にします。以後「shizuma」の部分は各自のユーザー名になります。
$ sudo passwd shizuma
#ここで、新規ユーザーのパスワードを設定します。
$ sudo visudo
-----------------------------
#vimが起動するので新規ユーザーにroot権限を与える。
root    ALL=(ALL)       ALL
shizuma ALL=(ALL)       ALL #この行を追加
-----------------------------
$ sudo su - shizuma #ユーザー切り替え
#先ほど設定したパスワード

ここでローカルに一旦戻り、鍵を作成します。

$ cd .ssh
$ ssh-keygen -t rsa
-----------------------------
Enter file in which to save the key ():first_aws_rsa #ここでファイルの名前を記述して、エンター
Enter passphrase (empty for no passphrase): #何もせずそのままエンター
Enter same passphrase again: #何もせずそのままエンター
-----------------------------
$ vi config
-----------------------------
# 以下を追記
Host first_aws
  Hostname 54.64.22.197 #自分の設定に合わせて
  Port 22
  User shizuma #先ほどのユーザー名
  IdentityFile ~/.ssh/first_aws_rsa #秘密鍵の設定
-----------------------------

次に、サーバーに戻り作成した 公開鍵 をサーバーに設定します。

$ mkdir .ssh
$ chmod 700 .ssh
$ cd .ssh
$ vi authorized_keys
-----------------------------
#localの「first_aws_rsa.pub」の中身のコピペ。(localで $ cat first_aws_rsa.pubとかすると良い)
ssh-rsa sdfjerijgviodsjcIKJKJSDFJWIRJGIUVSDJFKCNZKXVNJSKDNVMJKNSFUIEJSDFNCJSKDNVJKDSNVJNVJKDSNVJKNXCMXCNMXNVMDSXCKLMKDLSMVKSDLMVKDSLMVKLCA shizuma@shizuma-no-MacBook-Air.local
-----------------------------
$ chmod 600 authorized_keys
$ exit
$ exit

これで設定が完了したので、以降作成したユーザーでアクセスするようにします。

基本ライブラリとrubyの環境構築

$ sudo yum install 
git make gcc-c++ patch 
openssl-devel 
libyaml-devel libffi-devel libicu-devel 
libxml2 libxslt libxml2-devel libxslt-devel 
zlib-devel readline-devel 
mysql mysql-server mysql-devel 
ImageMagick ImageMagick-devel 
epel-release
$ sudo yum install nodejs npm --enablerepo=epel
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source .bash_profile
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ rbenv rehash
$ rbenv install -v 2.3.1
$ rbenv global 2.3.1
$ rbenv rehash
$ ruby -v

gitの設定

最低限の設定だけしておきます。

$ vi .gitconfig
.gitignore
[user]
  name = your_name #自分の名前
  email = hoge@hoge.com #自分のメアド

[alias] #これはお好きに
  a = add
  b = branch
  ch = checkout
  st = status

[color] #色付け
  ui = true

[url "github:"] #pull、pushのための設定
    InsteadOf = https://github.com/
    InsteadOf = git@github.com:

アプリケーションフォルダの設置

/var/www/rails にアプリケーションフォルダを設置します。

$ cd /
$ sudo mkdir -p /var/www/rails
$ sudo chown -R shizuma var/www

GitHubの接続とアプリケーションのclone

$ cd ~/.ssh
$ ssh-keygen -t rsa
-----------------------------
Enter file in which to save the key ():aws_git_rsa #ここでファイルの名前を記述して、エンター
Enter passphrase (empty for no passphrase): #何もせずそのままエンター
Enter same passphrase again: #何もせずそのままエンター
-----------------------------
$ chmod 744 config
$ vi config
-----------------------------
# 以下を追記
Host github github.com
  Hostname github.com
  User git
  Port 22
  IdentityFile ~/.ssh/aws_git_rsa #秘密鍵の設定
-----------------------------
$ cat aws_git_rsa.pub
-----------------------------
ssh-rsa sdfjerijgviodsjcIKJKJSDFJWIRJGIUVSDJFKCNZKXVNJSKDNVMJKNSFUIEJSDFNCJSKDNVJKDSNVJNVJKDSNVJKNXCMXCNMXNVMDSXCKLMKDLSMVKSDLMVKDSLMVKLCA shizuma@ip-10-0-1-10
-----------------------------

ここで、これをコピペしてgithubに公開鍵を登録する。
githubへの鍵の登録がよくわからない方は以下の記事を参考に。
gitHubでssh接続する手順~公開鍵・秘密鍵の生成から~

そして、git clone する。

$ cd /var/www/rails
$ git clone https://github.com/kuboshizuma/cheerfull # 自分のアプリケーション

RDSの設定

EC2の環境構築に一区切りついたのでRDSの設定を行います。

DBサブネットの登録

RDSで使うサブネットを登録します。
この設定には異なるアベイラビリティゾーンにあるサブネットが最低1つずつ計2つ必要になります。
また、Railsアプリケーションをおいたサブネットはゲートウェイに繋がったpublicなサブネットなので、privateなサブネットを異なるアベイラビリティゾーンに一つずつ作成します。

スクリーンショット_2017-03-04_11_56_01.png

パラメータグループの設定

パラメータグループを設定します。
mysqlを使用していると設定なので、ここではmysqlを選択します。バージョンは適宜選択。
「chara…」で検索すると出て来るcharasetをutf-8に変更。

スクリーンショット_2017-03-04_12_00_57.png

設定が完了した後に、パラメータの編集を押してパラメーター変更。

スクリーンショット_2017-03-04_12_04_29.png

セキュリティグループの作成

VPCでセキュリティグループを作成します。

スクリーンショット_2017-03-04_12_08_19.png

インバウンドルールにMySQLを設定し、MySQLのアクセスのみ許可します。
「送信元」は0.0.0.0/0で設定。特定のRailsアプリケーションサーバーがあるサブネットに限定するほうがよさそう。

スクリーンショット 2017-03-04 12.55.42.png

RDSインスタンスの作成

エンジンの選択

スクリーンショット_2017-03-04_12_12_05.png

本番稼働用?

スクリーンショット_2017-03-04_12_12_45.png

DB詳細設定の指定

無料枠はt2.micro

スクリーンショット_2017-03-04_12_17_35.png

詳細設定の設定

各種作成したもので設定。

スクリーンショット_2017-03-04_12_19_44.png

スクリーンショット_2017-03-04_12_19_53.png

接続確認

hostは各自の作成したRDSインスタンスのエンドポイントをみる。

$ mysql -h hogepoge.ap-northeast-1.rds.amazonaws.com -u shizuma -P 3306 -p

接続出来たら完了!

絵文字の扱い

絵文字も登録出来るようにする場合。
以下を参考に utfmb4を採用する。

ActiveRecordをutf8mb4で動かす

MAMPでは /Applications/MAMP/conf/my.cnfmy.cnf を設置した。

Railsアプリケーションの起動のための準備

puma setting

以下を追記する。

cofig/puma.rb
# add setting for production
_proj_path = "#{File.expand_path("../..", __FILE__)}"
_proj_name = File.basename(_proj_path)
_home = ENV.fetch("HOME") { "/home/#{ENV['PUMA_USER']}" }
pidfile "#{_home}/run/#{_proj_name}.pid"
bind "unix://#{_home}/run/#{_proj_name}.sock"
directory _proj_path
# add end

ENV['PUMA_USER'] にサーバーのユーザー名が入るように環境変数を設定。

database setting

以下のように編集。

config/database.yml
production:
  <<: *default
  database: cheerfull
  username: <%= ENV['DATABASE_USER_NAME_PRODUCTION'] %>
  password: <%= ENV['DATABASE_PASSWORD_PRODUCTION'] %>
  host: <%= ENV['DATABASE_HOST_PRODUCTION'] %>

それぞれ該当の環境変数を設定。

rake secret

以下のsecret keyの箇所に値が入るように環境変数を設定。
rake secret とコマンドを打つと出て来る文字列を設定する。

config/secrets.yml
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
$ gem install bundler
$ bundle install
$ rake db:create RAILS_ENV=production
$ rake db:migrate RAILS_ENV=production

PumaとNginxの起動

Pumaの起動

アプリケーションディレクトリにて以下を実行。

$ RAILS_SERVE_STATIC_FILES=true RAILS_ENV=production puma -w 4

「Ctr+c」でプロセス消さなければ生き残る。
プロセス消す必要があれば以下のようにする。

$ ps aux | grep puma # プロセスIDを探す
$ kill -9 (ID)

Nginx の起動

「cheerfull」の部分は自分のアプリケーションディレクトリ名に変更する。

/etc/nginx/conf.d/cheerfull.conf
  # log directory
  error_log  /var/www/rails/cheerfull/log/nginx.error.log;
  access_log /var/www/rails/cheerfull/log/nginx.access.log;
  upstream app_server {
    # for UNIX domain socket setups
    server unix:/home/shizuma/run/cheerfull.sock fail_timeout=0;
  }
  server {
    listen 80;
    server_name 12.134.156.178; # 自分のIP
    # nginx so increasing this is generally safe...
    # path for static files
    root /var/www/rails/cheerfull/public;
    # page cache loading
    try_files $uri/index.html $uri @app_server;
    location / {
      # HTTP headers
      proxy_pass http://app_server;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
    }
    # Rails error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/rails/cheerfull/public;
    }
    client_max_body_size 4G;
    keepalive_timeout 5;
  }

ユーザーをnginxから自分のユーザー名に変更しておく。

/etc/nginx/nginx.conf
#user nginx;
user shizuma;

nginxを起動します。

$ sudo service nginx restart

これで、IPアドレスでアクセスするとアプリケーションが表示されるようになりました。

Capistranoの設定

諸々動くことが確認出来たのでデプロイが出来るように設定します。
デプロイのためにCapistranoの設定をします。
慣れてきたら、いきなりCapistranoの設定をしていけばいいと思います。

socketの場所もアプリケーションディレクトリに変更するのでnginxの設定もそれに合わせて変更します。

「cheerfull」の部分は自分のアプリケーション名に変更する。

deploy.rb
require 'dotenv'
Dotenv.load

lock "3.7.1"

set :application, "cheerfull"
set :repo_url, "git@github.com:your_gitname/cheerfull.git"
set :branch, 'master'
set :deploy_to, '/var/www/rails/protospacce'
set :linked_files, fetch(:linked_files, []).push('.env')
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
set :keep_releases, 5
set :rbenv_ruby, '2.3.1'

# puma setting
set :puma_threads,    [4, 16]
set :puma_workers,    0
set :pty,             true
set :use_sudo,        false
set :stage,           :production
set :deploy_via,      :remote_cache
set :deploy_to,       "/var/www/rails/#{fetch(:application)}"
set :puma_bind,       "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
set :puma_state,      "#{shared_path}/tmp/pids/puma.state"
set :puma_pid,        "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{shared_path}/log/puma.error.log"
set :puma_error_log,  "#{shared_path}/log/puma.access.log"
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true

namespace :deploy do
  desc 'Create database'
  task :db_create do
    on roles(:db) do |host|
      with rails_env: fetch(:rails_env) do
        within current_path do
          execute :bundle, :exec, :rake, 'db:create'
        end
      end
    end
  end
end

実際にdeployする前に、共有ファイルは用意しておきましょう。この場合、.env

ここでpumaのセッティングをしたので、config/puma.rb はもう不要になります。
また、dotenvを使用する場合は変更を読み込むために config/deploy/templates/puma.rb.erb を用意します。以下を用意。

config/deploy/templates/puma.rb.erb
#!/usr/bin/env puma

directory '<%= current_path %>'
rackup "<%=fetch(:puma_rackup)%>"
environment '<%= fetch(:puma_env) %>'
<% if fetch(:puma_tag) %>
  tag '<%= fetch(:puma_tag)%>'
<% end %>
pidfile "<%=fetch(:puma_pid)%>"
state_path "<%=fetch(:puma_state)%>"
stdout_redirect '<%=fetch(:puma_access_log)%>', '<%=fetch(:puma_error_log)%>', true


threads <%=fetch(:puma_threads).join(',')%>

<%= puma_bind %>
<% if fetch(:puma_control_app) %>
activate_control_app "<%= fetch(:puma_default_control_app) %>"
<% end %>
workers <%= puma_workers %>
<% if fetch(:puma_worker_timeout) %>
worker_timeout <%= fetch(:puma_worker_timeout).to_i %>
<% end %>

<% if puma_preload_app? %>
preload_app!
<% else %>
prune_bundler
<% end %>

on_restart do
  puts 'Refreshing Gemfile'
  ENV["BUNDLE_GEMFILE"] = "<%= fetch(:bundle_gemfile, "#{current_path}/Gemfile") %>"
  ENV.update Dotenv::Environment.new('.env')
end

<% if puma_preload_app? and fetch(:puma_init_active_record) %>
before_fork do
  ActiveRecord::Base.connection_pool.disconnect!
end

on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end
<% end %>
$ bundle exec production cap puma:config 

これでこの設定を本番に反映。

あとはデプロイコマンドを打つ。

$ bundle exec cap production deploy

デプロイdone!

nginxのlogの位置やsocketの位置を変更します。

/etc/nginx/conf.d/cheerfull.conf
  # log directory
  error_log  /var/www/rails/cheerfull/shared/log/nginx.error.log;
  access_log /var/www/rails/cheerfull/shared/nginx.access.log;
  upstream app_server {
    # for UNIX domain socket setups
    server unix:/var/www/rails/cheerfull/shared/tmp/sockets/cheerfull-puma.sock fail_timeout=0;
  }
  server {
    listen 80;
    server_name 12.134.156.178; # 自分のIP
    # nginx so increasing this is generally safe...
    # path for static files
    root /var/www/rails/cheerfull/current/public;
    # page cache loading
    try_files $uri/index.html $uri @app_server;
    location / {
      # HTTP headers
      proxy_pass http://app_server;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
    }
    # Rails error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/rails/cheerfull/current/public;
    }
    client_max_body_size 4G;
    keepalive_timeout 5;
  }
$ sudo service nginx restart

これで、デプロイ環境が整いました。
以前起動したpumaのプロセスはきっておきましょう。

複数サーバーへのデプロイ

multiconfig を使用します。

Gemfile
gem 'capistrano-multiconfig', '~> 3.1', require: false

Capfileのcapistrano/setupから変更。

Capfile
# require "capistrano/setup"
require 'capistrano/multiconfig'

config/deploy/production.rb から config/deploy/app1/production.rb に変更する。
以下のようにdeploy出来るようになる。

$ bundle exec cap app1:production deploy

違うサーバーにデプロイするときは同じように config/deploy/app2/production.rb を準備すればオーケー。

$ bundle exec cap app2:production deploy

これで違うサーバーにデプロイ出来る。

これまでと同様にec2サーバーをもう一度用意して2箇所にデプロイ出来るようにする。

ロードバランサー ELB

ロードバランサーをhttpsで運用したいときadmin@hoge.comのようなアドレスへのメールを受信可能のしないといけない。

以下でとりあえずzoneファイルのインポートまでする。
Amazon Route53 ネームサーバへの移行手順(お名前.comからの)

その後にこの記事にあるようにお名前.com側の設定をする。
お名前.com + Route53で独自ドメインのメールをGmailに転送する

メールの転送が出来ていることを確認する。
そして、ELBの指示に従って設定。
下記参考。
【初心者向け】ELBにSSL証明書をインストールする

そこまでしたら
Amazon Route53 ネームサーバへの移行手順(お名前.comからの)
の続きでELBとつなげる。

注意点は、ターゲットグループにトラフィックをルーティングするときのプロトコルをHTTPにすること。これでELBへはHTTPSでアクセス出来、それをアプリケーションではHTTPで扱うことが出来る。

また、以下のように nginxのサーバーネームにドメイン名を追加する必要がある。

server_name hogepoge.com 00.000.00.00 sakamichi-app-elb-000000000.ap-northeast-1.elb.amazonaws.com

CloudFront

Amazon CloudFront + ACM 独自ドメインで HTTPS (SSL) 配信設定メモ

CloudWatchとか

必要の応じて加筆。

続きを読む

SpotFleet(diversified/lowest)で起動するインスタンスを常にELBにアタッチする

目的

  • SpotFleetで起動しているインスタンスを常に同一のELBにアタッチする
  • 生存戦略はdiversified/lowestで機能する

前提

本構成ではインスタンス1台の運用を想定しているのでインスタンスが停止後、他プールからインスタンスが起動するまで〜2分かかるので、可用性を保つためにはオンデマンドを入れるなり、工夫が必要

方法

スポットインスタンスリクエスト時にユーザーデータを利用する

 イメージ

Untitled (5).png

設定

  1. ELBに対する権限を持つIAMRoleを作成
  2. IAMRoleを使用するスクリプト作成
  3. EC2Resourcesのaws_spot_fleet_requestの引数にuser_dataを記述
spot_fleet_request.tf
# Request a Spot fleet
resource "aws_spot_fleet_request" "aws-simple-console-spotfleet" {
  iam_fleet_role                      = "arn:aws:iam::xxxxxx:role/xxxxxx"
  spot_price                          = "0.0127"
  allocation_strategy                 = "diversified"
  target_capacity                     = 2
  terminate_instances_with_expiration = false

  launch_specification {
    instance_type               = "m3.medium"
    ami                         = "ami-xxxxxx"
    availability_zone           = "ap-northeast-1c"
    subnet_id                   = "subnet-xxxxxx"
    iam_instance_profile        = "xxxxxx"
    key_name                    = "xxxxxx"
    vpc_security_group_ids      = ["sg-xxxxxx"]
    monitoring                  = false
    associate_public_ip_address = true
    user_data                   = "${file("userdata.sh")}"

    root_block_device {
      volume_size = "10"
      volume_type = "gp2"
    }
  }
}
userdata.sh
#!/bin/bash
export INSTANCEID=`curl http://169.254.169.254/latest/meta-data/instance-id`
aws elb register-instances-with-load-balancer --load-balancer-name xxxxxx --instances $INSTANCEID --region ap-northeast-1

続きを読む

【AWS】ELB+Gitlabの構築・設定(Google認証付き)

目的

AWSでGitlabを構築するにあたり、ELBでHTTPSを受け付け、後段EC2のGitlabサーバにて処理。
それを実現するためのGitlab設定方法をまとめる。
併せて、Gitlabでoogle認証を行う場合の設定も記載。

全体概要はこんな感じ

  • ELBには「https://xxx」で通常443ポート開放
  • https://xxx/gitlab/」でアクセスした場合はGitlabサーバに「http://xxx/gitlab」で送る。
  • 他に「https://xxx/app1/」とした場合は別のサーバへ振り分けるイメージ

スクリーンショット 2017-08-06 16.51.02.png

やらないこと

  • ELB、EC2などのAWSの設定
  • 公式に載っているGitlabのインストール手順
  • google認証を行うためのgoogle側の設定

前提

Gitlabのバージョン:GitLab Community Edition 9.3.6
標準のgitlabのインストールを実行済み
公式インストール手順(CentOS6)
※AWSLinuxでも同様

gitlabへSSH通信は行わない。PushなどはHTTPSでの認証とする。

1. ELBとGitlabの連携

1-1. gitlab.rbの編集

==[ELBの連携まで]==
#cd /etc/gitlab/gitlab.rb
#diff gitlab.rb gitlab.rb.org

13,14c13
< external_url 'https://xxx/gitlab'
---
> external_url 'http://xxx'
807c806
< nginx['listen_port'] = 80
---
> # nginx['listen_port'] = nil
811c810
< nginx['listen_https'] = false #https通信の無効化
---
> # nginx['listen_https'] = nil
817c816
<  nginx['proxy_set_headers'] = {
---
> # nginx['proxy_set_headers'] = {
821c820
<   "X-Forwarded-Proto" => "https",
---
> #  "X-Forwarded-Proto" => "https",
825c824
<  }
---
> # }

1-2. 再起動

gitlab-ctl reconfigure
gitlab-ctl start

1-3. ELB連携のポイント

gitlab.rbのみ編集し、reconfigureで反映するため一番きれいな方法だと思います。
直接nginx.conf等修正するとreconfigure時に上書きされるので。

ブラウザからの画面表示(リンクやPOSTのパス)はHTTPSにする必要がありつつ、
gitlabサーバ自体をHTTPで処理させるのが面倒でした。

2. GitlabでGoogle認証を設定する場合

2-1. gitlab.rbの編集

==[GoogleAuthまで]==
#cd /etc/gitlab/gitlab.rb
#diff gitlab.rb gitlab.rb.org

221c220
< gitlab_rails['omniauth_enabled'] = true
---
> # gitlab_rails['omniauth_enabled'] = false
223d221
< gitlab_rails['omniauth_allow_single_sign_on'] = ['google_oauth2']
230,239c228,235
< gitlab_rails['omniauth_external_providers'] = ['google_oauth2']
< gitlab_rails['omniauth_providers'] = [
<   {
<     "name" => "google_oauth2",
<     "app_id" => "各自のapp-id",
<     "app_secret" => "各自のシークレット",
<     "args" => { "access_type" => "offline", "approval_prompt" => "",
<     "hd" =>  "許容ドメイン(hoge.com)" }
<   }
< ]
---
> # gitlab_rails['omniauth_providers'] = [
> #   {
> #     "name" => "google_oauth2",
> #     "app_id" => "YOUR APP ID",
> #     "app_secret" => "YOUR APP SECRET",
> #     "args" => { "access_type" => "offline", "approval_prompt" => "" }
> #   }
> # ]

2-2. 再起動

gitlab-ctl reconfigure
gitlab-ctl start

2-3. google認証のポイント

特定のドメインのみ許可したい場合は、hdオプションを利用することです。
google認証側で特定ドメインのみ対応してくれます。
特定ドメインが不要であればオプション外せば大丈夫なはずです。

おまけ

Gitlab, Redmine, Jenkinsなど利用しますが、どれも/配下やHttps->Httpの切り替え方法が異なるので、
いちいち調べるのが面倒。なんかいい方法ないかなぁ。

参考

似たような対応として下記の記事を参考にさせて頂きましたが、Reconfigureをすると、
設定が変更されてしまうためメンテナンスする際に面倒なため、/etc/gitlab/gitlab.rbのみで対応できる方法を書きました。
http://qiita.com/morozumi_h/items/128d3254fd2eb4671966

続きを読む

Go/Java SE環境のElastic BeanstalkでHTTPをHTTPSにリダイレクトする

まとめるまでもなかったのかもしれませんが自分がかなりハマってしまったので書いておきたいと思います。

前提

基本的なSSLのセットアップができている前提で進めていきます。Elastic Beanstalk (EB) の環境にElastic Load Balancer (ELB) が紐付けされており、ELBに対して適切なSSL証明書とHTTPS/ポートの設定されているか今一度ご確認ください。

リダイレクトの設定

ELBは受け取ったリクエストのプロトコルをX-Forwarded-Protoヘッダに格納してあるため以下の設定をnginxのサーバブロック内に読み込ませればHTTP→HTTPSのリダイレクトが実現できます。

if ($http_x_forwarded_proto != 'https') {
    rewrite ^ https://$host$request_uri? permanent;
}

nginxの設定

では肝心のnginxの設定はどうやって上書きするのか。Go/Java SE環境では簡単にnginxの設定をいじくるための仕組みが導入されています。どちらも設定方法は同じです。

.ebextensions/nginx/nginx.confというファイルを作ってEBでデプロイするアプリケーションのルートディレクトリに置いておくとこのnginx.confがEC2上の/etc/nginx/nginx.confに上書きされます。

もう一つ、.ebextensions/nginx/conf.d/*.confにファイルを置いておくとこれらのファイルが/etc/nginx/conf.d/elasticbeanstalkコピーされ、nginx.confを上書きしていない場合はその中のserver{}ブロックの中で、あるいはnginx.confを上書きしている場合はinclude conf.d/elasticbeanstalk/*.conf;と記述すればそこで読み込んでくれますが、なぜか上記のリダイレクト設定を記述したファイルを.ebextensions/nginx/conf.d/02_proxy.confとして置くとnginxの設定ファイル読み込み時にif文が許されず弾かれます。

注意

CodeBuildを噛ましている場合、artifactを出力する際に.ebextensionsを出力するのを忘れてしまうと当然コピーされないのでご注意を。

nginx.confの準備

そんなわけでincludeされるファイルに記述してもうまくいかないので既存のnginxの設定ファイルに上書きしてしまいます。server{}ブロック内に上記のリダイレクト設定を書き込むと以下のようになります。

# Elastic Beanstalk Nginx Configuration File

user                    nginx;
error_log               /var/log/nginx/error.log warn;
pid                     /var/run/nginx.pid;
worker_processes        auto;
worker_rlimit_nofile    33193;

events {
    worker_connections  1024;
}

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

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

    include       conf.d/*.conf;

    map $http_upgrade $connection_upgrade {
        default     "upgrade";
    }

    server {
        listen        80 default_server;
        access_log    /var/log/nginx/access.log main;

        client_header_timeout 60;
        client_body_timeout   60;
        keepalive_timeout     60;
        gzip                  off;
        gzip_comp_level       4;
        gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;

        # Redirect to https
        if ($http_x_forwarded_proto != 'https') {
           rewrite ^ https://$host$request_uri? permanent;
        }

        # Include the Elastic Beanstalk generated locations
        include conf.d/elasticbeanstalk/*.conf;
    }
}

上記をコピペして.ebextensions/nginx/nginx.confとして保存してやればHTTP→HTTPSリダイレクトが有効になるはずです。

続きを読む

ELBに証明書をアップロードしたら「Server Certificate not found for the key」とエラーが出た

TL;DR

掲題のエラーが起きたら一旦キャンセルして既存の証明書を確認してみましょう。

現象

既存の ELB に SSL 証明書を追加しようとすると Server Certificate not found for the key というエラーになる件の解決方法
と同様の現象です。

ポートのリスナーを作成できませんでした: 443。 Server Certificate not found for the key:xxxxxxxxxxxxxxxxxxx

というエラーが出ます。

対応

調べてみるとELBに対する新規SSL証明書の適用はAWS CLIからでしかできないという世論が多数でした。しかしCLIからは面倒だったのでもう一度深呼吸して設定項目を見返してみました。
行なったことを以下に記します。
以下の画像では現行のSSL証明書をcertification,新しく追加する証明書をnew_certificationとしています。

  1. 一度キャンセルする
  2. 再度SSL証明書の欄の変更ボタンを押す。
    スクリーンショット 2017-08-02 17.14.53.png

3.「AWS Identity and Access Management (IAM) から、既存の証明書を選択する」にチェックを入れる

スクリーンショット 2017-08-02 17.48.13.png

修了書のプルダウンを確認してみるとnew_certificationが選択できるようになっていました!

まとめ

同様の対応で対処できない場合もあると思いますが、一度既存の証明書を確認してみてくださいくコ:彡

続きを読む

AutoScaleを設定し、lambdaでAMIとAutoScaleの設定を自動更新する

今回はAuto Scaleの設定のまとめです。

・概要

負荷分散のためにAutoScaleを検討することはよくあることです。

ゲームのイベントなどでは負荷がかかる時間帯が決まっているので、常時起動しているサーバーを減らしAutoScaleのスケージュールで設定することで費用を抑えることが出来ます。

※AutoScaleは起動に時間がかかるので、スケーリングポリシーでは突発的な負荷に対応できないのでスケジュールを使っています。

・問題点

AutoScaleを設定した場合、設定しているAMIのassetやsourceが起動中のインスタンスと違うといった事が起こります。

そういった場合にAMIを更新し、自動的AutoScaleに割り当てる設定を行います。

・AutoScaleグループの作成

今回の設定は元々あるAutoScaleグループの設定を置き換えるので先にAutoScaleグループを作成しておきます。

・AWSコンソールの[AUTO SCALING]→[起動設定]→[起動の作成]

※ここの起動設定は最終的に使わないので適当でOKです

・AWSコンソールの[AUTO SCALING]→[AUTO SCALINGグループ]→[AUTO SCALINGグループの作成]

上記の起動設定を選択、下記を設定

ネットワーク
グループ名
ロードバランシング

※今回はスケジュールなのでスケーリングポリシーは設定しない
※通知を使用するとauto scaleの情報をlambda等に引き渡して色々出来ます。
※タグを使いauto scaleで作成されたインスタンスに適用できます。

※ここで作成したAUTO SCALINGグループの名前を控えておきます。

lambdaを設定

・[Blank Function]→リストから[CloudWatch Events]を選択
・ルール:新規のルール
・ルール名:任意
・ルール説明:任意
・ルールタイプ:スケジュール
・スケジュール式:cron(0 * * * ? *)
※cronの設定と同じ、上記は1時間毎
・トリガーの有効化にチェック

・名前:任意
・説明:任意
・ランタイム:python

・ロール:[カスタムロールの作成]を選択、IAMに下記をアタッチし作成、再度[既存のロールを選択]で作成したIAMを選択

・コード:下記を貼り付けて***の部分を自分の環境に置き換える

import boto3
import time
from botocore.client import ClientError
from datetime import datetime, timedelta, tzinfo
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

ec2 = boto3.client('ec2')
autoscaling = boto3.client('autoscaling')

instance = "***" #コピー元のec2インスタンスID(例:i-22a9edc2ted27d2a1)
device_name = "***" #コピー元のec2のブロックデバイス(例:/dev/sda1)
image_prefix = "***" #amiの名前の識別子(任意※他のAMIで使っていない文字)
launch_prefix = "***" #auto scaleの起動設定の名前の識別子(任意※他の起動設定で使っていない文字)
ec2_volume_size = *** #ebsのボリュームサイズ(単位:GB)
ec2_volume_type = "***" #ebsのボリュームタイプ(例:gp2)
secrity_group = [***] #ec2インスタンスのセキュリティグループ
ec2_instance_type = "***", #ec2インスタンスタイプ(例:c4.2xlarge)
ec2_key_name = "***", #ec2インスタンスのキーペア名
auto_scaling_group_name ="***",#作成したAutoScaleGroup名


def lambda_handler(event, context):
    try:
        dstr = datetime.now().strftime('_%Y-%m-%d-%H-%M-%S_') + instance
        logger.warning("create ami:%s" % (dstr))
        new_ami = ec2.create_image(Name=image_prefix + dstr ,InstanceId=instance,NoReboot=True,DryRun=False)
        recv = ec2.describe_images(Owners=['self'])
        list=[]
        target = image_prefix
        for img in recv['Images']:
            if target in img['ImageLocation']:
                list.append(img)
        s_list = sorted(list,key=lambda h: h['CreationDate'])
        if len(s_list) > 0:
            del_target = s_list[0]
            logger.info("delete ami:%s" % (del_target['Name']))
            ec2.deregister_image(ImageId=del_target['ImageId'],DryRun= False)
        dstr = datetime.now().strftime('_%Y-%m-%d-%H-%M-%S')
        logger.info("create auto scale launch config:%s" % (dstr))
        device = {}
        device['DeviceName'] = device_name
        ebs = {}
        ebs['VolumeSize'] = ec2_volume_size
        ebs['VolumeType'] = ec2_volume_type
        ebs['DeleteOnTermination'] = True
        device['Ebs'] = ebs
        device_mapping = [device]
        launch_name = launch_prefix + dstr

        res = autoscaling.create_launch_configuration(
            LaunchConfigurationName = launch_name,
            ImageId = new_ami['ImageId'],
            InstanceType = ec2_instance_type,
            SecurityGroups = secrity_group, 
            KeyName = ec2_key_name,
            BlockDeviceMappings = device_mapping,
            AssociatePublicIpAddress = True
        );
        logger.info("update auto scale group name:%s" % (launch_name))
        res = autoscaling.update_auto_scaling_group(
            AutoScalingGroupName=auto_scaling_group_name 
            LaunchConfigurationName = launch_name
        );  
        target = launch_prefix
        recv = autoscaling.describe_launch_configurations()
        list=[]
        for launch in recv['LaunchConfigurations']:
            if target in launch['LaunchConfigurationName']:
                list.append(launch)
        s_list = sorted(list,key=lambda h: h['CreatedTime'])
        if len(s_list) > 2:
            del_target = s_list[0]
            logger.info("delete launch config:%s" % (del_target['LaunchConfigurationName']))
            autoscaling.delete_launch_configuration(
                LaunchConfigurationName=del_target['LaunchConfigurationName'],
            )
        logger.info("end ami update")

    except ClientError as e:
        logger.error( e )
    return 'end'

・auto scaleのスケジュールを登録する

負荷の時間に合わせて自由に

・注意事項

※最初はelbのhealthチェックだけじゃなくwebサーバーのログやawsコンソールで正常な起動を確認すること

※デプロイがAMIの更新と被らないようにすること

続きを読む

ALB+WAF利用時のCloudWatchアラーム設定

概要

  • Classic ELB から ALB + WAF へ移行するに辺り、CloudWatch アラーム設定をどうしようか考えてみる(結局、結論は出ず。。)

bad behavior なリクエストが来た場合

※ 例えば、PHPMyAdminを狙った /PMA/

Classic ELB (WAFなし) のとき

  • CloudWatch HTTPCode_Backend_4XX は、作業ミスやコードの不具合・バグ検知用に入れたはずが、クローラー/愉快犯/画像泥棒/攻撃事前調査(Probe)などによるアラートの方が多い。
  • PHPMyAdmin を狙った攻撃事前調査系の bad behavior も、PHPMyAdmin なんて入れていないので HTTPCode_Backend_4XX (=404) アラートが飛ぶ (最近特に多い)。
  • そこで、AWS WAF を導入したら HTTPCode_Backend_4XX アラートが減るんじゃないかなと期待してみた(けど。。)

AWS ALB + AWS WAF

うーむ

とりあえず?

  • ALB CloudWatach HTTPCode_ELB_4XX_Count と WAF CloudWatch BlockedRequests を設定する。
  • で、
    ALB の CloudWatach HTTPCode_ELB_4XX_Count アラートが飛ぶ 

    WAF の CloudWatch BlockedRequests アラートが来なかったら、Kibanaなどで状況を確認する

メモ

AWS ALB のみの場合

構成

ALB —> httpd on EC2

bad behavior request が来た

http://ALB-DNS-Name/PMA/

① ALB が受ける
② ターゲットグループの httpd on EC2 が受ける

アクセスログ

① ALBアクセスログ

  • elb_status_code: 404
  • target_status_code: 404

② httpd on EC2 アクセスログ

  • GET /PMA/ HTTP/1.1″ 404

Amazon CloudWatch メトリクス

ALB

  • elb_status_code: 404 ⇒ HTTPCode_ELB_4XX_Count (統計: 最も有用な統計は Sum)
  • target_status_code: 404 ⇒ HTTPCode_Target_4XX_Count (統計: 最も有用な統計は Sum)

AWS ALB + AWS WAF の場合

構成

ALB —> httpd on EC2
+–> WAF (rule:bad-behaviorをブロック)

bad behavior request が来た

http://ALB-DNS-Name/PMA/

① ALB が受ける
② AWS WAF へ転送
③ AWS WAF が ルールに基いて検査
④ AWS WAF が ALB へリクエストのブロックを指示
⑤ ALB がブロックする (403 Forbidden)

アクセスログ

① ALBアクセスログ

  • elb_status_code: 403
  • target_status_code: –

② httpd on EC2 アクセスログ

  • なし

Amazon CloudWatch メトリクス

ALB

  • elb_status_code: 403 ⇒ HTTPCode_ELB_4XX_Count (統計: 最も有用な統計は Sum)

WAF

  • BlockedRequests (有効な統計: Sum)

続きを読む

JAWS-UG コンテナ支部 #9

ECS アップデート情報 Amazon Web Services Japan K.K. 岩永亮介さん (@riywo)

  • ConsleからSpotFleetが利用可能になった。
  • 時間やイベント駆動のタスク起動が可能になった。
  • RunTaskとStartTaskでCPUとメモリの設定が上書き可能になった。
  • AWSブログでFAQ的なソリューションを公開してる。
    • CI/CDパイプラインはどうすればいい?
    • デプロイはどうすればいい?
    • Blue/Greenデプロイメントのやり方には何がある?
    • クラスタを安全にスケールインするには?
    • 機密情報をうまく扱いには?
    • etc…

本番サービスでECSを使ってみた話(仮) 株式会社ナビタイムジャパン 渡部茂久さん

  • Slide
  • ECS移行にあたりハマったポイントを紹介
  • ELBのアイドルタイムアウトにハマる
    • ELBのタイムアウト<webサーバーのKeepAlive
  • ECSAgentの接続性監視
    • agentConnectedがfalseのインスタンス自動でクラスタから除外されず
  • インスタンスIDとDockerコンテナIDのマッピング
    • DockerコンテナIDを確認するにはホスト上でintrospectionAPIを実行する必要がある
    • fluentdコンテナ起動時にEC2のメタデータ取得APIを叩いてる
  • Dockerコンテナのメトリクス監視
    • Cloudwatchで監視
  • コンテナインスタンスのドレイニング
    • Labmdaで。クラスターサイズによってsleepを調整。
  • オートスケールとデプロイの両立
    • ecs-deployをカスタマイズして利用
  • ECSの良かったところ
    • 柔軟なデプロイ
    • Cloudwatch/Lambda/Cloudformationとの親和性
  • あとで清書。

AWS ECSのサービスをslack botでデプロイする話 株式会社クラウドワークス 福島明さん (@h3_poteto)

  • Slide
  • ecs-deploy使ってた。
  • DBのmigration問題。ecs-deployでは出来ない。
  • ecs-goploy作った。

GitHub + ECSで快適Review環境 株式会社Speee 天野太智さん (@pataiji)

LT: AWS CodeBuild カスタムDockerイメージを使ってビルドする 株式会社オープンストリーム 飯分俊行さん (@tiibun)

  • Slide

LT: ECS x Mackerel ~ 動的ポートマッピングに対応したタスクのメトリクスを取得する ~ 株式会社サイバーエージェント・クラウドファンディング 吉田慶章さん (@kakakakakku)

続きを読む

TerraformでAWSの構成を読み取るぞ(Terraform import)

前書き

Terraform import については、Qiitaでお2人も記載してくれていますので、参照ください。

tyasuさん:terraform importの使い方メモ
kt_higaさん:terraform importを試してみた

同じスタンスで記載してもしょうがないので、勉強している最中に違う観点でいくつかTerraform import について記載します。

環境

Windows にTerraform(2017/7/18時点:v0.9.11)をインストール
AWSには、EC2,ELB,RDS,S3 など以前手作業で作った環境があります。

準備

まずは、最低限Terraformの利用には、access_key, secret_keyが必要です。
こちらを参照してキーを確認しましょう。
Windowsの作業フォルダにファイルを作成して、確認したキーを記載します。
※ ファイル名は、<任意の名前>.tf としてください。
ここでは、c:work フォルダを作業フォルダとします。

provider "aws" {                                            ・・・・決め打ちです。
    region      = "ap-northeast-1"                          ・・・・リージョンを記載(ap-notheast-1:日本リージョン)
    access_key  = "XXXXXXXXXXXXXXXXX"                       ・・・・access_keyを登録
    secret_key  = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" ・・・・secret_keyを登録
}

※ ほとんどが決め打ちです。
   今回は、AWS環境のため"aws" となります。

インポート!

叩くコマンドは、簡単!

AWSのマネジメントコンソールから、インスタンスID(赤枠部分)を確認して、コマンドのオプションに指定します。
C:> cd work
C:work> terraform import aws_instance.<任意の名前> <インスタンスID>

題.png

ここでは、こんな感じでコマンドを叩いてみました。
もともとの環境では、タグ名を「batchserver01」という名前を付けていましたが、ここではわざと「batch01」に変更しています。
タグ名と合わせても勿論OKです。

import02.png

無事、terraform.tfstate ファイルが作成されました。

※ <> 部分は、修正しています。

{
    "version": 3,
    "terraform_version": "0.9.11",
    "serial": 0,
    "lineage": "XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "aws_instance.batch01": {
                    "type": "aws_instance",
                    "depends_on": [],
                    "primary": {
                        "id": "<INSTANCE_ID>",
                        "attributes": {
                            "ami": "<AMI_ID>",
                            "associate_public_ip_address": "false",
                            "availability_zone": "<AvailabilityZone_ID>",
                            "disable_api_termination": "false",
                            "ebs_block_device.#": "0",
                            "ebs_optimized": "false",
                            "ephemeral_block_device.#": "0",
                            "iam_instance_profile": "",
                            "id": "<INSTANCE ID>",
                            "instance_state": "running",
                            "instance_type": "<INSTANCE_TYPE>",
                            "ipv6_addresses.#": "0",
                            "key_name": "<KEY_NAME>",
                            "monitoring": "false",
                            "network_interface.#": "0",
                            "network_interface_id": "<NW_INTERFACE_ID>",
                            "primary_network_interface_id": "<NW_INTERFACE_ID>",
                            "private_dns": "<DNS_NAME>",
                            "private_ip": "<PRIVATE_IP>",
                            "public_dns": "",
                            "public_ip": "",
                            "root_block_device.#": "1",
                            "root_block_device.0.delete_on_termination": "false",
                            "root_block_device.0.iops": "100",
                            "root_block_device.0.volume_size": "10",
                            "root_block_device.0.volume_type": "gp2",
                            "security_groups.#": "0",
                            "source_dest_check": "true",
                            "subnet_id": "<SUBNET_ID>",
                            "tags.%": "1",
                            "tags.Name": "batchserver01",
                            "tenancy": "default",
                            "volume_tags.%": "0",
                            "vpc_security_group_ids.#": "1",
                            "vpc_security_group_ids.2148762998": "<SECURITY_GROUP_ID>"
                        },
                        "meta": {
                            "schema_version": "1"
                        },
                        "tainted": false
                    },
                    "deposed": [],
                    "provider": "aws"
                }
            },
            "depends_on": []
        }
    ]
}

ここから本題

★ EC2を1台しかimportしていないので、もう1台importしたらどうなるんだ?

import01.png

別に問題なくコマンド終了。
先ほど作成されたterraform.tfstate は、terraform.tfstate.backup にCOPYされ、terraform.tfstate に2台目が追記されました。
(省略)

★ S3をimport しても大丈夫だよね?

C:work> terraform import aws_s3_bucket.<任意の名前> <バケット名>

わざとバケット名を間違えて見ました。

import03.png

★ ALBをimport !!

C:work> terraform import aws_alb.<任意の名前> <ARN>

import04.png

結果、terraform.tfstate ファイルにはどんどんimport すればしただけ、追記されていきました。
どんどん下に追記されていくわけではなく、DataSource(aws_s3_bucket, aws_instance, aws_albなど)の名前で順に並んでいくようです。

参考サイト

対応しているDataSource をはじめ、オプション確認にはやっぱりここは外せません。

本家HashiCorp:AWS Provider

続きを読む

実務経験がなくても2ヶ月でAWSソリューションアーキテクトに合格するためにやったこと

ゴールデンウィークくらいからAWSを趣味で触り始めてちょっと面白そうだったので、ソリューションアーキテクトの試験を受けてみようかと思い立ちました。
2017年7月12日になんとかギリギリ69%で合格できていたのでどういう風に勉強していったのかを残してみます。
なお、Qiitaには初投稿になります。

まずAWSに触り始める前事の自分自身の知識としては

  • 普段はエンプラ系の製品開発に従事
  • IPAのネットワークスペシャリストとデータベーススペシャリストを保持
  • AWSの主要サービスがなんなのかくらいはうっすらとわかる
  • たまにAWS関連のニュースを見てる
  • 実務でのAWSの経験は今まで全くない(そしてこれからもなさそう…

という感じでした。

自分としてはネットワークやデータベース周りの基本的な知識はある方だと思っています。
しかし、実際のお仕事ではクラウドのクの字にもかすりもしない、全く縁がない分野の人間です。

また、平日はあまり勉強することはなく(Evernoteに記録したノートを読み返す程度)、主に土日のみ勉強していました。実際には休みの日に4~5時間程度だったと思います。

アカウントを作成する

なにはともあれまずは無料枠を全力で駆使するためにアカウントを作成しました。とはいえ最初は何からやって良いのかわからないので、ざっとチュートリアルを触ってみました。

AWS 10分間チュートリアル

ほんとに10分だけでいろいろと体験できるのでありがたいです。
チュートリアルでは途中でつまづくことものほぼありませんでした。
チュートリアルとしてはとても役に立ったと思います。

試験ガイドを見てみる

AWSソリューションアーキテクトの右側にある試験ガイドのダウンロードからダウンロードして試験の範囲などを確認しました。特に製品名が明示的に書かれているものは重点的勉強する必要があると感じました。

セキュリティの基本的な考え方を知る

セキュリティプロセスの概要を読んで共有責任モデルを始めとする考え方を知っておきます。ページ数が結構あります。

サンプル問題をやってみる

AWS 認定ソリューションアーキテクト – アソシエイトの右側にある[サンプル問題のダウンロード]からダウンロードして解いて見ました。最初には全然わかりませんでした…

AWSのドキュメントを見てみる

基本的には勉強といってもAWS ドキュメントをひたすら読むということが大半でした。あとはAWS用語集も割と目を通しました。
さらに、AWSクラウド活用資料集も必要そうな製品のものについては一通り目を通しました(その内リンク切れしそう…)。
また、よくある質問は確実に目を通しておいたほうが良いと思います(特にページの上の方にある質問。下の方はより細かい質問に対する解答になっている気がしてあまり読みませんでした)。
以下のドキュメントはだいたい目を通しましたが、応用っぽい使い方などについてはあまり深追いはしませんでした。

重要そうな項目や説明はEvernoteの方に残してマーカなどで印をつけていきました(上記だけでも結構な分量になりました)。
上記の製品をいかに組み合わせて問題を解決するのかといった視点が重要になるようです。

AWS Summit 2017に参加してみる

1日だけでしたが、入門系のセッションだけ参加してきた。過去の分も含めて後からPDFでも動画でも確認できるのは非常にありがたいです。モチベーションも上がりました。

AWS Summit Tokyo 2017 セッション資料・動画一覧

また参加していて思ったこととしては、最新情報はある程度キャッチアップしておく必要があると思いました。例えば、「既存のAmazon EC2インスタンスにIAM Roleがアタッチできるようになりました」のように、今までできていなかったものができるようになったのはよくチェックした方が良いと思います。

書籍関連

書籍としては以下のものを読んで、その内のいくつかは実際に書籍の従って動かしてみたりもしました。
たまたまKindle版が半額になっていたものやポイントが多かったりすることもあって購入もしています(運が良かった…)。
購入していないものは図書館で借りて2週間(図書館の期限が2週間なので)で隅から隅まで熟読してました。
実務経験がない以上、とにかく手を動かしていくことが重要だと考えました。

いろいろなリンクを回ってみる

クラスメソッドさんのブログは情報も早くてよく見てました。他にもAWS公式ブログやqiitaの記事なども目を通していました。特にQiitaのソリューションアーキテクトに合格した系の記事は大変参考に、かつモチベーションの維持に効果がありました!

Amazon Web Services ブログ
Developers.IO
Qiita -ソリューションアーキテクト-

また、以下のサイトでは実際に試験のような問題を解くことができます。有料なところもあるので、やるかどうかは自己判断になります。

AWS Web問題集で学習しよう

あまり深く勉強しないものを知っておく

「この製品はなんですか?」「この製品で何ができますか?」くらいの知識でも大丈夫そうなものについては、あまり深く勉強しないようにしました。例えばRedShiftやKinesis、OpsWorkなどがそれに当たるかと思います。
また、SDKやコマンドラインツールなどについては範囲外だと思うので一切見ていません。

模擬試験を受けてみる

1週間前くらいに模擬試験を受けてみた結果、70%でした。
模擬試験は本試験の半分くらいのボリュームでしたが、それでも結構難しく感じました。
マークをつけることで後から見直すことができるので、ちょっとでも不安な問題にはマークをつけて復習できるようにメモっておきました(試験が終わると問題文などに確認はできなくなります)。
この時点ではちょっとまだ本番に自信がなかったので、ここから本試験まではあまり手を動かさずに、ここまで勉強したことの復習をメインに、Evenoteを読み返したりドキュメントの再確認をやり始めました(この時点で書籍はほぼ読了済みの状態)。

実際の試験のときの心構え

試験は午前中にして朝早めにおきて、現地には早めにいき近くの喫茶店で最終確認をしておきました。
本試験では、とにかく問題文をしっかりと読むこと大切だと感じました。
また、ちょっと引っ掛けのようなものも混じっていたりして、うろ覚えではなくキチンと理解することが重要だと感じました。

最後までやってみたところで時間は結構余ったので、読み返しに結構時間が使えました。
採点結果としては実務経験がないこともあって、トラブルシューティングの得点はやはりよくありませんでした(なんと33%…)。
ここはやはり経験が生きるところなのだと思いますが、ドキュメントのトラブルシューティングをもっと読み込んでおけば良かったと後悔しています。

今後

趣味で終わらせるには勿体無い分野だと思うので、クラウド絡みのお仕事したいけど今のところじゃまずそんな仕事はないという…
でもプロフェッショナルもいずれはとってみたいので、やはり実務経験が欲しいところです。

続きを読む