2024.07.09

GitHub ActionsでCI/CDを実験してみる

こんにちは。次世代システム研究室のK.X.D.です。

はじめに

ソースコード管理といえば、今ではGithubがすぐに思い浮かぶでしょう。

GitHub上でCI/CD実現できる機能はGithub Actionsです。

ソースコード管理しながら、外部サービス連携しなくても、CI/CD運用するのはとても便利と思いますので、

Github Actionsの実験を積んで、もっと自由に活用したいと思います。

やりたいこと:

Githubのアクティビティ(PR作成、PRマージなど)により、CI/CDを自動化にしたいと思います。

具体的に、下記のような作業が実施したいです。

  1. Docker ContainerでGolangのRevelアプリケーションを動かす
  2. Githubでソースコード管理して、PRを作成すると自動テスト実行(CIワークフロー実現)
  3. PRをMainへマージすると、自動でContainerイメージを作成して公開する(CDワークフロー実現)
  4. Github上のリリースタグを自動作成する

実装:

早速実装に入りたいと思います。

アプリケーション環境作成

DockerFile

FROM golang:1.21-alpine

# Install dependencies
RUN apk --no-cache --update add \
    tzdata \
    ca-certificates \
    openssh \
    git \
    wget \
    curl

# Install command line tools
RUN go install github.com/revel/cmd/[email protected]

ENV PATH $PATH:/go/bin
EXPOSE 9000


RUN mkdir -p /go/src
COPY init.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/init.sh

WORKDIR "/go/src"

CMD ["init.sh"]

 

init.sh

#!/bin/bash

app_name="myapp"

cd /go/src

# 起動
revel run -a ${app_name}か

下記の手順でRevelアプリケーションを生成する

docker build --no-cache -t github-action-revel .

docker run -it -p 9000:9000 -v ./src:/go/src --name github-action-revel -d github-action-revel /bin/sh

docker exec github-action-revel "revel new app"

git init

実行結果

 

 

 

 

 

 

 

Github Actionsのワークフローファイル作成するために、.githubのフォルダを作成しておく

CI/CDワークフロー作成

.github下にworkflows/ci.yml, workflows/cd.ymlファイル作成

 

 

 

  • workflows/ci.yml

CIファイルで実装したことは、

[opened, synchronize]のトリガーでMainブランチマージ先のPRを作成また、新いCommitがあると、ジョブを動かす

testジョブは、UnitTestでソースコードのロジック検証する

golangciジョブは、GolangのLintでソースコードの文法、適切さまで検証する

ジョブの特別設定がなければ、デフォルトは並列で実行されてる

name: CI

on:
  pull_request:
    branches:
      - main
    types: [opened, synchronize]

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

      - name: Set up containers
        run: |
          docker build --no-cache -t github-action-revel .
          docker run -it -p 9000:9000 -v ./src:/go/src --name github-action-revel -d github-action-revel /bin/sh

      - name: Run tests
        run: docker exec github-action-revel sh -c 'revel test myapp dev'

  golangci:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: stable
          cache-dependency-path: src/myapp/gosum
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v6
        with:
          version: v1.59
          working-directory: src/myapp
          problem-matchers: true


画面上に実行

 

Lintで通せない場合、「problem-matchers: true」の設定で問題あったソースコードまで表示される

  • workflows/cd.yml

cd.ymlは、自動テストとレビューを通すと、Mainにマージして、即時リリースのCDを実現するためのワークフロー

Mainへのマージをトリガーにして、下記の作業を実施する

  1. Containerイメージをビルドする
  2. Containerレジストリに公開する(今回は、Githubのレジストリを利用する)
  3. 公開した後に、リリースタグ生成して、GITHUB_TOKENで登録する

release、create-release-tagsの2ジョブは、通常のジョブデフォルト挙動が並列で実行してますが、

release、create-release-tagsは順次に実行したいですので、「needs: release」でreleaseジョブ実行完了した後に、create-release-tags実行するようにしてます。

記述したものの中で重要な項目を説明します。

「permissions: write-all」:Githubレジストリに公開権限付与ため設定

concurrency: group: ${{ github.workflow }}:同じワークフローで2重で実行されると、前に実行したワークフローをキャンセルする

repository_lowercase:Githubレジストリはアップケース文字列禁止で、repository名を小文字にしておくステップ

secrets.GITHUB_TOKEN:ワークフローを実行すると、Githubリソースアクセスためのトークンが自動に生成されています。

name: CD

on:
  pull_request:
    types: [closed]
    branches:
      - main
  workflow_dispatch:

permissions: write-all

concurrency:
  group: ${{ github.workflow }}

env:
    DOCKER_IMAGE_NAME: github-action-revel
    RELEASE_BRANCH: main

jobs:
  release:
    # マージされたときのみ実行
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
       # Githubレジストリにログインする
      - name: 'Login to GitHub Container Registry'
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{github.actor}}
          password: ${{secrets.GITHUB_TOKEN}}

      - id: repository_lowercase
        name: Repository to lowercase
        run: |
            echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT

      - name: Build docker image
        run: docker build -t ghcr.io/${{ steps.repository_lowercase.outputs.repository }}/${{ env.DOCKER_IMAGE_NAME }}:latest .
    
      - name: Push Docker Image
        run: docker push ghcr.io/${{ steps.repository_lowercase.outputs.repository }}/${{ env.DOCKER_IMAGE_NAME }}:latest

  create-release-tags:
    if: github.event.pull_request.merged == true
    needs: release

    runs-on: ubuntu-latest
    env:
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    steps:
      - uses: actions/checkout@v4

      # 前回のリリースタグを取得する
      - name: Get previous tag
        id: pre_tag
        run: |
          echo "pre_tag=$(curl -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)" >> $GITHUB_OUTPUT

      # タグを生成する 「{YYYY.MM.DD}-{当日リリース回数}」
      - name: Generate release tag
        id: release_tag
        env:
          TZ: 'Asia/Tokyo'
        run: |
          today=$(date +'%Y.%m.%d')
          pre_release_date=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $1}')
          pre_release_count=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $2}')
          if [[ ! $pre_release_date = $today ]]; then
            echo "init count"
            pre_release_count=0
          fi
          echo "release_tag=$today-$(($pre_release_count + 1))" >> $GITHUB_OUTPUT

      # リリースタグを作成する
      - name: Create Release
        run: |
          curl -X POST \
            -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            -d "{ \"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"target_commitish\": \"${{ env.RELEASE_BRANCH }}\", \"name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"body\": \"Release for ${{ steps.release_tag.outputs.release_tag }}\"}" \
            https://api.github.com/repos/${{ github.repository }}/releases

release、create-release-tagsの2ジョブは順次に実行

releaseジョブ実行

「ghcr.io/{repogistory}/github_actions_revel/github-action-revel:latest」のURLで公開確認

create-release-tagsジョブ実行

まとめ

今回のブログでは、実際の業務に関する作業をGithub Actionsで実験しました。

Githubを利用するあたって自動化が必要な作業は色々あって、その場でGithub Actionsで活用を提案したいと思います。

また、実際にGithub Actionsの処理でエラーが発生した時にどうすればカバできるかなども考えないといけないです。

それらのケース対策はGithub Actionsは十分に提供されていますが、ブログの内容に挟めると長くなりますので、省略させていただきます。

Github Actionsを作成の際に、異常なケースでも考慮して、完璧なGithub Actionsを設計いただくと良いと思います。

最後に

グループ研究開発本部 次世代システム研究室では、最新のテクノロジーを調査・検証しながらインターネット上の高度なアプリケーション開発を行うエンジニア・アーキテクトを募集しています。募集職種一覧からご応募をお待ちしています。

  • Twitter
  • Facebook
  • はてなブックマークに追加

グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。

 
  • AI研究開発室
  • 大阪研究開発グループ

関連記事