アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Kubernetesの導入で考えたこと、一緒に導入したもの

morishitaです。

yamamotoが次のエントリで紹介しましたが、いこーよを Kubernetes 上で運用し始めて3ヶ月になろうとしています。

tech.actindi.net

まあトラブルもありますし、やってみてEC2で運用していたのとは勝手が違うところに苦労しつつですが、移行してよかったと思っています。

移行前の状況

いこーよは弊社のメインサービスであり、最も長くメンテナンスを続けているシステムでもあります。
トラブルが発生するとご迷惑をおかけするユーザが多いサービスでもあります。

そのため、とりあえず落ちずに稼働しているしということで、改善のためにインフラに大きく手を入れることには及び腰になっていました。
この数年はサービスの成長にともない現れるシステムの綻びをちょっとづつ改良しながらいこーよを支えているという状況でした。

いこーよの姉妹サービスであるいこレポでは、Railsアプリケーションを Docker コンテナで稼働させており、 コンテナ稼働環境として ElasticBeanstalk を採用しました。 それ以降のプロダクトはそれを踏襲したシステム構成で運用しています。

一方、いこーよは EC2 上で直接 Rails を稼働させる昔ながらのインフラでした。 Ruby やその他モジュールのアップデートは Docker 環境とは異なり手間が大きいのことが課題として大きくなりつつありました。

また、メールサーバや memcached サーバ、Zookeeperなど扱い方が異なるサーバ・ソフトウェアが相乗りで1つのサーバに直接インストールされていたりすることも運用を煩雑にしていました。

この状況を改善するため、昨年の秋からいこーよインフラ刷新プロジェクトとして、インフラ基盤に kubernetes を導入する準備を進めてきました。 その検討過程で考えていたこと、やったことを少し紹介します。

考えたこと

要件定義

プロジェクトを開始する前に、どのようなインフラを作りたいかについてエンジニアチームで話し合いました。

すでに問題が顕在化しているものから、それってインフラの問題? なものまで次の様な項目が数十個挙がりました(恥を偲んでいくつか披露します)。

  • 繁忙期の負荷を心配しながら見守らなくてよくしたい
  • リリース時にパフォーマンス低下しないようにしたい
  • 長期間参照できるメトリクスを収集したい
  • インフラもコード化したい
  • etc

それらに優先度をつけ、やるものやらないことを決めました。
更にそれらを汎化し、一般的にインフラとして検討すべき次の観点について判断の指針を要件定義として定めました。

  • 可用性
  • 性能
  • 運用容易性
  • セキュリティ
  • 開発効率
  • コスト

あとから出てくる課題もこの要検定義を満たすのに必要なものはやる。
そうでないものは優先度を下げるという判断を行いました。

コンテナを何で動かす?

インフラを刷新するに当たり、どの様な形であれ Docker コンテナ上で稼働させるということは決めていました。
Dokcer コンテナを使えば、Ruby、それに必要なミドルウェアやライブラリなど Rails を動かすために必要なものがすべてがイメージに含められます。 デプロイするだけでそれらのアップデートも適用できてしまいます。
すでに Docker コンテナで稼働している他のサービスの運用でそのメンテナンス性の良さは実感できていましたし、求める運用容易性を満たすためにも必要なものでした。

アクトインディのインフラは AWS にあります。
AWS のコンテナ環境には ECS や、ECS をベースとしているElasticBeanstalk があります。
ElasticBeanstalk はすでにいこーよ以外のサービスで運用している実績があります。
ただ、次の点で満足していませんでした。

  • デプロイに時間がかかる1
  • Railsサーバ以外も稼働させたいけれど扱えるコンテナ定義は1種類2

じゃあ、ECS はどうかとも考えましたが、ElasticBeanstalk で不満に思っていることの一部は ECS に起因しているものもあり決定打に欠けました。
そこで、最近事例が増えつつある Kubernetes なるものを自分もやってみたいと思い Kubernetes ありきで構成を検討し始めました3
Kubernetes を使うにあたり、Kubernetes そのものの運用負荷はできるだけ下げたいと考え、AWS EKS を採用することにしました。

検証を進める中で、求めていた可用性、運用容易性や開発効率も実現できそうだと手応えを強めながらプロジェクトを進めていきました。

GitOps を実現する

いこーよは私が入社した当時から Github のプルリクエストをマージすれば本番環境にデプロイされるリリースプロセスの自動化がされていました。
プルリクエストをマージすると Github のリポジトリをポーリングしている Jenkins が RSpec のテストを実行して、capistrano で、EC2 にデプロイするというものです。

一方、Kubernetes の CI/CD のキーワードとして GitOps というのをよく見かけます。
GitOps とは weaveworks 社が提唱するCI/CDの考え方です。極々簡単に言うと宣言的にインフラを定義できる Kubernetes では Git リポジトリと実稼働するシステム状態を一致させるよう管理しようというものです。そうすることですべての変更が Git の履歴として管理でき、現在のシステム構成がどうなっているのかリポジトリを見ればわかるようになります。

具体的にアプリケーションとインフラの変更を稼働するシステム上にどの様に適用するのかというと、プルリクエスト上の操作を起点に CI/CD を自動化するということになります。

すでに運用としては GitOps っぽいものはすでにできていたので、この運用はそのまま残したいと思いました。
ただ、従来のリリースプロセスでは CI と CD が一体化しており、運用しづらい部分がありました。 そこで weaveworksの提唱通り CI と CD を分離し、GitOps を取り入れることにしました。

いこーよに関わるGitリポジトリを次の2つに分けました。

  • アプリケーションリポジトリ
  • マニフェストリポジトリ

アプリケーションリポジトリの変更を起点に CI を実行し、マニフェストリポジトリの変更を起点に CD を実行しています。 アプリケーションリポジトリの変更とはプルリクエストのマージです。コードレビュー後、各開発メンバーがマージします。 すると、Dockerイメージをビルドし、RSpec のテストを実行します。 ここまでが CI です。

そして、テストされたDockerイメージを使うようにマニフェストリポジトリのマニフェストを更新する。
すると、その通りに本番環境の Kubernetes へのデプロイが実行されるという CD の処理を実現することとしました。

これにより日々のリリースという運用の容易性と開発効率を維持できると考えました。

導入したもの

上記のようなことを考えつつ、次のような方針を軸にCI/CDも含めたいこーよの本番運用のシステム構成を検討しました。

  • Kubernetes クラスターを運用する。
  • 運用には GitOps を取り入れる

その上で前述の要件を満たすために今回のインフラ刷新プロジェクトでアクトインディとして新たに導入した技術要素は kubernetes 以外では次のものがあります。

  • AWS CDK
  • ArgoCD
  • Datadog

AWS CDK

今回のインフラ刷新でもう1つ決めていたことがあります。
それは「できるだけインフラをコード化する」ということです。

AWS コンソールをポチポチいじって設定するのは、変更履歴が管理できないし、担当者任せの部分が多くなり再現性を担保できません。 なのでできる限りコンソールによる変更作業をなくしたいと考えました。

Kubernetes の中はすべて YAML のマニフェストでコード化しますが、Kubernetes だけでは CI/CD も含めた運用に必要なインフラ全ては実現できません。 第一、Kubernetes を稼働させるEKSクラスタは kubernetes では作れません。

もともとAWS謹製ということ、無理な変更は適用されずロールバックされる点で CloudFormation を信頼していました。 でも、あのYAMLは規模が大きくなっていくると管理がちょっと大変だなぁとも思っていました。 それなら、Teraformってどうだろうか?そう考えていたところに、ちょうどいいタイミングで AWS CDK が GA となりました。 試してみると、YAMLとは違って、次の点で実装しやすいと感じました。

  • クラスやメソッドを使って見通しよく分割できる
  • 通常のプログラミングと同じ手法でテストも可能
  • それでいて最終的に CloudFormation のスタックとしてデプロイできる

いくつか習作も作ってみた上で CDKを採用しました4

CDKでは次を構築しました。

  • EKS クラスタ
  • EKS クラスタで利用する IAM Role
  • CI で使う CodePipeline や CodeBuild
  • ECR レジストリなど

DockerイメージのビルドCIなどは設定ファイルにソースリポジトリを追加する程度で新設できるようになりました。

CDKのコードは cdk deploy コマンドでデプロイできます。
しかし、それをローカル環境から実行してしまうと環境差異がでる恐れがあるので、本番環境へは CodeBuild でcdk deploy コマンドを実行するようにしています。
CDK のリポジトリでプルリクエストをマージすると自動的に実行される様にして、インフラ変更の CD の自動化も実現しました。
もちろん、CDK をデプロイする CodeBuild の構築も CDK で行います。そしてそのデプロイも CodeBuild です。

ちなみに、CDKでは実装言語をいくつか選べますが TypeScript を使っています。
VS Code だと関数定義などがポップアップで表示されるため引数の定義を調べる手間が少ないですし、何より型が強力でコードの間違いを検出しやすいのでおすすめです。
ESLintでコードの書き方もある程度統一したルールを強制できます。

ArgoCD

GitOps を実現するに当たり、どうやって Kubernetes にデプロイするのか? FluxJenkins X などの候補がありましたが、ArgoCDを採用しました。

ArgoCDは GitOps のためのCDツールであると謳っている OSS です。
マニフェストのリポジトリとディレクトリを登録すると、そこに変更があったときに自動的に Kubernetes に適用してくれます。

採用の決め手は次のとおりです。

  • インストールが比較的簡単
  • ドキュメントがわかりやすい
  • Github アカウントを使った認証がある
  • GUI がある

インストールは ArgoCD のリポジトリにあるマニフェストで特につまるところなくできました。
Github アプリとして連携させると Github アカウントでログイン可能となります。 Github のオーガニゼーションでどのチームに属しているかによって付与する権限の変更も可能となっているところも使い勝手がいいです。

そして GUI があることが大きかったです。
Kubernetes のマニフェストを編集するのは一部の開発メンバーですが、アクトインディではリリース作業はデザイナーも含めコードを変更するメンバー全員が行う日常的な作業です。
デプロイの進行状況などが確認できる GUI があったほうがそれほど詳しくないメンバーにもわかりやすい。 自分で状況を確認する場合にも便利です。

実際に運用してみて次の機能も便利で、改めて導入してよかったと思いました。

  • 各コンテナの標準出力が GUI で確認できる
    • コンテナのログをさっと確認するのにとても便利です。
  • デプロイの PreSync, PostSync で実行されるJob
    • DBのマイグレーションをデプロイ前に実行したり、デプロイ完了をSlackに通知するのに便利です。

ArdoCD を組み込んで最終的に、CI/CD は次のように構築しました。

  • CI
    • Github でプルリクエストをマージするとCodePipeline + CodeBuild で Docker イメージのビルドとテストを実行
    • テストが通るとマニフェストリポジトリに自動でプルリクエストを作成して Kubernetes のマニフェストを更新、自動的にマージ
  • CD
    • マニフェストの変更を ArgoCD が検知。Kubenetes に適用し本番環境にデプロイ

CI の最後で CD をトリガーしているところが CI と CD を分離しきれてないといえばそうでしょう。しかし、多いときには1日10回程度リリースするのでそこは自動化しておかないと手間がかかってしょうがないというところです。

Datadog

システムのモニタリングには AWS の CloudWatch を使ってきましたが、kubernetes 導入とともに Datadog も導入しました。

Cloudwatch は便利だと思うのですが、不満に思うこともいくつかありもっと便利なツールはないかと思っていました。
かと言って、Prometheus を導入してその運用もというのはちょっとしんどい。
というようなことを勉強会か何かでたまたま出会った人に話してみました。
その人は Prometheus の同人誌を作っているような人だったのですが、「だったら Datadog いいですよ!」とのことだったので検討し始めました。

評価してみて、次の点がよいと思い導入を決めました5

  • Kubernetes 内のリソースのモニタリングが便利
    • 自動的に作られる Kubernetes ダッシュボードが独自ダッシュボードを作る際に非常に参考になりました
  • メトリクスが探しやすい
    • リソースを特定して、そのメトリクスを選択していく Cloudwatch に比べ、メトリクスを選んでから対象リソースを絞り込む方式が使いやすかった
  • Slack や AWS SNS への通知が簡単
  • Note 機能が便利
    • あれ? と思ったメトリクスの動きにメモを付けて残しておけます
  • データが丸められない

特に最後のデータが丸められないというは重要でした。Webサービスを運用していると急なアクセス急増、いわゆるスパイクが発生することがあります。
発生直後はメトリクスグラフで鋭いピークが確認できますが、時間が経つと30分平均の値に丸められたりして、その時システムがどうなっていたのか調べきれなくなります。
Datadog だと長い期間のグラフでは表示上は丸められるものの、期間を絞り込んでいくと詳細なメトリクスが参照できます。 そのおかげであのときどうなってたんだろう? ということを時間が経っても調べられます。

ただ、ALBやRDSなどのAWS のマネージドサービスを監視するにはやはり Cloudwatach が必要な部分もあり併用しています。

移行に際して

構築、機能検証をしながら、本番のいこーよを動かす構成を作っていきました。 それができたら負荷試験、パフォーマンス試験を行い、運用を想定して障害時の対応についても検証しました。

上記に加えて、移行までに特にやっておこうと思ったのは EKS のマネージドノードグループや、EKS クラスタそのもののリプレース手順を確立しておくということでした。
Kubernetes もまだまだ多分に発展途上のプロダクトですし、インフラを構築するのに使っている CDK も高頻度で更新が続いているツールです。
この先、単純にはアップデートはできず、まるごと入れ替えるしかない局面がきっとあるだろうと考えています。 そのときにサービスを止めずにリプレースしていければ躊躇せずアップデートしていけるだろうと思いました。
その手順のためにシステム構成を見直したり、CDK の実装をリファクタリングしたりといった調整を行いました。

今週、クラスターのインスタンスタイプの変更を行うためにノードグループの入れ替えを行いましたが、これらの準備のおかけで慎重に、しかし恐れることなく作業できました。

最後に

ちょっととりとめないエントリになりましたが、いこーよのインフラの再構築に際して考えたこと、 これまでの取り組みを整理しておこうと思い書いてみました。 なにかの参考になれば幸いです。

参考


  1. イミュータブルなローリングアップデートを選択しているためかもしれませんが…。

  2. 複数のコンテナを稼働できないわけではないです。いわゆるサイドカーなマルチコンテナは可能です。

  3. 決定できる立場を最大限利用しました。もちろんサービスとチームの幸せが前提ですが。

  4. このブログでもいくつかCDKに関するエントリがありますが、それらがCDKを採用するかどうか検討過程で作った習作です。

  5. とりあえずと思ってユーザ登録してみると、翌日には営業担当からコンタクトがありトライアル期間を延長してくれたり対応がとても良かったことにも背中を押されました。

"AWSなんもわからん"を脱する入り口にはCDKがおすすめ

こんにちは!WEBエンジニアのkanekoです。

最近AWSで遊ぶのが楽しいです。

でも少し前まで

「AWS?できたらいいなとは思うんですけど、そもそもインフラのこと何もわからないですし、そもそものそもそもアプリケーションコード書くのもまだまだですし、できる方にやっていただくほうがいいですし、そんな私には手が出せないですよ」

と心の底から思っていました。

そんな怯み気味だった私がCDKですごく楽しいと思ったので楽しいぞという気持ちとその要因を書き綴ります。

私と同じような「やってみたいけど何からやれば」という状態の方がいらしたら、この記事が参考になれば嬉しいです。

始める前のステータス概要

  • NginxとかApacheあたりなら少しだけ触ったことがある
  • ネットワーク?よくわかりませんね
  • インフラ関係の横文字用語全般聞き慣れない
  • コンテナ技術もさわりだけ、複雑なことはわからない
  • やってわかりたい気持ち <<<<<<<<<< 心理的なハードルの高さ

ざっとこのような感じです。4月で現場2年目になりました。Rubyが大好きです。

最初の挑戦と挫折: 試験の不合格

去年からアプリケーションエンジニアもインフラやっていこうという流れがあり、自分もその波に乗ってチームのみんなとAWSソリューションアーキテクトアソシエイトの試験を受けました。

このように書くと雰囲気に流されたり言われたからやったように見えるかもしれませんが、決めたときはかなりやる気で「試験っていうわかりやすい目標!!テキストで知識をインプットして画面の操作重ねていけば身につくと思う!頑張る!!!」と張り切っていました。

でも「何回やっても何回やってもVPCが倒せない」とか「ポリシーって結局なに」とか、全然飲み込めず徐々に心が折れていきました。

そして2019年末に受けた試験では見事不合格

試験勉強楽しくないし、やってもわからないし、そこそこ高いお金払って受けた試験は落ちるしでやる気も底につきてしまいそうになっていました。

私はそんな感じでしたが、nakamuraの合格体験記がエントリーにあるので貼っておきます。ぜひ読んでくださいー。

tech.actindi.net

CDKとの出会い

そんなこんなで過ごしているとき、全くやったことなかったんですがCDK関係のタスクを自分が担当しました。正確に言うと自分でそのタスクを作って自分でやりました。できないのにタスクにして自分でとったの、今思えばすごいですね・・・。見守ってくだったみなさまには本当に感謝しています。

本題: CDKはいいぞ

CDKとは

AWS CDKとはAWS Cloud Development Kitの略で以下のように紹介されています。

AWS クラウド開発キット (AWS CDK) は、使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースをモデル化およびプロビジョニングするためのオープンソースのソフトウェア開発フレームワークです。

AWSにたくさんあるサービスを理解して使っていくのは、自分にとってハードな道のりに感じています。 でも、CDKは使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースをモデル化してるんですよ!!!これが最高によくて、私が"AWSなんもわからんを脱する入り口にはCDKがおすすめ"と思った理由です。

これをもう少し私が感じた事柄で言い換えてみます。

1.オブジェクト指向でできる

リファレンスをざっと見ていただくとわかるように、オブジェクト指向言語で書いていくことができます

そのため単にWEBコンソール触るより概念の理解やまとまり、全体像、関係性を把握しやすかったです。

クラス名やコンストラクタなどからサービス名とか関係性、それを構成するために必要な情報や設定を知り、メソッド名から振る舞いを知ることができるので、詳細な仕組みを理解する前に役割を知ることができます。

そこを起点とすると「なるほど、だからこれはこう振る舞うのか」とつなげていけるので、本でインプットする時知識を深めやすいと感じています。

ネットワーク?よくわかりませんね状態だった私でもネットワークを本当にちゃんとわかっていきたいと心から思って関心が持てたり、「サイダーブロックっていうのは」と意味がわかって書けるまで落とし込めるかとか、サンプルコードではこう書いてあったな等思ったりして楽しく勉強しています。

オブジェクト指向最高ですね!!!

2. 他の人にレビューしてもらえる

CDK含めIaCの良さだと思うのですが、コードで表現することによって自分のやりたいことを他の人にシェアしたり、確認してもらいやすくなります。

ということは、アプリケーションコードと同じように、自分の意志と根拠を持って実装できるか自分で手応えを確認しながら書くことができるし、他の方からみて「ここはもっとこうするといいですね」とか「どうしてこのような構成に?」とフィードバックを頂いたり、他のコードを見て「なるほどね」と学んだりしてさらに定着を計ることができます。

やってわかりたい気持ち <<<<<<<<<< 心理的なハードルの高さという状態だった私にとって、これはそのハードルを下げる一助でもありました。

3. 簡単にデプロイ・削除できる

「AWSわからないうちに触って思わぬ料金が発生した」という経験のある人は、きっと私だけじゃないはず。

CDKはスタック単位でCLIからdeployしたりdestroyしたりできます。(詳細は省きますが) 「動かない!もう終わり!」と半ば投げやりな気持ちで手を止めて、1ヶ月位忘れていたとしても、立ち去る前のdestroyコマンドさえ忘れなければ大丈夫。関係するものは全部なくなります。

また、WEBコンソールをクリーンにしても、手元にコードがあるのでまた同じ状態をすぐに作り出すことができます。 料金面だけではなくて、何回も何回もやってみるのにもすごくいいと思います。

具体的に何をしたか

これはあくまで私がやっただけの一例なのですが

  1. まずAWSのハンズオンやチュートリアルなどでどういう要件やゴールがあるのかを知る
    • 例えば「S3を使って静的サイトをインターネットに公開する」とか「EC2インスタンスにSSH接続できるようにする」とうようにシンプルな感じで
    • ハンズオン中などにはわけがわからなくてもよくて、要件だけでも知れればOKと思って参加していました
  2. 1.のゴールを目指してやる
    • コンソールで直接やったりCDKでやったり
    • CDKのクラス単位で「あれとこれとこれがこうなってこう」と分かるとコンソールでも情報を見つけ易くなった気がします
  3. ちょっとずつ前進して自己肯定感を高める
  4. 楽しむ

楽しいのは大事。一番大事。

もっとやっていきたいこと

  • AWSの範囲の話をするとCDKはわかりやすくて便利な半面、 自分で指定してない箇所をデフォルトでやってくれていることもたくさんあって、自分で気づかないうちにいい感じになっている部分をブラックボックスのように感じてもいます。なので、ゼロベースでやろうとしても同じようなことができるレベルまで自分の経験値を上げていきたいなと考えています。
  • AWSに限らない話をするともっと物理サーバーで遊んだりもしてみたいです。
  • すごく偉そうにこのエントリーを書いていますが、実際はまだ何もわからんのでもっともっと手を動かしリファレンスも読んで習熟度高めていいサービスづくりに反映していきたいと思っています。

最後に

文字ばかりになりましたが、最後まで読んでくださりありがとうございました。

kubernetesを導入しました!

こんにちは!!こんにちは!!
インフラエンジニアのyamamotoです。

ついに、ついに「いこーよ」も kubernetes(k8s) を導入しました!
弊社morishitaと半年みっちり集中して、ようやく導入することができました。
今回はその体験談をお話したいと思います。

とにかく用語を覚えるのが大変

kubernetesを勉強し始めた時、まず大変だったのが、難解な用語を覚えることでした。
pod、replicaset、deployment、ingress、daemonset、クラスタ、ノード……と言われても、何のことやらって感じですよね。

しかしkubernetesをやるなら、これらは覚えて概念を理解しないと始まりません。
とにかく理解できるまで書籍やWebサイトを読みまくり、手元のMacで実際に動作させて覚えました。
概念的には、従来のインフラ環境から連想できることも多く、なんとなくイメージできたので、意外とすんなり入ってきました。
基礎を知っていることは非常に大事なことですね。

環境を整えるのが面倒

kubernetesはコンテナ技術を使っています。そのため、dockerイメージを保存しておくリポジトリがどこかに必要になります。
また、サイトのWebアプリケーションをリリースするのに、毎回dockerイメージをビルドする必要があります。
まずはこれらの環境を整える必要がありました。従来のインフラ環境に比べて、このあたりがちょっと面倒に感じられるかもしれません。

当社はAWSをメインに使っているので、CodePipelineやCodeBuildを活かして、GitHubリポジトリのmasterにマージされたら自動的にdockerイメージをビルドするCIを作成しました。
このあたりのパイプラインや、サブネット、EKSなどのAWS環境を整えるのに、今回AWS CDKを使って全てコード化しました。これはCloudFormationのスタックをTypeScript等で作れるツールです。YAMLやJSONでスタックを作るのはしんどいので、今回大活躍しました。

マニフェスト?

kubernetesでインフラをデプロイするにはマニフェストが必要になります。
マニフェストとはなんぞや?と思いましたが、要はどういうインフラを構築したいかをYAML等で書いたものになります。
まさにインフラのコード化ですね。

kubernetesを使うにはマニフェストは必須で、非常に多くの設定があり複雑です。また、デプロイする環境も開発環境、本番環境でもリリース前検証環境、実本番環境と複数の種類があります。
環境の差については「kustomize」を使う必要がありました。これはマニフェストを差分更新したりできる機能で、環境に依る部分はkustomizeを使って書き換えることで、各環境の設定を簡単に管理できるようになりました。

マニフェストについても、とにかく書いてみて、デプロイしてみて覚えるのが一番だと思います。
kubernetesの用語や概念を覚えられたら、書くこと自体はそれほど難しくないです。

実際のパフォーマンスはどうか?

kubernetes環境を構築して、podも立ち上げたところで、実際パフォーマンスがどのぐらい出るのか気になりますよね。
当社では負荷テストやパフォーマンステストを行って検証しました。

負荷テストはjmeterを使って、徐々にリクエストを増やして最大200req/sぐらいまで試してみました。
その中で、Horizontal Pod Autoscalerで自動的にpodが増え、Cluster Autoscalerによってノードが増える状態が確認できました。

パフォーマンステストでは、webpagetestを使ってレスポンスタイムを測りました。
kubernetesのpodは稼働させていないと休眠状態になるのか、何もない状態でいきなりアクセスするとレスポンスが悪くなるようです。
でも継続したアクセスがあると、結構いいレスポンスタイムが得られるんですよね。
こちらも、現行環境と比較して遜色ないレスポンスタイムだったので問題は無さそうでした。
実際にリリースしてみると、これまでのEC2の環境より安定して早いレスポンスを返せるようになりました。

最後に

なんだかマイナス面ばかり書いてしまいましたが、導入してみるととても便利ですね。
障害復旧が早いですし、実行環境のアップデートもコンテナを更新するだけ。デプロイも非常に楽ですし早いです。
何よりkubernetesがいろいろよしなにやってくれるところがイイですね!
機会があったらぜひ触ってみてください。

Amplify ConsoleのE2Eテストを導入してみた

morishitaです。

Nuxt.js で作っている SPA を Amplify Console で運用していることはこれまで数本のエントリで紹介してきました。

tech.actindi.net

tech.actindi.net

その Amplify ConsoleCypress によるE2Eテストがサポートされたという発表が2019年の10月にありました。

aws.amazon.com

その時から気になっていたのですが、ついに導入したので紹介します。

結論から言うと、Preview 機能と併用するとむちゃくちゃ便利です。
更にDependabotを組み合わせるとライブラリのアップデートもサクサク進みます!

Amplify ConsoleのE2Eテスト

次の手順で、E2E テストを追加できます。

  • Cypressの導入
  • テストの実装
  • Amplify.yml への Test ステップの追加

Cypress の導入

まずは Cypress を追加します。

> yarn add -D cypress

追加できたら次のコマンドを実行します。

> cypress open

すると cypress ディレクトリが作成され、サンプルコードが生成されます。
と同時にCypressのテストランナーウィンドウが開きます。
初回だと次のようなダイアログが表示されます。
サンプルコードを追加したので試してみろって書いてありますね。

Cypress初回起動
Cypress初回起動

とりあえず、OKボタンをクリックで閉じます。 ダイアログが閉じて次のように表示されます。

Cypressウィンドウ
Cypressウィンドウ

先程のダイアログのメッセージにあったサンプルのテストコードが表示されていると思います。
右上の Run All specs をクリックすると新たにブラウザが開いて、サンプルのテストが実行されます。

Cypressテスト実行
Cypressテスト実行

パタパタと画面が進んでなかなか見ていて楽しいです。
うまく動けば、Cypressのインストールは成功です。

テストの実装

自分のアプリケーションのテストコードは cypress/integration ディレクトリに追加していきます。

*.ts ファイルでも実装できますが、特にトランスパイルされて実行されるわけではなく、JSとして解釈され実行されるようです。なので、TSならではの型アノテーション等を実装するとエラーとなります。

きちんとトランスパイルために次のサイトに紹介されている通りに設定していきます。

typescript-jp.gitbook.io

次のコマンドで @cypress/webpack-preprocessor をインストールします。
トランスパイルには Webpackts-loader も使うのですが、SPAアプリケーションのプロジェクトに追加するならすでにインストール済みだと思うので足りないものだけインストールします。

> yarn add -D @cypress/webpack-preprocessor

そして次のように cypress/plugins/index.js を実装します。

const wp = require('@cypress/webpack-preprocessor');
module.exports = (on) => {
  const options = {
    webpackOptions: {
      resolve: {
        extensions: ['.ts', '.tsx', '.js'],
      },
      module: {
        rules: [
          {
            test: /\.tsx?$/,
            loader: 'ts-loader',
            options: { transpileOnly: true },
          },
        ],
      },
    },
  };
  on('file:preprocessor', wp(options));
};

さて、あとは自分のアプリケーションに合わせてテストを書いていきます。
例えば、サイトトップにアクセスして、タイトルに特定の文字列(例では「ページタイトル」)が含まれるかを確認するテストは次のとおりです。

describe('Top Page', () => {
  beforeEach(() => {
    cy.viewport('iphone-x');
    cy.visit('/');
  });
  it('Title is valid', () => {
    cy.title().should('to.match', /ページタイトル/);
  });
});

ページを操作するコマンドや、テストに利用するアサーションは多数用意されています。
詳しいドキュメントがあるので、それを参考にテストを実装していきます。

docs.cypress.io

amplify.yml の設定

Cypressでテストが実装できたら、Testステップをamplify.yml に追加します。

そうすると次のように Amplify Console にテスト結果が表示されるようになります。
Preview機能を利用していると、プルリクエストを作ると自動的に実行されるので手間いらずです。

Amplify Consoleテスト結果表示
Amplify Consoleテスト結果表示

テストフェーズの前のビルドフェーズの実行結果がそのまま残っているので、テストフェーズでは改めてNuxtアプリケーションをビルドしたりする必要はありません。
E2Eテストを実行するためには次のように amplify.yml に追加します。

test:
  phases:
    preTest:
      commands:
        - mkdir noto # ① 日本語 Fonts のインストール [ここから]
        - curl -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip
        - unzip NotoSansCJKjp-hinted.zip -d ./noto
        - mkdir -p ~/.fonts/noto
        - cp ./noto/*.otf ~/.fonts/noto/
        - chmod 644 ~/.fonts/noto/*.otf
        - fc-cache -fv # ① 日本語 Fonts のインストール [ここまで]
        - nvm use $VERSION_NODE_12 # ② Nodeバージョンの変更
        - yarn install
        - yarn add wait-on
        # ③ mocha のインストール ↓
        - yarn add mocha@~7.1.1 mochawesome@~5.0.0 mochawesome-merge@~4.0.3 mochawesome-report-generator@~4.1.0 
        - 'yarn start & npx wait-on http://localhost:3000'
    test:
      commands:
        - 'npx cypress run --reporter mochawesome --reporter-options "reportDir=cypress/report/mochawesome-report,overwrite=false,html=false,json=true,timestamp=mmddyyyy_HHMMss"'
    postTest:
      commands:
        - npx mochawesome-merge cypress/report/mochawesome-report/mochawesome_*.json > cypress/report/mochawesome.json
        # ④ HTMLファイルの生成 ↓
        - ./node_modules/.bin/marge cypress/report/mochawesome.json -o cypress/report/html 
  artifacts:
    baseDirectory: cypress
    configFilePath: '**/mochawesome.json'
    files:
      - '**/*.png'
      - '**/*.mp4'
      - '**/mochawesome.json'
      - 'report/html/**/*' # ④ HTMLファイルの生成

上記の設定は次の AWS Amplify のドキュメントほぼそのままですが、丸数字でコメントしている部分を変更しています。

docs.aws.amazon.com

以下にそのポイントを説明します。

ポイント① 日本語Fonts のインストール

Amplify Console の実行環境には日本語フォントがインストールされていません。
テスト実行時にテスト実行の様子が自動的にビデオが作成されます。エラーが発生するとその画面のスナップショットが取られます。しかし、そのままでは日本語部分に文字が表示されません。それではエラーが起こった際になんだかよくわからず、デバッグに役立ちません。
なので日本語フォントをインストールしておきます。ここでは Google Noto Fonts をインストールしています。

ポイント② Nodeバージョンの変更

Amplify Consoleの実行環境では複数のNode.jsのバージョン管理にnvmを使っているようです。
ということは、nvm use で Nodeバージョンを切り替えられます。

次の環境変数に利用可能なNode.jsのバージョンが設定されています。

  • VERSION_NODE_10=10.16.0
  • VERSION_NODE_12=12
  • VERSION_NODE_8=8.12.0
  • VERSION_NODE_DEFAULT=10.16.0

指定しなければ、 VERSION_NODE_DEFAULT10.16.0 で動作します。
今となってはちょっと古めなので、VERSION_NODE_12 を指定したほうがいいかと思います。
VERSION_NODE_12はメジャーバージョンしか指定されていませんが、2020年3月の時点では 12.8.1 がインストールされます。

ポイント③ mocha 関連モジュールのインストール

テスト結果を mochawesome 形式で整形したいので、関連モジュールをインストールします。
前述したAWSのドキュメントだと、mocha@5.2.0が指定されています。
これはE2Eテストサポートがリリースされた当初、mochawesomemocha のバージョン5系にしか対応していなかったためです。
今は最新の mochamochawesome が対応しているのでバージョン指定なしでも構わないのですが、この先のアップデートで依存関係が壊れてエラーになっても嫌なのでバージョンを指定しています。

ポイント④ HTMLファイルの生成

結果は mochawesome 形式で出力され、Amplify Consoleではそれを使って表示が作られます。エラーが発生したときにはもっと見やすい詳細結果がほしいので、HTML形式の結果も出力しています。
アーティファクトにも含めておけば、ダウンロードして次のようなファイルで結果が確認できます。

HTML形式のテスト結果
HTML形式のテスト結果

注意点

既存のAmplify Consoleのアプリケーションにテストステップを追加しても、CIフロー内で実行はしてくれるのですが、Web Console上てテストフェーズが表示されないことがあります。
その場合は、アプリケーションを作り直してやると、Test が認識され Web Console上で反映されます。
まあ、独自ドメインの設定等をしていると作り直し自体が結構厄介なのですが...。

次のように Amplify Console のアプリの設定でフレームワークとして Cypress と表示されていれば、Web Console上でちゃんと表示されます。

Amplify Console アプリの設定
Amplify Console アプリの設定

まとめ

Amplify ConsoleのE2Eテストについて紹介しました。
この機能について紹介しているサイトはいくつかありますが、あまり触れられていない次の項目についても紹介しました。

  • 日本語対応
  • Nodeのバージョン指定
  • HTML形式の結果出力

Amplify Consoleでは Preview という超便利な機能があります(参考:Amplify Console の Preview は超便利)。 設定しておくとプルリクエスト毎にプレビュー環境を作成する際に、E2Eテストも実行してくれます。
更にGithubのリポジトリにDependabotを設定しておくと、Dependabotが作ったプルリクエストでも同様にE2Eテストを実行してくれます。
NPMパッケージのアップデートは結構頻繁に行われるのですが、しっかりE2Eテストを作っておくと自信を持ってマージでき、面倒なライブラリのアップデートがサクサク片付きます。

最後に

アクトインディではエンジニアを募集しています。 actindi.net

Lambda@Edge を利用したリアルタイム画像変換で学ぶ DevOps

概要

Lambda@Edge でリクエストに応じてリアルタイムに画像のリサイズ、WebP 変換するプログラムを作成したら、インフラ構築、アプリケーション設計、実装、テスト、CI によるビルド & デプロイ の一連の流れを広く身につける事ができるチュートリアルに最適な案件でした。自分と同じ、インフラやサーバーサイドの経験が浅い人向けの記事で、実装に関するコードは後日紹介したいと思います。作成したものは以下で、黒が既存。赤が今回新たに作成したものになります。

f:id:t-namikata:20200317093416p:plain

Lambda@Edge の良かった点と課題

良かった点
  • 画像変換を行う Lambda はサーバレスなので、管理コストがかからない。
  • AWS の S3 を利用しているサービスは導入が容易。
課題
  • CloudFront にキャッシュがない時は、 Lambda やっぱり遅い(Lambda の起動と画像処理で 2 秒ぐらいかかる)。
  • 頑張って作る必要ある? imgix 等の外部サービスの実際の使用感と金額知りたい。

はじめに

ユーザーが投稿してくれた画像を綺麗に見せてあげたい

iOS アプリの開発を担当している namikata です。今回は、アプリ用に WebP 変換した画像を提供する仕組みを AWS の Lambda@Edge を利用して作成した話をしたいと思います。実際にイチユーザーとして、いこーよアプリを利用しているのですが、前々から、アプリ内で表示している画質が荒いなぁと感じていました。それもそのはずで、いこーよアプリでは、ユーザーが投稿してくれた画像は 450x450px のサイズで画像を表示していた為、横幅いっぱいまで広げると 2x, 3x だと、ぼやけて表示されてしまいます。口コミ投稿する度に、なんだか画像がぼやけてしまって、楽しかった思い出も、なにやらぼやけてしまう感覚に陥ったり、陥らなかったり、やっぱり陥ったり。

左: アップロード後、右: オリジナル画像 f:id:t-namikata:20200317083928p:plain

子育てで忙しい中、せっかくユーザーの皆さんが、他の人の参考になればと投稿してくれたものなので、出来るだけ綺麗に表示してあげたい。ただ、日々使ってもらうアプリとして、通信量はとても大切なポイントとなるので、ユーザー体験を損なわないように出来る限り通信量は抑えたい。という、通信量は抑えつつ、綺麗な画像を配信する仕組みが必要になりました。

現状のいこーよの画像配信の仕組み

いこーよでは、コンテンツデリバリーの方法として CloudFront + S3 という一般的な組み合わせで画像を配信していて、画像 URL には、画像から計算されたフィンガープリントを付与することで、画像に変更があれば、画像の URL が変更になり、変更が即座に反映される仕組みで構築されています。画像をアップロードすると、アプリケーション内で定義したサイズにリサイズして S3 に保存され、 View で利用したい画像サイズを指定します。

  • source.jpeg( 2000x2000px 元の画像)
  • normal.jpeg( 450x450px)
  • thumbnail.jpeg ( 180x180px )

※ 既にいこーよでは nginx を利用して任意の画像サイズへリサイズする仕組みが導入されてますが、WebP 変換にはまだ未対応で、説明を簡単にする為にその紹介はスキップします。気になる方は是非こちらのブログを参照ください。

tech.actindi.net

綺麗な画像を見せたいだけであれば source.jpeg を参照すれば良いだけですが、前述した通り、できる限り通信量を抑えたいので、容量の大きい source.jpeg は参照したくありません。その為、一番直感的な方法としては、normal より大きい large.jpeg ( 1080x1080px ) を作成してあげる事です。ただそれだけだと、やっぱり通信量は増えてしまうので、アプリでの利用であれば WebP 配信してあげるのがいいでしょう。

それぞれ画像アップロードの際に生成するようにすると、結構な枚数になりました。

  • source.jpeg( 2000x2000px 元の画像)
  • large.jpeg( 1080x1080px )
  • large.webp( 1080x1080px )
  • normal.jpeg( 450x450px)
  • normal.webp( 450x450px)
  • thumbnail.jpeg ( 180x180px )
  • thumbnail.webp ( 180x180px )

出来ない事はないですが、今後サイズをまた変更したいなと思った時には、どこまで笑顔で対応できるか自信がありません。また ImageMagick を利用して画像加工をしている場合は WebP に対応したバージョンを利用する必要があります。今後の運用を考えると、理想の形として見えてきたのは

  1. S3 では元画像の source.jpeg のみ保存する
  2. リクエストに合わせて source.jpeg から適切に画像をリサイズ、圧縮(WebP)し CloudFront に返却する
  3. CloudFront は画像をキャッシュし、配信する

といった形が、楽しく仕事をしていく為の条件になっていきそうです。

サービスを選定する

早く、簡単に実現するなら、外部サービスを利用するのが最適解です。ImageFlux、Akamai、Fastly、imgix 等が上げられ、全て調べた訳ではないですが、基本的にはリクエスト数に応じての重量課金で提供されていると思います。 https://example.com?w=1080&webp=t などのようにリクエストすると、画像を保管しているサーバーから画像を取って来て、クエリに応じた形式に変換してくれるサービスを提供しています。また、顔認識を行い、人物にフォーカスが当たる形でトリミングを行ってくれるサービスもあったりします。

最初は外部サービスを利用する前提で調査を進めていたのですが Lambda@Edge を利用すれば、同様の事が出来るという事がクックパッドさんの記事で分かったので、実際に利用できそうか検証してみる事にしました。いこーよアプリは画像ギャラリーなどがあるサービスなので、画像へのリクエストも多めで、極力コストを抑えたいと思っていましたし、何より、実際に変換する処理を自分で書く事で、画像変換のノウハウを学習するチャンスです。

大変参考にさせていただきました。ありがとうございます。

techlife.cookpad.com

また、公式サイトでも Lambda@Edge を利用した画像変換プログラムについての紹介記事があります。

aws.amazon.com

初学者でも、インフラ構築、アプリケーション設計、実装、テスト、CI によるビルド & デプロイ の一連の流れを広く身につける事ができるチュートリアルに最適な案件だった。

最近 Firebase の CloudFunction とか使う機会が増えてきて、しっかり javascript を勉強したいと思い、勉強できる機会をさぐっていたのですが、正に適任といえる案件でした。Lambda@Edge は Node.js v12 をサポートしているので node + typescript で実装する事に決めました。また、インフラ周りは AWS CDK を利用することで typescript によるコード管理が可能です。

具体的な実装については次回の記事でまとめたいと思いますが、 Lambda@Edge を利用した画像最適化の実装には、インフラを含めて必要な言語の知識は typescript のみで済むので、とてもとっつきやすいと思います(AWS の各種サービスの知識はモリモリ必要です。あと、AWS のドキュメントって何であんなに日本語難しいんだろう)。実際に、自分も typescript を書いた事は、総人生の中で 10 時間ぐらいで、全くの初学者というレベルでしたが、処理の 99% は sharp というライブラリの活用で、実装がとても簡単だった為、すんなりと書き進める事ができました。

インフラ構築

作成するプログラムは AWS CDK を利用して、コードで管理することができます。コードで管理できる事がすごく重要で、AWS の管理コンソールを触らなくても、 AWS CDK のドキュメントを参照しながら、 Lambda や CloudFront を作成するコードを書いていく事で、自然と Lambda の実行に必要な権限の付与(アクセスコントールの仕組み)、 CloudFront と Lambda@Edge の紐付け(複数サービスの連携)などを学んで行く事ができます。また、開発基盤として各種用途に合わせたステージ(開発検証用の development, 本番リリース前に検証する staging, 本番の production 等)を用意することで、なるべく本番と差異なく、でも、必要な人だけアクセスできるように管理をしないといけない staging 環境を作るのが一番しんどいんだということを身を持って学ぶ事ができます。

また、今回作成するアプリケーションは、

  • CloudFront: リクエスト数に応じて課金
  • Lambda: リクエスト処理にかかる稼働時間に応じて課金
  • CodeBuild: デプロイ処理にかかる稼働時間に応じて課金

と全てが稼働時間やリクエストに応じての重量課金になっているので、 staging や development 環境など、リクエストが少ない環境ではほとんど金額が発生しないメリットがあります。

アプリケーション設計、実装、テスト

Lambda@Edge とかあまり聞いた事ないサービスを利用するので、少し敷居が上がるイメージがありますが、画像変換の処理自体はとてもシンプルで簡単なものです。簡単なアプリケーションですが、 Lambda で実行するプログラムを書く事になるので、外部依存する処理(CloudFront や S3 にアクセスする処理)と、本質的な処理である画像加工(Lambda 環境でなくても使い回せるアプリケーションが叶えたい目的)に分けられ、どのように実装していくと、テストが書きやすく、コードの品質が担保しやすいかを学ぶいい機会になります。こちらの和田さんの記事がとても参考になりました。

speakerdeck.com

テストを書きながら、実装とリファクタリングを通して、設計のブラッシュアップが出来る良い教材です。

CI によるビルド & デプロイ

インフラとアプリケーションのコードができたら Github と連携して各種ステージに自動的にビルドできる環境を作成します。AWS には CodeBuild というデプロイツールがあるので、それを利用する事にしました。特定のブランチへの push に応じて、development, staging, production へ自動でデプロイする方法を学びます。

また push をトリガーに インフラ用のスナップショットテストや、アプリケーション用の単体テストの実行、lint チェックを Github Actions で行い、テストがパスする事でサービスの品質が担保されている事の確認、コード規約が守られている事をチェックすることで、コードの品質が担保されている事の確認を行い、継続的な運用に必要な処理を行います。

Lambda@Edge を利用したリアルタイム画像変換の良かった点と課題

最後に Lambda@Edge を利用したリアルタイム画像変換を実装してみてと良かった点と課題をまとめたいと思います。

良かった点

画像変換を行う Lmabda はサーバレスなので、管理コストがかからない点は、外部サービスを利用する際と同様で、一度構築してしまえば、運用の手間がなくとても楽です。Lambda の同時実行数に対して上限が設けられているので、サービスの規模に合わせて、緩和申請が必要になる点には注意が必要です。

また、AWS の S3 を画像の保管場所として利用している場合は、既存の仕組みに手を加える事なく、画像変換処理を追加する事ができるので、新規実装時に本番に与える影響がなく、変更後の切り戻しも容易な点はメリットかなと思いました。

課題

CloudFront にキャッシュがない時は、画像変換用に Lambda が起動するのでやっぱり遅いです(Lambda の起動と変換で 2 秒ぐらいかかる)。ただ、画像はあまり変更頻度が高くないので、キャッシュの生存期間を 1 年など長めに設定し、変更があった際にはキャッシュの削除を行う設計にすれば、CloudFront にキャッシュがある場合は Lambda が起動せず、高速に動作するので、そこまでクリティカルな問題にはなりませんでした。ただ、ユーザー投稿により画像は常に増えるので、キャッシュが存在しない新規に増えた画像用に、アプリ側でいい感じに Prefetch して画像を先読みしたり、キャッシュヒット率を上げる為に、アプリ内で利用する画像サイズを統一したりとユーザー体験を上げる施策の実施は効果的です。

また、学習目的以外には、外部サービスを利用しない明確なメリットが感じられなかったので(当然、外部サービスに依存しないというメリットがありますが、餅は餅屋に的な話もあり。もしかしたら料金面で大きなメリットがあるかも)、どこかで外部サービスとの比較を行えると、Lambda@Edge の適正な評価になるのかなと思いました。

今、作成した Lambda@Edge を本番リリースに向けて準備中なので、具体的な実装の紹介は、近日中にまとめたいと思います。

【AWS認定】ソリューションアーキテクト – アソシエイト合格体験記

こんにちは、nakamuraです。1月末にAWS認定ソリューションアーキテクト – アソシエイトを受験し、なんとか合格できたので、その経緯と勉強方法、感想などを記したいと思います。

まず、経緯ですが、10月から社内のエンジニア数名でインフラに関する勉強会を周1で開催することにしました。そこで漠然と勉強するよりも目標があった方が取り組みやすいだろうということで、業務でも利用することが多いAWSのSAA合格を目標にしました。

業務経験

  • ウェブエンジニア歴:10年程度
  • インフラ業務経験:nil
  • AWS経験:S3を使っていた程度

勉強期間

  • 勉強開始:11月から
  • 受験日:12月下旬 => 延期して1月下旬

使用した教材

この本で基礎的な知識を詰め込みました。1周目はわからないところが多かったですが、とりあえず、最後まで読み進めました。2周目は他の教材でわからないところを重点的に振り返りながら学習しました。2章 ネットワーク、3章 コンテンツ配信、6章 ストレージサービス、7章 データベースサービスは試験直前でも振り返りました。

VPCとか、ネットワークACLとかサブネットとか、セキュリティグループとか、なんとなく解ったつもりになっていたので、実際に手を動かしてシンプルな構成であればイメージ出来るようにしました。チュートリアル形式で少しずつ進めるので基本的なことを確認しながら勉強するのに役立ちました。

有料になりますが、結果的に、この問題集を問いておいて良かったと思っています。沢山問題があるので、自分の苦手な分野がわかるし、出題傾向なども掴めたと思います。自分は模擬試験を受験しなかったのですが、このWEB問題集で個人的には十分だったと思っています。特に後半の問題は数回繰り返しました。

勉強方法

11月:AWS認定資格試験テキスト 週末に1時間勉強する程度で少しずつ学習開始。なので、ころ頃は本気度がまったく伺えず。。。8章の「セキュリティとアイデンティティ」とか、読み始めると、数分で寝落ち。

12月:受験日を12月中旬で申し込んでいたので、焦り始める。この頃からAWS WEB問題集の学習開始。ほぼ回答できないレベル。仕事終わりで自宅に帰り、机に向かうも全く集中できず。近所のファミレスと喫茶店で勉強するようにして、1日、2時間くらい集中して取り組める時間を強制的に作り出す。それでも十分なレベルに達していると思えなかったので、受験日を1ヶ月延期(戦略的撤退)。

1月:AWS WEB問題集の後半の問題を中心にファミレスと喫茶店で引き続き勉強。VPC周辺はしっかりと学んでおきたかったので、[Amazon Web Services 基礎からのネットワーク&サーバー構築]と併せて復習。週末は、午前2時間、午後2時間くらいのペースで自分の苦手なところを潰す。

感想

f:id:iam-nakamura:20200313174452p:plain
SAA RESULT

720点が合格ラインなのでほんとギリギリでした(汗)。詳細については↓の通りです。

f:id:iam-nakamura:20200313174554p:plain
SAA REVIEW

受験した感想としては、VPC関連とストレージ、データベースに関する問題は比較的多めに出題されていたと思います。特にストレージに関する部分は、業務でも少しだけ使っていたこともあり他と比較して甘めに勉強していたのがよくなかったと反省しています。ライフサイクル管理やストレージに関する各サービスの特徴はしっかり抑えておくべきでした。

最後に、SAAの学習を通して、業務で使用している各AWSサービスの特徴をより深く把握できたことで、AWSへの抵抗感がかなり減りました。まだまだ知識、経験、共に不足しているインフラ分野なので、引き続き学習を続けて行きたいと考えています。あと、自分の勉強スタイルをある程度確立できたのは思わぬ副産物でした。今後も、スキルアップのために同様の努力を続けて行きたいと思います。

アクトインディではエンジニアを絶賛募集中です。興味のある方は↓をポチ。

actindi.net

AWS CDKでALBを作成する

こんにちは!!こんにちは!!インフラエンジニアのyamamotoです。

今日もAWS CDKでクラウドインフラを構築するよー!!

今回はCDKでALBを作ってみました。
コードはこちら!!

※当ブログのコードはTypeScriptで記載しています。

import { Construct, Duration, Stack } from '@aws-cdk/core';
import {
  Peer,
  Port,
  SecurityGroup,
  Vpc,
} from '@aws-cdk/aws-ec2';
import {
  ApplicationLoadBalancer,
  ApplicationProtocol,
  ApplicationTargetGroup,
} from '@aws-cdk/aws-elasticloadbalancingv2';

const env = {
  region: process.env.CDK_INTEG_REGION || process.env.CDK_DEFAULT_REGION,
  account: process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT,
};

/**
 * ALBを作るスタック
 */
export default class AlbStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id, { env });

    // 既存のVPCを調べる
    const vpc = Vpc.fromLookup(this, 'vpc', { vpcName: 'vpc' });

    // 自動生成されるセキュリティグループには問題があるため
    // セキュリティグループを作成する
    const albSecurityGroup = new SecurityGroup(this, 'albSecurityGroup', {
      allowAllOutbound: true,
      securityGroupName: 'alb-sg',
      vpc,
    });
    albSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(80));
    albSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(443));

    // ALBを作成する
    const alb = new ApplicationLoadBalancer(this, 'alb', {
      internetFacing: true,
      loadBalancerName: 'alb',
      securityGroup: albSecurityGroup,
      vpc,
      vpcSubnets: { subnets: vpc.publicSubnets },
    });

    // ALBのターゲットグループを作成する
    const targetGroup = new ApplicationTargetGroup(this, 'targetGroup', {
      healthCheck: {
        healthyHttpCodes: '200',
        healthyThresholdCount: 2,
        interval: Duration.seconds(30),
        path: '/',
        timeout: Duration.seconds(5),
        unhealthyThresholdCount: 2,
      },
      port: 80,
      protocol: ApplicationProtocol.HTTP,
      targetGroupName: 'alb-tg',
      vpc,
    });

    // ALBのリスナーを作成する
    alb.addListener('Listener80', {
      defaultTargetGroups: [targetGroup],
      open: true,
      port: 80,
    });
    alb.addListener('Listener443', {
      certificateArns: ['certificate-arn'],
      defaultTargetGroups: [targetGroup],
      open: true,
      port: 443,
    });
  }
}

ALBを作成するとき、特に指定しないとセキュリティグループを自動的に作ってくれるのですが、これが残念ながらうまく動きませんでした。
そのためこちらであらかじめ用意しています。

また、AWS CDKのリファレンスには、AutoScalingGroupからALBを作るケースが載っていますが、諸事情によりここではALBのTargetGroupを作っています。
ただ、AutoScalingGroupとのアタッチはしていないので、そこは手作業なり他のスクリプトでやる必要があります。

こういう事例がいっぱいあると、CDKももっと書きやすくて身近になっていくんじゃないかと思っています!
CloudFormationのYAMLやJSONより簡単に書けますよー、TerraFormと比べると……どうだろう?

さいごに

アクトインディではインフラエンジニアを募集しています!
クラウドやりたいクラウドエンジニアはぜひ! actindi.net

Amplify Console の Preview は超便利

morishitaです。

この記事は actindi Advent Calendar 2019 の15日目の記事です。

adventar.org

Nuxt.js の SPAをAmplify Consoleで運用しているということは以前紹介しました。

tech.actindi.net

これまで、ステージング環境の作成は Branch Autodetect 機能を利用してやっていたのですが、最近 Preview 機能に乗り換えたので紹介します。

CDKも便利ですが、今年出たAWSのサービスでおすすめは? と聞かれたら、「S3で静的なSPAをホストしているならすぐに Amplify Console への乗り換えを検討すべき」と思うくらいワタシ的にはイチオシです。

Amplify Console の Preview機能って?

Githubと連携して、プルリクエスト(PR)が作られると、そのブランチをビルド、デプロイして動作確認できる環境を自動的に作ってくれる素敵機能です。
特にブランチごとに独立した環境が作られるっていうところがすばらしいです。

設定方法

設定も難しくありません。

GithubリポジトリにAppを追加

最初に、Github にAmplifyのappを追加する必要があります。 アプリの設定に Previews(①)というメニューがあるので選択すると次のようなページが開きます。

f:id:HeRo:20191215085639p:plain
Previewの設定ページを開く

注)上のキャプチャはすでにプレビューが設定されている状態なので作られたプレビューの一覧も表示されています。

続いて Preview settings(②)ボタンをクリックすると次のページが開きます。

f:id:HeRo:20191215085732p:plain
プレビューのブランチ設定

上のスクショはすでに設定済みなので Re-install Github app となっていますが、初めてのときは Install Github app となっています。いずれにしてもクリックするとGithubと連携して、リポジトリに Amplify Previewのアプリをインストールできます。

インストールすると Github の Installed Github Apps にも表示されます。

f:id:HeRo:20191214094141p:plain
Github Apps

プレビューをつくるブランチを設定

Github Appがインストールできたらプレビューを作るブランチを選びます。

次のページに戻って、プレビューを作りたいブランチを選択して、Manage ボタンをクリックします。

f:id:HeRo:20191215085815p:plain
ブランチ設定

プレビューを有効にするかどうかを問うダイアログが表示されるので、Enabled に設定します。

f:id:HeRo:20191214094734p:plain
ダイアログ

すると選択したブランチをベースとするPRにプレビューが作られます。
masterだけでなく、例えば developブランチを指定すると develop ブランチをベースにするPRにもプレビューが作られます。 Git Flowを採用しているチームにも使いやすいのではと思います(ちなみに当社はGithub Flow)。

Githubではどう見えるのか?

実際に PRを作ってみるとマージボタンの上に次の様に表示されます。

f:id:HeRo:20191214094815p:plain
GithubのPRでの表示

すでにプレビュー環境へのデプロイが終わっていれば、Details リンクをクリックするとプレビュー環境へアクセスできます。

デプロイ処理中だと次のようなページに遷移して、矢印で示したリンクをクリックするとAWSコンソールのAmplify Consoleが開いて、このプレビューの処理の状況を確認できます。

f:id:HeRo:20191214094849p:plain
Github Actionでの確認

Dependabotとの組み合わせは最高!

自分が作ったPRにはもちろんプレビュー環境を作っってくれますが、Dependabotが作るPRにももちろんプレビュー環境が作られます。これが最高に便利です。

プレビュー環境が作られば、Slackに次のような通知が来るようにしています。

f:id:HeRo:20191214094927p:plain
Slackへの通知

これを見たらPRをみてユニットテスト、ESLintのチェックの結果を確認します。これらはGithub Actionで自動実行されるようにしています。

チェックをパスしていれば、ライブラリのアップデート内容を確認しつつ、プレビュー環境で動作チェックします。
問題なさそうなら、マージして本番でプロイします。

ライブラリのアップデートはいっぺんにやらないとyarn.lock などのコンフリが発生しがちです。でも1つづつアップデートしてコンフリしても Dependabot が自動的に解決してくれるので、どんどんマージできます。

このおかげでJavaScriptのライブラリはアップデートが速く、対応が滞りがちだったのですが、サクサクアップデートできるようになりました。

不満点

不満点がないわけでもありません。
多分、現状のAmplify Consoleではデプロイが完了したタイミングを得る手段がありません。
前述のSlackへの通知はビルドに成功したタイミングで送っているので実際のデプロイ完了は通知より2,3分遅れます。
なにか方法はないのだろうか…。

まとめ

PR毎にプレビュー環境を作ってくれる Amplify ConsoleのPreview機能は設定も簡単でとても便利です。

特に Dependabot との組み合わせは強くおすすめします。一度使うとやめられません。

最後に

アクトインディではエンジニアを募集しています。 actindi.net

AWS CDKでつまづいたところ3選

この記事は actindi Advent Calendar 2019 の13日目の記事です。 adventar.org

こんにちは!!こんにちは!!インフラエンジニアのyamamotoです。

AWSのリソースを作成・修正するのに、CloudFormationを使うととても便利に作れますね。
でもループ処理できなかったり、生のYAMLが見づらかったりと面倒なこともあります。

そこでCloud Development Kit(CDK)を使うと、CloudFormationスタックを手軽に作ることができます!

しかしCDKについてはまだまだ情報が少なく、思い通りに扱うのも割と大変です。ここでは私がCDKを使っていてつまづいた点を共有させていただきます。

※当ブログのコードはTypeScriptで記載しています。

マルチスタックにするときはこうする

ネット上のCDKのサンプルはシングルスタック(CloudFormationスタック一つで実行する)で扱われたものが多くあります。見通しがよく、一気にいろいろなリソースが構築できるので便利ですね。
ただ、実際の運用を考えると、VPCはVPCだけ、EC2はEC2だけ、というように、それぞれのリソースで別々に実行したくなってきます。

そこで、CDKではマルチスタックにできるようになっているのですが、マルチスタックのサンプルがあまり無いので、どういう書き方がスタンダードなのか分かりづらかったです。

結局、CDKのマルチスタック対応は、このように書きました。

bin/stack.ts

#!/usr/bin/env node
import 'source-map-support/register';
import { App, Tag } from '@aws-cdk/core';

import FooStack from '../lib/FooStack';
import BarStack from '../lib/BarStack';
import BazStack from '../lib/BazStack';

const app = new App();

new FooStack(app, 'foo-stack');
new BarStack(app, 'bar-stack');
new BazStack(app, 'baz-stack');

単純に、このような感じで並べて書きました。
一つ一つ書くのが面倒なのですが、プログラムですし、色々なやり方があると思います。

なお、実行するときは、下記のように引数で実行したいスタックを指定します。

$ cdk deploy foo-stack

EKSでは既存のVPCを読むときにSubnetが無いとダメ

せっかくなのでKubernetesをやりましょう。CDKでEKSクラスタを作成して、でも既存環境があるので既存のVPCを読み込みたい、というときはVpc.fromLookup()メソッドを使います。
しかし、VPCを読み込んでEKSクラスターを作成しようとするとエラーが出てしまいます。
これもいろいろ探してみたものの事例が無く、原因もわかりませんでした。

あれこれ試してみたところ、EKSに読み込ませるVPCにはパブリックサブネットとプライベートサブネットが定義されてないとダメだとわかりました。
パブリック?プライベート?なんだかわからない定義ですが、要はインターネットゲートウェイにルーティングされたサブネットか否か、というだけのようです。

では、VPCスタックでサブネットを作ってVPCを読み込むように書いて、とやってもやはりエラーになります……なんで?

どうもVPCを読み込むときは、サブネットの作成がCDKコードに記載されていてもダメで、サブネットが実在していないと通らないようでした。
なので、サブネットを作成するスタックを先に実行して、作成が済んでからVPCを読み込ませるようなスタック構成にしないとうまく動きません。

bin/stack.ts

const vpcStack = new VpcStack(app, 'vpc-stack');
const vpc = Vpc.fromLookup(vpcStack, vpcName, { vpcId, vpcName });

上記の例では、VpcStack()内でサブネットを作成して、その後外でVpc.fromLookup()を実行しています。
このようにすることで、サブネットが作成された状態でVPCを読み込むことができます。

EKSマスターロールにはパスを付けてはダメ

さて、あれこれ試しながらようやくCDKでEKSクラスタを立ち上げました。しかしなぜか突然kubectlで接続できなくなってしまいました。
ロールを見直してもだめ、何度やっても不可思議なエラーが出てしまう。これは一体なんなんだろう。

こないだまではkubectlも問題なく動いていたのになんでだろう……ふと、最近修正したところを考えてみると……
ひょっとして、EKSのマスターロールにパスを追加したからダメなのか?

    const eksRole = new Role(this, 'EksRole', {
      roleName: 'eks-master-role',
      assumedBy: new AccountRootPrincipal(),
      path: '/cdk',
    });

ロールにパスを追加しようとしたのには訳があって、CDKで取り扱うロールを明示的に区分けしたかったからなのですが、まさかEKS側で対応していないとは……
とはいえ、日進月歩で進化しているAWSですから、そのうち対応するでしょう。早く対応してほしいなぁ。

さいごに

CDKはまだまだ発展途上ではありますが、未来を感じさせるソリューションですね。
昔から考えると、コードでネットワークを構築してサーバーを立ち上げる、とか想像もつかないですよね。どんどん便利に快適なインフラ環境になっててワクワクします。

アクトインディではWebエンジニアを募集しています! actindi.net

AWS CDKでEKSクラスタを作る

CDK で EKS構築してみる

morishitaです。

前回に引き続きCDKでやってみた系のエントリです。

アクトインディは基本Railsでサービスシステムを開発している会社ですが、 私はこの1ヶ月ほどRubyを書かずにCDK+Typescriptでインフラをいろいろいじっています。

いこーよは当社では最も長いサービスで、それを支えるインフラ構成も改善はしていますが、長年大きくは変更しておらず幾分古さが気になる状況になってきました。 そこで、コンテナベースのインフラに移行しようと思うのですが、今やるならやはり kubernetes で GitOps な環境を構築したいなぁと検討しているところです。

自前でKubernetesのクラスターを構築・運用するのは大変そうなので、Amazon EKS を検討しています。

AWS EKS

EKS は AWS の Kubernetesのサービスで、マネージドなコントロールプレーンを提供してくれます。 AWSコンソールや専用CLIの ekscliから Kubernetes クラスターを作成するのは簡単で、10-15分ほど待てば構築されます。

プロダクションへの導入を検討しているもの

クラスタそのものを作るのは簡単ですが、本番環境での運用を考えると、素のEKSは幾分機能不足です。 本番運用に向けて次のKubernetesリソースの導入を検討しています。

  • Metrics Server
  • Cluster Auto Scaler
  • EBS CSI Driver
  • ALB Ingress Controller
  • External DNS

Metrics Server

クラスター内のリソース使用状況データを収集してくれます。 Horizontal Pod Autoscaler や Vertical Pod Autoscaler を利用するのに必要、 つまり、Pod をオートスケールさせるのに必須となります。

Cluster Auto Scaler

Horizontal Pod Autoscaler や Vertical Pod Autoscaler はクラスター内で Pod の数や大きさを負荷に応じて自動調整してくれるものです。 なので、クラスターに Pod を追加する余地がなければ、それ以上はどうしようもない状態になります。
CPUやメモリの不足が原因でポッドが起動できなかった場合にワーカーノードを追加してクラスタそのものをスケールアウトしてくれるリソースです。 クラスター内のノードの利用率が低く、そのポッドをクラスター内の他のノードに再スケジュールできる場合には減らしてもくれます。
Fargate が Pod の実行環境として使えるようなりましたが、制約があるので 本番サービスでは Cluster Auto Scaler も導入したほうがいいと思います。

EBS CSI Driver

EBS ボリュームをクラスタ内から永続化ストレージとして利用できるようにするドライバーです。
ワーカーノードのストレージを不用意に圧迫してしまいたくないですし、Podがなくなっても消えてほしくないデータの保管に役立ちます。
EBSに対応するストレージクラスを追加して、それを指定した PersistentVolumeClaim を作ると、EBSボリュームを作成しPodから利用できるようにしてくれます。PersistentVolumeClaimを削除すると、EBSボリュームも削除されます。EBSのライフサイクルを Kubernetes 側から制御できるので運用が楽になります。

ちなみに同様のドライバーにストレージとしてEFSを利用する EFS CSI Driver もあります。

ALB Ingress Controller と External DNS

ALB Ingress Controller は ALB を Kubernetesリソースとして登録するためのコントローラです。 metadata.annotations.kubernetes.io/ingress.class: alb のIngressを作成すると、自動的にALBが作成されます。Kubernetes のサービスにつないでやるとWebアプリケーションをインターネットに公開できるようになります。

External DNS は Kubernetes から Route53 のレコードを制御できる様になります。 ALB Ingress で指定したホスト名でアクセスできるように Route53 のレコードも追加してくれます。 本番環境で利用するかはともかく、テスト環境を作ってインターネットからアクセスできるにする場合などにとても重宝しそうです。

まあ、ALB Ingressは使わない運用をすることもあるかと思いますが、使えるようにしておくと便利だと思います。

習作

上記のセットアップと動作確認のための習作として上記リソースをすべて使える状態で Kubernetes クラスタを作成するCDKプロジェクトを作成しました。

HeRoMo/cdk-eks

AWSのルートアカウントではない AdministratorAccess権限のあるIAMユーザで実行してください。

$ git clone https://github.com/HeRoMo/cdk-eks.git
$ cd cdk-eks
$ yarn
$ yarn build
$ yarn cdk deploy *Stack

コマンドを実行して15 ~ 20分程度待つとクラスターの出来上がると思います。

最後に次のようなコマンドが出力出力されるので、コピって実行すると、kubectlで接続できるようになります。

aws eks update-kubeconfig --name MyEKSCluster ...

削除するには次のコマンドを実行します。

$ yarn cdk destory *Stack

EKSはただクラスターを立ち上げておくだけで $144/month(30days) +ワーカーノードの稼働費がかかってしまうので使いもしないのに立ち上げっぱなしにしないように注意が必要です。

おすすめ開発環境

CDK & Kubernetes の開発のおすすめ環境を紹介します。

ずばり、VSCodeに次の拡張を入れるのがおすすめです。

marketplace.visualstudio.com

AWS謹製の拡張。まだPreview版ですが、最近CDKに対応した機能が追加されました。 最近CDKプロジェクト内のスタックとリソースがツリーで確認できるようになりました。

marketplace.visualstudio.com

Microsoft謹製の拡張です。kubeconfigで設定されているクラスタのリソースを手軽に確認できます。リソースを選択するとそのマニフェストもYAMLで表示できます。

まとめ

CDK 使って EKS クラスタはもちろん、kubernetes リソースも作成できます。 AWSのリソースを利用する kubernetesリソースはIAMロールをワーカーノードに付与する必要があったりするので一緒に作ってしまうと便利です。

便利ですが kubernetesリソースはできるだけKubernetesウェイで管理したほうがいいと思っており、CDKコードの中にマニフェストを入れるのはどうなのだろうかと思っているところです。

実際のプロダクション環境ではAWSリソースはCDK、kubernetesリソースはそれとは別にmanifest群のリポジトリで管理する方針なのですが、迷うところです。

また、ちょっと触れましたが、EKS で Pod を Fargate を使って動かせる機能が GA になりました。

aws.amazon.com

Persistent volumes はダメ、Daemonsetsはダメとかいくつか制限はあるもののクラスタ自体のサイズを気にせず Pod を増やせるのは良さそうです。
まだ、CDKどころか、CFn でも扱えないようですが試してみたいと思います。

面白くなってきました。

最後に

アクトインディではGitOpsでサクサク開発 -> リリースできる環境を作りたいエンジニアを募集しています。

actindi.net