エンジニア

2016.10.25

AWS OpsWorksでNginx+PHPのWebサーバを構築する

概要

AWS OpsWorksはEC2上にアプリケーションのデプロイやミドルウェアのインストール・更新をするためのツールです。特に「ミドルウェアの更新が簡単にできるようになる」というのはサービス担当者としてはすごくメリットがあると思います。
私も以前、Webサービスを担当していましたがミドルウェアのインストール自体はインストール済みのAMIをコピーすれば良かったのですが、運用フェーズに入ると稼働しているサーバに対して更新が必要な時がたまにありました。

  • セキュリティパッチを充てる時
  • 新しくバーチャルホストを追加する必要が出た時
  • Nginxの出力ログの形式を変えたい時
  • fulentdやlogstashなどミドルウェアを後から追加したい時

などなど。
上記のような時に当時AMIを新しくとって展開したり、直接サーバに入って変更したり割と時間を取られた思い出しかなかったので是非ともハンズラボでOpsWorksを導入して行きたいと思いまずはOpsWorksでWebサーバ(Nginx+PHP)を構築してみました。
流れは下記の通りです。

  1. スタックを作成する。
  2. レイヤーを作成する。
  3. NginxとPHPをインストールするレシピを用意してレイヤーに設定する
  4. 実行

1. スタックを作成する

スタックは例えばWebサービスであればWebサーバ、DB、LB(ロードバランサ)など必要になりますが
これらリソースをグループとして管理するための最上位のエンティティになります。
もちろん、作成したスタックをコピーして新たなスタックを作成することもできます。

  1. まずはAWSコンソールから[Add your first stack]ボタンをクリック
  2. Chef 12 stackを選択。設定は以下の通りです。
    • Stack name : WebSample
    • Region : Tokyo
    • VPC : 任意(インターネットに出れること)
    • Default subnet : 任意
    • Default operationg system : Linux(Amazon Linux)
    • Default SSH key : 任意
    • Chef version : 12(固定)
    • Use custom Chef cookbooks : No(後ほど設定します)
    • Stack Color : 任意
    • Default IAM instance profile:S3にアクセス可能なロール
  3. [Add Stack]ボタンを押下

2. レイヤーを作成する

レイヤーはWebサーバ、DB、LBなどそれぞれのコンポーネントを表すものです。
例えば1つのインスタンスでWebとDBどちらの役割も果たしたい場合はWebサーバレイヤとDBレイヤに追加することもできます。

  1. 右メニューの[Layers ] から [Add Layer]ボタンをクリック
  2. OpsWorksタグの項目を以下のように入力して[Add layer]ボタンをクリック
    • Name : WebServer(Nginx+PHP)
    • Short name : webserver
  3. 最終的にブラウザから確認をしたいので[Network]のPublic IP AdressesをYesにしておきます。
  4. インスタンス起動時にS3に対してChefのライブラリやレシピを取得しに行くので[Security]のEC2 Instance profileにはS3にアクセス可能なロールを付与しておきます。

3. NginxとPHPをインストールするレシピを用意してレイヤーに設定する

スタック作成時にChef11を選んだ場合レイヤー作成時にレイヤータイプを選べてサクPHPサーバー(Apache)を作れるのですが、今回Chef12を選びましたのでクックブックは自分で用意する必要があります。
以下の流れでレシピを用意しました。

  1. Chef SupermarketにてNginxクックブックとPHPクックブックをダウンロード
    $ git clone https://github.com/chef-cookbooks/chef_nginx.git
    $ git clone https://github.com/chef-cookbooks/php.git
    
  2. ちょっと設定ファイルとか修正して固めてS3にアップロード
    • chef_nginx/attributes/default.rbを変更
      #35行目あたり
      default['nginx']['default_root'] = '/var/www/nginx-default'
      ↓
      default['nginx']['default_root'] = '/usr/share/nginx/html'
      
    • php/attributes/default.rbを変更
      #65行目あたりを変更
      #現時点ではAmazon Linuxのphp–fpmのデフォルトバージョンは5.3ですが76行目あたりでAmazon Linuxだった場合php56をインストールするようになっているようなので合わせてphp–fpmのバージョンも合わせておく
      default['php']['fpm_package']   = 'php-fpm'
      ↓
      default['php']['fpm_package']   = 'php56-fpm'
      #68行目あたりに以下を追加
      default['php']['fpm_nginx_conf'] = '/etc/nginx/conf.d/php-fpm.conf'
      #70行目あたりを変更
      default['php']['fpm_user']      = 'apache'
      default['php']['fpm_group']     = 'apache'
      default['php']['fpm_listen_user'] = 'apache'
      default['php']['fpm_listen_group'] = 'apache'
      ↓
      default['php']['fpm_user']      = 'nginx'
      default['php']['fpm_group']     = 'nginx'
      default['php']['fpm_listen_user'] = 'nginx'
      default['php']['fpm_listen_group'] = 'nginx'
      
    • php-fpmのインストールレシピを作成(php/recipes/php-fpm.rb)
      #
      # Author::  Shuta Kakura
      # Cookbook Name:: php
      # Recipe:: php-fpm
      #
      # Copyright 2009-2016, Chef Software, Inc.
      #
      # Install a FPM pool named "default"
      php_fpm_pool "default" do
         action :install
      end
      # connect nginx at socket
      template "#{node['php']['fpm_nginx_conf']}" do
          source 'php-fpm.conf.erb'
          owner 'root'
          group 'root'
          mode  '0644'
      end
      script 'put phpinfo.php' do
       interpreter "bash"
       user 'root'
       code <<-EOH
       echo '<?PHP echo phpinfo(); ?>' > /usr/share/nginx/html/phpinfo.php
       EOH
      end
      service 'nginx' do
        action :restart
      end
    • php-fpm.confのテンプレートを作成(php/templates/default/php-fpm.conf.erb)
      # PHP-FPM FastCGI server
      # network or unix domain socket configuration
      #
      upstream php-fpm {
               server unix:<%= node['php']['fpm_socket'] %>;
      }

      今回nginxとfpmの間はソケット通信するようにいたします。tcp通信にする場合はセキュリティグループでポートを開けておかないといけないみたいですのでご注意ください。

    • berkshelfを使ってレシピをパッケージング(圧縮)
      # phpのBerksfileにchef_nginxを依存モジュールとして追加
      $ echo 'cookbook "chef_nginx", path: "../chef_nginx"' >> php/Berksfile
      # パッケージング
      $ berks package ~/php.tar.gz
      Cookbook(s) packaged to /home/ec2-user/php.tar.gz
      # S3にアップロード
      $ s3 cp ~/php.tar.gz s3://<任意のバケット>/
      
  3. スタックのクックブックの参照先をS3にアップしたファイルにする
    • コンソールからWebSampleスタックの[edit]リンクをクリック
    • [Use custom Chef cookbooks]をYesにして[Repository type]に「S3 Archive」を選択
    • Repository URLに先ほどS3にアップしたファイルのURL、アクセスキー、シークレットキーは任意のものを入力
  4. WebServer(Nginx+PHP)レイヤーにてどのタイミングでレシピを実行するかを設定する
    • WebServer(Nginx+PHP)レイヤーの[Recipes]をクリック
    • Setup(インスタンス起動時に発火する)イベントに以下のレシピを追加
      • chef_nginx
      • php
      • php::php-fpm
    • [Save]ボタンをクリック

4. コマンド実行

  1. [Instances]の[Add an instance]からインスタンスを作成
  2. [Start All Instances]ボタンをクリック

 
しばらく待っていると…
webserver1-instances-websample-aws-opsworks
インスタンスのステータスがonlineになりました!
そして先ほど、レシピでドキュメントルートにphpinfo.phpを設置しておいたので
確認してみます。
phpinfo
PHPがインストールできていました!

まとめ

少しChefの学習は必要でしたがOpsWorksを使うこと自体は難しくありませんでした。
何より1度レシピを作ってしまうと使いまわせるのは非常に便利だと思います。
チームが大きくなってきたりプロジェクトが大きいと例えばミドルウェアのチューニングはインフラ?開発?なんて話も出て来ると思います。
私も開発側にいてこれって僕の仕事なのか??なんて思うこともありました。
DevOpsという考えがみんな大事なことだと分かってはいても仕事増やしたくなかったり、一度引き受けてしまうとずっとやり続けないといけなくなるリスクを恐れて消極的になってしまうこともあるんですよね。
しかし、チーム全員が気持ちよく開発できるためにもAWSの管理ツール系をうまく使って行くことが重要だと思います。
ハンズラボでも今後DevOpsに対しての取り組みを率先して行い効率化を図っていきたいと思います。
次回はCloudFormaiton + OpsWorksの記事を書いてみようと思います。

一覧に戻る