Webアプリケーションをデプロイしたくなったのでsupでやってみる
sup
Golangで書かれたシンプルなデプロイ要ツール。
機能がとてもシンプルで、SSHコマンドを手で書くよりは簡単に書ける程度で、 逆に機能が少なすぎて必要なことが書きづらいことも。
大雑把に現時点でサポートしている機能は以下のような感じ。
- リモートでのコマンド実行(並列可能)
- ローカルでのコマンド実行
- ローカルからリモートへのアップロード
- デプロイ対象の切り替え
Capistranoとかをイメージしていると機能の少なさにびっくりするが、 シンプルゆえに書きやすいところもあるし、 例に挙がっているdockerで使うならいい感じかもしれない。
で、そんなデプロイツールを無理やり感もありつつ、 Webアプリケーションのデプロイに使うとしたらどうするかを考えてみた。
あまりよい例ではなさそうなので、本運用には使えないかもしれない。
というか、ここまでやるならcapistranoを使えという話でもあるが、 諸事情でRubyを入れられないので...
gist91393876ec369f1f1701948b4a648771
時間を取るためにファイルを分けるのは正直微妙だし、変数的な何かは使えなかったのかという気はするけど、 そもそもドキュメントが微妙なので、コード読んで雰囲気でやったしまあこんなもんな気はする(本当か?)
あと、自前でシンボリックリンク張り返したり古いreleaseを消すのは面倒。もうちょっと何とかなってほしい気もする。
うん、微妙だな。なしなし。
nginxでupstreamをhttpsにしたいとき
ちょっとだけ知って置く必要がある設定があるので残しておく。あまり難しくはない。
証明書を更新しておく
CentOSならyum、Debianならapt、その他はいい感じに。
yum install ca-certificate
nginx をビルドする
がんばってください。
SSLを使うことになるので、http_ssl_moduleは入れておく必要がある。
opensslをリンクする場合、新しいものを使うのがおすすめ。
nginx.confを設定する
クライアント証明書が必要な場合は、ちゃんとした証明書と検証用にtrustedな証明書を設定する。
nginx.conf
server { resolver 8.8.8.8; listen 8080; server_name _; proxy_ssl_protocols TLSv1.2; proxy_ssl_ciphers HIGH:!aNULL:!MD5; proxy_ssl_server_name on; proxy_ssl_name REPLACE_REMOTE_ADDR; proxy_ssl_password_file /path/to/client_cert_password; proxy_ssl_certificate /path/to/client_cert_pem_with_password; proxy_ssl_certificate_key /path/to/client_cert_pem_with_password; proxy_ssl_trusted_certificate /path/to/ca-bundle.crt; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Real-IP $remote_addr; location / { proxy_pass https://REPLACE_REMOTE_ADDR; } }
各設定値について
resolver
外に行く場合の名前解決に使うDNS。IP指定の場合はいらないかもしれない。適切なDNSを指定する。
listen
nginxがlistenするポート
server_name
_
なので何でもマッチする。default_serverにするといい気もする。
proxy_ssl_protocols
proxyで接続する際に使うプロトコル。TLS1.2をサポートしているサーバの場合はこの設定でいい。
proxy_ssl_ciphers
proxyで接続する際に使うcipher。リモート側でサポートしているもので強いものを使う。
proxy_ssl_server_name
SNI対応用。proxy_ssl_nameとセットで使う。
proxy_ssl_name
REPLACE_REMOTE_ADDR
を実際に接続する先の名前に置き換える。
リモートサーバにつなぎに行くときの名前を置く。SNIを使っているサーバがぼちぼちあるので指定するのが無難。
proxy_ssl_password_file
プロキシに使うSSL証明書のパスワードファイル。
クライアント証明書がパスワード付きならここにパスワードファイルを設定する。
proxy_ssl_certificate
クライアント証明書を指定する。クライアント証明書が不要ならいらない気もする。
proxy_ssl_certificate_key
クライアント証明書を指定する。クライアント証明書が不要ならいらない気もする。
proxy_ssl_trusted_certificate
証明書の検証に使う信頼された証明書を指定する。
CentOSだと /etc/pki/tls/certs/
とかに置いてあるらしい。
ディストリビューションによって違うのでいい感じに設定するか、curlのをもってきてもいい。
proxy_set_header
いい感じに設定する
終わり
この辺触る人あんまりいなさそうなので地味なハマり方をして消耗する人はいそう。頑張って欲しい。
カスタムして作った CentOS7.4 をAWSの M5, C5 で動かすときに落ちた沼について書いておく
今回もそこそこの不幸があった。普通にセットアップする分には簡単な案件だけど、ちょっと面倒なオプションがついて、カスタムしてダレカの作ったAMIをいい感じに新しいタイプのインスタンスで動かしたいというのがきた。やれやれ。
前知識
新しい世代のEC2インスタンスタイプ M5/C5 が東京リージョンで利用可能になりました
NITRO世代(C5、M5)へのEC2インスタンスタイプ変更を試してみた(Amazon Linux編)
- https://dev.classmethod.jp/cloud/aws/change-type-to-m5-nitro-generation/
- 元々 M5 タイプに対応していないAMIをアップグレードで対応する場合のことがちょっとだけ書いてある。
- 記事を読むと、ENAを有効にし、NVMeドライバが使えるようにすれば起動するように見える。
- AmazonLinuxの例だが、CentOSでも同じようにできるはず。
EC2で稼働中のCentOS 7を新しいインスタンス M5/C5 で起動させる準備をする
- http://info.m-up.com/news/?category=staffdiary&id=2000001333
- もうちょっと泥臭い感じの内容
- AWSのドキュメント通りにコマンドを流してだいたいうまく起動できた、と書いてある
- 後半kernelを4系にあげてあるが、単に動かしたいだけなら3系でも問題はない
- 新しいkernelを使うメリットは例えばnvmeドライバの改善、まあ気にしなくてもいいかもしれない
VPC 内の Linux インスタンスにおける Elastic Network Adapter (ENA) を使用した拡張ネットワーキングの有効化
- https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/enhanced-networking-ena.html#enhanced-networking-ena-linux
AWSの公式ドキュメント
- 拡張ネットワーク (ENA) が有効になっているか調べる手順
- 同じように modinfo でnvmeドライバも調べておく。CentOS7.4以降のkernelは標準でnvmeドライバをサポートしている
amzn-drivers
から dkms を使ってenaモジュールをビルドする設定を入れておく- この手順で少し足りない箇所があり沼に落ちた。具体的には
dracut
での追加ドライバの指定。詳細は後述
- この手順で少し足りない箇所があり沼に落ちた。具体的には
- 予測可能なネットワークインターフェイス名を無効にする (Disable predictable network interface names)
/etc/udev/rules.d/70-persistent-net.rules
を消す
起動しないM5インスタンス
ena supportのオプションはインスタンスで有効にした状態でスナップショットを取るとAMIにも引き継がれ、AMIから起動したEC2インスタンスもENAが有効になります。
OSの起動前に問題が発生した場合、システムログの出力がないため調査が難しいのですが、インスタンスを停止するとシステムログの出力が得られることがあるので、起動してしばらくしてもSSHができない場合は、インスタンスを停止してシステムログを確認する。
今回の場合、 dracut-initqueue timeout
で停止しており、 switch_root でEBSに切り替えようとしているところでパーティションが見つけられずエラーになっていた。
ログを見た感じ、疑われたポイントは、fstab、grub、initramfs、nvmeドライバ。
EC2 HVMの仕組み
仮想化方式(HVM と PV)についてまとめ
HVMインスタンスの仕組みは、物理マシンに近く、ルートデバイスにあるブートローダーからkernelを起動する。裏側はXenだけど。
なのでシステムログをよく見ると
- ブートローダ (grub) から grub2 を起動
- /boot/grub2/grub.conf を読んで、
kernel vmlinuz
、initrd initramfs.img
みたいなことが行われて - initramfsから起動した小さなOSがルートデバイスの
/
へ switch_root - / からOSを起動
という流れになっているのがわかる。
dracut-initqueue timeout
は initramfs のところで起きているのでその辺りを調べていく。
dracut
RHEL系のディストリビューションで使われているinitramfs生成ツール。
結論を書くと、dracutの設定にnvmeドライバの追記をしていなかったため、/
を見つけられず起動に失敗していただけだった。
dracutにドライバを含めるには、 /etc/dracut.conf.d
以下にファイルを置く。例えば /etc/dracut.conf.d/nvme.conf
として以下のファイルを作成する。
add_drivers+=" nvme "
その後、 dracut
を実行する。
dracut -v -f
これで initramfs が再生成され、中に nvme ドライバが含まれるようになる。確認したい場合は、cpioとかを使って生成されたinitramfsを展開すれば確認できる。
終わりに
疲れた。勘弁してくれ。
まあ、この辺は以前にinitramfsとかブートローダとかnvmeドライバとかその辺を調べてあったから比較的早く解決できた気はする。
どっちかというとPXEBOOT周りを触ってる際に覚えたことだけど、事前知識がなかったらこの問題は相当難しい問題だったと思う。難of難。
AWS触るときにもこの辺りの知識が必要になるってのは意外だけどそんなもんかな。いや、普通にやってたらハマらないしこの辺触らないんだよな...
zabbixについてまとめておく
Zabbixは総合監視ツールでmuninのようなメトリクスグラフを出したり、nagiosのような死活監視を行う機能をまとめて持っている便利ツール。
prometheus + grafana のように高速ではないが、長く使われているためそれなりの情報がある。
反面、古いといえば古臭さはあり、WebGUIでポチポチやる必要があるところが今ひとつだったり、agentで送れるデータが1つだったりする。
また、好みはあるが、個人的にはグラフはイマイチでメトリクスグラフはmuninのほうが簡単で綺麗という感想です。
一応WebAPIがあるので、自動的に設定を作ることはできなくはなさそうだけど、何回も設定するようなものではないし、 自前で作るのは正直面倒ではあります。
あと、zabbix-agentは監視項目が多くなるとホストマシンのCPUを消費する傾向があるので、多すぎる監視項目に気をつける必要がある。 この辺りは最適化テクニックもあるらしいけど、必要な監視のみに削る方が楽。
Zabbixは非常に高機能なので色々なことができるみたいですが、とりあえず使い始めるに当たって必要なところだけまとめる
zabbix-web
ZabbixサーバのWebインターフェイス。 Zabbixは設定をMySQLなどのDBに持つが、Webインターフェイスから設定を変える場合に使う。
管理 > ユーザ
Zabbixはユーザごとにプロファイルを持ち、Webのビューや障害通知のメールなどを設定できる。
デフォルトで Admin
ユーザが設定されているが、複数人で扱う場合にはそれぞれ適切にユーザ登録を行うのがよい。
ユーザには種類があり、「Zabbixユーザー」と「Zabbix特権管理者」がある。 一般ユーザは「Zabbixユーザー」になるが、「設定」や「管理」が行えず、ホスト登録や障害通知のトリガー設定など多くの 運用に必要な設定ができないため、運用者は「Zabbix特権管理者」の権限を使うのがよい。
単に閲覧だけできればよいのであれば「Zabbixユーザー」でもよい。 グループごとに閲覧できる範囲を限定したり、大規模な会社向けの機能っぽいものもあるが割愛する。
設定 > アクション
アクションはZabbixが何かしらの状態変化を検出した場合に何かの動作を自動的に行うというもので、その名前の通りの機能。
何かしらのイベントには、「トリガー」、「ディスカバリ」、「自動登録」、「内部イベント」がある。
トリガーというのは、ホストから取得している数値に変化があった場合に通知として使えるもので、 いわゆるホスト監視目的で使える。障害復旧通知とか。
ディスカバリと自動登録はZabbixがホストを見つけた場合に使うのもので、例えば名前を自動で埋めたり監視項目を自動設定したりできる。 ディスカバリと自動登録は、受け攻めの違い程度なのでどちらも似たような感じで設定する。
内部イベントはその他色々なイベントを実行の条件にできるようだが、特に使っていないのでわからない。
とりあえずの設定で言えばトリガーを設定できれば死活監視の仕組みを作れる。
デフォルトの通知は「Report problems to Zabbix administrators」で「Zabbix administrators via すべてのメディア」となっている。
「Zabbix administrators」というのはデフォルトのユーザグループで、 通知を受け取りたいユーザは「Zabbix administrators」に所属させておけばよいと思う。
「すべてのメディア」とあるが、メディアというのはユーザごとに設定できる通知の方法で、「メール」「SMS」「スクリプト」など任意に設定できる。
Slackのようなチャットツールに通知したい場合は、自前でスクリプトを設定すると簡単にできる。 設定方法はユーザプロフィールの右上のアイコン。
設定 > テンプレート
テンプレートというのは、複数のホストに対してまとめて設定をいれるために使う設定のセットのようなもの。
Zabbixのマニュアルだと、「アイテム」、「トリガー」、「グラフ」、「アプリケーション」、「スクリーン」、「ローレベルディスカバリ」などが 例になっている。
とりあえずの設定で言えば、まずは「アイテム」と「トリガー」を設定できれば十分。
「アイテム」とはホストから取得するデータのことで、具体的には「CPU使用率」や「メモリ使用量」、「起動プロセス数」など。 監視項目と考えてもいいかもしれない。取得するデータは組み込みのもののほか任意のスクリプトから取得することもできる。
「トリガー」はテンプレートの単位で「アイテム」で取得した数値が一定の値を取った時にアクションを行う設定。 具体的には、「アイテム:CPU使用率が50%を超えた場合」といった条件をトリガー起動の条件として設定する。 トリガーの設定にはアイテムの設定が必須となる。
アイテムやトリガーはテンプレートではなくホストごとにも設定できるが設定管理の面からテンプレートを使うのがベター。
設定 > ホストグループ
テンプレートには必ずホストグループを設定する必要があるが、設定してもテンプレートの内容がホストグループに適用されるわけではない。
先の説明通りになるが、テンプレートはホストごとに設定するもので、この設定は対象ホストの選択時に絞り込めるだけにすぎない。
実際にホストにテンプレートを適用するにはリンクをする必要があり、これはホスト側からもテンプレート側からも行える。
ホストグループはホストをまとめる論理単位だが、テンプレートには直接影響しないため注意。イマイチ慣れない機能。
とりあえず
死活監視はこれだけで十分作れる感じはある。長く使われているだけあって、情報は多いし悪くはない。
グラフは好みあるけどZabbix単体だとホストグラフを横断的にみることができないからキツいかなあ。
まあとりあえずいい感じのツールなのは間違いないです。
長い付き合いだったNagiosさんもIcingaになりそうで古いプラグインが使えなくなるとかなんとかだそうで この手の監視ツールも大変だなーという感想です。
AWS CodeDeployについて書いておく
CodeDeployはAWSが提供しているアプリケーションコードデプロイの仕組みで、各サーバに特権で動くagentをあらかじめインストールしておき、CodeDeployにデプロイ指示を出すと、agentがコードをダウンロードし、サーバ上で任意のコードを実行することで、アプリケーションのデプロイをするというもの。
これだと、単なるプル型デプロイの仕組みのように見えるが、実際には少しだけ高機能なところもあって、例えばAutoScalingGroupに対してまとめてデプロイしたり、EC2インスタンスに結びつけたタグを元にデプロイ対象を選択することもできる。
一番便利なところはおそらくロードバランサーとの強調動作で、ELBのようなロードバランサーから一時的に隔離しながらデプロイをすると綺麗にデプロイができる。
さらにはインスタンスごと切り替えるいわゆるBlue/Greenデプロイの仕組みも用意されているので、Javaのアプリケーションのように再起動を避けられないプログラムでも比較的安全な自動デプロイの仕組みを比較的簡単に用意できる。 とはいえ、この辺りはAWSの機能にベッタリなため仮にAWSから引き上げる時には足枷になるような気はする。 同じような仕組みを用意するのはやや骨が折れる。 まあ、AWSの場合はCodeDeployだけの問題ではなく、ELBやアクセス権限周りの話もあるのでどのみち引き上げる時は面倒か。 あと、EC2が提供しているイメージ以外は自前でagentをインストールする必要があるが、agentはruby製なのでシステムにRubyを入れる必要はある。
まあ、この点はRubyの場合アプリケーションコードを動かす目的ではよくrbenvが使われるため、システムワイドにインストールしたRubyの影響を受けることはほぼないと考えていいと思う。 特権で動く点も気になるという人もいるとは思うが、アプリケーションコードのデプロイ作業ではなんだかんだで特権が必要になるケースはある。
実際に動かすアプリケーションコードは権限を落として実行すればいい。 この辺りはsystemdになって書きやすくなったと思う。悪くない。
CodeDeploy Agentのインストール
CodeDeployの場合、特に公式ドキュメントの日本語がややおかしい気もするけど自動翻訳とか使ってる可能性もあるなーという感想です。 気が向いたらgithubに公開されているドキュメントにコントリビュートするといいかもしれない。(これは一般的な話なんですが、AWSでもよく使われる場所のドキュメントは整備されているので、裏返せばCodeDeployはあまり使われていないのかもしれないですね) * Amazon Linux または RHEL の AWS CodeDeploy エージェントをインストールまたは再インストールします。 * https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html
CodeDeployにサービスロールをつける
CodePipelineとかはこの辺り自動的に設定してくれたような気もしていたけど、CodeDeployだと自分で用意する必要があるっぽい。面倒だがそういうものなら仕方ない。
codedeploy.amazonaws.com
を信頼したエンティティにして、CodeDeployに適切に権限を与える。
EC2インスタンスにS3のアクセス権をIAM Roleで与える
CodeDeployのagentでS3からコードを取ってくるようにしている場合、取ってくるためのS3バケットへのアクセス権が必要になる。
これの設定自体は大したものではないので、単にEC2にアクセス権を与えておけばいい。
s3バケットのバージョニング
コードのバージョン管理にS3のバージョニング機能を使うことができる。これはコードが動かない場合にrollbackをしたい場合に便利。
CodeDeployにpush
アプリケーションコードをCodeDeployにpushすると、デプロイタスクが作られagentがコードを取りにいく。
gitリポジトリだとデプロイ対象には含めたくない例えば .git
などがいるので、デプロイ前のタスクで対象のファイルのみ別ディレクトリに書き出しておき、アーカイブするのが綺麗な気はする。
で、実際にはこのタイミングではWebだとassetのビルドとかがあるし、いわゆるビルドタスクなのかもしれない。まあ普通か。 デプロイについて気をつけないといけないのは、サーバ台数が最低でも2台必要になる点。
これは最低でも1台ごとにアプリケーションを切り替える都合、1台では一時的にダウンしてしまうため、CodeDeploy側で制限されているため。
検証環境みたいなところだと、この制限がやや邪魔だなあと思うこともあるが、検証環境へのデプロイは別の方法でやればいい。共通ができないじゃないか。やれやれ。 最低台数の話ついでに、デプロイをどの程度まとまった台数行うかどうかは設定で決めることができて、1台ずつ、半分、全部と選ぶことができる。
1台ずつは徐々に切り替わっていくので、LBから外れる台数も少なくデプロイ時の負荷に余裕はあるが時間がかかる。
半分は速度優先。LBから外れる台数も半分なので、負荷が高い時には厳しい選択かもしれない。
全部はなんだ、ワイルドだな!
終わり
まあ、使えるといえば使えるんだけど、Webコンソールは開発者にはあんまり親切じゃないかも。雰囲気だけど。どうすればいいかというのも難しいので仕方ないか。 とりあえず使えはするし、AWSの仕組みとは親和性はそこそこよいという感想だけど、AWS以外で使えないんだよなあ...いやそれは違うか。使えはするけど利点があんまりないというか。
プル型のデプロイツールについてはあんまり調べてないけど、自前で作るのはそこそこ手間だし、何かないかなという気持ちはありますね。
クラウドだと、インスタンスをポコポコ立てたくなるし、そういう時にデプロイ対象を増やすよりは勝手に拾ってくれる方が向いてる。 とはいえ、作るのは多少手間ではある。やれやれ。
AWS CodePipelineからCodeBuildを呼んでlambdaで古いAMIを消すといい感じになる
前回の続き
AWSでAMIの作成をCodeBuildを使って自動化したけど、CodeBuildが動く部分も自動化したいので、Githubに置いてあるリポジトリのmasterにマージされたタイミングで自動的にAMIが作られるといい感じなんじゃないかと思って、「これくらい簡単にできるっしょ」ってやろうとしたらCodeBuildのトリガーなんてものはないと気づいてAWSめんどいってなったので、めんどい部分を書いておきます。
これ作ってから言うのもアレなんですが、CodePipelineはAWSコンソールから操作する分にはある程度自動化されていて便利な反面、 自前でAMI周りの権限をうまくつけるのは結構面倒なので、terraformとかでCodePipeline/CodeBuild/Lambda/CloudWatchLogs/IAM/S3まわりを一気に設定するのは骨が折れるなあという感想です。
まあ、やろうと思えばできます。ただただ面倒です。なんとかならんのか。
で、本題なんですが、CodePipelineっていうのはCIツール的なやつで、CodeBuildがビルド周りしか担当しないのに対して、CodePipelineはソースの取得、ビルドタスクの実行、ビルド後のタスクなどなど、パイプラインの名の通り色々なタスクをルールに従って処理してくれるやつです。
ビルドの並列化などもできるので、CodeBuildだけで足りないところをいい感じに補ってくれるやつと捉えておくといいかもしれません。
CodePipelineのソースにgithubのリポジトリを指定して、masterブランチを監視しておけば、Pull Request単位でレビューをしてmasterにマージされたタイミングで自動的にCodeBuildを走らせるなんてことが簡単にできます。
便利ですね。
流れとしては
- githubのブランチを監視、変更があればソースで取り込む
- CodeBuildでAMIを生成
- Lambdaで古いAMIを削除
という感じになります。
CodePipeline
CodePipeline自体はUIをみたらなんとなく使い方はわかるくらい簡単なので、特に説明はしないのですが、いくつか気になったところだけ補足します。
ソースはアーティファクトの出力が必要
ソースフェーズで取り込んだgithubのリポジトリをCodeBuildに渡す際にアーティファクトという形でs3を経由する必要があります。
まあ、ソースなんでそのソースはs3に置くでしょという感じな気もするのですが、一時的にs3に置かれるのはちょっと面倒だなあという感想もあります。
なんか、噂によるとパーミッション周りが消えるとかいう話もあるし、ソースじゃなくて単なるトリガーみたいなのをつくれないのかなあという気持ちはあります。
ここはあんまり気にせずいきましょう。必要な権限があればCodeBuildのprebuildとかでつければいいかなという感想です。
タイムアウト時間には余裕を持っておく
CodeBuildもLambdaもですが、意外にも時間がかかることがあるのでタイムアウト時間が短すぎるとエラー扱いになります。
CodeBuildは初回実行時におおよその時間を割り出しておくと良いでしょう。LambdaはAMI削除程度なら15秒もあれば十分かなという程度です。(実際には2秒程度で終わる)
LambdaからCodePipelineのタスクが成功したかどうかは自分で報告する必要がある
地味にハマった問題で、タスク呼び出しで呼んだLambdaが成功したかどうかはLambdaでCodePipelineに通知する必要があります。
そうしないと、たとえLambdaが正常終了したとしてもタイムアウトまで待った上で失敗扱いになるので、見栄え上よくないです。
これはテンプレート的に成功の場合は通知処理を書いておけばまあ問題にはならないでしょう。
Lambdaで古いAMIを削除する
まあ、消しましょう。Jenkinsのように古いビルドとアーティファクトを消すものはないので自前で作る必要があります。面倒ですね。
Lambdaなので、Pythonでサクッと実行します。itemgetter
あたりが知らなかったので勉強になりましたね。
images_limit
とかは適当にいじってやるといいと思います。ec2のAPIを呼ぶのでこのLambdaにはEC2の操作権限をつけないといけません。
あと、上で書いたCodepipelineへの通知は code_pipeline.put_job_success_result(jobId=event['CodePipeline.job']['id'])
のところになります。
もうなんかとりあえず最後にこれを置いておくかという感じですね。
終わりに
とりあえず、AMI作成はこれで自動化できるようになりました。
あとは本投入周りのことは考える必要がありますが、まあその辺がインフラエンジニアの腕の見せどころでしょう。
正直この辺はハンコなのでインフラエンジニアは隠居していたいところなのですが、まあサービスしておきましょう。
はー、こんなことより早くアプリケーション側から呼ばれているMySQLのめっちゃ遅いクエリ消さないとなー!!!
Terraformのcodebuild_projectでParameter Storeがサポートされてない件、普通にissueとPull Requestが出ていたけど、テスト周りでゴニョゴニョしてる感じっぽい。しかもここ最近触られててタイムリー
— シリル@ガルパ沼🍊⚓️😈 (@d_cyrill1129) 2018年3月23日
あ、書くの忘れてたんですが、terraformだと今のところCodeBuildの環境変数設定でParameter Storeを指定することができないので、CodeBuildのタスク内で秘密情報はSSMから取ってくる必要があります。
まあそのうち改善しそうですが、具体的にはansible-vaultのパスワードみたいなのは自分で取ってくる必要があるという感じです。頑張って下さい。
なんか雑にginとGORMでWebAPIを書き始めたけど意外といい感じに動きそうな感じになってきた
— シリル@ガルパ沼🍊⚓️😈 (@d_cyrill1129) 2018年3月22日
最近暇すぎてGolangを勉強がてら書いてます。
本当はGoroutineを使った並列処理とかそういうのを書きたいんですが、ベンチマーカーくらいしかそんな用途なさそう。
だいたい速度面で困ってないし。
MySQLくん、10万程度のrows_examineでも重いことに気づかれないので優秀なのかもしれない(秒で治せるんだけどな
— シリル@ガルパ沼🍊⚓️😈 (@d_cyrill1129) 2018年3月22日
次回はこの辺書くかなという感じです。世界を笑顔に。
AWS CodeBuild と packer と ansible でプロビジョニング済みのAMIを自動生成する
AMI生成部分を自動化できたので、ちょっとだけまとめておく。
CodeBuild
- AWSのDockerで動くCIツール。
- CodeBuildの名前の通り、ソースコードをビルドする環境を簡単に作れることをウリにしている
- CircleCIなどのサービスでビルドしたり、Jenkinsを自前で用意するのと比較してそれほど違いはない
- 料金はビルド時間ごとの課金
- 一応無料枠もある
Packer
Ansible
参考ソース
AWSのブログにCodeBuildとPackerを使ったAMIビルダーの構築方法がある。
他にはクラスメソッドさんのブログにAnsibleとPackerの連携周りがある。
- AWS CodeBuild と HashiCorp Packer を用いた AMI ビルダーの構築方法
- Packer+AnsibleによるAMIの作成
- Packer 0.9の新機能 リモートからのAnsible Provisionerが追加されました
この辺りを読めばなんとなく作り方のイメージはできるようになるが、いくつかハマった点があるので補足としてまとめておく。
このリンクだけで十分な人はここから先は読まなくてもOKです。
大まかな流れ
- Ansibleのplaybookはgithubで管理しておく
- CodeBuildでビルド用のDockerを起動、事前のセットアップを行う
- ansible-vaultで使うパスワードをKMSから取ってくる
- 平文を嫌う場合、KMSに登録しておき、環境変数にセットする方法が使える
- packer build でAMIを生成する
AWSのブログと違う点
- Packer 1.2.1
- ansible provisionerを使うため、Docker環境へAnsibleのインストールする
- packerがIAM Roleを扱えるようになったため、クレデンシャルの生成は不要になった
EC2 IAMロールがなんか使えないの謎だから後で調べておくか..
— シリル@ガルパ沼🍊⚓️😈 (@d_cyrill1129) 2018年3月15日
- 環境変数 USER を設定する
クラスメソッドさんのPacker+AnsibleによるAMIの作成と違う点
- アクセスキーはIAM Roleを使うので変数を使わない
- ansible-localではなくansibleを使う
- ansibleのインストールは
buildspec.yml
で行う
クラスメソッドさんのPacker 0.9の新機能 リモートからのAnsible Provisionerが追加されましたと違う点
- vaultファイルの管理をする
本題
CodeBuildとPackerを使うため、それぞれのための設定ファイルを用意する。
CodeBuildが buildspec.yml
で Packer は任意の名前のJSON。
buildspec.yml
--- version: 0.2 phases: pre_build: commands: - echo 'Install Packer 1.2.1' - curl -qL -o packer.zip https://releases.hashicorp.com/packer/1.2.1/packer_1.2.1_linux_amd64.zip && unzip packer.zip - echo 'Install Ansible' - apt-get update -y - apt-get install -y software-properties-common - apt-add-repository ppa:ansible/ansible - apt-get update -y - apt-get install -y ansible - echo 'Validate packer json' - ./packer validate packer_ec2.json build: commands: - aws configure set region $AWS_REGION - echo "$ansible_vault_pass" > vault_password_file - echo 'Build AMI' - ./packer build packer_ec2.json post_build: commands: - echo "Build finished `date`"
packer_ec2.json
{ "variables":{ "vault_path": "vault_password_file" }, "builders": [{ "type": "amazon-ebs", "region": "ap-northeast-1", "source_ami": "ami-XXXXXX", "instance_type": "t2.micro", "ssh_username": "root", "ssh_timeout": "5m", "ami_name": "packer_{{ timestamp }}" }], "provisioners": [{ "type" : "ansible", "extra_arguments": "--vault-password-file={{user `vault_path`}}", "playbook_file" : "playbook.yml", "groups" : ["aws", "webserver"] }] }
groups
を配列で渡すと inventory file で複数のグループに属した場合と同じ扱いになる。
これは group_vars
などで設定を切り替える必要がある場合に便利。
vaultファイルは buildspec.yml
で出力したパスを extra_arguments
を使ってそのまま渡している。
IAM Role
XXXXXXXX にはアカウントIDを入れる
以下のポリシーを作成し、CodeBuildで使うIAM Roleにアタッチする。(インラインでも構わない)
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Action": [ "ssm:GetParameters" ], "Resource": [ "arn:aws:ssm:ap-northeast-1:XXXXXXXX:parameter/ansible-vault-pass" ] }, { "Sid": "", "Effect": "Allow", "Action": [ "kms:Decrypt" ], "Resource": [ "arn:aws:kms:ap-northeast-1:XXXXXXXX:key/alias/aws/ss" ] } ] }
KMSからデータを取ってくるために必要。名前は適宜設定すること。
終わりに
いくつかハマりポイントはあったが、これでAMIをCodeBuildで自動的に作れるようになった。
あとはgithubのMergeにHookするとかすれば自動でAMIがどんどん作られていくので便利。
ただ、古いのを消さないと溜まり続けて課金がすごいことになりそう。大変ですね。
packer ansible provisioning 謎のソリューションによっていい感じにAMIが出来上がるんだけど、inventoryファイルとかが自動生成されてるっぽい。リモート側へはSSHのトンネリングかなこれは、イケてる振る舞いしてるけど裏側は結構頑張ってる
— シリル@ガルパ沼🍊⚓️😈 (@d_cyrill1129) 2018年3月15日
ついでにこれをCodeBuildに乗せたら失職チャンスでは…(人がいなくても勝手にAMIが生産されていく
— シリル@ガルパ沼🍊⚓️😈 (@d_cyrill1129) 2018年3月15日
AWS CodeBuildでPackerを使ってAnsibleのプロビジョニング走らせるところまではできたけど、これAutoScalingと組み合わせるの難しくない?みんなどうやってやってるんだ…????
— シリル@ガルパ沼🍊⚓️😈 (@d_cyrill1129) 2018年3月16日
現場からは以上です