GitHub Actionsでprivate repositoryのPythonライブラリを含むDockerイメージをビルドする

# 背景・目的

Github ActionsでPythonのイメージをビルドするとき、別のprivate repositoryにある社内用ライブラリを持ってくる方法についてまとめました。

既に同僚の @elyunim26 の「[GitHub Actionsでビルドするコンテナ内でGitHubのprivate repositoryをセキュアに参照する](https://qiita.com/elyunim26/items/02a4f14bdf99874d8c16)」という記事があったのですが、私はマシンユーザーではなく[デプロイキー](https://docs.github.com/ja/free-pro-team@latest/developers/overview/managing-deploy-keys)を利用していたため、安全にssh認証をする方法が必要でした。Build-time secretsを使うことでsshキーも安全にイメージのビルドに利用できます。




# 実装方法

簡単に説明すると、以下のような手順で実現できます。

* Github Actionsでsecretsからデプロイキー（秘密鍵）を読み込む
* DockerのBuildkitの機能を使ってセキュアに受け渡す
* 通常通り`pip install`を行う

## デプロイキーの設定

まず、ライブラリ側のリポジトリにデプロイキーを設定する必要があります。デプロイキーの設定方法は[Githubの公式ドキュメント](https://docs.github.com/ja/free-pro-team@latest/developers/overview/managing-deploy-keys)を参照してください。

## Github Actionsの設定

次のような要素に気をつける必要があります。トラブルシューティングには(Pythonとnode.jsという違いはありますが)[GitHub Actions で private repository の node module をインストールする](https://qiita.com/couzie/items/33d837d6f0dc049155cd)が役に立ちました。

* `GITHUB`で始まるsecretsは禁止されている
* secretsでは改行が使えないため、事前に改行を`\n`などに置換した形で登録して、`sed`で置換するなどの回避方法が必要

以下は設定例です。我々のチームではAWSを利用しているため、後半にECRにpushするコードも含まれています。

```yaml:github_actions_ecr.yml
      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ steps.extract_repository.outputs.repository }}
          IMAGE_TAG: ${{ github.sha }}
          DEPLOY_KEY_GITHUB: ${{ secrets.DEPLOY_KEY_GITHUB }}
        run: |
          echo ${DEPLOY_KEY_GITHUB} > .deploy_key
          sed -i -e "s#\\\\n#\n#g" .deploy_key
          chmod 600 .deploy_key
          DOCKER_BUILDKIT=1 docker build --secret id=ssh,src=.deploy_key \
            -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
            -t $ECR_REGISTRY/$ECR_REPOSITORY:latest \
            .
          rm .deploy_key
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
```


## Dockerfileの設定


我々のチームでは[poetry](https://github.com/python-poetry/poetry)を使うことが多いので`pyproject.toml`を利用していますが、`requirements.txt`を使う場合でも同様の設定でいけるはずです。

```dockerfile:Dockerfile
# syntax=docker/dockerfile:experimental
FROM python:3.7

# ssh-keyscanの実行のため、元にするイメージによってはopenssh-clientのインストールが必要
RUN apt-get install -y openssh-client git

COPY ./pyproject.toml /app/pyproject.toml
COPY ./src /app/src
WORKDIR /app

RUN mkdir -m 700 $HOME/.ssh
RUN ssh-keyscan -H github.com > $HOME/.ssh/known_hosts
RUN --mount=type=secret,id=ssh,dst=$HOME/.ssh/id_rsa \
  pip install --upgrade pip && \
  pip install . && rm pyproject.toml
```

`pyproject.toml`では次のように記述されています。これは[poetryの公式ドキュメント](https://python-poetry.org/docs/cli/#add)にもある通り、`poetry add`コマンドでリポジトリを指定することで作成されるものです。

```toml:pyproject.toml
[tool.poetry.dependencies]
{ライブラリ名} = {git = "ssh://git@github.com/{ユーザー名}/{ライブラリ名}.git", rev = "main"}
```


# 参考にした記事

* [GitHub Actionsでビルドするコンテナ内でGitHubのprivate repositoryをセキュアに参照する](https://qiita.com/elyunim26/items/02a4f14bdf99874d8c16)
* [Docker の BuildKit を使ってセキュアなビルドを試す](https://qiita.com/takasp/items/56e1399a484ed5bfaade)
* [GitHub Actions で private repository の node module をインストールする](https://qiita.com/couzie/items/33d837d6f0dc049155cd)