GitHub Actionsとdockerを使用して、ECRのイメージに、安全にmaster.key を含める方法

# 概要
`Rails`の環境を`ECS`で作成する際、多くの開発者が`master.key`をコンテナーに環境変変数として設置すると思います。この方法でも問題ないですが、コンテナーの環境変数を使用したくないと言う要望もあるかと思い、表題の方法について手順を書きたいと思います。

# 前提条件
- `Docker`のバージョンが`18.09`以上である
- `Ruby on Rails`の環境がある
- `GitHub Actions`が導入されている
- `Docker`を使用して、`Ruby on Rails`の環境立ち上がっている

# GitHubの設定
- `Settings`を押下すると、サイドバーに`Secrets and variables`がある。その中に`Actions`がある為、そこに環境変数を入れる

![githubの画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664659/764f7d3d-e61a-459b-4397-413c7b77d5ae.png)


:::note warn
- `GitHub`の権限設定によっては、`settings`の管理画面が見れない可能性があります。その場合は、管理者に問い合わせて下さい
- 必要に応じて環境変数を作成して下さい
:::



# GitHub Actions
- `config`のディレクトリーに、`master.key`のファイルを作成する
- `master.key`のファイル内に、暗号化キーを書き込む
```.github./workflows/deploy.yml
- name: Create rails master key for build
  run: |
    touch $GITHUB_WORKSPACE/config/master.key
    echo ${{ secrets.RAILS_MASTER_KEY }} > $GITHUB_WORKSPACE/config/master.key

# $GITHUB_WORKSPACE: .githubが置いてある場所をルートディレクトリーと定義している
```
- `env`に　環境変数を設定する
- `Docker`のバージョンが`18.09`以降であれば、`BuildKit`と言う機能を使う事ができる
- `BuildKit`の機能を使用する事で、秘密情報（今回の場合、`master.key`）を`ECR`の`image`に安全に渡す事ができる
(`imgae`内に、`master.key`の情報が含まれない為、`image`から`master.key`の情報が漏洩しない)
```.github./workflows/deploy.yml
- name: Build, tag, and push image to Amazon ECR
  id: build-image
  env:
    DOCKER_BUILDKIT: 1
    ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
    REPOSITORY: "<ECRリポジトリ名>"
    IMAGE_TAG: ${{ github.sha }}-${{ github.run_id }}
  run: |
    docker buildx create --use
    docker buildx build \
      --secret id=master_key,src=$GITHUB_WORKSPACE/config/master.key \
      -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
      --push \
      -f containers/production/Dockerfile .
    echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

# -f containers/production/Dockerfile . ：各々のリポジトリーの構成に合わせて修正して下さい
```
:::note warn
REPOSITORYは環境変数に設定する事を推奨する
:::

## BuildKitの概要
 - `Docker`イメージのビルドや他のビルドプロセスを効率的に行う為のツール
 - 通常のビルドよりも並列処理やキャッシュの利用が優れており、ビルド時間を大幅に短縮できるのが特徴
 - `BuildKit`は、`buildkitd`(デーモン)と`buildctl`(クライアント)から構成されている

### 主な機能
- **キャッシュ効率の向上** : ビルドの各ステップを細かくキャッシュし、再ビルド時には変更された部分だけを再実行する。これにより、無駄なビルドを避け、時間を短縮出来る

- **並列処理** : 複数のビルドステップを並列に実行する事ができ、`CPU`リソースを効率的に活用出来る。これにより、ビルドの速度が向上する

- **`Dockerfile`を使った柔軟なビルド** : `Dockerfile`をビルドの定義として利用するが、独自の中間フォーマット(`LLB`)に変換してビルドを実行する。これにより、複雑な依存関係の管理が簡単になる

- **シークレットやSSHキーの安全な取り扱い** : ビルド時にシークレットファイル(例: `API`キー)や`SSH`キーを安全に取り扱う事が出来る。ビルドの際、外部に機密情報が漏れなくなる為、セキュリティが強化出来る


### 補足
- `buildkitd`(デーモン) : BuildKitのビルドエンジンとして機能している。リクエストを受け取って、ビルドを実行し、ビルドの各ステップを処理する
- `buildctl`(クライアント) : ユーザーがビルドリクエストを送信する為のインターフェース。ユーザーが`CLI`を使ってビルドを開始したり、オプションを設定したりする際に、この`buildctl`コマンドを使用する
- `LLB`(Low-Level Build) : ビルドの処理手順や依存関係を効率的に管理する為の中間フォーマット。通常、`Dockerfile`を使ってビルドを行う際、各コマンド（`FROM`, `RUN`, `COPY`など）はそのまま実行されるが、`BuildKit`はこれらの命令を一旦`LLB`形式に変換してからビルドを実行する

# Dockerfile
- `Dockerfile`でも`BuildKit`を使用して、`image`を作成する
```Dockerfile
# master.keyをコピー
RUN --mount=type=secret,id=master_key,target=master.key,required=true \
    rails assets:precompile RAILS_ENV=Production
```

# コマンドの叩き方
```shell
# Dockerfileのファイルパスを指定するコマンド
DOCKER_BUILDKIT=1 docker build --no-cache --secret id=master_key,src=path/to/master.key -t rilas_image -f build_files/porduction/Dockerfile .

# または
# Dockerfileのがカレントディレクトリにある場合のコマンド
DOCKER_BUILDKIT=1 docker build --no-cache --secret id=master_key,src=path/to/master.key -t rilas_image .
```

## コマンドの構造
- BuildKitを有効にする為の環境変数。このオプションを設定すると、`BuildKit`の機能が使用可能となる
```sh
DOCKER_BUILDKIT=1
```

- Dockerイメージをビルドする標準的なコマンド
```sh
docker build
```

- キャッシュを使わずに、常に新しいビルドを行うオプション。`BuildKit`はキャッシュ機能が強力ですが、キャッシュを使いたくない場合に下記を指定する
```sh
--no-cache
```

- ビルド中に シークレット情報(ここでは `master_key`)を安全に使う為の仕組み。`path/to/master.key`というファイルに保存された秘密情報（例: APIキーや暗号キー）を`Docker`ビルド中で参照するが、この情報は外部に漏れない様に保護された状態で処理される。シークレット情報は、ビルドが終わると自動的に削除され、外部には見えない様になる
- 要約 : ビルド中に一時的に鍵やパスワードを使いたい際、セキュリティを確保しながらそれらを渡す方法
```sh
--secret id=master_key,src=path/to/master.key

# 相対パスの書き方 : src=path/~/master.key
# 絶対パスの書き方 : src=/Users/test/path/~/master.key
#　先頭に/が入ると絶対パスでの指定になる
```

出力される`Docker`イメージにrails_imageという名前を付与する。名前は任意で決めてよい
```sh
-t rails_image
```

- 現在のディレクトリをビルドコンテキストとして指定する。現在のいるディレクトリからDockerfileを探す
```sh
.
```

- ビルドに使用するDockerfileの場所を指定する
```sh
-f build_files/porduction/Dockerfile .
```



# 参考資料
- [Railsのcredentials.yml.encとmaster keyをDockerで安全に扱う](https://techblog.lclco.com/entry/2021/07/27/110000)
- [docker buildkitを使いecsで本番モードでrailsを起動させる方法(master.keyの配送方法など)](https://zenn.dev/hiro1111/articles/6113a7e347237f)
- [[公式] docker buildx build](https://docs.docker.jp/engine/reference/commandline/buildx_build.html)
- [[公式] buildx を使ったビルド](https://matsuand.github.io/docs.docker.jp.onthefly/buildx/working-with-buildx/#build-with-buildx)
- [[公式] BuildKit](https://github.com/moby/buildkit)
- [Dockerに統合されたBuildKitのLLB (low-level builder)の仕様を探ってみよう](https://qiita.com/po3rin/items/4fa7686c271a20f89c66)

# まとめ
`GitHub Actions`の理解や`BuildKit`と言うDockerの新たな機能について知見が増えました。`BuildKit`に関して、セキュアなコンテナーを作成する以外にも様々使い道がありそうなので、いろいろい試して見たいと思います。