Terraform と GItHub Actions で DigitalOcean Droplets の CI/CD を作る

VPS は DigitalOcean の Droplets が安いのでたまに利用しています。

www.digitalocean.com

Terraform は DigitalOcean に対応していて専用のプラグインが HashiCorp から提供されています。

www.terraform.io

Terraform 用の GitHub Actions も公開されていました。

GitHub - hashicorp/terraform-github-actions: Terraform GitHub Actions

Droplet は Web 画面をポチポチして作るのも簡単ですが、Terraform の勉強を兼ねて Actions で CI/CD することにしました。

Droplet を作る tf ファイル。5$ / mo のマシンを指定しています。DigitalOcean プロバイダーを定義しておくと、terraform init 実行時に必要なプラグインをダウンロードしてくれます。API トークンは環境変数 DIGITALOCEAN_TOKEN に展開しておきます。

provider "digitalocean" {
  # export DIGITALOCEAN_TOKEN="Your API TOKEN"
}

data "digitalocean_ssh_key" "ssh_key" {
  name = "blink"
}

resource "digitalocean_droplet" "dev" {
  image = "${var.ubuntu}"
  name = "dev-01"
  region = "${var.do_sgp1}"
  size = "s-1vcpu-1gb"
  ssh_keys = [data.digitalocean_ssh_key.ssh_key.id]
}

リージョンや OS は変数用の tf ファイルに書いて参照しています。

# Datacenter regions
variable "do_sgp1" {
  description = "Digital Ocean Singapore Data Center 1"
  default     = "sgp1"
}

# OS
variable "ubuntu" {
  description = "Eoan Ermine"
  default     = "ubuntu-19-10-x64"
}

GitHub Actions のワークフロー定義。PR 作成時に tf ファイルを検証して plan を出力、レビューできるようにします。

各 step で terraform のサブコマンド、init / validate / plan を実行しています。PR を編集するための GitHub アクセストークンと、DigitalOcean の API トークンは Secrets に格納して環境変数に渡しています。

name: Terraform Plan for droplet

on:
  pull_request:
    types: [opened]

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout'
        uses: actions/checkout@master
      - name: 'Terraform Init'
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.15
          tf_actions_subcommand: 'init'
          tf_actions_working_dir: 'droplet'
          tf_actions_comment: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TF_TOKEN }}
          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
      - name: 'Terraform Validate'
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.15
          tf_actions_subcommand: 'validate'
          tf_actions_working_dir: 'droplet'
          tf_actions_comment: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TF_TOKEN }}
          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
      - name: 'Terraform Plan'
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.15
          tf_actions_subcommand: 'plan'
          tf_actions_working_dir: 'droplet'
          tf_actions_comment: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TF_TOKEN }}
          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}

この状態で branch を作成し tf ファイルを更新して PR を作成すると、ワークフローが実行され PR の画面で実行結果をレビューすることができます。

f:id:kondoumh:20191119215754p:plain

plan の結果も表示できます。

f:id:kondoumh:20191119215828p:plain

PR がマージされる時に実行するワークフローです。init の後続でサブコマンド apply を指定しているのと、PR がマージされたかどうか (github.event.pull_request.merged == true) を判定する if を入れています。

name: Terraform Apply droplet

on:
  pull_request:
    types: [closed]

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout'
        uses: actions/checkout@master
      - name: 'Terraform Init'
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.15
          tf_actions_subcommand: 'init'
          tf_actions_working_dir: 'droplet'
          tf_actions_comment: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TF_TOKEN }}
          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
      - name: 'Terraform Apply'
        if: github.event.pull_request.merged == true
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.15
          tf_actions_subcommand: 'apply'
          tf_actions_working_dir: 'droplet'
          tf_actions_comment: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TF_TOKEN }}
          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}

PR をマージするとワークフローが実行されました。

f:id:kondoumh:20191119222408p:plain

そして無事 Droplet が作成されました。

f:id:kondoumh:20191119222440p:plain

文字通り Infrastructure as Code / GitOps を地で行くようなワークフローです。

単体の Droplet 作成には羊頭狗肉感は否めませんが、業務で AWS の複雑な依存関係を持つリソースを Terraform で作っているような場合にはかなり役立ちそうです。