tail my trail

作るのも使うのも、結局は、人なのだ

Apex エントリー & 細かい TIPs

f:id:uorat:20170731120316p:plain

apex.run

今までも AWS Lambda は採用していましたが、アプリケーションエンジニアに必要な Policy を付与して、あとは各自よしなにで久しく運用していましたが、自分も本格的に使うようになり、デプロイや運用の効率を考え、今更ながら Apex を使い始めました。

kakakakakku さんの Apex エントリー に触発されたのが大きかった kakakakakku.hatenablog.com

使い始めると痛感するのですが、ホント Apex いいですよね。重すぎずライトな作りで柔軟性も高く、痒いところの大抵の場所に手が届く作りなので、 Serverless Framework を使うまでもないような軽量な functions の開発にはもってこいのフレームだと思います。

ハマるところ

上述の通り、軽量なスクリプティングAWS Lambda 上で行うような時にハマると思います。 例えば、

  • minutely hourly, daily で動かす軽量なスケジューラー
    • curlAPI 叩く程度のものなど
  • ちょっとした運用スクリプト
    • AMI や EBS snapshot 取得するようなバックアップ
    • 開発, 検証環境の up/down
    • EC2 や RDS のメンテナンスウィンドウの検知

軽量な処理だから LL で書いて Lambda Function で実行したいけど、Lambda Console でコピペ運用なんてしたくないし、コーディングからデプロイまで通貫して開発したいし、環境分離したいし、というようなニーズに応えてくれます。というか、基本的にこのレールに載っとくと余計なことを気にしたり実装せずにコアロジックに専念して楽に運用まで持っていけると思います。

代表的な特徴をあげると、

  • AWS Lambda がサポートしていない言語 (Golang) をサポート
  • Build や Build など各アクションに対する command hook をサポート
  • Project 単位, Function 単位で変数や設定値の管理, 分離が可能, Override も可能
  • Binary や Thirdparty Library の梱包が簡単
  • Dry-run 実行や Function の rollback, ログの参照など運用に必要なひととおりの機能あり

Golang サポートもうれしいですが、その他にあげたような痒いとことに手が届く機能は、素の AWS Lambda を使っていると辛みを感じるところなのでとても嬉しい限りです。

Install 〜 Hello World

Apex.run に丁寧に記載されているので、基本的にこれに準じれば特に困ることなく Hello World レベルは始められると思います。Completion (入力補完) も設定しておきましょう。

以下 Mac OS X w/ Homebrew の例です。

$ brew install bash_completion
$ vim /usr/local/etc/bash_completion.d/apex
$ cat /usr/local/etc/bash_completion.d/apex
_apex()  {
  COMPREPLY=()
  local cur="${COMP_WORDS[COMP_CWORD]}"
  local opts="$(apex autocomplete -- ${COMP_WORDS[@]:1})"
  COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
  return 0
}

complete -F _apex apex

$ exec $SHELL -l
$ apex
Apex version 0.15.0
$ apex <TAB>
--chdir      --iamrole    --region     delete       exec         init         logs         upgrade
--dry-run    --log-level  alias        deploy       help         invoke       metrics      version
--env        --profile    build        docs         infra        list         rollback

AWS CLI 使っている人であれば、大抵は $HOME/.aws/_config & credential を設定していると思いますので、その PROFILE を使えば apex init ですぐに始められると思います。 default 入れてない場合は export AWS_PROFILE=${YOUR_PROFILE_NAME}環境変数セットするか、 apex コマンドに --profile (-p) でプロファイル名渡してあげればいつもどおり実行できます。 あとは apex init を実行すると対話モードで Project 名など淡々と入力させられ、AWS上に Lambda 用の IAM Role & Policy と、手元にプロジェクトの雛形ディレクトリが出来上がります。

あとは、ドキュメント見てお作法に習いながら開発を進めていけばOKです。

Example

以下、乱雑なサンプルコードです。

指定した Region の AWSPublic CIDRs を取得するfunction 実装例 & 実行例です。

uorat/hello-apex

echo '{ "region": "ap-northeast-2"}' | apex invoke helloworld-python | jq .
[
  "13.124.0.0/16",
  "13.125.0.0/16",
  "52.78.0.0/16",
  "52.79.0.0/16",
  "52.92.0.0/20",
  "52.94.6.0/24",
  "52.94.198.64/28",
  "52.94.248.176/28",
  "52.95.111.0/24",
  "52.95.192.0/20",
  "52.95.252.0/24",
  "52.219.56.0/22",
  "52.219.60.0/23",
  "54.239.0.192/28",
  "54.239.116.0/22",
  "54.239.120.0/21",
  "52.92.0.0/20",
  "52.219.56.0/22",
  "52.219.60.0/23",
  "13.124.0.0/16",
  "13.125.0.0/16",
  "52.78.0.0/16",
  "52.79.0.0/16",
  "52.95.252.0/24",
  "52.78.247.128/26"
]

かなり柔軟ですが、幾つかの決まり事を以下列挙しときます。

Project (project.json)

その名の通りプロジェクトで、この中に Function を包含できます。プロジェクトの設定値は project.json で設定することが出来ます。このプロジェクトで管理される Function に適用される設定値となります。 代表的な設定は以下です。

  • name: プロジェクト名
  • description: 説明
  • runtime: 実行エンジン。AWS Lambda でネイティブサポートされているものも含めて以下を設定可能
    • java
    • python2.7
    • python3.6
    • nodejs4.3
    • nodejs4.3-edge (Lambda @Edge)
    • nodejs6.10
    • golang (any version)
    • clojure (any version)
    • rust-musl[^rust-runtime][^rust-linux-only] (any version)
    • rust-gnu[^rust-runtime][^rust-linux-only] (any version)
  • memory: メモリ
  • timeout: タイムアウト
  • role: Lambda Function に当てる IAM Role
  • profile: apex 実行時に使用する AWS Profile ($HOME/.aws/)
  • defaultEnvironment: このプロジェクトで使用するデフォルトの環境名 (※後述します)
  • environment: Function 実行時に渡す環境変数
  • nameTemplate: Apex で Lambda Function を作成する場合の Function 命名ルール. デフォルトは {{.Project.Name}}_{{.Function.Name}}

Function (function.json)

Lambda Function ごとに持たせたい設定値を記載します。project.json の設定値を override できます。 例えば、 Function の処理内容によって memory や timeout 値, 環境変数を変えるような使われ方が多いと思います。

Function 固有の設定値は handle , すなわち invoke (呼び出し ≒ 実行) 時の Event Handler 名です。 Apex で標準で使われる handler name は言語ごとに以下が標準値なので、変えたい場合はここに定義して変えましょう。

  • nodejs: index.handle (index.js file with handle exported function)
  • python: handle
  • java: lambda.Main::handler

知っておくと良いこと

幾つか TIPs を載せておきます。

Switch Role (AssumeRole) して apex 実行する方法

IAM Role を切り替えて運用する人は多いと思います。 特に、幾つも AWS アカウントを運用している人は、入り口の Credential だけ発行して、他のアカウントや Role に Switch して運用するのがデファクトだと思います。 このブログでも AWS CLI で Switch Role する方法を紹介したことがあります。

結論、Apex でも Switch できます。ただ、手法が AWS CLI のそれとは異なるので、以下で紹介しておきます。

AWS CLI の場合は、 $HOME/.aws/configrole_arn で切り替え先の IAM Role の ARN を入れた Profile を入れておけば良いのですが、

AWS CLI のprofileを簡単に切り替える (SwitchRole編) - tail my trail

残念ながら Apex は この role_arn は対応していないようで、 NoCredentialProviders error が出てしまいます。

$ echo '{ "region": "ap-northeast-2"}' | apex invoke helloworld-python | jq .
   ⨯ Error: function response: NoCredentialProviders: no valid providers in chain. Deprecated.
        For verbose messaging see aws.Config.CredentialsChainVerboseErrors

どうしたものかとApex のソースを眺めてみると、 AssumeRole や STS のロジックがあるわけですね。

github.com

ドキュメント Apex.run #via-iam-role にもサラッと書いてありました。

Via IAM Role

Using an IAM role can be achieved in two ways, via the AWS_ROLE environment variable or via a command line flag –iamrole. As with other Apex credential loading, the command line flag will supersede the environment variable.

Switch 先の IAM Role ARN を –iamrole で渡してあげれば良いです。つまり、これだけ。

$ apex -p uorat --iamrole arn:aws:iam::987654321098:role/lambda-maintener deploy

IAM Role ARN を入力するのが面倒であれば、環境変数 AWS_ROLE export してしまうだけで良いようです。

環境の分離方法

同じコードベースで 本番, 検証, 開発 と各環境で設定値を切り替えたい場合は、 Apex の environment 機構を使うと良いです。

.
├── README.mkd
├── functions
│   ├── function1
│   │   ├── function.dev.json
│   │   ├── function.prod.json
│   │   ├── function.stage.json
│   │   ├── main.py
│   │   └── requirements.txt
│   └── function2
│        ├── function.dev.json
│        ├── function.prod.json
│        ├── function.stage.json
│        ├── main.py
│        └── requirements.txt
├── project.dev.json
├── project.prod.json
└── project.stage.json

このようにすれば 環境毎に Lambda Function にあてる IAM Role を切り替えたり出来ます。 AWS アカウントを環境毎に分離していればこれだけで十分ですが、もし同一AWSアカウントで各環境を稼働させている場合は、これだけだと AWS Lambda 上の Function name が被ります。

環境毎に function name を変えたい場合は、 project_${ENV_NAME}.jsonnameTemplate をカスタマイズしてあげれば良いです。 デフォルトが {{.Project.Name}}_{{.Function.Name}} なので、これを例えば以下のように変えてあげれば、複数環境の Apex project を同一AWS アカウントで運用できます。

まとめ

Apex が日本で流行りはじめて1年以上経っており、とても今更感のある記事ですが、あらためて Apex 良いなと思ったので、ざっくりエントリー記事にしておきました。 軽量な Serverless scheduler 組もうとしている人はとりあえず Apex 使っておいて損はないと思います。

最後に、これ読んどけば間違いないかと。