AWS LambdaでDockerHubの定期ビルドを設定したときのメモ
DockerHubに登録しているCommon Lisp実行環境eshamster/cl-baseと、それをベースとした開発環境eshamster/cl-devel2ですが、RoswellやQuicklispリポジトリがそれなりの頻度で更新されるので、latestに対しては定期的に更新をかけておきたかったです。そのために、AWS Lambdaをcron代わりに設定したときのメモです。
単なるメモなので過不足たくさんで、特にまとまってもいません。
想定読者
想定シチュエーション
- 大体の操作はWebコンソールで実施する
- 特に高度な使い方はしていないので、メモは極薄です
- 言語には取りあえずNode.jsを選択する(執筆時点で最新のNode.js 8.10)
- Node.jsのモジュールを含めたいので関数の実体は手元の開発機で作成する
- 開発機がリモートにあってWebコンソールではzipの移動が面倒なので、アップロードだけはawsコマンドで実施する
開発環境の用意(on Docker)
Node.jsの開発やAWSへのアップロードを行うための開発環境を作っておきます。ということで、まずDockerfileを用意します。
lessは最初から入っていますが、デフォルトではaws help
でエラーになってしまうので、アップデートしておきます。ついでに、デフォルトのviだけでは物寂しいのでvimを入れておきます。
FROM node:8.10.0-alpine RUN apk --update add py-pip && \ pip install awscli &&\ apk --update add less groff && \ apk --update add vim WORKDIR /root RUN mkdir /root/.aws COPY credentials config /root/.aws/ COPY .vimrc /root/
毎回 aws configure
するのも面倒なので、同コマンドで生成されるファイルを用意しておいて、docker build
時にコピーしてしまいます。後から思うに、この辺りは環境変数の設定でやる方が賢かった気がします。
$ cat config [default] output = json region = us-west-2
$ cat credentials [default] aws_access_key_id = XXXXXXXXXXXXX aws_secret_access_key = XXXXXXXXXXXXX
.vimrc
ですが、普段はEmacsを使っていて特にこだわりの設定もないので、タブの設定だけしておきます。好み8割、AWS LambdaのWebエディタのデフォルト設定に合わせて置きたい気持ちが2割です。
$ cat .vimrc set expandtab set tabstop=4 set shiftwidth=4
ここまでのものはcredentials
を.gitignore
した上でGitHubに上げました。
IAMの設定
- 適当にユーザを用意します
- (credentialsの設定をしているので済のはずですが)
- 適当にグループを用意して上記のユーザを所属させます
- グループにインラインポリシーを設定します
「AWS Lambda でアイデンティティベースのポリシー (IAM ポリシー) を使用する」あたりも見つつ、必要なAction
だけを登録していく…つもりだったのですが、面倒になってlamdba:*
としてAWS Lambda系の関数を全許可しています*2。
なお、アップロードしたファイルはS3上に置かれるようなので、ダウンロードしようと思うとS3系の権限もいるのかもしれません(今回は一方的なアップロードしかしていないので試していません)。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllLambdaFunctions", "Effect": "Allow", "Action": "lambda:*", "Resource": "*" } ] }
反映には数分かかるようなので他にすることがなければ待ちます。
# Dockerコンテナ上 $ while : ; do aws lambda list-functions ; if [ $? -eq 0 ]; then break ; fi ; sleep 10; done
関数を作成する
AWS Lambdaに関数を追加する
Webコンソール上で適当に作成してNode.js 8.10を選んでおきます。以上。
関数の実体を作成する
Dockerコンテナ上での作業です。Docker HubのビルドをトリガするためのNode.jsファイルを作成します。
準備として、フォルダを用意してcurl代わりのrequest
モジュールをインストールしておきます。
$ mkdir sample $ cd sample $ npm install request
Node.jsコードの前に、curlでlatestタグのビルドをトリガする凡例を示すと次のようになります。<image_name>
は、例えばeshamster/cl-base
で、<token>
はDocker HubのBuild Settingsのページで取得できます(また、同ページでcurlの例を見ることもできます)。
$ curl -H "Content-Type: application/json" --data '{"docker_tag": "latest"}' -X POST https://registry.hub.docker.com/u/<image_name>/trigger/<token>/
requestモジュールを使って、これをNode.js実装に置き換えます。TODOがあったりエラー処理がおざなりだったりしますが見なかったことにします。${event.*}
の部分が実行時に与えるパラメータです。docker_tag
の指定もパラメータ化した方が良い気もしますが、当面latest以外に適用する見込みもなかったので直に指定しました。
$ cat index.js exports.handler = (event, context) => { const request = require('request'); /* TODO: Check event.image_name and event.token */ let options = { url: `https://registry.hub.docker.com/u/${event.image_name}/trigger/${event.token}/`, method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ "docker_tag": "latest" }) }; let response = request(options, (err, res, body) => { console.log('ERR: ' + err); }); return response; };
アップロードする
zip化した上で、AWS Lambdaにアップロードします。
zip化ですが、解凍されたときにindex.js
がルートに来るように注意します。
$ cd ~/sample $ ls index.js node_modules package-lock.json $ zip -r ../sample.zip * ...
ここまで来れば、後はコマンド1発でアップロード完了です。
$ cd $ aws lambda update-function-code --zip-file fileb://sample.zip --function-name <function名>
helpが充実しているので、それらしいサブコマンドを aws lambda help
で見繕って、さらにそのサブコマンドのhelpを見る、という感じで使い方が分かるのは良いですね。
AWS Lambdaの設定
ここからはまたWebコンソール上での作業です。
タイムアウトの設定変更
DockerHubからレスポンスが返ってくるまで数秒かかるので、デフォルトのタイムアウト(3秒)では心許ないです。10秒にしておきます。
CloudWatchの設定
cron代わりにCloudWatch Eventsを設定します。
イベントソースはcron式のスケジュールを設定します(例. 0 15 ? * FRI *
← 日本時間の土曜0時)。rate式にしなかったのは、 近い時間にcl-base
→ cl-devel2
と実行したかったためです。
入力の設定は「定数 (JSON テキスト)」を選択し、先程のNode.jsソースの${event.*}
に対応する値を設定します。
{ "image_name": "eshamster/cl-base", "token": "xxxxxxxxxxxxxxx" }
以上で週1でDockerHub上のイメージの更新が走るようになりました(完)