「サービス単位 × 開発環境/本番環境毎に、専用のEC2 AMIを作りたくない!」
はい。今回はいきなりそんな願望から記事がスタートします。
こんにちは、クイックSREチームのみっちーです!
昨今ではAWSやGCPをはじめとしたクラウド化の波に乗って、インフラでもコード化が進んでいますね。
お陰様で突発的なアクセス増に対しても手動でサーバーを増設したり、オートスケールでカバーしたりが、以前と比べ比較的容易になりました。
しかしいくらクラウド化、コード化が進んできているとはいっても、業務効率化が必要なことはいつの世も変わりありません。
そこで今日は、弊社での「インフラコードの環境依存度を減らす」ことで業務を効率化した取り組みについてお話しさせてください。
目次
1.「サービス単位 × 開発環境/本番環境毎に専用AMIを作りたくない!」そんな希望を叶えるためには?
1.「サービス単位 × 開発環境/本番環境毎に専用AMIを作りたくない!」そんな希望を叶えるためには?
ボトルネックの洗い出し
◆ AMIからEC2を起動するには、そこそこ時間がかかる
AWS EC2を利用している環境でサーバーを増やそうとすると、大体はAMIを使って起動することとなると思います。
構成管理ツール等を利用すれば、比較的簡単に「その環境に適した設定をしたEC2」を短時間で作ることができます。
ただいくら短時間で作れると言っても、アクセススパイク時など一刻を争うときに、パッケージをインストールしてパラメータの初期設定をして…というのは現実的に厳しいですよね。
◆ 必要なパッケージは開発環境/本番環境で共通の場合が多い
弊社ではその大半が「必要なパッケージは開発環境/本番環境で共通」です。
みなさんの環境ではいかがでしょうか?そういった運用は実際多いのではないかと推測しています。
事前に必要なパッケージをインストールしたAMIを作成しておくことで、起動時間対策としても有効だと思います。
ですが、ここで各種パラメータも固定値としてAMIに含めてしまうと、「サービス単位 × 開発環境/本番環境毎に専用AMIを作る」運用になってしまうんじゃないかと思います。
◆ 細かいパラメータや接続ノード情報は開発環境/本番環境で違う
開発環境の場合はコスト面からも、インスタンススペックを落としていたり、接続先のDBやCacheサーバーを本番環境とは分離している、といった運用は多いのではないでしょうか。(もちろん弊社でもそうです)
構成管理ツール等を利用し、必要な各種パラメータ設定まで含めたAMIを事前に作っておくことは比較的容易で、かつ起動時間も短くすることができるのはメリットと言えますが、
逆に管理コストの高さがデメリットとなります。
例えば本番環境用のAMIを作るときに、間違って開発用のドメインを指定してしまったり。
本番用のAMIを指定したつもりが、実は開発環境用のAMIだったり…細かく記載すればきりがありませんが、いずれも大変なリスクをはらんでいます。
以上から、最大のボトルネックは「環境毎に動的に変化する値の扱い方」にあるように思います。
2.弊社的解決手法の提案
さて、このお悩みに対する弊社での回答としては…
「開発環境/本番環境でも、動的に変化する値を気にすることなく使えるようなAMIがあれば解決する!」です。
具体的には、環境依存が起きる箇所。
例えば、apacheのvirtualhostのServerName、Mysqlの接続情報(nodeやpassword)、php.iniのパラメータ等を「環境変数化」し、環境変数用のファイルを作成して管理。起動時に更新して読み込ませることで、脱環境依存を実現しようというものです。
「AWS Secrets Manager」と「/etc/environmentファイル」を使う
弊社では、「AWS Secrets Manager」と「/etc/environmentファイル」の2つを使うことで上記を実現しています。
aws.amazon.com
www.ibm.com
3.やってみよう
それでは具体的にどんな感じになるのかサンプルコードを書いてみましょう。
3-1.AMIの作成
前提として、弊社ではEC2をWebサーバーとして運用する際、以下で構成を組んでいます。
作成時のポイントは、「環境依存設定」を「環境変数化」することです。
以下にいくつか例を挙げてみます。
◆apacheのvirtualhost
<VirtualHost *:80> ServerAdmin root@admin.dayo ServerName ${HTTPD_SERVER_NAME} ServerAlias ${HTTPD_SERVER_ALIAS} DocumentRoot /var/www/html ErrorLog /var/log/httpd/error_log CustomLog /var/log/httpd/access_log combined DirectoryIndex index.php <Directory /var/www/html> Options FollowSymLinks Options -Indexes AllowOverride All Require all granted </Directory> </VirtualHost>
ServerName ${HTTPD_SERVER_NAME}
、ServerAlias ${HTTPD_SERVER_ALIAS}
とすることで、環境変数から読み込めるようにしています。
◆ php.ini のパラメータ設定
※長いので、変数化した周辺設定のみ抜粋します。
session.save_handler = memcached session.save_path = "${PHP_SESSION_SAVE_SERVER}" memory_limit = "${PHP_MEM_LIMIT}"
apacheの時と同様、開発環境/本番環境で異なるキャッシュサーバーや、メモリリミット値を環境変数から読み込めるようにしています。
◆ /etc/environment の設定
では、環境変数にはどのように書けばいいでしょうか。
HTTPD_SERVER_NAME=dev.sample.servername.dayo HTTPD_SERVER_ALIAS=*.sample.servername.dayo test.servername.dayo *.test.servername.dayo PHP_SESSION_SAVE_SERVER=tcp://dev.sample.dayo:11211 PHP_MEM_LIMIT=256M
これまでべた書きしていた値をそのまま置き換えるだけです。とても簡単ですね。
同様に、DBのnode情報や、passwordなどを格納しても良いと思います。
◆ 各デーモンが/etc/environment を読み込む設定
ただ、これだけだと各デーモンが環境変数を読めないので、
/etc/systemd/system/デーモン名.service に以下の様に環境変数ファイルを読み込むように指示します。
・apacheの例
下記の内容で/etc/systemd/system/httpd.service
を作成。
.include /lib/systemd/system/httpd.service [Unit] After=network.target remote-fs.target nss-lookup.target httpd-init.service cloud-config.service [Service] EnvironmentFile=/etc/environment
・php-fpmの例
下記の内容で/etc/systemd/system/php-fpm.service
を作成。
.include /lib/systemd/system/php-fpm.service [Unit] After=syslog.target network.target cloud-config.service [Service] EnvironmentFile=/etc/environment PrivateTmp=false
※ php-fpmの場合は、加えて以下が必要です。
/etc/php-fpm.d/www.conf
に対して下記の1行を追加
clear_env = no
3-2.AWS Secrets Manager に環境変数値を格納
上記のままでは、べた書きだった設定を/etc/environment へまとめただけですので、AMIを共通化できません。
そこで登場するのが、AWS Secrets Manager です。
本来、パスワードをセキュアに管理することを目的として作られたものですが、弊社ではそれ以外にも「環境依存を排除する」目的でも利用しています。
※ AWS Secrets Manager の使い方については、以下がとても参考になりました! dev.classmethod.jp
3-3.EC2の起動時に、保存したAWS Secrets Managerの値を取得し、/etc/environment へ格納する
ここは、EC2の起動時に以下のようなスクリプトを[User data] フィールドに入力することで実現しています。
◆保存したAWS Secrets Managerの値を取得し、/etc/environment へ格納するサンプルコード
#!/bin/bash aws --region=ap-northeast-1 secretsmanager get-secret-value --secret-id <シークレットファイル名> --query 'SecretString' --output text > /etc/environment
ここまでできたら、いつものようにEC2を起動してください。
これで AWS Secrets Manager に格納された値が /etc/environmentへコピーされ、各デーモンからも呼び出せるようになっているはずです。
(祝!!さよなら環境依存~~~~)
今回は以上です。
最後に
インフラはその職種上、責任が重い上にエンジニア業界でも絶対数が少なく、業務量が比較的多い印象があります。
ただその分、事業を支える土台であるというやりがいのある仕事だとも思いますので、日々の業務を効率化しつつ、新しいチャレンジが色々とできる時間を作っていきたいですね!
最後までお付き合頂きありがとうございました。
それではー!
\\『真のユーザーファーストでマーケットを創造する』仲間を募集中です!! //