HerokuでGoアプリケーションを動かしたい

Posted by on Thu, Mar 23, 2017

はじめに

Goで作ったWebアプリケーションをHerokuにデプロイする手順を書いておこうと思います。最初はDockerを使わずにデプロイし、あとでDocker化してみようと思います。

セットアップ

Herokuのコマンドラインツールのインストール

Herokuのコマンドラインツールをインストールしましょう

こちらから各環境のコマンドラインツールをインストールできます。

そしてツールをインストールしたら、

% heroku login

でログインしておきます。

Heroku側にnew appを作成

HerokuにデプロイするためにHerokuに新規アプリを作成しましょう

% heroku apps:create heroku-with-go --buildpack heroku/go

--buildpackはHeroku上でビルドするのに必要ですのでGo用のbuildpackを設定しておきます。オープンソースですので、こちらで見ることが出来ます。

新規アプリが作成されたかブラウザで確認してみます。

% heroku open --app heroku-with-go

Goアプリケーション作成してからHerokuデプロイまで

Goアプリケーションを準備するのですが、Getting Started on Heroku with Goには、Githubからサンプルをクローンしてきて、デプロイしてみようみたな感じですが、ここではこのサンプルは使わず、1から自分で作ってみようと思います。

プロジェクト(ディレクトリ)作成

$GOPATH にWebアプリケーションプロジェクトを作成しましょう。 今回はheroku-with-goディレクトリを作成しました。

$ mkdir $GOPATH/src/github.com/kwmt/heroku-with-go

heroku-with-go にWebアプリを書いて行きましょう。

main.go作成

とりあえず、簡単のためにURLのPathを表示するだけのアプリを書いてみます。(環境変数PORTを取得できるようにする必要があります)

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
)

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		log.Fatal("$PORT must be set")
	}

	http.HandleFunc("/", handler)
	http.ListenAndServe(":"+port, nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %q", r.URL.Path[1:])
}

Procfile作成

Herokuで動かすには、Procfileというファイルが必要ですので、それも作成しましょう。(Procfileについては、こちらに詳細があります)

Procfileには、Heroku上で自分のアプリケーションを動かすためのコマンドを記述しますので、たとえば次のように記述します。

web: heroku-with-go

heroku-with-goというのは、最初に作成したディレクトリです。つまり、ここでは$GOPATH/src/github.com/kwmt/heroku-with-goheroku-with-goの部分です。 下記でBuildpackというのを設定するのですが、そのなかで自動的にGoプログラムをgo buildして go install$GOPATH/binにインストールされる仕組みになっていますので、herokuプロジェクト名を書いて置くといいでしょう。ソースはこのあたりを参照すると良いかもしれません。

vendor.json作成

vendor/vendor.jsonファイルを作成しましょう。これは依存ライブラリを使っていなくても必須のようです。簡単でいいので作成しておきます。

{
	"rootPath": "github.com/kwmt/heroku-with-go"
}

簡単といってもrootPathだけは必要のようです。{}だけだと

-----> Go app detected
-----> Checking vendor/vendor.json file.
 !!    The 'rootPath' field is not specified in 'vendor/vendor.json'.
 !!    'rootPath' must be set to the root package name used by your repository.
 !!    Recent versions of govendor add this field automatically, please upgrade
 !!    and re-run 'govendor in

と怒られてしまいます。

また、vendor/vendor.jsonがないときにherokuにpushすると、次のようにdeployに失敗してしまいます。

% git push heroku master
Counting objects: 15, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (12/12), done.
Writing objects: 100% (15/15), 1.64 KiB | 0 bytes/s, done.
Total 15 (delta 2), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Failed to detect app matching https://github.com/heroku/heroku-buildpack-go.git buildpack
remote:        More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure
remote: 
remote:  !     Push failed
remote: Verifying deploy...
remote: 
remote: !	Push rejected to heroku-with-go.
remote: 
To https://git.heroku.com/heroku-with-go.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/heroku-with-go.git'

Herokuにデプロイ

ここまでで最低限の準備ができましたので、Herokuにデプロイしてみます。下記のようにherokuのgitレポジトリにpushするだけです。

% git push heroku master

heroku は新規アプリ作ったとき(heroku createしたとき)に自動的に作成されています。git remoteコマンドで確認できます。

% git remote -v
heroku	https://git.heroku.com/heroku-with-go.git (fetch)
heroku	https://git.heroku.com/heroku-with-go.git (push)

ちゃんとデプロイされたかブラウザで確認してみます。

% heroku open world

とすると

と期待どうりになっていることがわかるかと思います。

Dockerを使いたい

前提として

上記ディレクトリをコピーして名前heroku-with-dockerと少し変更して作業を進めています

cp -r $GOPATH/src/github.com/kwmt/heroku-with-go $GOPATH/src/github.com/kwmt/heroku-with-docker

heroku-container-toolsプラグインをインストールしておきます。(このプラグインはdeprecatedなので本当は使いたくないのですが。。)

% heroku plugins:install heroku-container-tools 

app.json

下記のようなapp.jsonファイルを作成します。

{
  "name": "Heroku with Docker",
  "description": "Sample",
  "image": "heroku/go:1.6",
  "mount_dir": "src/github.com/kwmt/heroku-with-docker",
  "website": "http://github.com/kwmt/heroku-with-docker",
  "repository": "http://github.com/kwmt/heroku-with-docker"
}

これはheroku-container-toolsプラグインの

% heroku container:init

というコマンドを使って、次のようなDockerfiledocker-compose.ymlファイルを自動生成するためのものです。

FROM heroku/go:1.6
web:
  build: .
  command: 'bash -c ''heroku-with-docker'''
  working_dir: /app/user/src/github.com/kwmt/heroku-with-docker
  environment:
    PORT: 8080
  ports:
    - '8080:8080'
shell:
  build: .
  command: bash
  working_dir: /app/user/src/github.com/kwmt/heroku-with-docker
  environment:
    PORT: 8080
  ports:
    - '8080:8080'
  volumes:
    - '.:/app/user/src/github.com/kwmt/heroku-with-docker'

ぶっちゃけこれらのファイルの書き方が分かってしまえば、app.jsonは不要です。といいますか、heroku-container-toolsはdeprecatedになっているので、今後heroku container:initする手段がなくなります。。。

そもそもheroku container:initheroku-container-toolsプラグインのコマンドなんですが、このプラグインはdeprecatedになってたり。 addonはここにあるものしかかけないし・・・

デプロイ

% heroku container:release

以上でデプロイできて、動いた。わーい!

とはいかなくて、Godeps/Godeps.jsonファイルを作らないと動きません。

% cat Godeps/Godeps.json
{
    "ImportPath": "github.com/kwmt/heroku-with-docker",
    "GoVersion": "go1.6",
    "Deps": []
}

Dockerイメージheroku/goがGodepsに依存してしまってて。。

とはいえ、Godeps.jsonファイルを作れば下図のようにデプロイ出来ました。

まとめ(Heroku+Dockerが微妙なとこ)

  • Dockerのベースイメージを自由にできない
  • ベースイメージを自由にできないに関係するが、docker-composeのリンクは決まったものしか対応していない(redisとかpostgresは使えるがmysqlが使えないとか)
  • vendoringツールはGodepsだけしか使えない(サンプルではgovendorに移行したというコミットコメントがあったのでできるのかと思ったけど、ベースイメージのソース見る限りできなさそう?やり方がありそうだが、READMEにもGodepsが必要ってあるし、頑張るところでもない気がするので諦める)
  • おそらく上記を解決するのがheroku-container-registryプラグインな気がするけど、まだ解決できてなさそう。

サンプルソース

参考



comments powered by Disqus