[2章] GitHub ActionsでDockerイメージを、AWS ECRへPUSHする

takahiko tominaga
nextbeat-engineering
21 min readJul 12, 2021

目次

  1. はじめに
  2. 概要
  3. 処理の確認
  4. おわりに

1. はじめに

こんにちは、ネクストビートの富永です。
本ブログは前回の1章で紹介した構成を、実際に作成していく内容になります。

これから作成を行う構成の概要を知りたい方は、前回の1章を先に読むことをおすすめします。

今回は、タイトルにあるようにGitHub Actionsの処理の中で、DockerイメージをAWS ECRへPUSHさせるところまでを行います。

構成図

2. 概要

以下の項目を行っていきます。

  • 事前準備
  • SBT Native Packagerの設定
  • sbt-ecrの設定
  • sbt-releaseの設定
  • GitHub Actionsの設定

2.1 事前準備

今回は、Play Framework(Scala)で作成したアプリを使用します。

以下コマンドを実行して、アプリの初期化を行っておきましょう。

$ sbt new playframework/play-scala-seed.g8

今回は、masterブランチにPUSHを行った時に、処理を行うように設定します。
なので、masterとは別にブランチを作成して、設定は作成したブランチで行います。
※筆者は、developブランチを作成して実装を進めます。

AWS ECRの作成

今回の実装で、DockerイメージをPUSHするECRを作成します。
※今回作成するECRは、テスト用です。最終的にはCDK for Terraformで作成したものを使用します。

まずは、AWSのECRの設定を行うページへいきます。
次に、「リポジトリを作成」を選択します。

リポジトリの種類は、プライベートを選択します。

そして、リポジトリ名を入力し「リポジトリを作成」を選択します。
※イメージスキャンの設定や暗号化設定は今回行いません。

以下のように作成されていれば、ECRの設定は完了です。

GitHubにAWS用の環境変数を作成

GitHub ActionsでAWS APIを使用できるように、アクセスキーとシークレットアクセスキーを環境変数に登録します。

アクセスキーとシークレットアクセスキーの取得方法に関しては、AWSのIAM ユーザーのアクセスキーの管理を参照してください。
※ECRへのアクセス許可のあるIAMユーザーを使用してください。

アプリのGitHubへいき、上タブの「Settings」を選択し、左側のタブから「Secrets」を選択します。

そして、「New repository secret」を選択しアクセスキーとシークレットアクセスキーの登録を行います。

アクセスキーの環境変数は、以下のように「AWS_ACCESS_KEY_ID」とします。
valueにアクセスキーを入力し、「Add secret」で作成します。

シークレットアクセスキーの環境変数は、以下のように「AWS_SECRET_ACCESS_KEY」とします。
先ほどと同じように、valueにシークレットアクセスキーを入力し、「Add secret」で作成します。

両方作成できると以下のように表示されているはずです。

これでAWS用の環境変数の設定は完了です。

2.2 SBT Native Packagerの設定

今回使用するバージョンは、以下です。

sbt-native-packager: v1.7.6

今回使用するSBT Native Packagerはsbtのプラグインであり、プロジェクトをDockerイメージとしてビルドできるようにしてくれるものです。
※実際には、Dockerイメージ以外もできます。

筆者は、これ以外の方法でプロジェクトをDockerイメージ化したことはありませんが、本来はalpineなりdebianなりのベースイメージにjdkとsbtをインストールしたりして生成を行うようです。

先ほど作成したアプリに、SBT Native Packagerの設定を追記していきます。

記述ファイル: アプリ名/project/plugins.sbt

plugins.sbtに以下設定を追記します。

addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.7.6")

プラグインの追加はできましたので、次はbuild.sbtに設定を追記していきます。Docker用の設定は、Keys.scalaで確認できます

まずは、SBT Native PackagerでのビルドをDockerイメージとして出力するために以下を追記します。

enablePlugins(DockerPlugin)

次は、Dockerイメージのために以下を設定します

maintainer in Docker := "Dockerアクセス用のメールアドレス"

SBT Native Packagerのデフォルトベースイメージの設定です。
デフォルトでは、openJdkですが今回はAmazon Correttoを使用します。

dockerBaseImage := "amazoncorretto:8"

Dockerで使用するポート番号を設定します。
Play Frameworkのデフォルトポートが9000なので、同じにしてみました。

dockerExposedPorts in Docker := Seq(9000, 9000)

SBT Native Packagerは、UIDが1001に設定されたdemiourgos728というデーモンユーザーをデフォルトで作成し、USER 1001をエミットします。
ただ今回設定したデフォルトベースイメージのamazoncorrettoは、amazonlinuxをベースにしており、amazonlinuxではaddgroupやadduserが使えません。
なので、代わりにUSERデーモンをエミットするように以下を追記します。

daemonUser in Docker := "daemon"

追記完了後は、以下のようになっています。

enablePlugins(DockerPlugin)
maintainer in Docker := "メールアドレス"
dockerBaseImage := "amazoncorretto:8"
dockerExposedPorts in Docker := Seq(9000, 9000)
daemonUser in Docker := "daemon"

これで、SBT Native Packagerの設定は完了です。
ちゃんとDockerイメージとしてビルドできるか気になる方は、以下コマンドを実行してみてください。

$ sbt docker:publishLocal

2.3 sbt-ecrの設定

今回使用するバージョンは、以下です。

sbt-ecr: v0.15.0

sbt-ecrも先ほどのSBT Native Packagerと同じsbtのプラグインです。
sbt-ecrは、AWS ECRへDockerイメージをPUSHすることができるプラグインです。

先ほどと同じようにplugins.sbtに、以下を追記してアプリにプラグインを追加します。

addSbtPlugin("com.mintbeans" % "sbt-ecr" % "0.15.0")

次は、sbt-ecrのためにbuild.sbtに設定を追記していきます。

まずは、以下をインポートしてリージョンの情報を取得できるようにします。

import com.amazonaws.regions.{Region, Regions}

次に、sbt-ecrを使用できるように以下を追記します。

enablePlugins(EcrPlugin)

以下設定を追加し、どこのリージョンを使用するか指定します。
今回は、ap-northeast-1を使用します。
※事前準備で作成したECRと同じリージョンを指定してください。

region in Ecr := Region.getRegion(Regions.AP_NORTHEAST_1)

以下は、PUSH先のリポジトリの設定です。
事前準備で作成したECRのリポジトリ名を指定します。

repositoryName in Ecr := "AWS ECRリポジトリ名"

以下は、ECRへPUSHする際のバージョンを指定します。
今回は、任意のバージョンと固定のlatestを指定します。
※version.valueは、この後sbt-releaseで設定するversionの値です。

repositoryTags in Ecr := Seq(version.value, "latest")

以下で、Dockerイメージにタグを紐付けます。

localDockerImage in Ecr := (packageName in Docker).value + ":" + (version in Docker).value

追記完了後は、以下のようになっています。

import com.amazonaws.regions.{Region, Regions}

enablePlugins(EcrPlugin)

region in Ecr := Region.getRegion(Regions.AP_NORTHEAST_1)
repositoryName in Ecr := "AWS ECRリポジトリ名"
repositoryTags in Ecr := Seq(version.value, "latest")
localDockerImage in Ecr := (packageName in Docker).value + ":" + (version in Docker).value

これで、sbt-ecrの設定は完了です。

2.4 sbt-releaseの設定

今回使用するバージョンは、以下です。

sbt-release: v1.0.13

sbt-releaseもsbtのプラグインです。
sbt-releaseは、プロジェクトをリリースする際に登録している処理を自動で行ってくれるものです。
デフォルトの設定では、gitタグを生成し、リポジトリに公開、開発用にバージョンを上げてくれたり、テストをしたりといった処理が登録されています。

このsbt-releaseを利用して、Dockerイメージの生成、ECRへのPUSHを自動でやってもらいます。

それでは、他のプラグインと同じようにplugins.sbtに以下を追記して、アプリにプラグインを追加します。

addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13")

今回使用したPlay Frameworkの初期化方法では、build.sbtに以下のようなversionが記載されています。

version := "1.0-SNAPSHOT"

sbt-releaseの設定では、上記のversionは使用せずversion.sbtというファイルを作成し、このファイルでversionの管理を行っていきます。

build.sbtに記載されている上記versionは削除します。

次にversion.sbtというファイルをbuild.sbtと同じ階層に作成します。

アプリ名
∟build.sbt
∟version.sbt

作成したversion.sbtのファイルに以下のようにversion情報を追記します。

version in ThisBuild := "1.0.0-SNAPSHOT"

これで、sbt-releaseのversionに関する設定は完了です。

次は、リリース時に行う処理の設定を行っていきます。

まずは、build.sbtに以下を追記します。

これで、sbt-releaseに定義されているメソッド達を使用可能になります。

import ReleaseTransformations._

次に、以下を追記します。

releaseVersionBump := sbtrelease.Version.Bump.Bugfix

これは、versionをあげる際にどのレベルでバージョンをあげるかの設定です。
versionのレベルに関しては、READMEのConvenient versioningに記載されています。
今回は、バグフィクスのversionをあげる設定にしています。
例: 1.0.0 -> 1.0.1

次は以下を追記します。
このSeq[ReleaseStep]()の中に行いたい処理を順番に追記していきます。

releaseProcess := Seq[ReleaseStep](
)

デフォルトの設定は、以下のようになっています。
デフォルトの設定に関しては、READMEのRelease Processを確認してください。

releaseProcess := Seq[ReleaseStep](
checkSnapshotDependencies,
inquireVersions,
runClean,
runTest,
setReleaseVersion,
commitReleaseVersion,
tagRelease,
publishArtifacts,
setNextVersion,
commitNextVersion,
pushChanges
)

今回は、行いたい処理のみを追記していきます。
まずは、ECRへのログインを行う処理を追記します。

releaseProcess := Seq[ReleaseStep](
ReleaseStep(state => Project.extract(state).runTask(login in Ecr, state)._1),
)

このReleaseStepは、独自に行いたい処理をカスタマイズして作成できるものです。

こちらも詳細な説明はREADMEのCustomizing the release processに記載されています。

次は、以下を追記します。

releaseProcess := Seq[ReleaseStep](
...
inquireVersions,
runClean,
setReleaseVersion,
)

inquireVersionsでリリースバージョンと次の開発バージョンの確認を行い、クロスビルド用にクリーンを行い、version.sbtに記載されているバージョンをこのビルドに反映させます。

次は、先ほどと同じように独自のリリース項目を追記します。
以下の処理は、SBT Native Packagerを使用したDockerイメージの生成を行います。

releaseProcess := Seq[ReleaseStep](
...
ReleaseStep(state => Project.extract(state).runTask(publishLocal in Docker, state)._1),
)

sbt-releaseの処理の中で、dockerイメージを生成する sbt docker:publishLocalを行っていると考えてください。

そして、次は以下を追記して生成したDockerイメージをECRへPUSHします。

releaseProcess := Seq[ReleaseStep](
...
ReleaseStep(state => Project.extract(state).runTask(push in Ecr, state)._1),
)

こちらもsbt-releaseの処理の中で、sbt ecr:pushコマンドを実行していると考えてください。

最後に以下を追記します。

releaseProcess := Seq[ReleaseStep](
...
commitReleaseVersion,
tagRelease,
setNextVersion,
commitNextVersion,
pushChanges
)

上から順番に、version.sbtのversionでtagのコミット・リリースを行い、version.sbtに次のバージョンをセット・コミット、プッシュして反映を行います。

上記全てを追記したら以下のようになっているはずです。

releaseProcess := Seq[ReleaseStep](
ReleaseStep(state => Project.extract(state).runTask(login in Ecr, state)._1),
inquireVersions,
runClean,
setReleaseVersion,
ReleaseStep(state => Project.extract(state).runTask(publishLocal in Docker, state)._1),
ReleaseStep(state => Project.extract(state).runTask(push in Ecr, state)._1),
commitReleaseVersion,
tagRelease,
setNextVersion,
commitNextVersion,
pushChanges
)

上記と同じになっていれば、sbt-releaseの設定は完了です。

2.5 GitHub Actionsの設定

次は、これまで設定してきた実装をGitHub Actionsで使用できるように設定を行っていきます。

まず、作成したアプリのGitHubにいきます。
以下画像のように、「Actions」のタブを選択するとworkflow templateの一覧が表示されています。
今回Scalaでアプリを作成しているので、Scala用のテンプレートを使用します。
「Set up this workflow」を選択して、設定を行います。

設定画面では、以下のようにデフォルトの設定が記載されているはずです。

まずは、ファイル名を「scala.yml」から「deploy.yml」に変更します。
※名前はなんでも大丈夫です。

次は以下のように、GitHub ActionsをmasterブランチにPUSHした時のみ、起動するように設定します。

on:
push:
branches: master

次は、GitHub ActionsでAWSへアクセスできるように設定を追記します。
secretsで、事前準備で登録しておいた環境変数を使用します。

- name: Configure AWS Credentials
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

以下Javaの設定に関しては、何も変更を行いません。

- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8

最後にsbt-releaseの設定を追記します。

sbt-releaseコマンドを実行するために、ユーザーの設定とGitHubトークンの設定を追記します。

- name: Run sbt release
env:
GITHUB_USER: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --global user.email "メールアドレス"
git config --global user.name ${{ github.actor }}
sbt "release with-defaults"

※GITHUB_TOKENもsecretsを使用していますが、これはGitHub 側で提供されている Actions 用 bot アカウントの認証トークンです。このトークンを利用することで GitHub を API で操作することができるようになります。

actions/checkout@v2では、nameとemailを設定する必要があるので、以下の様にgit configで設定します。

git config --global user.email "メールアドレス"
git config --global user.name ${{ github.actor }}

全ての設定が終わると下記のようになっているはずです。

コミットを行い設定を完了させましょう。

3. 処理の確認

本ブログで行う設定は、全て完了しました。
最後に、masterブランチにPUSHを行い、DockerイメージをECRへPUSHできるかを確認していきます。

今まで設定を行ってきたブランチで、masterブランチに向けてプルリクエストを作成します。
設定に漏れがないかを確認して、「Merge pull request」でマージします。

マージ後に、上タブ「Actions」を選択しGitHub Actionsのページへ行くと以下のようにGitHub Actionsの処理が動いているのを確認できるはずです。

この処理が正常に終了したら、事前準備で作成したECRを確認しにいきましょう。

リポジトリを選択し、以下のようにイメージタグが生成されていれば成功です。

4. おわりに

最後まで読んでいただき、ありがとうございます。

今回はいろいろなプラグインを使用して、一連の処理作成を行いました。
個人的にsbt-releaseがとても便利だなと感じました。
なぜなら筆者は最初sbt-releaseの存在を知らず、GitHub Actionsの中でタグの生成、取得、Dockerイメージへの紐付けを自力でやろとしていました。
色々試行錯誤して最後は、物理的に不可能じゃねという結論に至りましたが…
その後に、このsbt-releaseというものを知って軽く感動を覚えました。

ゆくゆくは、こんな便利なプラグインを作ってみたいものです。

今回の処理で、DockerイメージをECRへPUSHすることができましたので、次はこのPUSHを検知して、ECRの更新内容をSlackへ通知する設定を行っていきます!

それでは、次の3章でお会いしましょう。

参考文献

ScalaのPlayアプリケーションをsbt-native-packagerの力を借りてDockerで動かす
sbt-native-packager のベースイメージに amazoncorretto を指定して脆弱性を解決する
sbt-native-packager Docker Plugin
GitHub Actions を用いて bot から Pull-Request にコメントをする方法
ワークフローで認証する

We are hiring!

株式会社ネクストビートでは

「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」
を理念に掲げ一緒に働く仲間を募集しております。

https://www.nextbeat.co.jp/recruit

--

--