GitHub ActionsでDockerコンテナをPullしてテストを動作させる。

# 目的

- Dockerコンテナイメージを生成しghcr.ioへPushできるようになる。
- GitHub Actions内でghcr.ioからコンテナイメージをPullし、コンテナ内でテストができるようになる。
- ghcr.ioへPushしたコンテナイメージを編集することができるようになる。（サブクエ）

# 背景

GitHub ActionsのworkflowでDockerコンテナを利用したテストを導入する方法を知りたいと思っていた。
今回はやっていないが、制限された環境（メモリ制約など）における試験などは、コンテナを使って試験をするのが簡単なため、まずはDockerコンテナをActions内で利用するところから知識を蓄えたいと思った。

# ghcr.ioとは

Dockerコンテナイメージを保存、管理することができるコンテナレジストリです。
Docker公式にもDocker Hubがありますが、同様のコンテナレジストリです。

https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry

# サンプルコード

今回のサンプルコードは下記リポジトリにあります。

https://github.com/MamedenQ/python-test-example/tree/v1.0

# 1.Dockerfileからコンテナイメージを作成しghcr.ioへPush

## 1.1.Dockerfileを作成

コンテナイメージの元となるDockerfileを作成します。

```dockerfile:Dockerfile
FROM python:3.10

WORKDIR /app

RUN pip install --upgrade pip && \
    pip install pytest \
    pip install pytest-cov
```

## 1.2.コンテナイメージを作成

ghcr.io/OWNER/IMAGE_NAME:TAGの部分については適宜置き換えてください。

```
% docker build . -t ghcr.io/OWNER/IMAGE_NAME:TAG
```

## 1.3.作成したコンテナイメージの構築確認

pythonバージョンと、pytest、pytest-covが入っていることを確認します。

```
% docker run -it 32 /bin/bash

root@e162a649e4d6:/app# python -V
Python 3.10.6

root@e162a649e4d6:/app# pip freeze
attrs==22.1.0
coverage==6.4.4
iniconfig==1.1.1
install==1.3.5
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.0.9
pytest==7.1.2
pytest-cov==3.0.0
tomli==2.0.1
```

## 1.4.コンテナイメージをPush

こちらの公式ドキュメントを参考に、自身が必要な箇所のみをピックアップしました。

https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry

まずはアクセストークンの生成です。
生成方法は下記に手順があるのでそちらを参照とします。

https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token

アクセストークンの生成にてスコープの選択がありますが、コンテナイメージの操作を行う場合は下記スコープを選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38724/7389da2b-58de-69f9-3970-97dc0d5c8a39.png)
次にログインとコンテナイメージのPushを行います。
`YOUR_TOKEN`を生成したトークンに置き換えて実行してください。
USERNAME、ghcr.io/OWNER/IMAGE_NAME:TAGの部分についても適宜置き換えてください。

```
% export CR_PAT=YOUR_TOKEN
% echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin
% docker push ghcr.io/OWNER/IMAGE_NAME:TAG
```

GitHubを確認するとパッケージが出来上がってますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38724/09557186-adea-4f47-5192-645a768ae037.png)

# 2.GitHub Actionsでghcr.ioからコンテナイメージをPullしテストを実行

## 2.1.ワークフローを作成

すぐに試せるように実行トリガーは`on: workflow_dispatch`としています。

コード中のdockerリポジトリは公開しているため、ログイン処理をコメントアウト、リポジトリURLも固定としてあります。
非公開のリポジトリの場合は、コメントアウト箇所を戻す、または、置き換えて利用してください。

IMAGE_NAME:TAGの部分については適宜置き換えてください。

```yaml:unit-test.yml
name: unit-test

on: workflow_dispatch

jobs:
    unit-test-job:
        name: run unit-test job
        runs-on: ubuntu-latest
        steps:
            - name: checkout code
              uses: actions/checkout@v3
            # - name: Login to Docker
            #   uses: docker/login-action@v1
            #   with:
            #       registry: ghcr.io
            #       username: ${OWNER,,}
            #       password: ${{ secrets.GHCR_PAT }}
            #   env:
            #       OWNER: ${{ github.repository_owner }}
            - name: Set up Docker
              run: |
                  docker pull ghcr.io/mamedenq/python-test-env:latest
                  docker-compose up -d
              # run: |
              #     docker pull ghcr.io/${OWNER,,}/python-test-env:latest
              #     docker-compose up -d
              # env:
              #     OWNER: ${{ github.repository_owner }}
            - name: test
              run: docker-compose exec -T app pytest --cov
```

コメントアウトしている箇所で、小文字変換している処理があります。
```:抜粋
- name: Login to Docker
  uses: docker/login-action@v1
  with:
    username: ${OWNER,,}
  env:
    OWNER: ${{ github.repository_owner }}
```
最近登録した人はどうなのかわかりませんが、私の場合はオーナー名称に大文字が入っており、dockerリポジトリ名のルールに抵触しエラーが出るため、リポジトリオーナーの変数をそのまま使わず小文字変換するようにしないといけません。固定文字列で指定してしまう、また、不要な人は取り除いても構わないかと思います。

## 2.2.docker-compose.ymlを作成

docker-compose実行時に立ち上げるサービスを指定します。
指定するイメージはPullしたイメージとなるよう、`image`に指定します。

```yaml:docker-compose.yml
version: "3"
services:
    app:
        image: ghcr.io/mamedenq/python-test-env:latest
        volumes:
            - ./:/app
        tty: true
```

## 2.3.シークレット値を追加（Dockerリポジトリが公開されている場合は不要）

`unit-test.yml`から参照しているシークレット値`GHCR_PAT`を追加します。
リポジトリの設定から追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38724/8b5e61df-05ef-336e-6d10-485aabe5ca1f.png)


## 2.4.ワークフローを実行

リポジトリのActionsからworkflowを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38724/0cf740b5-74ca-623e-a1be-feb84b05c951.png)
無事成功し、テスト結果も良好です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38724/313d9854-788f-c856-7b0c-39abd86c2ef1.png)

# 3.（サブクエ）ghcr.ioへPushしたコンテナイメージを編集

一応PushしたイメージのPull、編集、コミット、Push方法も載せます。

## 3.1.コンテナイメージをPull

ghcr.io/OWNER/IMAGE_NAME:TAGの部分については適宜置き換えてください。

```
% docker pull ghcr.io/OWNER/IMAGE_NAME:TAG
```

## 3.2.コンテナイメージを修正してPush

1つのターミナルでできるのかもしれませんが、やり方がわからなかったため、2ターミナルに分かれての操作となります。

コンテナを起動し任意の修正を入れます。
```:ターミナル１にて実行
% docker run -it ea /bin/bash
root@4f0465605a3f:/app# mkdir /new_dir
```

コンテナの修正差分をcommitします。
AUTHOR_NAME、MESSAGE、CONTAINER_ID、ghcr.io/OWNER/IMAGE_NAME:TAGの部分については適宜置き換えてください。
```:ターミナル２にて実行
% docker container commit -a AUTHOR_NAME -m MESSAGE CONTAINER_ID ghcr.io/OWNER/IMAGE_NAME:TAG
```

コンテナから抜け、修正したコンテナイメージをPushします。
ghcr.io/OWNER/IMAGE_NAME:TAGの部分については適宜置き換えてください。
```:ターミナル１にて実行
root@4f0465605a3f:/app# exit

% docker push ghcr.io/OWNER/IMAGE_NAME:TAG
```

# 最後に

シンプルにGitHub ActionsでDockerコンテナを動かすことはできた。

一部大変だったところは、リポジトリオーナー名に大文字が入っていたこと。最近は登録時に制限されているのだろうなとは思うが、これをworkflow内で小文字に変換するにはどうしたらいいか時間がかかってしまった。
ここについては、GitHub Actionsの基礎や構文についての知識がまだ不足しており、shell上でやり方はわかっても、GitHub Actions上でどう表現するかのボキャブラリーがなく対処ができなかった。やってきたことをテンプレ的にやるのであれば問題ないが、例外的なことに対処ができていないため、もう少し基礎的なことを身につける必要があると感じた。

背景にある制限された環境における試験については、コンテナを作る際にメモリ制限などの環境情報を設定できるため、今後習得しておきたいと思う。