LoginSignup
87
59

More than 3 years have passed since last update.

【GitHub】新GitHub Actionsを使ってAWS ECRにイメージをプッシュするCIを作ってみた

Last updated at Posted at 2019-08-15

前置き

GitHub Actionsが刷新されてHCLからYAMLでCIを作成できるようになりました。
前まではAction毎にDockerfileを必ず用意する必要がありましたが、Ubuntu・Windows・MacOS上でCIを回すことが可能となり、Dockerfileのメンテナンスが要らなくなりました。<- まじ感謝:pray:

では、新しくなったGitHub Actionsを使用してどのようにCIを作ったか説明していきたいと思います。

概要

今回作成してみたCIは、Pythonで開発したアプリケーションをDockerizeしてAWS ECRへプッシュするというものです。
Pushする前にソースコードのLintチェック、テストの実行、Trivyを使用した脆弱性診断、Dockleを用いたセキュリテイ診断を並列に実施します。並列に実行できるJobは20個までとなっているので、今回の規模であれば余裕で並列実行できます。
Jobを並列実行する際に注意すべきは、Job毎に仮想インスタンスが異なるのでファイルシステム上で情報が共有されないことです。情報を共有したい場合は同一のJobで全てのStepを実行する必要があります。そのため、脆弱性診断・セキュリティ診断・ECRへPushする際にそれぞれでDockerイメージをビルドしています。まぁ、トレードオフですね。
オンラインでCIを回す時に気をつけたいのが、クレデンシャルデータの扱いです。GitHubではSecretsという機能があり、データを暗号化して保存してくれます。GitHub Actionsでも、Secretsに設定されている値にアクセスすることができるので、YAMLにクレデンシャルデータを直接書かなくて済みます。

処理の流れ

github_actions.png

ソースコード

サクッと、完成形だけ知りたい方のためにソースコードを書いておきます。
※[追記] trivyのリポジトリがaquasecurity/trivyに変更しました

.github/workflows/main.yml
on:
  push:
    branches:
      - master
      - develop
name: Build and Push Image to ECR
jobs:
  lint:
    name: Lint check
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@master
      - name: Set up Python 3.7
        uses: actions/setup-python@v1
        with:
          version: '3.7'
          architecture: 'x64'
      - name: Install dependencies
        run: |
          pip install --upgrade pip pipenv flake8
          pipenv install --system
      - name: Lint check with flake8
        run: flake8 src tests

  test:
    name: Python Test
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@master
      - name: Set up Python 3.7
        uses: actions/setup-python@v1
        with:
          version: '3.7'
          architecture: 'x64'
      - name: Install dependencies
        run: |
          pip install --upgrade pip pipenv pytest
          pipenv install --system
      - name: Test with pytest
        run: python -m pytest -v

  trivy:
    name: Trivy Scan Vulnerability
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@master
      - name: Build docker image
        run:
          docker build -t ${IMAGE_NAME} . --file Dockerfile
        env:
          IMAGE_NAME: app
      - name: Install trivy
        run: |
          sudo apt-get install apt-transport-https gnupg
          wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
          echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -cs) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
          sudo apt-get update
          sudo apt-get install trivy
      - name: Vulnerability Scan with Trivy
        run: trivy --only-update alpine -q --severity HIGH,CRITICAL --exit-code 1 ${IMAGE_NAME}
        env:
          IMAGE_NAME: app

  dockle:
    name: Dockle
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@master
      - name: Build docker image
        run: docker build -t ${IMAGE_NAME} . --file Dockerfile
        env:
          IMAGE_NAME: app
      - name: Install dockle
        run: |
          VERSION=$(curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
          grep '"tag_name":' | \
          sed -E 's/.*"v([^"]+)".*/\1/' \
          )
          curl -L -o dockle.deb https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.deb
          sudo dpkg -i dockle.deb
          rm dockle.deb
      - name: Check image with dockle
        run: dockle ${IMAGE_NAME}
        env:
          IMAGE_NAME: app

  build:
    name: Build and Push Docker Image
    runs-on: ubuntu-18.04
    needs: [lint, test, trivy, dockle]
    steps:
      - uses: actions/checkout@master
      - name: Setup Python 3.7 for awscli
        uses: actions/setup-python@v1
        with:
          version: '3.7'
          architecture: 'x64'
      - name: Install awscli
        run: pip install --upgrade pip awscli
      - name: Login to ECR
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: $(aws ecr get-login --no-include-email --region ap-northeast-1)
      - name: Build docker image
        env:
          CONTAINER_REGISTRY_PATH: ${{ secrets.CONTAINER_REGISTRY_PATH }}
          IMAGE_NAME: app
        run: |
          TAG=`echo $GITHUB_REF | sed 's/refs\/heads\///g'`
          docker build -t ${CONTAINER_REGISTRY_PATH}/${IMAGE_NAME}:${TAG} .
      - name: Notify push start to Slack
        run: curl -X POST -H 'Content-type:application/json' --data '{"text":"'"${GITHUB_REPOSITORY}"'\n【START】Push Image to ECR!!"}' ${{ secrets.SLACK_WEBHOOK }}
      - name: Push image to ECR
        env:
          CONTAINER_REGISTRY_PATH: ${{ secrets.CONTAINER_REGISTRY_PATH }}
          IMAGE_NAME: app
        run: |
          TAG=`echo $GITHUB_REF | sed 's/refs\/heads\///g'`
          docker push ${CONTAINER_REGISTRY_PATH}/${IMAGE_NAME}:${TAG}
      - name: Notify push finish to Slack
        run: curl -X POST -H 'Content-type:application/json' --data '{"text":"'"${GITHUB_REPOSITORY}"'\n【FINISH】Pushed Image to ECR!!"}' ${{ secrets.SLACK_WEBHOOK }}

解説

ソースコードに書かれていることを分割して解説していきます。

イベントトリガー

on:
  push:
    branches:
      - master
      - develop

onによってトリガーとなるイベントの設定をします。
設定できるイベントはpush、issues、pull_requestなどがあります。全てのイベントを知りたい方はEvents that trigger workflows - GitHub Helpを参照してください。
ここではpushをトリガーとして、かつmasterとdevelopブランチのみを対象としています。イベント対象を制限するためにbranches以外にtagsとpathsがあります。tagsは名前の通りですが、pathsは対象となるファイルやディレクトリを指定します。

Jobs

jobs:
  lint:
    name: Lint check
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@master

jobsで実行したいCIのフローを宣言します。複数のjobがある場合はデフォルトで並列実行されます。<-すごい(小並感)
シーケンシャルにjobを実行したい場合はneedsを使います。これによって、指定されたjobが完了するまで待ちます。
lintはjobのIDです。jobsにおいて一意である必要がありますが、割り当てるIDはお好みでOKです。
runs-onで仮想インスタンスを指定します。指定できるOSはUbuntu、MacOS、Windowsです。また、バージョンの指定もできます。
これでjobが実行できる環境が整ったので、stpesで実際の処理内容を宣言します。

Steps

steps:
  - uses: actions/checkout@master
  - name: Set up Python 3.7
    uses: actions/setup-python@v1
    with:
      version: '3.7'
      architecture: 'x64'
  - name: Install dependencies
    run: |
      pip install --upgrade pip pipenv flake8
      pipenv install --system
  - name: Lint check with flake8
    run: flake8 src tests

stepsでは処理内容を記述していきます。全てのsteps内の処理は同一仮想インスタンス上で実行されます。情報共有したい場合は同一steps内に処理を書きましょう。
usesは利用したいアクションを指定します。同一リポジトリにあるアクション、パブリックリポジトリのアクション、公開されているDockerイメージを指定することができます。stepsの一番最初に宣言しているアクションactions/checkout@masterは、裏ではgit fetchgit checkout $GITHUB_SHAと同様のことを実行しています。つまり、イベントトリガーとなったコミットへチェックアウトしてくれます。このアクションは必ず指定しましょう。ちなみにactions/setup-python@v1は裏ではTypeScriptが動いています。
withはアクションの入力値を指定します。何を指定可能かはアクションごとのYAMLに書かれているので、そちらを確認してください。
実行したいコマンドはrunで指定します。実行したいコマンドが長くなりすぎる場合は可読性が落ちますのでアクションを作成することをお勧めします。

環境変数

env:
  IMAGE_NAME: app

環境変数はenvで指定します。「environment」とフルで書く必要がないので楽です。

Secrets

${{ secrets.AWS_ACCESS_KEY_ID }}

Secretsに設定した値にアクセスしたい場合は、上記のように書きます。secretsの後に取得したい値のキー(AWS_ACCESS_KEY_ID)を指定します。

余談

余談ですが、GitHub Actionsの実行画面がかなりリッチになりました。

スクリーンショット 2019-08-15 17.09.45.png

おわりに

GitHub ActionsでCIを書いてみましたが、他のCIツールと遜色がないぐらいのクオリティになったと感じました。ただ、CIの結果を通知する手段がメールしかサポートされていないのは痛いです。今回のCIでは、とりあえずcurlでSlackへ通知する処理を組み込みましたが、アクションを作った方がいいと思いました。

本記事が、これからGitHub ActionsでCIを書こうと思っている方の参考になれば幸いです:smile:

Reference

87
59
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
87
59