たった23行で Amazon ECS にデプロイできた。あらゆるプラットフォームでアプリケーションをビルド、デプロイする Waypoint を使ってみた。

これはワクワクするぞ・・。
2020.10.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

先日、Terraform でお馴染みの HashiCorp 社から ”Waypoint" という新たなオープンソースプロジェクトが発表されました。

アプリケーションのビルド、デプロイ、リリースといったワークフローを、Kubernetes/AWS/GCP/Azure など 12 以上のプラットフォームで利用可能。マルチプラットフォームに対応しているだけならば同様のツールはあるかもしれませんが、Waypoint は各プラットフォームごとのお作法などを抽象化し、開発者にインフラを意識させることなくデプロイ出来ることを目指したツールのようです。(位置づけてきに Spinnaker に近い?)

まだプロジェクトの初期ということで本番環境で利用できるものではないと思いますが、ひとまず ECS へのデプロイをサクッと試してみました。

Waypoint とは

まずは Waypoint のコンセプトや目標について簡単に説明します。 (以降の内容は Waypoint の開発に至った背景やプロジェクトの目標です。執筆時点では提供されていない機能も含んでいる点、ご注意ください。)

Waypoint が目指すところ

  • ワークフローの標準化
    • Waypoint は、アプリケーションを開発から本番環境へと移行するための使いやすく一貫性のあるワークフローを提供することを目指しています。プラットフォームに基づくワークフローの断片化は複数のビルド、デプロイ、リリースツールを使用しようとするチームにとって大きな課題です
  • デプロイに確信をもつ
    • デプロイ後に開発者が最初に行うことは、ブラウザを開いたり、ページを更新したり、ログをチェックしたりと、動作を確認することです。Waypoint はバージョン管理可能な検証用の URLや、シェルスクリプトを介してリモートコマンドを実行する exec、デバッグ用の一時ログセットなどを提供し、デプロイが成功したことを確信できるようにします
  • エコシステムによる拡張性
    • Waypoint は、カスタムビルダーやデプロイメントプラットフォームなどを取り入れることができるプラグインを介して完全に拡張可能です

なぜ Waypoint?

Waypoint は以下のような問題を解決します

  • 「デプロイしたいだけやねんけど」問題
    • 開発のパターンがコンテナ、スケジューラ、YAML ファイル、サーバーレスなど複雑さに溢れていることが現実。最初のアプリケーションをデプロイするまでに学習コストがかかります。Waypoint はプラットフォームの違いをラップします。
  • 「ツール多すぎんねん」問題
    • もう 1 つの課題はアプリケーションをデプロイする場所によって使用するツールが異なるということ。Kubernetes には kubectl と Docker、VM には Packer と Terraform、各サーバーレスにはカスタム CLI など。このような現実が個人においては学習コスト、チームにとっては一貫性という課題をもたらしています。Waypoint はこれらの環境をラップし、オーケストレーターとして機能することでワークフローを標準化します。

使いやすさの提供

Waypoint は使いやすさを提供するために作られました。もう Dockerfile や YAML を書く必要はない。言語を自動検出し、イメージをビルドし、デプロイするためのプラグインも用意されている。最低限の設定を書く必要はあるが、1 つのツールで 15 行程度で済みます。

どんなプラットフォームでも

Waypoint はどんなプラットフォームでも一貫性があるように構築されている。プラグインを使って拡張が可能であり、あらゆるビルド、デプロイ、リリースのロジックをターゲットにすることができます。また、Waypoint そのものは継続的インテグレーションやテスト自動化を提供するものではなく、既存のそういったツールのラッパーでありオーケストレーターという位置づけのようです。

「開発者は、ただデプロイをしたいだけです。Waypoint はそれを実現します」

とても魅力的なメッセージですね。 

やってみる

Waypoint が目指している方向性を理解したところで、さっそく触ってみましょう。

事前準備

  • Amazon ECS へのデプロイを検証します。該当の AWS アカウントで十分な IAM 権限を持っていることを前提としています。(今回は Administrator 権限で検証しています)
  • 今回、ビルドのアーティファクト の push 先に ECR を利用します。ECR は事前に作成済みとします。

インストール

Waypoint は Windows, Linux, Mac OS X の環境で動作します。今回は公式ガイドの Homebrew on OS X 手順に従ってインストールします。

$ brew --version
Homebrew 2.5.6
Homebrew/homebrew-core (git revision d1b6d; last commit 2020-10-19)
Homebrew/homebrew-cask (git revision b0b12; last commit 2020-10-19)

# Hashicorp のリポジトリを追加
$ brew tap hashicorp/tap
Updating Homebrew...
==> Tapping hashicorp/tap
Cloning into '/usr/local/Homebrew/Library/Taps/hashicorp/homebrew-tap'...
remote: Enumerating objects: 176, done.
remote: Counting objects: 100% (176/176), done.
remote: Compressing objects: 100% (103/103), done.
remote: Total 585 (delta 89), reused 154 (delta 73), pack-reused 409
Receiving objects: 100% (585/585), 113.37 KiB | 331.00 KiB/s, done.
Resolving deltas: 100% (256/256), done.
Tapped 8 formulae (44 files, 192KB).

# インストール
Updating Homebrew...
==> Installing waypoint from hashicorp/tap
==> Downloading https://releases.hashicorp.com/waypoint/0.1.2/waypoint_0.1.2_darwin_amd64.zip
######################################################################## 100.0%
/usr/local/Cellar/waypoint/0.1.2: 3 files, 141.7MB, built in 5 seconds
==> `brew cleanup` has not been run in 30 days, running now...
Removing: /Users/marumo.atsushi/Library/Caches/Homebrew/cert--0.15.0.tar.gz... (2.1MB)
Removing: /Users/marumo.atsushi/Library/Caches/Homebrew/krb5--1.18.1.catalina.bottle.tar.gz... (1.3MB)
Removing: /Users/marumo.atsushi/Library/Caches/Homebrew/libpq--12.2_1.catalina.bottle.tar.gz... (5.9MB)
Removing: /Users/marumo.atsushi/Library/Caches/Homebrew/openssl@1.1--1.1.1g.catalina.bottle.tar.gz... (5.3MB)
Removing: /Users/marumo.atsushi/Library/Logs/Homebrew/krb5... (64B)
Removing: /Users/marumo.atsushi/Library/Logs/Homebrew/gettext... (64B)
Removing: /Users/marumo.atsushi/Library/Logs/Homebrew/cert... (115B)
Removing: /Users/marumo.atsushi/Library/Logs/Homebrew/libpq... (64B)
Removing: /Users/marumo.atsushi/Library/Logs/Homebrew/pcre2... (64B)
Removing: /Users/marumo.atsushi/Library/Logs/Homebrew/openssl@1.1... (64B)
Removing: /Users/marumo.atsushi/Library/Logs/Homebrew/git... (64B)
Pruned 1 symbolic links and 2 directories from /usr/local

$ waypoint version
Waypoint v0.1.2 (edf37a09)

これで実行環境は整いました。

Amazon ECS にデプロイ

以下のチュートリアルに従って進めます。(ほかに Kubernetes、Google Cloud、Azure といったチュートリアルもあるので、興味のある方はお試しください)

git からサンプルプロジェクトを clone し、ECS 用のサブディレクトリに移動します。

$ git clone https://github.com/hashicorp/waypoint-examples.git
$ cd ./waypoint-examples/aws-ecs/nodejs
$ tree
.
├── Procfile
├── README.md
├── index.js
├── package.json
├── public
│   ├── hashi.svg
│   ├── language.svg
│   ├── logo.svg
│   ├── pattern-br.svg
│   ├── pattern-tl.svg
│   └── stylesheets
│       └── main.css
├── views
│   ├── pages
│   │   └── index.ejs
│   └── partials
│       └── header.ejs
└── waypoint.hcl

waypoint.hcl

waypoint.hcl は Waypoint の設定ファイルです。ビルド、デプロイ、リリースのワークフローをこの設定ファイル 1 つで定義します。驚くべきはコード量の少なさですね。空の行を除くとわずか 23 行です。シンプルなチュートリアルとはいえ、少なすぎる!!

project = "example-nodejs"

app "example-nodejs" {
  labels = {
    "service" = "example-nodejs",
    "env" = "dev"
  }

  build {
    use "pack" {}
    registry {
      use "aws-ecr" {
        region = "ap-northeast-1"
        repository = "waypoint-example"
        tag = "latest"
      }
    }
  }

  deploy {
    use "aws-ecs" {
      region = "ap-northeast-1"
      memory = "512"
    }
  }
}

ざっくり説明すると、build スタンザで Waypoint がアプリをビルドする方法を定義します。use "pack" オプションは Cloud Native Buildpack を使用してアプリケーションをビルドするように指示しています。

registry スタンザはビルドしたアーティファクトの push 先を指定します。この例では use "aws-ecr" を指定し ECR に push します。

deploy スタンザはビルドで作成したアーティファクトを取得、ターゲットプラットフォームにデプロイします。この例では ap-northeast-1 の ECS へのデプロイを指示しています。memory のみ指定していますが subnetalblog_group などの指定も可能です。(その他のオプションはこちらを参照

また、以下のようなプラットフォームの指定が可能です。

  • Kubernetes
    • AWS EKS、Azure AKS、Google GKE、Kubernetes for Docker Desktopなど
  • HashiCorp Nomad
  • AWS EC2
  • AWS ECS
  • Google Cloud Run
  • Azure Container Instances
  • Netlify

その他にも hook スタンザを使用してビルドやデプロイの前後にコマンドやスクリプトを実行することが可能です。

ローカル Waypoint サーバーのインストール

Waypoint サーバーはデータを保存し、ビルド、デプロイなどの操作をオーケストレーションするサーバーです。グループなどメンバーで利用する場合は 1 つの(共通の) Waypoint サーバーを利用する必要がありますが、今回は個人利用なのでローカルサーバーとして、Mac 内の Docker に Waypoint サーバーを作成します。

まずは Docker イメージをダウンロードします。

$ docker pull hashicorp/waypoint:latest
latest: Pulling from hashicorp/waypoint
df20fa9351a1: Pull complete 
464c4c2e60be: Pull complete 
802d9b65569c: Pull complete 
Digest: sha256:689cae07ac8836ceba1f49c0c36ef57b27ebf61d36009bc309d2198e7825beb9
Status: Downloaded newer image for hashicorp/waypoint:latest
docker.io/hashicorp/waypoint:latest

$ docker images hashicorp/waypoint 
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
hashicorp/waypoint   latest              3792b8e01ae0        2 days ago          145MB

次に、install コマンドでローカルの Docker インスタンスに Waypoint サーバーをインストールします。Waypoint サーバーを Kubenetes にすることも出来ます。(accept-tos は利用規約への同意フラグです)

$ waypoint install --platform=docker -accept-tos
✓ Server container started
Waypoint server successfully installed and configured!

The CLI has been configured to connect to the server automatically. This
connection information is saved in the CLI context named "install-1603095163".
Use the "waypoint context" CLI to manage CLI contexts.

The server has been configured to advertise the following address for
entrypoint communications. This must be a reachable address for all your
deployments. If this is incorrect, manually set it using the CLI command
"waypoint server config-set".

Advertise Address: waypoint-server:9701
HTTP UI Address: https://localhost:9702

https://localhost:9702 は Waypoint サーバーへのアクセス URL になります。

Waypoint サーバーへのログインは waypoint token new コマンドで取得した token を入力し、Authenticate with Token をクリック。

Waypoint を初期化

アプリケーションをビルドしてデプロイする前に init コマンドでプロジェクトを初期化します。初期化プロセスでは、アプリケーションディレクトリ内の Waypoint 構成ファイル waypoint.hcl を探します。構成ファイルが存在しない場合は、スターターファイルとしての waypoint.hcl を作成します。

$ waypoint init
✓ Configuration file appears valid
✓ Connection to Waypoint server was successful
✓ Project "example-nodejs" and all apps are registered with the server.
✓ Plugins loaded and configured successfully
✓ Authentication requirements appear satisfied.

Project initialized!

You may now call 'waypoint up' to deploy your project or
commands such as 'waypoint build' to perform steps individually.

アプリケーションのビルド、デプロイ、リリース

up コマンドでアプリケーションをビルド、デプロイします。

$ waypoint up

» Building...
Creating new buildpack-based image using builder: heroku/buildpacks:18
✓ Creating pack client
✓ Building image
 │ [exporter] Adding 1/1 app layer(s)
 │ [exporter] Adding layer 'launcher'
 │ [exporter] Adding layer 'config'
 │ [exporter] Adding label 'io.buildpacks.lifecycle.metadata'
 │ [exporter] Adding label 'io.buildpacks.build.metadata'
 │ [exporter] Adding label 'io.buildpacks.project.metadata'
 │ [exporter] *** Images (35082e96dc14):
 │ [exporter]       index.docker.io/library/example-nodejs:latest
 │ [exporter] Adding cache layer 'heroku/nodejs-engine:nodejs'
 │ [exporter] Adding cache layer 'heroku/nodejs-engine:toolbox'
✓ Injecting entrypoint binary to image

Creating new buildpack-based image using builder: heroku/buildpacks:18
✓ Creating pack client✓ Building image
 │ [exporter] Adding 1/1 app layer(s)
 │ [exporter] Adding layer 'launcher'
 │ [exporter] Adding layer 'config'
 │ [exporter] Adding label 'io.buildpacks.lifecycle.metadata'
 │ [exporter] Adding label 'io.buildpacks.build.metadata'
 │ [exporter] Adding label 'io.buildpacks.project.metadata'
 │ [exporter] *** Images (35082e96dc14):
 │ [exporter]       index.docker.io/library/example-nodejs:latest
 │ [exporter] Adding cache layer 'heroku/nodejs-engine:nodejs'
 │ [exporter] Adding cache layer 'heroku/nodejs-engine:toolbox'
✓ Injecting entrypoint binary to image

Generated new Docker image: example-nodejs:latest
Tagging Docker image: example-nodejs:latest => 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/waypoint-example:latest
Docker image pushed: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/waypoint-example:latest

» Deploying...
✓ Created new ECS cluster: waypoint
✓ Created IAM role: ecr-example-nodejs
✓ Created CloudWatchLogs group to store logs in: waypoint-logs
✓ Created ALB target group
✓ Created new ALB Listener
✓ Configured security group: example-nodejs-inbound-internal
✓ Created ECS Service (example-nodejs-A8Q6ECFPE8G9R2R5G, cluster-name: waypoint)

» Releasing...

The deploy was successful! A Waypoint deployment URL is shown below. This
can be used internally to check your deployment and is not meant for external
traffic. You can manage this hostname using "waypoint hostname."

   Release URL: http://waypoint-ecs-example-nodejs-397207959.ap-northeast-1.elb.amazonaws.com
Deployment URL: https://manually-prepared-sawfish--v1.waypoint.run

http://waypoint-ecs-example-nodejs-397207959.ap-northeast-1.elb.amazonaws.com にアクセスして、正常にデプロイされたことを確認します。

Deployment URL では Let's Encrypt で自動的に生成された証明書を利用し、デプロイしたアプリケーションを素早く表示したり、他の人とアプリケーションを共有したりすることができるそうなのですが、なぜだか表示できませんでした。まだリリース初期であるためバグなのか、設定が足りていないのか判断することが出来ず今回はスルーしました。

AWS 環境の確認

up コマンドのログからもわかるように、以下のようなリソースが作成されています。

  • ECS タスク、サービス、クラスター
  • IAM ロール
  • CloudwatchLogs グループ
  • ALB、ターゲットグループ
  • セキュリティグループ

いくつか確認してみましょう。まずは ECR ですが、ビルドされたイメージが push されてます。

次に ECS ですが、クラスターやサービスが作成され、タスクが起動しています。今回のサンプルですと Fargate で起動していました。VPC は特に指定していなかったので default VPC で起動しています。

セキュリティグループも自動的に作成されています。

ELB は、ALB が作成されリスナーやターゲットグループが設定されています。

Waypoint サーバーを確認

https://localhost:9702 にアクセスし、Waypoint サーバーを確認してみるとプロジェクトが追加されています。

example-nodejs をクリック すると、このようなダッシュボードが表示されます。

ビルド時のログがなども確認できます。

ちなみに Exec タブからリモートコマンドの実行もできるようですが、こちらの機能はまだ提供されていないようです。

環境の破棄

destory コマンドでプロビジョニングを解除します。destroy コマンドは、レジストリまたは ECS クラスター自体のコンテナーイメージを削除しません。ECR コンソールにアクセスして、レジストリ全体または個々のコンテナイメージを手動で削除します。

$ waypoint destroy
Destroy successful!

と書きましたが、現状はまだ動作しないようなので、後片付けは手作業でやりましょ。。

さいごに

プラットフォームのインフラ部分を意識せずにデプロイのみに集中したい開発者の方は、是非一度触っていただきたいです。

逆に細かい設定部分までをコントロールしたいユーザーには抽象度が高すぎるかもしれません。

Waypoint はまだリリースされたばかりなので、その実力をはかるには、もうしばらく時間が必要かと思いますが今後の展開から目を離せないサービスであることは間違いなさそうです!ロードマップの一部は以下で公開されています。

今回はチュートリアルを実行しただけですので、次はもう少し AWS の観点でどこまでの設定ができるのか試してみたいと思います。

以上!大阪オフィスの丸毛(@marumo1981)でした!

参考