DevOps エンジニアの 根本 征 です。
7月からエクサウィザーズ にジョインし、CI / CD パイプラインの改善や自動テストの布教などを行っています。
今回は GitHub Actions の self-hosted runners を AWS ECS 上に構築し運用してみたので、その試行錯誤について紹介したいと思います。
- GitHub Actions と self-hosted runners
- self-hosted runners を Docker で動かす
- self-hosted runners を AWS ECS で動かす
- アプリケーションを AWS ECS へデプロイする Workflow を作る
- おわりに
GitHub Actions と self-hosted runners
GitHub Actions は GitHub ユーザーであれば現在多くの方がご存知・ご活用されているかと思います。
GitHub Marketplace で公開されている Actions と組み合わせることによって、簡単に様々な CI / CD パイプラインを構築することができます。
そんな GitHub Actions ですが、GitHubが提供する Runner を使う代わりに自前で用意することもできます(self-hosted runners)。
GitHub が提供する Runner で GitHub Actionsを利用する場合、無料利用枠 + 従量課金の課金体制になります。
しかし、self-hosted runners で GitHub Actions を利用する場合には別途料金がかかることはありません。
Repository または Organization の設定からself-hosted runners をセットアップすることができます(Linux / MacOS / Windows 毎に手順が示されます)。
セットアップが完了すると、Runner として追加されていることが確認できます。
Repository で設定した Runner はその Repository で、そして Organization で設定した Runner はその Organization 内の全ての Repository で実行することができます。
最後に GitHub Actions の設定ファイルにおいて、self-hosted runners で実行することを記述します。
jobs: build: runs-on: self-hosted
self-hosted runners はマシン自体は自分たちで調達・メンテナンスをしないといけないですが、ワークフローを管理する部分(Jenkins でいう master)を自前で用意せず無料で利用できる点は魅力的だと感じます。
self-hosted runners を Docker で動かす
self-hosted runners 自体はオープンソースとして公開されていますが、現在 Docker イメージは提供されていません。
調べたところ、Docker・Kubernetes で self-hosted runners 動かすためにオープンソースプロジェクトで様々な試行錯誤が行われているみたいです。
本記事では AWS ECS をメインに下記の手順で解説します。
self-hosted runners を Docker で動かす
self-hosted runners を AWS ECS で動かす
その self-hosted runners を用い、アプリケーションを AWS ECS にデプロイするパイプラインを作る
まず下記の Docker イメージを使い、手元で self-hosted runners を立ち上げてみます。
Organization に対して Runner を立ち上げるためには下記 docker コマンドを実行します。
docker run -d --restart always --name github-runner \ -e RUNNER_NAME_PREFIX="myrunner" \ -e ACCESS_TOKEN="footoken" \ -e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \ -e ORG_RUNNER="true" \ -e ORG_NAME="octokode" \ -e LABELS="my-label,other-label" \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \ myoung34/github-runner:latest
GitHub Actions では Docker の操作(ビルドなど)を行うため、このコンテナ内でも Docker コマンドが使えることが必要になります。
そのため、ホストマシン上の Docker daemon を共有することで解決しています(-v /var/run/docker.sock:/var/run/docker.sock
の部分、Docker outside of Docker)
Using Docker-in-Docker for your CI or testing environment? Think twice.
self-hosted runners を AWS ECS で動かす
次に先ほどの Docker イメージを使って、AWS ECSで動かしてみます。
今回は AWS CDK(Typescript) をベースに3つのポイントに絞って解説します。
1つ目に AWS ECS で Docker outside of Docker(DooD) をどう実現するかについてです(-v /var/run/docker.sock:/var/run/docker.sock
の部分)。
AWS ECS だと Task 側に Volume を追加し、Container 側にその Volume をマウントすることで解決することができます。
taskDefinition.addVolume({ name: "docker_sock", host: { sourcePath: "/var/run/docker.sock" } })
containerDefinition.addMountPoints({ containerPath: "/var/run/docker.sock", sourceVolume: "docker_sock", readOnly: true })
AWS ECS では起動タイプとして Fargate と EC2 がありますが、現状 Fargate で上記を行うことがサポートされていなかったため、今回はEC2起動タイプを選択しました。
2つ目に ECS Task に与える Role についてです。
今回の GitHub Actions 上でのパイプラインでは、Docker ビルド ・ECRへのイメージのアップロード・ECSへのデプロイまで行おうとしています。
もちろん GitHub 側に AWS アクセスキーを保存し、それを GitHub Action に渡すことで上記を実現することができます。
しかし、self-hosted runners のコンテナ自体に上記に必要な Role を与えることによって、GitHub側に AWS アクセスキーを保存する必要自体をなくすことができます。
const taskRole = new iam.Role(this, 'GitHubActionsSelfhostRunnerTaskRole', { assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerServiceFullAccess'), iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryPowerUser'), ], }) const taskDefinition = new ecs.Ec2TaskDefinition( this, `GitHubActionsTaskDefinition`, { taskRole: taskRole } );
3つ目に、スポットインスタンスについてです。
self-hosted runners は CI / CD として使うため、スポットインスタンスを活用してコストを抑えることができます。
AWS CDK の場合、spotInstanceDraining
プロパティを true にすることでスポットインスタンスを利用することができます。
cluster.addCapacity('GitHubActionsCapacity', { instanceType: new ec2.InstanceType("t3.small"), spotInstanceDraining: true });
アプリケーションを AWS ECS へデプロイする Workflow を作る
今回はこの GitHub Actions と self-hosted runners を活用して、アプリケーションを AWS ECS へデプロイする Workflow を作りたいと思います。
同じ AWS ECS なのでややこしくなってしまいますが、self-hosted runners を動かすクラスタとアプリケーションを動かすクラスタは別という想定です。
具体的には下記のような手順になります。
Docker build して ECR にPushする ↓ Task Definition ファイルを編集する(Dockerイメージの部分を新しくする) ↓ Task Definition を新たに登録し、Service が更新されるまで待つ
AWSで提供されている GitHub Actions の Step と組み合わせると下記のようになります。
name: Deploy to ECS on: push: branches: - master jobs: build: runs-on: self-hosted steps: - uses: actions/checkout - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials with: aws-region: ap-northeast-1 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login - name: Build, tag, and push image to Amazon ECR id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }} run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - name: Fill in the new image ID in the Amazon ECS task definition id: task-def uses: aws-actions/amazon-ecs-render-task-definition with: task-definition: task-definition.json container-name: hoge-container image: ${{ steps.build-image.outputs.image }} - name: Deploy Amazon ECS task definition uses: aws-actions/amazon-ecs-deploy-task-definition with: task-definition: ${{ steps.task-def.outputs.task-definition }} service: hoge-service cluster: hoge-cluster wait-for-service-stability: true
先ほども述べましたが、本来は aws-actions/configure-aws-credentials
の Step で下記のように AWS アクセスキーを GitHub に保存し渡してあげる必要があります。
- name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1
しかし、今回は self-hosted runners に必要な権限を渡してあげているのでこの必要はありません。
おわりに
今回はGitHub Actions の self-hosted runners を AWS ECS 上に構築してみましたが、下記のような状況でメリットがあると考えています。
GitHub Action を使ってアプリケーションをAWSへデプロイする際、GitHub 側に不必要に AWS アクセスキーを渡したくない
スポットインスタンスや活用することによって、コストパフォーマンスよく CI / CD 環境を運用したい(特に無料枠を大幅に超えることが予想される場合)
GitHub が提供する Runner よりスペックの高いマシンで CI / CD 環境を運用したい
今後、この GitHub Actions をベースにより良い CI / CD パイプラインが作れたら良いと考えています。