Github Actions にて MySQL container imageを設定+初期化してTestで使う方法と注意

皆さんはどんなCI環境を使用していますか?

私は 新規に作る場合は Github Actions を選択する機会も多くなってきました。

Github Actions には まだまだ 品質的な課題もあります。
しかしGithub 上で 無料からすぐに始められるのが大きな利点です。
また、Github を使用しているのなら一つのUI上で CI/CD まで完結するのも魅力ですね。

そんな Github Actions ですが、いくつかハマるポイントがあったりするので、その注意点と 標準の mysql image を 設定 + 初期化 しながら docker run する方法を書きます。

今回やりたいこととしては、以下とします。

  • Github Actions 上で docker run して test にMySQL image を使う
    • docker hub にある mysql image をそのまま使う
    • push した commit の内容で以下の前処理を行うようにする
      • 設定
        • /etc/conf.d/my.cnf を用意してそれを読み込ませる
      • 初期化
        • /docker-entrypoint-initdb.d/ を使って簡単に行う
          • MySQL image は、docker run の -v で /docker-entrypoint-initdb.d/ を使うとその directory 内にある sql ファイルを mount 時に自動的に読み込んで実行してくれます

※ 本記事のGithub Actions の問題と workaround は 執筆 2020/8 時点を指していて、今後改善に期待しています。

構成とファイル

ディレクトリとファイルは以下のようになっているとします。

./github -actions  
├── .github  
│   └── workflows  
│       └── docker-run-mysql.yml # これが Github Actions の Workflow です。  
└── db  
    ├── conf.d  
    │   └── my.cnf               # MySQL の設定ファイルです。  
    └── schema.sql               # MySQL の初期化用SQLです。  

Workflow

以下のようにします。

.github/workflows/docker-run-mysql.yml

name: Docker Image CI  

on: push  

jobs:  
  test:  
    runs-on: ubuntu-latest  
    timeout-minutes: 3  

    steps:  
    - uses: actions/checkout@v2  

    - name: docker run  
      run: |  
        sudo docker run \  
          -d \  
          --name mysql \  
          -v ${{ github.workspace }}/db/conf.d:/etc/mysql/conf.d \  
          -v ${{ github.workspace }}/db/schema.sql:/docker-entrypoint-initdb.d/schema.sql \  
          -e MYSQL_ROOT_PASSWORD=pass \  
          -e TZ=Asia/Tokyo \  
          -p 3306:3306 \  
          mysql:5.7  
        sleep 15 # 10s では足りない  

   # Connection test  
    - name: It makes attempt to connect to the MySQL at some addresses  
      run: |  
        for host in 127.0.0.1 10.1.0.4 ::ffff:127.0.0.1  
        do  
          echo "===== Connect to $host ====="  
          mysql -h $host test_db -uroot -ppass -e 'SHOW TABLES' # 上記全て接続OK  
        done  

        echo "Show description of the table"  
        mysql -h 127.0.0.1 test_db -uroot -ppass -e 'DESC test_table'  

注意点

  • jobs.<job_id>.services.volumes を使用しない
    • これは、Github Actions で用意されている service container に対する volumes 設定です。Github Actionsとしては docker run -v ${volume} の代わりにこれを使用するようにしてほしいようですが問題があります
    • 現在の Github Actions で用意されている jobs.<job_id>.services.volumes では actions/checkout@v2 とも相性が悪いようです (詳細は下記)
  • run: にある \ の後ろに space が入ってはいけない
    • \ の後ろに space が入っていると 「docker: invalid reference format.」 で error が発生します
    • これはEditorで不可視文字を表示や、lint で設定していない限りは、気が付きにくいので注意してください
  • docker run でmysql の container 起動後は sleep が 最低でも 15s くらい必要
    • これを入れないとMySQLの初期化が間に合わずに接続が失敗します

jobs.<job_id>.services.volumes の 例 (動きません):

services は stepsの下では使えないので、今回の要件の場合は例えば以下のようなYAMLになると思います。

しかし、以下の問題点があります。

jobs:  
  checkout:  
    runs-on: ubuntu-latest  
    steps:  
      - uses: actions/checkout@v2  

  test:  
    runs-on: ubuntu-latest  
    timeout-minutes: 3  
        needs: checkout  
    services:  
      mysql:  
        env:  
          MYSQL_ROOT_PASSWORD: pass  
        ports: ["3306:3306"]  
        image: mysql:5.7  
        volumes:  
          - ${{ github.workspace }}/db/conf.d:/etc/mysql/conf.d  
          - ${{ github.workspace }}/db/schema.sql:/docker-entrypoint-initdb.d/schema.sql  
        options: --name mysql_docker  

services.volumes を使う場合の問題点

  • /docker-entrypoint-initdb.d/ , my.cnf が使えない
    • ERROR: Can't initialize batch_readline - may be the input source is a directory or a block device. が発生します
      • これは確認すると、 github.workspace に ファイルは存在しているのですが、 services.volumes で mount が 出来ていない事が判明しました
  • 仮に services を立ち上げた後に steps.usesactions/checkout@2 をする場合でも問題になる
    • checkout 先の ${{ github.workspace }} でmount する volume を 指定するとcheckout 時の初期化で rm が実行され、その際に Permission error を起こします

端的には、「services.volumes」は未サポートの物があるようで、 直接 docker run の -v でmount させるとこれらの問題は発生しません。

(他に回避方法はありそうですし) 今後解消されると思いますが、今は無難に 直接 docker run を使う方が良いと思います。(Dockerに慣れているなら、その方が理解もしやすいですし)

続いて、 db/ を説明していきます。

db/conf.d/my.cnf

[mysqld]  
# 設定したい内容を入れます。  

## 以下補足 ##  
# skip-networking # [注意] これは設定しないこと。On にすると access できなくなります。  
# bind-address        = 0.0.0.0 # MySQL5.6, 5.7 および 8.0 の image では不要です。(今後のimageは不明)  

db/schema.sql

初期化したいsqlを入れます。

例:

CREATE DATABASE `test_db`;  

USE `test_db`;  

CREATE TABLE IF NOT EXISTS `test_table` (  
  `name` VARCHAR(64) NOT NULL,  
  `site_url`  TEXT NOT NULL,  
  PRIMARY KEY (name),  
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_bin COMMENT='This is sample';  

Github Actions のCI結果例

このように 最初に定義した内容 通りにMySQL image で 設定 + table が作られてアクセス出来る様になっています。

まとめ

  • MySQL image の /docker-entrypoint-initdb.d/actions/checkout@2 を使う時は、 services: を使うのではなく、 run: にて、そのまま docker run を使うようにする
  • run:\ を使う場合は後ろに space が入らないようにする
    • 直後に改行コードを入れる必要がある
  • docker run で mysql image の container を起動した後は、必ず sleep15s 以上入れる

このポイントだけ抑えれば、 MySQL image の /docker-entrypoint-initdb.d/ が Github Actions 実行できます。

他にも色々あると思うのですが、Github の事ですからこういった点も次々と解消されていくと思います。
これからもどんどん使っていく予定です。