Code for Japanに飛び込んで、GitHub ActionsでElastic BeanstalkへのCI/CDを構築した流れ


背景

コロナで外出しづらくなったこともあり、以前から興味のあったCode for Japanに参加しました。

今回のプロジェクトは、Ruby on Railsでした。私は、Ruby初心者なので、コンテナやAWSまわりの足回りを固めるために活動してます。

私が、プロジェクトの参加からCI/CD構築までの流れを紹介します。興味がある方の参考になると嬉しいです。

3文まとめ

  • Code for Japanに参加するのは簡単
  • 当然だけど、AWSを触るのはNDAが必要
  • GitHub ActionsでCI/CDを構築するは簡単

Code for Japan プロジェクト参加のきっかけ

東京都のコロナ対策サイトで気になって、ROM専でSlackにいました。

Slack コミュニティ - Code for Japanから、気軽に参加できます。

たまたま、slack上でメンバーの募集を見かけ、市民の政治参加に興味があり参加しますと声を掛けました。
Ruby初心者でしたが、心よく受け入れて頂きました。笑

参加したDIY City プロジェクト(市民参加型合意形成プラットフォーム)

地域で暮らす人たちや、その地域を愛する人たちが主役になり、自分たちでどんなまちにしたいのか、そのためにどんなことが必要なのかを考え、手を動かし、自分たちでつくっていく都市です。できるだけ多様な人がまちづくりに関わり、「要望する」のではなく「つくる」側として参画します。

DIY 都市を作ろう|Hal Seki|note

上記のような構想をもとに、市区町村と協力してサービスを提供するプロジェクトです。市民が積極的に参加することで、より良い町づくりを目指します。

日本では、加古川市で2020/11から本稼働しています。

加古川市 市民参加型合意形成プラットフォーム

OSS decidim

上記のサイトは、Decidim (Free Open-Source participatory democracy for cities and organizations)をベースに構築されています。Ruby on railsで構築されており、これにパッチ修正をあてて稼働しています。翻訳など本家に取り込んでも問題ないものは、本家を修正しています。

このOSSで数年前から、バルセロナヘルシンキなどいくつかの複数の都市で運用されています。都市ごとに、カスタマイズされており、どれも個性的で興味深いです。

バルセロナでは、うまく軌道にのり、下記のような成果が出ているようです。

2016年から2019年の3年間で、すでに市民の70%が登録しており、9000以上の市民からの新たな政策提案が集まっている

「シティOS」で市民に還元。バルセロナが本当にスマートな理由

NDA締結

今回、CI/CD構築のため、AWSを触るので、NDA締結が必要でした。
一般的な内容で、クラウドサインで締結しました。

※私が勤めている弁護士ドットコムのサービスを使ってくれていました。

CI/CD概要

既にソースコードをGitHubで管理していたので、GitHub上で完結するGitHub Actionsを採用しました。

ユーザーがGitHubにコードをcommit pushすると、GitHub ActionsでイメージをBuildして、ECRにpush。その後、AWS ECRを使用してElastic Beanstalkにデプロイする非常にシンプルなパイプラインです。

AWS ECR作成

ECRは、Cloud Formationで作成しました。

AWSTemplateFormatVersion: "2010-09-09"
Description: Create ECR

Resources:
  TestEcrPoc:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: hoge
      ImageScanningConfiguration:
        scanOnPush: "true"
      ImageTagMutability: "MUTABLE"
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 1,
                "description": "Delete image without tag after 7 days",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "sinceImagePushed",
                  "countUnit": "days",
                  "countNumber": 7
                },
                "action": {
                  "type": "expire"
                }
              }
            ]
          }

イメージスキャン

イメージスキャン - Amazon ECRを有効化しています。Pushするたびに、スキャンされます。

イメージスキャンは、オープンソースのClairプロジェクトからCommon Vulnerabilities and Exposures(CVE)データベースを使用してスキャンし、GUI上から結果を確認できる機能です。

ライフサイクルポリシー

タグなしのイメージだけ、7日経過後に削除するライフサイクルポリシーを設定しています。
ライフサイクルポリシーだけJSONで記述する必要があります。yamlの中なので、補完が効かず書きづらいですが、仕方がありません。

AWS IAMユーザーの作成

Elastic Beanstalkのアクセス権だけでなく、上記で作成したECRへのReadWriteアクセスも必要です。

GitHub Actionsで使うSecretsの設定

GitHubでは、レポジトリごとにSecretsが設定できます。

それを使用して、下記のキーで、上記で作成したIAMのアクセス情報とECRのレポジトリ名を保存します。
レポジトリ名は場合によっては、Secretsでなくてもいいと思います。

AWS_ACCESS_KEY_ID: *********************
AWS_SECRET_ACCESS_KEY: *********************
AWS_ECR_REPO_NAME: hoge

AWS ECRへのイメージのpush

github/workflows/deploy.yml
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        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@v1

      - name: Build, tag, and push image to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }}
          IMAGE_TAG: staging
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

AWS ECRへのログイン & Push

公式で、下記のActionが提供されているので、非常に簡単にpushできます。

aws-actions/amazon-ecr-login: Logs into Amazon ECR with the local Docker client.

Dockerrun.aws.jsonの作成

/deployments/Dockerrun.aws.json
{
    "AWSEBDockerrunVersion": "1",
    "Logging": "/app/log",
    "Image": {
        "Name": "{RepositoryName}",
        "Update": "true"
    },
    "Ports": [
        {
            "ContainerPort": "3000"
        }
    ]
}

${RepositoryName}はデプロイ時に、タグ付きのレポジトリ名にリプレイスします。

あえてdeploymentsディレクトリを掘って、その下に置いています。同じ階層にDockerfileがあると、そちらが優先されてしまうためです。また、デプロイ時にDockerrun.aws.jsonだけをzipすることで転送量を減らすのが狙いです。

Loggingセクションに コンテナ内のディレクトリを指定することで、ebの管理下にあるLogディレクトリにログを出力できます。
またGUI上のコンソールからLogの閲覧も可能になります。

AWS Elastic Beanstalkへのデプロイ

github/workflows/deploy.yml
      - uses: actions/setup-python@master
        with:
          python-version: "3.7"

      - name: Install awsebcli
        run: pip install -U awsebcli

      - name: Deploy to Elastic Beanstalk
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }}
          IMAGE_TAG: staging
          EB_ENVIRONMENT_NAME: staging
        run: |
          cd deployments
          sed -i -e "s|{RepositoryName}|$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG|g" Dockerrun.aws.json
          eb deploy ${EB_ENVIRONMENT_NAME}

今回は、極力公式のActionだけにしたかったので、有志のBeanstalk Deploy · Actions · GitHub Marketplaceを使用しませんでした。要件次第では、使った方が楽だと思います。

eb コマンドを使用しているので、python環境の構築後にコマンドのインストールを行っています。

まとめ

GitHub Actionsを使用することで、比較的簡単にパイプラインが構築できました。

デジタル庁の民間登用やCode for Japanの東京都コロナ対策サイト、COCOAなどを契機に、一般市民がテクノロジーを活用して、行政サービスの問題や社会課題を解決するCivitechはこれから盛り上がっていくと思います。

コードの全体は、codeforjapan/decidim-cfj: Code for Japan Decidimにあります。

ぜひあなたのコントリビュートをお待ちしております。

参考