なになれ

IT系のことを記録していきます。

RenovateでKubernetesエコシステムのバージョン追従を試した

背景

CloudNative Days Tokyo 2021の下記を視聴し、Kubernetesエコシステムのバージョン追従を自動化せねばと思った次第です。

event.cloudnativedays.jp

発表の中では独自の仕組みで実現されているということで、Renovateで同じようなことができないかを試しました。
RenovateはGitHub Appで動作し、依存関係のあるモジュールに更新があれば、更新のためのPRを自動的に作成するツールです。

今回の成果物となるリポジトリは以下です。
github.com

なお、本内容は実運用に耐えうるかを試せてないので漏れがあるかもしれません。ご了承ください。

実践

発表にあったKustomize、Helm、Rawマニフェストでそれぞれ試しました。

Renovateの導入

下記からGitHub上にRenovateをセットアップします。

GitHub Apps - Renovate · GitHub

Kustomize

Kustomizeでは、Remote上のkustomization.yamlをBuildできます。Renovateはこの仕組みに対応しています。

kustomize/remoteBuild.md at master · kubernetes-sigs/kustomize · GitHub

元のkustomization.yamlでは、以下のように記載します。

kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
  app: example
resources:
- github.com/kubernetes/ingress-nginx/deploy/static/provider/baremetal?ref=controller-v1.0.5

github.com/kubernetes/ingress-nginx...でRemote上にあるkustomization.yamlを指定します。
refでGitのタグを指定します。
Gitのタグに更新があればRenovateが更新するPRを作成するという動きになります。

renovate.jsonでRenovateの設定を行います。

renovate.json

{
  "extends": [
    "config:base"
  ],
...
  "packageRules": [
    {
      "matchPackageNames": ["kubernetes/ingress-nginx"],
      "versioning": "regex:^(?<compatibility>.*)v(?<major>\\d+)(\\.(?<minor>\\d+))?(\\.(?<patch>\\d+))?$"
    }
  ]
}

matchPackageNamesで、特定のpackageを指定します。
今回の例で使用しているingress-nginxは一つのリポジトリに複数のタグが混在しているため、今回の対象とするタグを指定するためにversioningの設定を行なっています。
versioningでは、正規表現でバージョンのルールをカスタマイズできます。

詳しいバージョンの記述方法は以下にあります。

Versioning - Renovate Docs

Renovateが動作すると、下記のようにPRが作成されます。

Update dependency kubernetes/ingress-nginx to vcontroller-v1.1.0 by renovate[bot] · Pull Request #2 · hi1280/renovate-kubernetes-example · GitHub

Helm

Helmに対応するために、ここではHelm Chartのバージョンを宣言的に管理できるHelmfileを利用します。RenovateはHelmfileに対応しています。

helmfile.yamlは以下のように記載します。

helmfile.yaml

 repositories:
   - name: ingress-nginx
     url: https://kubernetes.github.io/ingress-nginx
 releases:
   - name: ingress-nginx
     chart: ingress-nginx/ingress-nginx
     version: 4.0.12

renovate.jsonの設定は特に不要です。

Renovateが動作すると、下記のようにPRが作成されます。

Update Helm release ingress-nginx to v4.0.13 by renovate[bot] · Pull Request #1 · hi1280/renovate-kubernetes-example · GitHub

Rawマニフェスト

素のマニフェストを更新する場合です。
結論としては、このケースは上手くいきませんでした。
RenovateのKubernetes対応はKubernetesマニフェスト(YAML)でimageのバージョンを更新するだけです。
マニフェスト自体が更新されることには対応していません。

一応、こちらのケースで試したRenovateのKubernetes対応について紹介します。

renovate.jsonを修正して、Kubernetesに対応します。

renovate.json

...
  "kubernetes": {
    "fileMatch": ["raw/.+\\.yaml"]
  },
...

rawディレクトリ内にあるyamlファイルをKubernetesで利用するマニフェストであると設定しています。

Renovateが動作すると、下記のようにPRが作成されます。

Update k8s.gcr.io/ingress-nginx/controller Docker tag to v1.1.0 by renovate[bot] · Pull Request #4 · hi1280/renovate-kubernetes-example · GitHub

まとめ

RenovateでKustomize、Helm Chartのバージョンに追従できることが確認できました。
Rawマニフェストでは、Renovateでマニフェスト自体を更新することができませんでした。
少し考えれば、Renovateは依存関係のあるモジュールを更新するためのものなので、この動作結果は当然かと思います。
このあたりは独自に仕組みを作るしかなさそうな気がします。

参考

2021年で学んだ技術

2021年を何を学んだかで振り返ります。仕事としてはインフラ、データエンジニアリング領域に関わりました。
ポジションとしてはチームリード的な立場でソフトスキル的な面も向上できた1年でした。

AWS

2021年はAWS Well-Architectedフレームワークに則り、会社組織の範囲でのAWSの利用方法を改善しました。
これを通して、AWSの運用といった面で多くのことを学びました。
システム単体の範囲でAWSを扱うのはよくあることかと思いますが、会社組織の範囲でAWSを扱う経験はなかなかできないかなと思います。
これは今の会社がスタートアップ規模だからこそできることだなと感じています。

具体的には、セキュリティを中心に以下の改善を行いました。

  • AWS IAM Access Analyzerを用いたセキュリティ的に危険な権限設定がなされているAWSリソースの特定方法
  • AWS Configについて
    • マルチアカウント対応
    • AWS Well-Architectedフレームワークのセキュリティに特化したコンフォーマンスパックの適用方法
    • advanced queryを使ったリソースのチェック
  • GuardDutyについて
    • マルチアカウント対応
    • 重大度が高い結果をEventBridgeを使って通知する仕組み作り
  • Google WorkspaceをIdプロバイダとしたAWS SSOの適用方法

セキュリティに配慮してAWSの運用を行うにあたっては、AWSの様々なサービスを利用する必要があり、今回学んだ内容でもまだまだ足りないと感じました。
このあたりは継続的にアップデートしていくべき領域だと思います。

AWS Well-Architectedフレームワークの取り組み以外には、Lambdaを使う機会が多かったので、そのノウハウを得ることができました。
本番環境やステージング環境毎にLambda関数を分けた方が運用上複雑にならないといったことを実践を通して学びました。

Aurora MySQL version2からversion3への更新作業を経験し、やり方を学びました。
version3はMySQL8系互換であるため、Window関数が使えることが更新に至った背景でした。
この作業を通してAuroraのバージョンを改めて確認する機会になり、version2でのマイナーアップデートでも重要な機能追加があることが分かりました。
マイナーバージョンでも適宜アップデートすることが大事だと思います。

監視

2021年に会社の環境にDatadogが導入されました。自分がDatadogの導入作業を行なったわけではないのですが、今までDatadogに関わることがなかったため、Datadogがどういうものかについては学ぶことができました。
Datadogを導入して良かったことは、APMが便利ということです。このあたりはアプリケーションエンジニアからも好評でした。
まだ使い方に慣れていないところがあるので今後も習熟が必要です。

データ基盤

2020年に構築したAWSを使ったデータ基盤のリファクタリングや品質を強化するといったことを行いました。
学んだことはブログの記事にいくつかまとめています。

リファクタリングの内容としては、データパイプラインを作るにあたり、バラバラにデータを処理していたところをStep Functionsの定義にほぼ全て実装するといったことを行いました。
日次で実行されるワークフローだとしても条件分岐を駆使すれば、特定の時間で実行するタスクを作れることが分かり、日次と特定の時間に実行するタスクをStep Functionsで一つにまとめることができました。
このようにStep Functionsの使い方がより習熟したと思います。

データ活用

社内では、Redashがデータ活用のための主力ツールになっているため、Redashの使い方が主な学びでした。
hi1280.hatenablog.com

今まであまり使っていなかったのですが、テーブル形式のクエリ結果にリンクを埋め込む機能が便利ということに気づいたのが最近の学びです。
リンク先を他クエリに設定することで、簡易的なドリルダウンが可能です。

データ活用ならPythonだろうということで、そこそこPythonを学習しました。Pandasやmatplotlib、seabornを使ったグラフでのデータ可視化を試したりしました。教材は下記です。

Pythonに関してはまだまだ勉強することがあると感じています。

ITエンジニアの作業効率化

チームリード的な立場になり、メンバーが日々の作業を効率化できるように、使用しているツールの使い勝手をよくするといったことを行いました。
特にSlackは日常的に使っているツールなので、Slack上で色々な情報が見れると良いと考えて改善に取り組みました。

具体的には、GitHub上でのイベントやAWS上でのイベントをSlackに通知するといったことをきちんと行うようにしました。
開発作業はGitHub上でPRを作成して行われるためにその通知を行ったり、AWS上での各種アラートはSlackに通知するようにしました。
このあたりの通知のやり方を学ぶことができました。

現環境では、Trelloでタスク管理をしているのですが、タスクボードについても効率化できるように整理しました。
Trelloの機能にあるAutomationやPowerUpを使ってできるだけ自動化しました。
Automationは細かなところを自動化してくれるので便利です。
例えば、特定のリストにカードを配置したら、チェックリストを全てチェックし、特定のラベルを削除し、リストの一番上にカードを移動するといったことを自動化してくれます。
自分のチームでは、カンバンを参考にタスクを管理していたために、PowerUpで仕組みをフォローできたのが良かったです。

このような細かなTrelloの使い方が習熟しました。

その他ソフトスキル

リーダー的なポジションだったために、会議体の運営を考えて実行することが多かったです。
事前の準備と会議が終わった時にどこまでの結果が出ているかを考えた上で望むとスムーズに運営ができたように思います。
情報共有、ワークショップ、勉強会といった目的の会議はこれらを意識すればほぼ問題なく進行できた手応えがあります。
イデア出し、課題解決が目的の会議は様々な意見があり、事前の想定とは異なる方向にいくことが多々あるのでまだ難しいです。
このあたりは引き続き工夫していきたいところです。

2021年後半に会社の同僚有志メンバーによる英会話の勉強会が始まり、可能な限り参加しました。
自分は英会話の経験が全くなかったので、当初はほぼしゃべれない感じでしたが、最近は聞かれたことに一言くらいは返せるようになったので少しは成長したのではと思っています。

2021年のまとめと来年の抱負

AWSに関しては2020年でそれなりに経験したかと思っていましたが、AWS運用面ではまだまだやるべきことが多いということを感じました。
以前に学んだデータエンジニアリングの技術を使った業務が中心となり、10月以降は技術的な学びが少なくなってしまいました。
来年以降は自分の担当範囲が拡大し、広くITエンジニアリング全般をフォローする必要があります。
特にフロントエンド周りはキャッチアップできていないので、そのあたりを身につけることと、データエンジニアリング領域の最新動向をフォローしていきたいです。
ソフトスキルは一朝一夕で身につくものではないので、メンバーの意見を聞きつつ、自チームにあったより良いやり方を見つけていければと思います。

GitHub Actionsを使ったECSのデプロイメントパイプライン構築

AWS Advent Calendar 2021 カレンダー2の6日目の記事です。

ここでは、GitHub Actionsを使ったECSのデプロイメントパイプラインを構築する方法を紹介します。
ECSのデプロイにおいて、GitHub Actionsを使う理由は以下のとおりです。

  • 環境構築、コンテナイメージのビルド、イメージのデプロイといった一連のタスクを一つのファイルに定義できる(ワークフローの定義)
  • Actionsといった形式で各タスクの記述を簡素化できる
  • GitHub OIDC トークンを使って、AWSへの処理をセキュアにできる

このような点で、現在ECSのデプロイメントパイプラインを構築するにはGitHub Actionsを使うのが有力だと考えています。

以前はGitHub ActionsからAWSにアクセスするにはアクセスキーをGitHub上で保持する必要があり、セキュリティ的に不安でした。
そのため、AWS上でパイプラインを構築する方法をよく採用していました。
ただCodePipelineやCodeBuildといったサービスを利用してパイプラインを構築するのは手間がかかると感じています。
この点が最近のGitHub Actionsのアップデートで解消されたというのが背景にあります。

今回のソースは以下にあります。

github.com

概要

デプロイメントパイプラインの構成

今回構築したデプロイメントパイプラインの構成は以下のとおりです。
左から順番に検証環境の作成、ステージング環境の更新、本番環境の更新と流れます。

f:id:hi1280:20211205153122p:plain

Pull Request作成時に検証環境が作成され、mainブランチにマージされた時にステージング環境が更新され、開発者の手動実行で本番環境が更新されます。
厳密には各環境の作成・更新はワークフローが分かれているため、処理の流れは繋がっていません。
そのため、ステージング環境更新の前に本番環境を更新するといったこともできてしまい、それを行うとエラーになります。
ただこのあたりは運用でカバーできる範囲と考えていますので、問題にはしていません。

AWSの環境構成

AWS上の環境構成は以下のとおりです。
バックエンドにDBが存在する標準的なWebアプリケーションを想定した構成です。

f:id:hi1280:20211205112639p:plain

検証環境ではAurora Serverlessを利用していて、ステージング環境、本番環境では通常のAuroraを利用しています。
検証環境は頻繁に作り直しが発生することが予想されるための配慮です。

構築方法

今回の構成を構築するにあたっての要点を解説したいと思います。

GitHub Actions環境でのAWS認証

前述のとおり、AWSとの認証にはGitHubのOIDCトークンを利用しています。
各ジョブにおいて、以下の通りにAWSと認証を行なっています。

create-qa.yml

...
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-role
        aws-region: ap-northeast-1
...

OIDCによる認証方式はaws-actions/configure-aws-credentialsのActionで対応されているので非常に簡単です。

認証を行うためのAWS側の設定は共通のTerraformリソースで作成しています。以下の内容になります。

iam.tf

resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"

  client_id_list = [
    "sts.amazonaws.com"
  ]

  thumbprint_list = [
    "a031c46782e6e6c662c2c87c76da9aa62ccabd8e"
  ]
}

resource "aws_iam_role" "github" {
  name = "github-role"

  assume_role_policy = <<-EOS
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Federated": "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"
        },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringLike": {
            "token.actions.githubusercontent.com:sub": [
              "repo:${var.repository}:*"
            ]
          }
        }
      }
    ]
  }
  EOS
}

resource "aws_iam_role_policy_attachment" "github" {
  role       = aws_iam_role.github.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}

内容を簡潔にするために、GitHub Actionsで利用するIAMロールにはAdministratorAccessのIAMポリシーを与えています。
実利用に合わせてIAMポリシーを見直してください。

GitHub Actionsのトリガー

各種GitHubのイベントをトリガーにすることで、パイプライン化しています。
検証環境への操作はPull Requestをトリガーにして、各種操作時にワークフローを実行します。

Pull Requestを作成した際にワークフローを実行します。

create-qa.yml

...
on:
  pull_request:
    types: [opened]
    branches:
      - main
...

Pull Requestを更新した際には、types: [synchronize]を指定することで、Pull Requestに対してのコミットで検証環境が更新されるようにします。
Pull Requestをマージした時やクローズした時には、types: [closed]を指定することで、検証環境が削除されるようにします。

ステージング環境の更新はPull Requestがマージされた時にmainブランチにPushされることをトリガーにします。

update-staging.yml

...
on:
  push:
    branches:
      - main
...

本番環境の更新はworkflow_dispatchで手動実行に対応します。

update-production.yml

...
on:
  workflow_dispatch
...

GitHub Actionsの画面から手動でワークフローを実行することもできますが、deploy-production.shという簡単なシェルスクリプトを用意して、ワークフローを実行できるようにしています。

Terraformの実行環境

検証環境、ステージング環境、本番環境といった各環境の構築には、CodeBuild上でTerraformを実行しています。
CodeBuildにVPCを設定することで、VPC上のAuroraに接続が可能になります。
これにより、DBへのクエリ実行をTerraformから可能になります。
CodeBuildに設定するサブネットはプライベートサブネットである必要があり、パブリックサブネットにNATゲートウェイを配置して、アクセスできるようにします。

以下が参考情報です。
Amazon Virtual Private Cloud での AWS CodeBuild の使用 - AWS CodeBuild

検証環境の構築

検証環境はPull Request毎に作成しています。これにはTerraformのWorkspaces機能で対応しています。
Workspacesを使うと、同じTerraformのリソースを元にして、別の環境を作成することができます。

以下のように、CodeBuildのbuildspecでTerraformを実行する際にWorkspacesを使っています。
terraform workspace newといったコマンドでWorkspacesで用意した別環境に対してTerraformを実行できます。

apply.yml

version: 0.2

phases:
  build:
    commands:
      - cd terraform/qa
      - terraform init -backend-config="bucket=$TFSTATE_BUCKET"
      - terraform workspace new $ENV || true
      - terraform workspace select $ENV
      - terraform apply -var tfstate_bucket=$TFSTATE_BUCKET -auto-approve

これにより、Pull Request毎に検証環境を作成することができています。

ECSへのデプロイ

ecspressoを利用しています。GitHub Actions上でのセットアップにActionが提供されており、GitHub Actionsで利用する場合には簡単に利用することができます。
ecspressoを使うと、環境変数の設定をコードで管理できますし、Terraformで作成した環境情報を参照できますので、非常に便利です。

GitHub Actionsの実行結果通知

検証環境への各種操作では、GitHubのPull Requestにコメントを行うことで通知としています。

create-qa.yml

...
    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Create comments
      env:
        GH_TOKEN: ${{ github.token }}
        GH_REPO: ${{ github.repository }}
        DOMAIN: ${{ secrets.DOMAIN }}
      run: |
        gh pr comment ${{ github.event.pull_request.number }} --body "検証環境を作成しました。
        https://$ENV.$DOMAIN"
...

GitHub CLI(ghコマンド)を使うとPull RequestへのコメントなどGitHubへの操作が簡単です。

ステージング環境、本番環境の更新では、Slackのチャンネルにメッセージすることで通知しています。

...
    steps:
    - name: Post to a Slack channel
      id: slack
      uses: slackapi/slack-github-action@v1.16.0
      env:
        SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
      with:
        channel-id: ${{ secrets.SLACK_CHANNEL }}
        slack-message: |
          ステージング環境を更新しました(${{ env.IMAGE_TAG }})
          https://staging.${{ secrets.DOMAIN }}
...

Slackへのメッセージにあたっては、以下のSlack App方式を採用しています。

GitHub - slackapi/slack-github-action: Send data into Slack using this GitHub Action!

各実行結果

Pull Requestを作成すると以下のように検証環境を作成して、コメントが投稿されます。
f:id:hi1280:20211205152451p:plain:w900

Pull Requestをクローズすると検証環境を削除します。
f:id:hi1280:20211205152535p:plain:w900

mainブランチにマージされてステージング環境が更新されるとSlackにメッセージされます。
また、本番環境が更新される場合でも同様です。
f:id:hi1280:20211205152615p:plain:w700

まとめ

今までECSのデプロイメントパイプラインを構築するにあたってはこれといった手法がなかったのですが、今回GitHub Actionsを使用してパイプラインを構築することで満足するものができたと思います。
GitHub OIDCトークンの話など各所で便利な仕組みが作られているので日々キャッチアップして、上手に使っていくことが必要だなと感じています。
厳密にパイプラインを管理したい場合、誰でも本番環境を更新できてしまうのでマズイところもあると思います。
ただ簡易的に運用したい場合には、今回のような仕組みで十分だと考えています。

参考

今回の内容を実施するにあたって、以下を参考にさせて頂きました。

GitHub Actions + AWS CodeBuildでPRごとの検証環境を作ってみた