11
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub ActionsAdvent Calendar 2023

Day 8

nektos/act を使って、ローカルからGithub Actionsを動かしてみる

Last updated at Posted at 2023-12-04

概要

以前別の記事で、少し触ってみたという記載しました。

私自身、Github Actionsに対する知識はまだまだ少ないですが、
この機会で nektos/act に触れながら、Github Actionsに対する理解も深められればと思って始めました。

どういうもの?

Github Actionsのworkflowをローカルで動作させるために使用するライブラリです。

Github Actionsの挙動確認のために、都度git add → commit → push→workflow確認、という操作を何度も実行するのは手間、だと思います。
ターミナル上でworkflowの挙動が確認出来ると便利ではないでしょうか!
ただ新しいライブラリ、という訳ではありません!(初回リリース4年前)

知ったきっかけ

以下のスライドで紹介されてました。

どうやって動く?(公式より)

When you run act it reads in your GitHub Actions from .github/workflows/ and determines the set of actions that need to be run. It uses the Docker API to either pull or build the necessary images, as defined in your workflow files and finally determines the execution path based on the dependencies that were defined. Once it has the execution path, it then uses the Docker API to run containers for each action based on the images prepared earlier.

.github/workflows/フォルダに配置したyamlファイルを読み込むところから始まります。
Docker APIを使用して、workflowに定義したイメージのpullとbuild実施し、定義された依存関係に基づいて実行パスを決定します。
各アクションのコンテナを実行して、workflowの処理を行っていきます。

セットアップ

さっそくですが、作業を進めます。

インストール

brewコマンドを使います。他はREADME.mdをご確認ください。

brew install act

コマンド初回実行時

Dockerイメージのサイズを聞かれます。Largeが特に大きいです。こちらの記事も見て、Mediumで進めることにしました。

$ act
? Please choose the default image you want to use with act:

  - Large size image: +20GB Docker image, includes almost all tools used on GitHub Actions (IMPORTANT: currently only ubuntu-18.04 platform is available)
  - Medium size image: ~500MB, includes only necessary tools to bootstrap actions and aims to be compatible with all actions
  - Micro size image: <200MB, contains only NodeJS required to bootstrap actions, doesn't work with all actions

Default image and other options can be changed manually in ~/.actrc (please refer to https://github.com/nektos/act#configuration for additional information about file structure)  [Use arrows to move, type to filter, ? for more help]
  Large
> Medium
  Micro

具体的な作業に入っていきます。
今回はpushイベント時のworkflowとします。また以下の2つに関するworkflowを作成しました。

  • アプリケーションに対するテスト実行
  • AWSサービスとの連携(ECR・Lambda)

アプリケーションのテスト

最初は軽めのものとして、FastAPIアプリケーションの簡単なテストを実施してみました。
python3.12のインストール(デフォルトでは3.10)、必要なライブラリインストール、テスト実行の順に実施します。

  - name: Install Python
    uses: actions/setup-python@v4
    with:
      python-version: "3.12"
  - name: Install dependencies
    run: |
      python -m pip install --upgrade pip
      pip install -r requirements.txt
  - name: Run app tests
    run: |
      pytest ./tests/unit/test.py
アプリケーションとテストのコード
main.py
from fastapi import FastAPI, status
from mangum import Mangum
from pydantic import BaseModel
from typing import Any

EventAny = Any

env = os.getenv("ENV")
if env is None:
    app = FastAPI()
else:
    app = FastAPI(
        root_path="/dev",
    )

class HealthCheck(BaseModel):
    """Response model to validate and return when performing a health check."""

    status: str = "OK"

@app.get(
    "/health",
    status_code=status.HTTP_200_OK,
    response_model=HealthCheck,
)
def get_health() -> HealthCheck:
    """Calculate speed as distance divided by time."""
    
    return HealthCheck(status="OK")

def handler(event: EventAny, context: EventAny):
    asgi_handler = Mangum(app)
    return asgi_handler(event, context)

/healthというエンドポイントにアクセスし、戻り値とHTTPステータスコードが期待値通りか確認するテストです。

test.py
from fastapi.testclient import TestClient
from fastapi import status
from src.main import app

client = TestClient(app)

def test_health():
    response = client.get("/health")
    assert response.status_code == status.HTTP_200_OK
    assert response.json() == {"status": "OK"}

コマンドを実行して、actでのテストが成功しました。(pushイベントの場合は、特に引数不要)

act

image.png

AWSを用いたworkflowで挙動確認

少し規模を大きくします。以下workflowが、actで動作するか確かめてみました。

  1. 認証情報設定
  2. ECRへのログイン
  3. DockerイメージのBuildとECRへのPush
  4. Dockerイメージを使用したLambda関数の更新

デフォルトの挙動
コマンド実行時にオプションを付けないと、実際にAWSリソースは更新されますので、ご注意ください。
更新したくない場合は、公式のREADME.mdにも記載の通り、nオプションを設定してください。

Run in dry-run mode:

act -n

認証情報の設定

  - name: Set up AWS
    run: |
      aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
      aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
      aws configure set region $REGION 

actで実行すると、以下のように怒られます。

command not found, please refer to https://github.com/nektos/act/issues/107 for more information

actには、AWS CLIがインストールされていないようです。
image.png

Github Actions(Ubuntu22.04)は、AWS CLIがインストール済なので、問題なく動きます。

image.png

actのためにawsコマンドを使える状態にするのも変なので、
以下を使って、awsコマンドを明記せずに認証情報を設定できる方法を採用します。

  - name: Set AWS credentials
    uses: aws-actions/configure-aws-credentials@v4  
    with:
      aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
      aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      aws-region: ap-northeast-1

actで実行すると、上記の実装で成功しました。
ただ私の場合、実行前の段階で、ターミナル上でaws configure認証情報を設定したので成功していた、と後で気付きました。
image.png

awsの認証情報は設定していない体で、もう少し詳しく確認してみました。

actのドキュメントの通り、Github Actions上で設定したsecrets値を、読み込むために新たにファイルを用意します。

.secrets
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

作成したシークレット値用のファイルを読み込むオプションを追加して実行すると成功しました。

act --secret-file .secrets

何度か実行する中でわかりましたが、
aws configureコマンドで設定した値 → .envファイル → .secretsファイル、この順に認証情報があるか確認しているようです。
いずれにも正しい認証情報がない場合、以下のエラーが表示されて、Jobが失敗しました。
image.png

ECRへのログイン

awsコマンドは、actでは使えないと、認証情報設定の段階で判明したので、ECRログイン時によく使われるであろう以下のコマンドは使わず進めてみます。

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin

以下のリポジトリを使用して、ログインします。

  - name: Login to Amazon ECR
    id: login-ecr
    uses: aws-actions/amazon-ecr-login@v2

以下を実行して、成功しました。

act --secret-file .secrets -s GITHUB_TOKEN="$(gh auth token)"

image.png

DockerイメージのBuildとECRへのPush

以下2つのリポジトリの機能を使って、Pushまで行います。

  - name: Set Docker Images and Tags
    id: metadata
    uses: docker/metadata-action@v5
    with:
      images: ${{ steps.login-ecr.outputs.registry }}/${{ vars.FASTAPI_LAMBDA_REPOSITORY_NAME }}
      flavor: latest=true
  - name: Docker build and push to ECR         
    uses: docker/build-push-action@v5
    with:
      context: .
      file: ./Dockerfile          
      push: true
     tags: ${{ secrets.AWS_REGISTRY_URL }}:latest
  • ${{ steps.login-ecr.outputs.registry }}:XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.comが設定されます。
  • login-ecr部分は、amazon-ecr-login@v2で設定したid名です。

actで実行すると、失敗しました。
v4に下げても同じエラーが表示されたので、バージョンは関係なさそうです。
image.png

こちらの記事に、以下の記載がありました。

act -s GITHUB_TOKEN="$(gh auth token)"

This value is provided to GitHub Actions workflows by the GitHub Actions Runner automatically on the cloud

クラウドでは自動的に与えられている値のようです。ローカルで発行するために、ghコマンドを使います。

gh auth tokenを実行してトークンを確認します。
作成済でなければ、no oauth tokenとなるはずです。
gh auth loginを実行して、いくつか質問に回答して、トークンを発行します。
ワンタイムパスワードが表示され、Enterキーを押すとブラウザが起動します。
ワンタイムパスワードを入力すると、ターミナル側でLogged in as XXXXXXXと表示されます。

ターミナルとブラウザの状態

image.png
ワンタイムパスワードを入力すると、ターミナル側でLogged in as XXXXXXXと表示されます。
image.png
image.png

完了したら、再度gh auth tokenを実行します。gho_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXが表示されたので、これでトークンを用意できました。

再度実行します。

act --secret-file .secrets -s GITHUB_TOKEN="$(gh auth token)"

metadata-action@v5の処理は成功しましたが、docker/build-push-action@v5の処理でエラーがありました。イメージタグに設定したシークレット値が読み込めていませんでした。
image.png

ERROR: invalid tag ":latest": invalid reference format

認証情報設定の段階で記載した通り、シークレットの値をファイルに設定します。AWS_REGISTRY_URLを追記しました。

.secrets
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export AWS_REGISTRY_URL=XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/repo-name

再度実行すると、すべて成功しました。
image.png

Dockerイメージを使用したLambda関数の更新

AWS for GitHub ActionsやDockerによって提供された機能ではなく、とある方(Hidetake Iwataさん)が開発された機能のようです。今回はこちらを使わせていただきます。

  - name: Update Docker image using Lambda
    uses: int128/deploy-lambda-action@v1
    with:
        function-name: ${{ vars.FASTAPI_LAMBDA_FUNCTION_NAME }}
        image-uri: ${{ secrets.AWS_REGISTRY_URL }}:latest

公式ドキュメントの通りですが、Github Actions上で設定したvariable値を、actでも読み込むために新たにファイルを用意します。

.variables
export FASTAPI_LAMBDA_FUNCTION_NAME=fastapi-sample-container-lambda

以下を実行して、成功しました。

act --var-file .variables --secret-file .secrets -s GITHUB_TOKEN="$(gh auth token)"

image.png

最終的なworkflowはGithub Actions側も成功です!

image.png

成果物と感想

決して量は多くないですが、
最後まで成功するフローがあることは確認できました。

job同士の依存関係を見返すなど、workflowも一部整理しましたが、
キャッシュを利用してないので、実行時間短縮ができていません。

そのあたりは、このAdvent Calenderの20日目で、別途公開予定です!

本記事の最終的なyamlファイル

lintは実際Linterを用いたチェックがありますが、今回は適当に出力する処理だけ設定しました。

name:  FastAPI App CI/CD Pipeline
run-name: ${{ github.actor }} is testing out GitHub Actions 🚀
on: 
  push:
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
  check-app:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.12"
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt     
      - name: Run app tests
        run: |
          pytest ./tests/unit/test.py
  release:
    runs-on: ubuntu-latest
    needs: [lint, check-app]    
    steps:          
      - uses: actions/checkout@v4    
      - name: Set AWS credentials
        uses: aws-actions/configure-aws-credentials@v4  
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1                            
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2        
      - name: Set Docker Images and Tags
        id: metadata
        uses: docker/metadata-action@v5
        with:
          images: ${{ steps.login-ecr.outputs.registry }}/${{ vars.FASTAPI_LAMBDA_REPOSITORY_NAME }}
          flavor: latest=true
      - name: Docker build and push to ECR         
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile          
          push: true
          tags: ${{ secrets.AWS_REGISTRY_URL }}:latest
      - name: Update Docker image using Lambda
        uses: int128/deploy-lambda-action@v1
        with:
            function-name: ${{ vars.FASTAPI_LAMBDA_FUNCTION_NAME }}
            image-uri: ${{ secrets.AWS_REGISTRY_URL }}:latest
      - run: echo "🍏 This job's status is ${{ job.status }}."

実務の場合、より多くの処理がGithub Actionsのworkflowで実装されており、それもactで代用できるの??開発進めやすいの??と私自身も疑問に思っています。
やってみないと分からないので、皆さんもぜひ導入してみてください!とは、まだまだ言えません。。

今回実施した範囲では、通常の流れ(add→commit→push→ブラウザで確認する流れ)よりは、開発しやすいと感じましたが、もう少し複雑なフローとなった場合はどうなるのか、気になりますね。

nektos/actについて、気になる方はぜひ触ってみてください!

11
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?