AWS SDKを使ったアプリケーションを効率よく開発する方法
前田です。
先日、AWS認定デベロッパー試験に無事に合格することができました。
得点が69%で結構ギリギリな点数だったと思いますが、なんとか合格出来てよかったです。
今日は、AWS SDKを使ったアプリケーションを開発する時に自分なりに行っている効率がよいと思っている方法をまとめたいと思います。
例として、下記ケースを題材に開発してみたいと思います。
題材
先日、「AWS Batchで非同期ジョブシステムが簡単に構築出来た話」という記事を書きました。
その時に下記のようなシステムを作った、という話をさせて頂いたのですが、改善したい箇所が1点ありました。
それは②のGoで作ったAPIからAWS Batchにジョブをサブミットする箇所です。
AWS Batchにジョブをサブミットするのは下記のようなコマンドでサブミットできます。
aws batch submit-job
--job-name submit_job_test
--job-queue arn:aws:batch:ap-northeast-1:99999999999999:job-queue/test-job-queue
--job-definition arn:aws:batch:ap-northeast-1:9999999999999:job-definition/test-job-definition:1
--parameters Hoge=test!
Goアプリケーション側でGoのAWS SDKを使用して上記のような処理を実装しています。
サブミットする時にジョブキューやジョブディフィニジョンのARNを指定しているのですが、ジョブディフィニジョンを更新した時はジョブディフィニジョンのARNが変わるのでGoアプリケーションのジョブディフィニジョンのARNを変えなければいけません。
Goのアプリケーション内ではジョブディフィニジョンは定義してある環境変数から読み込むようにしておりますので、ソースコードではなくジョブディフィ二ジョンの環境変数を変更すればOKなのですが、AWS Batch側のジョブディフィニジョンを更新した時に、Goアプリケーション側のジョブディフィ二ジョンARNの環境変数を変え忘れる、という危惧があります。
ジョブディフィニジョンを更新した時にGo側の環境変数を更新するのを忘れなければいいので現在の設計でも問題はないかもしれませんが、Go側の変更漏れなどのミスを事前に防ぐ為に出来ればジョブディフィニジョンの名前だけで最新のARNを自動取得してAWS Batchにサブミットするようにしておきたいと考えていました。
まとめると、やりたいことは下記のとおりです。
- Goアプリケーションで、ジョブディフィニジョンの最新のARNをジョブディフィニジョン名で取得する
この題材を下記のステップで実装していきます。
- フィジビリティチェック
- SDKのリファレンスを見ながら実装
- 実行して検証
1. フィジビリティチェック
いきなりGoのアプリケーションをコーディングしていくのではなく、まずフィジビリティ(実行可能性)をチェックします。
- Goアプリケーションで、ジョブディフィニジョンの最新のARNをジョブディフィニジョン名で取得する
は、なんとなく出来そうだ、と思っていますが、AWS側が対応出来ていなくて実現出来ない可能性もあります。
なので実現可能かどうか、というのをまず最初にチェックしておくことが重要です。
ここで私はaws cli
を使います。
AWSのGoのSDKのリファレンスを見ながら実際にソースコードを書いてみてレスポンスを見たりして、、、という風にチェックしてもいいかもしれませんが、時間がかかるし面倒です。
フィジビリティチェックなので簡単に実現可能かどうかだけをチェックしたい、というのもあります。
また、aws cli
に用意されているAPIはSDKにもほとんど用意されています。(と思っています)
なのでaws cli
で実現可能ならほとんどのSDKでも実現可能だと思います。
まれにSDK側が追いついていないこともあるかと思いますが、基本的にはaws cli
にSDKが追従していっていると思っています。
また、もしSDK側の機能がまだ実装されていないとしても、最悪Goアプリケーションからaws cli
コマンドを直接実行することによって可能になるので、aws cli
で実現可能、ということが保証されればフィジビリティ的にはOKということになります。
できればGoアプリケーションからaws cli
コマンドを叩く、という実装はやりたくありませんが。。
ということで何はともあれフィジビリティをaws cli
でチェックしていきます。
AWS Batchのaws cli
のドキュメンテーションはこちらです。
http://docs.aws.amazon.com/cli/latest/reference/batch/index.html#cli-aws-batch
Available Commands
は下記のとおりですが、
- cancel-job
- create-compute-environment
- create-job-queue
- delete-compute-environment
- delete-job-queue
- deregister-job-definition
- describe-compute-environments
- describe-job-definitions
- describe-job-queues
- describe-jobs
- list-jobs
- register-job-definition
- submit-job
- terminate-job
- update-compute-environment
- update-job-queue
該当しそうなものはdescribe-job-definitions
と分かります。
describe-job-definitions
の詳細を見てみると、--job-definition-name
というオプションで絞り込むことも出来そうだし、なんとなく実現できそう、ということが分かりました。
AWS Batch上にジョブキューやジョブディフィニジョンを作成し、実際にaws cli
コマンドを叩いてみます。
ここで少し話は逸れますが、aws cli
コマンドを試す時に手軽に試せるようになるテクニックを紹介します。
開発者の皆様は複数のAWSアカウントをお持ちの方が多いかと思います。
私もプライベート・会社用など複数のアカウントを所持しています。
複数のアカウントを所持している場合、AWSアクセスキーとAWSシークレットキーを~/.aws/config
と~/.aws/credentials
で定義し、aws cli
コマンドに--profile
オプションを付加してアカウントを使い分けているかと思います。
私もそうなのですが、aws cli
コマンドを叩く時に毎回--profile
オプションを設定するのが面倒だな、と思っていました。
AWS_DEFAULT_PROFILE
の環境変数に設定すれば、そのセッション中は--profile
オプションを付加せずにaws
コマンドを実行できるので便利なのですが、頻繁にアカウントを変えて実行したい時はまだちょっと不便に感じます。
なので、peco
を使用し、下記alias設定をしておくことで簡単に切り替えられるようにしたら便利になりました。
alias p='export AWS_DEFAULT_PROFILE=$(grep -iE "^[]+[^*]" ~/.aws/credentials|tr -d []|peco)'
これで、p
と叩けばインタラクティブに実行したいアカウントに切り替えでき、そのあとは--profile
オプション無しにaws
コマンドを実行出来るようになります。
話が逸れましたが本題に戻ります。
describe-job-definitions
に--job-definition-name
オプションを付与して実行してみます。
$ aws batch describe-job-definitions --job-definition-name test-job-definition
JOBDEFINITIONS arn:aws:batch:ap-northeast-1:999999999999:job-definition/test-job-definition:2 test-job-definition 2 INACTIVE container
CONTAINERPROPERTIES busybox 2000 1
COMMAND echo
COMMAND 'hello world'
JOBDEFINITIONS arn:aws:batch:ap-northeast-1:999999999999:job-definition/test-job-definition:3 test-job-definition 3 ACTIVE container
CONTAINERPROPERTIES busybox 2000 1
COMMAND echo
COMMAND Ref::Hoge
JOBDEFINITIONS arn:aws:batch:ap-northeast-1:999999999999:job-definition/test-job-definition:1 test-job-definition 1 INACTIVE container
CONTAINERPROPERTIES busybox 2000 2
COMMAND echo
COMMAND 'hello world'
簡単に取得出来ました。
ちゃんとARNの情報も取得出来ているので大丈夫そうです。
ACTIVE
とINACTIVE
があるのでACTIVE
のものだけに絞り込めそうです。
$ aws batch describe-job-definitions --job-definition-name test-job-definition --status ACTIVE
JOBDEFINITIONS arn:aws:batch:ap-northeast-1:999999999999:job-definition/test-job-definition:3 test-job-definition 3 ACTIVE container
CONTAINERPROPERTIES busybox 2000 1
COMMAND echo
COMMAND Ref::Hoge
出来ました。
しかし、もしACTIVE
が複数ある場合は複数取得してしまいます。
また、ジョブディフィニジョンの作成日、などもレスポンスとして返ってこないため、作成が一番新しいもの、で取得することもできないようです。
が、ARNを見ると、arn:aws:batch:ap-northeast-1:999999999999:job-definition/test-job-definition:3
となっており、この最後の3
という数値が一番大きいものが最新のジョブディフィニジョンになるので、Goアプリケーションのほうでその判定をしてやればいけそうです。
Goのテストアプリケーションを実装して、ということだとちょっと面倒そうでしたが、aws cli
であれば簡単にフィジビリティを確認することが出来ました。
ということで次のステップにいきます。
2. SDKのリファレンスを見ながら実装
ここでSDKのリファレンスを見ます。
GoのAWS Batchのリファレンスはこちらです。
https://docs.aws.amazon.com/sdk-for-go/api/service/batch/
ずばりDescribeJobDefinitionsというメソッドがありました。
Exampleのコードを基にコーディングしました。
main.go
package main
import (
"errors"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/batch"
"os"
"strconv"
"strings"
)
func main() {
result, err := getJobDefinitionARNByName(os.Getenv("JOB_DEFINITION_NAME"))
if err != nil {
println(err.Error())
} else {
fmt.Println(result)
}
}
func getJobDefinitionARNByName(jobDefinitionName string) (string, error) {
svc := batch.New(session.New())
input := &batch.DescribeJobDefinitionsInput{
Status: aws.String("ACTIVE"),
JobDefinitionName: aws.String(jobDefinitionName),
}
result, err := svc.DescribeJobDefinitions(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case batch.ErrCodeClientException:
fmt.Println(batch.ErrCodeClientException, aerr.Error())
case batch.ErrCodeServerException:
fmt.Println(batch.ErrCodeServerException, aerr.Error())
default:
fmt.Println(aerr.Error())
}
} else {
fmt.Println(err.Error())
}
return "", err
}
ARN, err := getLatestARN(*result)
if err != nil {
return "", err
}
return ARN, nil
}
func getLatestARN(output batch.DescribeJobDefinitionsOutput) (string, error) {
if len(output.JobDefinitions) == 0 {
return "", errors.New("No defenitions")
}
comparition := 0
ARN := ""
for _, d := range output.JobDefinitions {
arn := strings.Split(*d.JobDefinitionArn, ":")
c := arn[len(arn)-1]
i, _ := strconv.Atoi(c)
if comparition < i {
comparition = i
ARN = *d.JobDefinitionArn
}
}
return ARN, nil
}
3. 実行して検証
下記環境変数をセットして実行します。
AWS_REGION
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
JOB_DEFINITION_NAME
期待通りに取得できました。
まとめ
結局、aws cli
でフィジビリティを確認する、というところがポイントで、もし無理なら実装しなくていいし、実装できるならaws cli
で呼び出した方法を参考に簡単にSDKで実装できる、ということでした。
実は今回はAWS Batch上に検証用のジョブキューとジョブディフィニジョンがすでにあったのでaws cli
コマンドですぐに確認できましたが、AWS側に環境がない場合は構築するのは面倒なのでリファレンスだけでフィジビリティを判定する場合もあるかと思います。
なのですべてのケースにおいて、この方法が最良かどうかは分かりませんが、aws cli
で実際に返ってきたレスポンスを見ながらフィジビリティチェックしておくと、その後の実装はかなり安心です。
以上、お役に立てれば幸いです。