LoginSignup
18

More than 5 years have passed since last update.

Go と Vue.js なアプリを Heroku で動かす

Last updated at Posted at 2017-11-06

サーバサイドを Go で WebAPI として独立して作り、フロントは Vue.js で SPA チックに作り、Static な成果物(HTML/CSS/JS)を Go バイナリに内包して Heroku で動かすメモです。

サンプルのソースコードはこちらです。

前提条件

  • Go 1.9+
    • パッケージ管理: dep
    • DBマイグレーション: pressly/goose
  • Vue.js 2.5+
    • vue-cli
    • ビルドツール: webpack
  • Heroku
    • PostgreSQL
    • Redis

デプロイの動作フロー

  • git push heroku master で Heroku にデプロイ
  • デプロイ
    • dep で必要パッケージをインストール
    • npm で必要パッケージをインストール
    • Vue.js を webpack でビルド
    • Go アプリをビルド
    • マイグレーションコマンドをビルド
    • マイグレーションコマンドを実行
  • デプロイ完了

Go

初期設定

cd $GOPATH/your-name
mkdir go-vuejs-heroku
cd go-vuejs-heroku

dep パッケージ管理

dep init
dep ensure

Heroku でコマンドビルド

pressly/goose のマイグレーションコマンドをデプロイ時に実行できるように、アプリのバイナリとは別にコマンドをビルドできる Heroku 用の設定を Gopkg.toml に追記します。

Gopkg.toml
[metadata.heroku]
  root-package = "github.com/zaru/go-vuejs-heroku"
  go-version = "go1.9.1"
  install = [ ".", "./cmd/..." ]
  ensure = "true"

ポイントは install = [ ".", "./cmd/..." ] です。これは Heroku の環境変数である GO_INSTALL_PACKAGE_SPEC と同じ役割になります。 dep 以外の glide などのパッケージ管理の場合はこちらの環境変数を修正します。

heroku config:set GO_INSTALL_PACKAGE_SPEC=". ./cmd/... "

goose コマンドスクリプトを配置

上記で設定したコマンドのスクリプト cmd/goose/main.go に下記コードを配置します。これは公式で用意されているサンプルコードです。

cmd/goose/main.go
package main

import (
    "database/sql"
    "flag"
    "log"
    "os"

    _ "github.com/lib/pq"
    "github.com/pressly/goose"
)

var (
    flags = flag.NewFlagSet("goose", flag.ExitOnError)
    dir   = flags.String("dir", ".", "directory with migration files")
)

func main() {
    flags.Usage = usage
    flags.Parse(os.Args[1:])

    args := flags.Args()

    if len(args) > 1 && args[0] == "create" {
        if err := goose.Run("create", nil, *dir, args[1:]...); err != nil {
            log.Fatalf("goose run: %v", err)
        }
        return
    }

    if len(args) < 3 {
        flags.Usage()
        return
    }

    if args[0] == "-h" || args[0] == "--help" {
        flags.Usage()
        return
    }

    driver, dbstring, command := args[0], args[1], args[2]

    switch driver {
    case "postgres", "mysql", "sqlite3", "redshift":
        if err := goose.SetDialect(driver); err != nil {
            log.Fatal(err)
        }
    default:
        log.Fatalf("%q driver not supported\n", driver)
    }

    switch dbstring {
    case "":
        log.Fatalf("-dbstring=%q not supported\n", dbstring)
    default:
    }

    if driver == "redshift" {
        driver = "postgres"
    }

    db, err := sql.Open(driver, dbstring)
    if err != nil {
        log.Fatalf("-dbstring=%q: %v\n", dbstring, err)
    }

    arguments := []string{}
    if len(args) > 3 {
        arguments = append(arguments, args[3:]...)
    }

    if err := goose.Run(command, db, *dir, arguments...); err != nil {
        log.Fatalf("goose run: %v", err)
    }
}

func usage() {
    log.Print(usagePrefix)
    flags.PrintDefaults()
    log.Print(usageCommands)
}

var (
    usagePrefix = `Usage: goose [OPTIONS] DRIVER DBSTRING COMMAND
Drivers:
    postgres
    mysql
    sqlite3
    redshift
Examples:
    goose sqlite3 ./foo.db status
    goose sqlite3 ./foo.db create init sql
    goose sqlite3 ./foo.db create add_some_column sql
    goose sqlite3 ./foo.db create fetch_user_data go
    goose sqlite3 ./foo.db up
    goose postgres "user=postgres dbname=postgres sslmode=disable" status
    goose mysql "user:password@/dbname?parseTime=true" status
    goose redshift "postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" status
Options:
`

    usageCommands = `
Commands:
    up                   Migrate the DB to the most recent version available
    up-to VERSION        Migrate the DB to a specific VERSION
    down                 Roll back the version by 1
    down-to VERSION      Roll back to a specific VERSION
    redo                 Re-run the latest migration
    status               Dump the migration status for the current DB
    version              Print the current version of the database
    create NAME [sql|go] Creates new migration file with next version
`
)

これによって dep の Gopkg.lockpressly/goose が追記されるようになっていると思います。より明示的に指定したい場合は Gopkg.toml で管理しても良いかもしれません。

Gopkg.toml
[[constraint]]
  name = "github.com/pressly/goose"
  version = "2.1.0"

Vue.js

初期設定

cd $GOPATH/your-name/go-vuejs-heroku
vue init webpack vue-app
cd vue-app
npm install

Vue.js は特別なことはないです。 vue-cli を使って webpack なアプリをテンプレを作っています。

Heroku

Heroku にアプリを作成します。そして Go と Node.JS のビルドパックを追加します。

heroku create go-vuejs-heroku
heroku buildpacks:add heroku/go --app go-vuejs-heroku
heroku buildpacks:add heroku/nodejs --app go-vuejs-heroku

PostgreSQL と Redis のアドオンを追加します。

heroku addons:create heroku-postgresql:hobby-dev --app go-vuejs-heroku
heroku addons:create heroku-redis:hobby-dev --app go-vuejs-heroku

PostgreSQL と Redis の接続情報は環境変数に入っているので、アプリ側が参照するように修正しておきます。

heroku config

デプロイ時に webpack でビルドできるように NPM_CONFIG_PRODUCTION を false にしておきます。そうしないと devDependencies のパッケージがインストールされません。ついでに Go のバージョン指定もしておきます。

heroku config:set NPM_CONFIG_PRODUCTION=false
heroku config:set GOVERSION=go1.9

別ディレクトリの npm を動かす

今回のサンプルアプリでは Go アプリの中に Vue.js のアプリを内包するようにしているので、 package.json はサブディレクトリにあります。そうすると Heroku の Node.JS ビルドパックではビルドしてくれません。

.
├── Procfile
├── server.go
└── vue-app
    ├── package.json

そこで、ルートディレクトリにも package.json を配置してビルドコマンドを記述します。

touch package.json
.
├── Procfile
├── server.go
├── package.json
└── vue-app
    ├── package.json

postinstall で vue-app ディレクトリの中で npm install と npm run build でビルドを実行しています。

package.json
{
  "name": "go-vuejs-heroku",
  "version": "0.0.1",
  "engines": {
    "node": "7.10.0",
    "npm": "4.2.0"
  },
  "scripts": {
    "postinstall": "npm --prefix ./vue-app install ./vue-app && cd ./vue-app && npm run build"
  }
}

マイグレーションコマンドを実行

Procfile にリリース時に実行するコマンドを指定できます。そこでマイグレーションコマンドを実行指定します。

release: goose postgres $DATABASE_URL up
web: go-vuejs-heroku

以上で、Heroku で Go と Vue.js を使った Web アプリケーションを動かすことができるようになりました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18