こんにちは、追いかけているRPGのシリーズの最新作の発売日が迫っていて、日々そわそわしながら過ごしているエンジニアの回路(@qazx7412)です。
私はいろんな言語でLambdaを使ってslack botを作るのが趣味なのですが、このbot達のリポジトリにGitHub Actionsで自動デプロイを仕込んだので今回はその話をします。
この記事は、以前Tech Do Book #2で解説をしたCodePipelineとCodeBuildを利用した自動デプロイの解説の続編になります。
読んでいなくとも問題ないようにしていますが是非こちらもよろしくお願いいたします。
GitHub Actionsの使い方
ということでまずはGitHub Actionsの使い方を説明します。
一見難しそうですがGitHub Actionsを使うのは簡単です。
リポジトリ内の .github/workflows/
に下記のような振る舞いを定義したymlファイルを設置するだけです。
# .github/workflows/sample.yml name: workflow-name on: push: branches: [ master ] jobs: deploy: name: deploy runs-on: ubuntu-latest steps: - name: sample run run: echo "何でもできる" - name: sample uses uses: actions/setup-go@v2 with: go-version: ^1.13 id: go
上から見ていくと、まず on
でいつ実行するかを定義しています。
この例だとmasterブランチにpushが行われたとき実行します。
次に jobs
で何をするかを定義しています。
サンプルではコマンドを直接実行したり、公開されているアクションを呼び出したりしています。
(詳しい構文の詳細はこちらを参照してください。)
Serverless Frameworkを利用した自動デプロイ
ここから、Serverless Framework(以下serverless)を利用したLambdaの自動デプロイの仕込み方を紹介します。
他のデプロイツールを利用したい場合は、適宜読み替えてください。
今回も例のごとく言語はGoです。
# .github/workflows/go-sls.yml name: go-sls on: push: branches: [ master ] jobs: deploy: name: deploy runs-on: ubuntu-latest steps: - name: set up uses: actions/setup-go@v2 with: go-version: ^1.13 id: go - name: checkout uses: actions/checkout@v1 - name: build run: go build -ldflags="-s -w" -o bin/handler handler/main.go - name: configure aws uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: get env run: | aws s3 cp s3://path/to/env.yml . - name: deploy uses: serverless/github-action@master with: args: deploy --verbose --stage prod env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
内容を詳しく見ていきましょう。
ビルド
- name: set up uses: actions/setup-go@v2 with: go-version: ^1.13 id: go - name: checkout uses: actions/checkout@v1 - name: build run: go build -ldflags="-s -w" -o bin/handler handler/main.go
最初にGoをビルドします。
まず actions/setup-go
を利用してGoのビルドのための環境をセットアップします。
Goはセットアップ用のアクションが公式で用意されているので、楽にセットアップできます。
次に、 actions/checkout
を利用して、ビルドしたいブランチ(今回の場合はmaster)にチェックアウトすることで、ビルドをできるようにします。
準備が完了したら手動でのビルドと同じように go build
を実行してビルドを行います。
- name: checkout uses: actions/checkout@v1 - name: build id: sbt uses: lokkju/github-action-sbt@11-1.3.0-2.13.0 with: commands: assembly sbt_project_directory: ./
Goはセットアップをするためのオプションが用意されていましたが、他の言語の場合ビルドコマンドやパッケージマネージャーが呼び出せるアクションが用意されている場合もあります。
上記の例はScalaのパッケージマネージャーのsbtを利用するアクションです。
これで sbt assembly
と同等のコマンドを実行できます。
deploy: name: deploy runs-on: ubuntu-latest container: image: google/dart:latest
- name: checkout uses: actions/checkout@v1 - name: pub get run: pub get - name: build run: dart2native ./src/main.dart -o ./bootstrap
また、アクションが存在しない場合は、コンテナを利用してビルドを行うことができます。 上記はDartの場合の例なのですが、公式で用意されているコンテナを利用してビルドをしています。
デプロイ
- name: configure aws uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: get env run: | aws s3 cp s3://path/to/env.yml .
次にAWS CLIをセットアップして、S3からなにやら怪しげなymlを落として来ています。
まずAWS CLIですが、 aws-actions/configure-aws-credentials
という AWS CLIの設定を行うことができるアクションがあるのでこれを利用しています。
ここでアクションに ${{ secrets.HOGE }}
という変数らしきものを引き渡していますが、これは secrets
に入っているクレデンシャルをアクションへ引き渡しています。
これはリポジトリのページの Setting
-> Secrets
から設定ができます。
そして、 env.yml
って何?という話ですが、これはserverlessの設定を定義する serverless.yml
から環境変数の設定を切り出したものです。
serverlessでは下記のように記述することでymlを分割することができます。
# serverless.yml provider: name: aws runtime: go1.x stage: ${opt:stage, self:custom.defaultStage} region: ap-northeast-1 environment: ${file(./env.yml)}
私がserverlessを利用するときは、上記で環境変数に関する部分を切り出して、gitignoreするように運用しています。
なので今回の場合はS3に適当に切り出したymlを置いておいて、デプロイ時にダウンロードするようにしました。
- name: deploy uses: serverless/github-action@master with: args: deploy --verbose --stage prod env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
最後にデプロイです。
serverlessでは公式でコマンドを利用できるアクションが用意されているのでそれを利用します。
こういうものが用意されていると特に変な小細工とかをする必要がないので楽ですね。
- name: setup node.js uses: actions/setup-node@v1 - name: install sls run: npm i -g serverless - name: deploy run: sls deploy -s prod > ./result.txt - name: push result run: | aws s3 cp ./result.txt s3://path/to/result.txt
また、もし公式のアクションではできないような小細工をしたいなどで既存のアクションを使わない場合は上記のように直接インストールして実行することもできます。
この例では、serverlessの標準出力をテキストファイルに出すようにして、そのままS3へ送っています。
これはserverlessではデプロイされるLambda関数にweb APIが含まれているとURLを標準出力に出してしまい、それをGitHub Actionsの場合誰でも閲覧可能になってしまうのでこういった小細工をするようにしています。
あとがき
ということで簡単ではありましたがGitHub Actionsを利用したLambdaの自動デプロイの仕込み方を紹介しました。
今回はデプロイにフォーカスして紹介しましたが、他にもPRをトリガーにして自動テストを動かすとかいろいろ使いどころがありそうでいいですね。
パブリックリポジトリならば無料で使えるので個人開発などで気軽に試せるのもいいです。
弊社ではデプロイを自動化したいエンジニアを絶賛募集中ですので、興味がありましたらぜひこちらからお願いいたします。
参考
付録
自分用の備忘録を兼ねて各言語のymlの例を載せて置きますので参考にしてください。
Scala
name: scala-sls on: push: branches: [ master ] jobs: deploy: name: deploy runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v1 - name: build id: sbt uses: lokkju/github-action-sbt@11-1.3.0-2.13.0 with: commands: assembly sbt_project_directory: ./ - name: configure aws uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: get env run: | aws s3 cp s3://path/to/env.yml . - name: deploy uses: serverless/github-action@master with: args: deploy --verbose --stage prod env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Dart
name: dart-sls on: push: branches: [ master ] jobs: deploy: name: deploy runs-on: ubuntu-latest container: image: google/dart:latest steps: - name: checkout uses: actions/checkout@v1 - name: pub get run: pub get - name: build run: dart2native ./src/main.dart -o ./bootstrap - name: install aws run: apt-get -y update && apt-get -y install awscli - name: configure aws uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: get env run: | aws s3 cp s3://path/to/env.yml . - name: deploy uses: serverless/github-action@master with: args: deploy --verbose --stage prod env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Nim
name: nim-sls on: push: branches: [ master ] jobs: deploy: name: deploy runs-on: ubuntu-latest container: image: nimlang/nim:1.0.6 steps: - name: checkout uses: actions/checkout@v1 - name: build run: nimble build -d:ssl && mv main bootstrap && chmod +x bootstrap - name: install aws run: apt-get -y update && apt-get -y install awscli env: DEBIAN_FRONTEND: noninteractive - name: configure aws uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: get env run: | aws s3 cp s3://path/to/env.yml . - name: deploy uses: serverless/github-action@master with: args: deploy --verbose --stage prod env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Crystal
name: crystal-sls on: push: branches: [ master ] jobs: deploy: name: deploy runs-on: ubuntu-latest container: image: crystallang/crystal steps: - name: checkout uses: actions/checkout@v1 - name: shards install run: shards install - name: build run: shards build --static && mv bin/* . && chmod +x bootstrap - name: install aws run: apt-get -y update && apt-get -y install awscli - name: configure aws uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: get env run: | aws s3 cp s3://path/to/env.yml . - name: deploy uses: serverless/github-action@master with: args: deploy -s dev env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}