SRE NEXT 2020 基調講演で「Webサービスを1日10回デプロイするための取り組み」を話しました

SRE NEXT 2020 という、国内初の SRE に特化したカンファレンスで基調講演をご依頼いただいて、「Webサービスを1日10回デプロイするための取り組み」を話してきました。

sre-next.dev

speakerdeck.com

これまでいろいろ発表はしてきましたが、基調講演というのは初めてで、さて何を話したほうがいいのか悩みました。とはいえ自分は基本的に技術でなにかを解決する話しかできないので、かなり現場寄りの話です。いつもどおり。

時間の関係でいろいろ省略した部分があるのですが、懇親会で聞かれた CircleCI に移行して増えたコストの話については LobiのCIをJenkinsからCircleCIに移行したはなし - KAYAC engineers' blog にあるとおり

数万円程度だったCI関連費用は、およそ2倍の十数万円になりました。

ぐらいなので、Jenkins サーバのお守りに掛かる諸々のコストがなくなったことや、体験の向上を考えると見合った投資だろうと思っています。


一日参加して、どれもいい発表だったのですが、印象に残った発表の感想を軽く書いておきます。

  • 基調講演 分散アプリケーションの信頼性観測技術に関する研究
    • まさに NEXT を見据えた研究で、オープニングに相応しい内容でした
  • パフォーマンスを最大化するための SRE のオンボーディング事例
    • どんなに人を集めても力を発揮できる受け入れ体制がないと何もならないので、組織的にそこを改善していくのは重要ですね
  • ZOZO MLOps のチームリーディングとSRE(Engineering)
    • まずひとつリリースまで持っていく、次にそれがまぐれでないことを証明するために複数で成功させる、というチームとしてのミッションが明確で、こういうリーダーの下で働けたら楽しいだろうな、と思わされました
    • 最後は技術で殴る必要がある (物事を解決できないで理想だけ語っても意味がない) というのもまさにその通り

懇親会でもいろいろな人と話ができて楽しかったですね。でもみんなもうちょっと野菜食べたほうがいいと思います。(この写真は懇親会終了時)

最高に楽しいカンファレンスでした。運営の皆さん、参加者の皆さん、ありがとうございました!

lambroll と bash layer で気軽に Lambda shell script を実行する

先日えいやと書いた AWS Lambda のデプロイツール lambroll ですが、これと公開済みの bash layer を使うとかなり気軽に(雑な) shell script を Lambda で実行できて体験がよかったので書いておきます。

AWS Lambda のミニマルなデプロイツール lambroll を書いた - 酒日記 はてな支店


今回はとある理由で ECS のサービス内のタスクを定期的に入れ換えたかったので、aws ecs update-service を一発実行する、という要件。やりたいことはただ aws-cli を一発叩くだけでできるのに、Lambda 化するには Node やら Go やらで SDK 使ってコードを書いて…というのがめんどくさいこと、よくあると思います。

まず適当にディレクトリを掘って、そこで lambroll init で初期化します。

$ mkdir ecs-task-replacer
$ cd ecs-task-replacer
$ lambroll init --function-name ecs-task-replacer
2019/11/13 22:16:21 [info] lambroll v0.2.1
2019/11/13 22:16:23 [info] function ecs-task-replacer is not found
2019/11/13 22:16:23 [info] creating .lambdaignore
2019/11/13 22:16:23 [info] creating function.json
2019/11/13 22:16:23 [info] completed

関数が存在しないので、次のようなスケルトンの function.json が生成されます。

{
  "FunctionName": "ecs-task-replacer",
  "Handler": "index.handler",
  "MemorySize": 128,
  "Role": "arn:aws:iam::123456789012:role/YOUR_LAMBDA_ROLE_NAME",
  "Runtime": "nodejs10.x",
  "Timeout": 3
}

これを bash layer GitHub - gkrizek/bash-lambda-layer: Run Bash scripts in AWS Lambda via Layers を使うように書き換えます。 Layers, MemorySize, Runtime, Role, Timeout を適当に指定。この例のように aws-cli を使う場合、Memory が少ない(CPUも比例して少ない) と aws コマンドを起動するのに大変時間がかかるので、1コアをフルに使える 1.5GB を指定するのがよいです。

{
  "FunctionName": "ecs-task-replacer",
  "Handler": "index.handler",
  "Layers": [
    "arn:aws:lambda:ap-northeast-1:744348701589:layer:bash:8"
  ],
  "MemorySize": 1536,
  "Role": "arn:aws:iam::123456789012:role/lambda-function",
  "Runtime": "provided",
  "Timeout": 30
}

関数本体は index.sh というファイルに、event を jq で処理して aws-cli を叩くだけのを書きます。今回は event で ECS cluster と service を指定できるように {"cluster": "CLUSTER_NAME", "service": "SERVICE_NAME"} という構造にしてみました。(決め打ちでいいならハードコードしちゃってもいいですね)

function handler {
    set -e

    EVENT=$1
    SERVICE=$(echo "$EVENT" | jq -r .service)
    CLUSTER=$(echo "$EVENT" | jq -r .cluster)

    aws ecs update-service \
        --cluster "$CLUSTER" \
        --service "$SERVICE" \
        --force-new-deployment

    echo "{\"success\": true}" >&2
}

lambroll deploy でデプロイします。

$ lambroll deploy
2019/11/14 08:40:52 [info] lambroll v0.2.1
2019/11/14 08:40:52 [info] starting deploy function ecs-task-replacer
2019/11/14 08:40:54 [info] creating zip archive from .
2019/11/14 08:40:54 [info] zip archive wrote 342 bytes
2019/11/14 08:40:54 [info] updating function configuration
2019/11/14 08:40:54 [info] updating function code
2019/11/14 08:41:00 [info] deployed version 2
2019/11/14 08:41:00 [info] updating alias set current to version 2
2019/11/14 08:41:01 [info] alias updated
2019/11/14 08:41:01 [info] completed

手元から実行するには lambroll invoke を使って、標準入力に event を与えます。--log-tail を付けると実行した Lambda のログ(の最後の4KB)が出力されます。

$ echo '{"cluster":"default", "service":"example"}' | lambroll invoke --log-tail
2019/11/14 08:46:35 [info] lambroll v0.2.1
{"success": true}
2019/11/14 08:46:57 [info] StatusCode:200 ExecutionVersion:$LATEST
s reached a steady state."
            },
            {
                "id": "8304a25b-8329-4099-903f-677013206fe8",
                "createdAt": 1572800779.798,
                "message": "(service example) has reached a steady state."
            },
...(略)
        "schedulingStrategy": "REPLICA",
        "enableECSManagedTags": false,
        "propagateTags": "NONE"
    }
}
END RequestId: c821f023-0af2-48e5-ae21-c6f381e9ec65
REPORT RequestId: c821f023-0af2-48e5-ae21-c6f381e9ec65  Duration: 20484.06 ms   Billed Duration: 20600 ms   Memory Size: 128 MB Max Memory Used: 103 MB Init Duration: 44.74 ms
2019/11/14 08:46:57 [info] completed

おしまい。

定期実行するには CloudWatch Events でポチポチとスケジュールを定義してやればよいですね。

最初に IAM role を用意するところと、init で生成された function.json を書き換えるところが少しだけ手間ですが、そのあとは index.sh を書き換えて deploy して invoke するのに1コマンドずつ叩くだけです。

個人的には雑に cron で shell script を定期実行したいだけなのに、コード書いて Lambda 用意してって億劫だなあ、みたいなストレスがだいぶ減る気がします。どうぞお試しください。

AWS Lambda のミニマルなデプロイツール lambroll を書いた

3行で

  • シンプル/ミニマルな Lambda のデプロイツール lambroll を書いてるよ
  • Lambda API 以外は極力触らないやつです
  • 既存 function の移行も簡単です

開発の経緯

AWS Lambda を管理、デプロイするのに数年来 Apex を使っていましたが、最近更新がないと思っていたら案の定というか、残念ながら No longer maintained となってしまいました。

で、代替を探したのですが…

SAM も Serverless もどちらも結局 CloudFormation がお出ましになるし、自分としては関連リソースは別に Terraform で管理しているし、悩ましいなあ、という状況でした。

とはいえ aws-cli だけでやるのもちょっと面倒なことがあり、仕方ないので先週金曜にえいやっと書き始めたのが lambroll です。

github.com

"lamb roll"で検索すると美味しそうですね。

f:id:sfujiwara:20191101010814p:plain

lambroll は何をして、何をしないのか

README に書いてありますが、こんなかんじです。

やること

  • zip archive の作成
  • Lambda 関数の設定とコード、alias、タグの作成、更新

やらないこと

  • 周辺リソースの作成と管理
    • IAM Role とかトリガとか API Gateway とか…
  • Lambda 実行環境用のバイナリ(ネイティブ拡張)のビルド

Lambda の API で叩ける範囲以外は基本しません (S3 へファイル上げる以外)。Runtime や Layer もなにも関知しないので、zip に入れるものは自分でいい感じにビルドしてください。個人的には最近 Go / bash layer しか使わないのでビルドに困ってないというのもあります。

Quick Start

インストールは mac なら brew install fujiwara/tap/lambroll でできます。Linux はリリースバイナリを適当に落としてください。

既存の Lambda 関数を lambroll init で持ってきて管理できます。--download を付けると $LATEST で動いている zip もダウンロードしてきます。

$ mkdir hello
$ cd hello
$ lambroll init --function-name hello --download
2019/10/26 01:19:23 [info] function hello found
2019/10/26 01:19:23 [info] downloading function.zip
2019/10/26 01:19:23 [info] creating function.json
2019/10/26 01:19:23 [info] completed

$ unzip -l function.zip
Archive:  function.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
      408  10-26-2019 00:30   index.js
---------                     -------
      408                     1 file

$ unzip function.zip
Archive:  function.zip
 extracting: index.js

$ rm function.zip

あとは lambroll deploy すると、

  1. カレントディレクトリを zip にまとめる
    • .lambdaignore ファイルで除外指定できます
  2. 関数の設定、コード、タグを更新
  3. alias current を設定

が行われてデプロイされます。簡単ですね!

rollback のための alias 設定は Apex のと互換仕様のつもりです。

command

現在 init, list, deploy, rollback, delete, invoke, archive があります。

function.json

Lambda 関数の設定は function.json に書かれているので、設定を変えたいときはそこをいじってください。構造は CreateFunction API + Tags です。

kayac/go-config による環境変数の展開記法が使えるので、可変にしたい部分に {{ must_env }} {{ env }} 記法を入れておくと、lambroll 実行時の環境変数を埋め込むことができます。stg と prod を別関数にする、などに使ってください。

{
  "Description": "hello function for {{ must_env `ENV` }}",
  "Environment": {
    "Variables": {
      "BAR": "baz",
      "FOO": "{{ env `FOO` `default for FOO` }}"
    }
  },
  "FunctionName": "{{ must_env `ENV` }}-hello",
  "Handler": "index.js",
  "MemorySize": 128,
  "Role": "arn:aws:iam::123456789012:role/hello_lambda_function",
  "Runtime": "nodejs10.x",
  "Tags": {
    "Env": "{{ must_env `ENV` }}"
  },
  "Timeout": 5
}

実際どうなの

一昨日から社内で dog fooding していて、いままで Apex や SAM でデプロイされていたものをどんどこ移行していますが、なにしろものが単純なので普通に動いてます (動かない理由がない)。

Apex の後継とはおこがましいので言いませんが、こういうシンプル (not easy) なやつをお好みなかたはどうぞご利用ください。

ecspresso で設定ファイル生成と CodeDeploy による Blue/Green デプロイに対応した

ecspresso は筆者が開発している Amazon ECS 用のデプロイツールです。

github.com

最近 AWS DevDay Tokyo 2019 の「第1回 AWS Fargate かんたんデプロイ選手権」でも紹介していただいて、気をよくして最近あれこれ機能追加をしていたのでそのご紹介です。

speakerdeck.com

分かりやすい導入記事をクラスメソッドさんの blog で書いていただきましたのでこちらもどうぞ。

dev.classmethod.jp

設定ファイル生成機能

ecspresso init で、既存の ECS サービスの情報を引っこ抜いて定義ファイルを生成し、そのまま ecspresso deploy できるようになりました。(v0.11)

README の Quick Start にもあるように、region, cluster, service, config を指定して (適切な credential がある状態で) 実行すると、以下の3ファイルをカレントディレクトリに生成します。

  • ecspresso の設定ファイル
  • ECS サービスの定義ファイル
  • サービスに設定されているタスク定義ファイル

特に動作しているサービスに影響はしないので、稼働中のサービスに対して実行しても問題ありません。

$ ecspresso init --region ap-northeast-1 --cluster default --service myservice --config config.yaml
2019/10/12 01:31:48 myservice/default save service definition to ecs-service-def.json
2019/10/12 01:31:48 myservice/default save task definition to ecs-task-def.json
2019/10/12 01:31:48 myservice/default save config to config.yaml

その後、そのまま deploy を行うと、新しいタスク定義を登録してサービスを更新し、デプロイが行われます。簡単!

$ ecspresso deploy --config config.yaml

実運用するには、タスク定義ファイルに {{ must_env "IMAGE_TAG" }} のような記法でデプロイごとに更新されるであろう部分に環境変数を展開するように記述しておき、実行時の環境変数設定でタスクを更新するのがお勧めです。

他のツールや、AWSコンソールからポチポチで作ってしまった ECS サービスやタスク定義を、簡単にコードによる管理に落とし込むことができます。もし ecspresso の使用をやめるとしても特に何も起こらないので、気軽にお試しいただけます。

CodeDeploy による Blue/Green デプロイに対応

これまで ECS の機能によるローリングデプロイのみに対応していましたが、Blue/Green デプロイを行いたいという要望があったので、AWS CodeDeploy によるデプロイにも対応しました。(v0.12)

{
  "deploymentController": {
    "type": "CODE_DEPLOY"
  },

サービス定義で deploymentController.type = "CODE_DEPLOY" となっている場合、ecspresso deploy を実行すると CodeDeploy に新しい Deployment を作成します。その後の進行は CodeDeploy 側で行われます。

$ ecspresso deploy --config config.yaml --rollback-events DEPLOYMENT_FAILURE
2019/10/15 22:47:07 myService/default Starting deploy
Service: myService
Cluster: default
TaskDefinition: myService:5
TaskSets:
   PRIMARY myService:5 desired:1 pending:0 running:1
Events:
2019/10/15 22:47:08 myService/default Creating a new task definition by ecs-task-def.json
2019/10/15 22:47:08 myService/default Registering a new task definition...
2019/10/15 22:47:08 myService/default Task definition is registered myService:6
2019/10/15 22:47:08 myService/default desired count: 1
2019/10/15 22:47:09 myService/default Deployment d-XXXXXXXXX is created on CodeDeploy
2019/10/15 22:47:09 myService/default https://ap-northeast-1.console.aws.amazon.com/codesuite/codedeploy/deployments/d-XXXXXXXXX?region=ap-northeast-1

ログはこんな感じになり、最後に CodeDeploy の AWS Console の URL が出力されています。端末で実行していて、かつ open コマンドが存在する場合には、open $url を実行することでブラウザの画面が開きます。

ecspresso create でサービスを新規作成する場合には、CodeDeploy のリソース (Application, DeploymentGroup) は特に作成はしないので、なんらかの方法で (コンソールポチでも aws cli でも Terraform でも CloudFormation でもお好みで) 各自作成してください。

ecspresso deploy 時にはサービスに対応する CodeDeploy Application / DeploymentGroup を自動的に見つけて、それに対して新規デプロイを発行します。


ということで、ecspresso の最近の新機能の紹介でした。ECS のデプロイ、タスク定義管理に悩んでいる方がいらっしゃいましたら、どうぞご利用ください。

WEB+DB Press vol.111 Perl Hackers Hub に「AWS X-Rayによる分散トレーシング」を寄稿しました

技術評論社 WEB+DB Press vol.111 の連載 Perl Hackers Hub に、「AWS X-Rayによる分散トレーシング……マイクロサービスのボトルネック,障害箇所の特定」という記事を書きました。

WEB+DB PRESS Vol.111

WEB+DB PRESS Vol.111

  • 作者: 松田明,y-yagi,佐藤建太,夜道,村田賢太,伊藤英明,見川孝太,新井剛,関陽介,海老原圭吾,長田洸明,大原壯太,藤原俊一郎,松本宏太,末永恭正,久保田祐史,牧大輔,笹田耕一,竹馬光太郎,池田拓司,はまちや2,竹原
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/06/24
  • メディア: 単行本
  • この商品を含むブログを見る

内容は1月にあった YAPC::Tokyo の発表をベースにしたものです。

sfujiwara.hatenablog.com

YAPC での発表をしたときの印象で、どうも分散トレーシングはそこまでメジャーじゃなさそうだぞ、ということで、Perl の連載枠ですが前半半分は分散トレーシングというものの概念、AWS X-Ray についての説明に割きました。なので、Perl 以外の言語をお使いのかたでも、分散トレーシングをなんとなく把握してみたいというかたにも参考になる内容になっていると思います。

後半は ISUCON 8 の Perl 実装を X-Ray で解析して可視化するという内容です。今年も ISUCON の季節がもうすぐやってきますね。ISUCON 9 の攻略のためにも是非参考にしていただければと思います。


さらに今号から1年間の予定で、勤務しているカヤックの SRE チーム持ち回りで「インフラ運用のアイデア&テクニック ── 小さなチームでマネージドサービスを活用」という連載が始まりました。

ここ数年のインフラ連載枠は、大規模!最先端!みたいな雰囲気が多かったと思うのですが、今年はそこまで先端でも大規模でもないけど少人数でクラウドを上手く活用していこう、レガシーなのもいい感じにマイグレーションしていこう、みたいな雰囲気になる計画です。

こちらも是非ごひいきにお願いします。


そのほかの記事も、Rails 6 (カヤックでは受託案件のサーバはRailsが多いので気になります)、Julia (データ解析との親和性が高そう)、見える化大作戦 (アナログも大事だなあと)、などなど、毎度ながら充実の記事ですので、是非お買い求めください。

Amazon ECS タスク定義のコンテナ依存関係を可視化する

Amazon ECS のタスク定義には、タスク内のコンテナの起動と終了の順序を制御する依存関係が定義できます。

docs.aws.amazon.com

アプリケーションのコンテナと、アプリケーションが依存しているミドルウェアのコンテナがあった場合、依存を定義すると

  • タスク開始時にミドルウェアが起動してからアプリケーションが起動する
  • タスク終了時にアプリケーションが停止してからミドルウェアが停止する

というような制御が可能です。

graphviz による可視化

しかし依存関係が複雑になると、JSON で文字列で書いただけでは実際にどう依存しているのかがよく分からないし、間違ってループを作ってしまうと起動できないので事前に確認したい、ということで可視化してみました。

f:id:sfujiwara:20190529144138p:plain

これはとあるアプリケーションタスクの依存関係を、task definition の JSON から graphviz 用の .dot ファイルを生成して画像にしたものです。

gist.github.com

taskdef の JSON を引数にして実行すると .dot を吐き出すので、それを dot コマンドで画像化します。

$ perl graph-depends-on.pl  app.json > app.dot
$ dot -Tpng app.dot -o app.png

グラフの各ノードがコンテナで、定義された dependsOn に従ってノードが接続されています。

内部的にそれぞれ必要そうな依存を全て記述するとこうなったのですが、しかしこれだと ECS task が起動しないということが判明。具体的には xray コンテナが起動したあと、他のコンテナが PENDING のまま起動できない状態になりました。

各コンテナから依存先に到達する経路が一意でないのがよくないのかと思いついて、依存を整理したのが次の画像です。

f:id:sfujiwara:20190529144154p:plain

これは無事に起動できました。

まとめ

  • Amazon ECS のコンテナ依存関係は依存を一意にしないと起動できないことがある
  • 依存を可視化できると便利
  • graphviz で可視化するスクリプトPerlRuby で書いたのでご利用ください

Stretcher をご利用中のかたに大切なお知らせ (S3 Signature V2廃止の件)

拙作の pull 型デプロイツール Stretcher をご利用中のかたに大切なお知らせです。

Amazon S3のAWS署名バーション2が、2019年6月24日をもって廃止されます

dev.classmethod.jp

Stretcher v0.x は Amazon S3 からのファイル取得に AdRoll/goamz を利用しており、このライブラリは Signature V2 を使用しているため、2019年6月24日以降 S3 からのファイル取得に失敗するようになります。

現状でも Signature V4のみサポートしているリージョン (us-east-2など) に対して実行すると、以下のようなエラーメッセージで失敗することが確認されています。

Load manifest failed: cannot detect valid credentials or region

ということで、この機に公式 SDK aws-sdk-go に実装を切り替えた v1.0.0 をリリースします。

現在 rc1 を提供していますので、ご利用中のかたは検証の上バージョンアップをお願いします。

(2019-03-17 追記) v1.0.0 をリリースしました。 github.com

基本的に互換性は保っていますが、credential の読みこみを自前のコードから SDK に変更した関係で一部非互換がある可能性があります。 現時点で確認されている非互換としては以下のものがあります。

  • $HOME が設定されていない状態で ~/.aws/credentials の認証情報を読み取れない
    • ホームディレクトリが解決できないため
    • 実行時に適切な $HOME を設定すれば解消します
    • 古い版では mitchellh/go-homedir で自前で解決していたので公式 SDK になって挙動が変わりました

他に何か問題がありましたら https://github.com/fujiwara/stretcher/ まで issue か、Twitter で @fujiwara までお知らせください。

github.com 報告していただいた varusan さん、ありがとうございました!