Wildcardな雑記

なんか書く

Go言語でデータベースを使う処理を書いてHeroku上に公開してみる手順の覚書

はじめに

Go言語でWebアプリを作ってみたいと思い,作ったWebアプリを簡単に公開できる方法について調べていた.まずは無料でできる方法から始めたかったので,そこでHerokuを使ってみることにした.

また,アプリを作る上でデータベースに関わる処理も必要になってくるので,データベースが関わるコードをHerokuで動作させるために必要な設定の仕方についても試してみることにした.

今回試すのは,取りあえずデータベースに適当なテーブルを作成し,そのデータを読み取ってHTMLに表示するだけのGo言語で書いたサーバーサイドプログラムである.Heroku上でデータベース連携の処理を実行するのは,アドオンを追加したり等ローカルで試すのとは勝手が異なるので,少しつまづきそうになった.手順を覚書として書いておく.

次節に手順を書く.

フォルダ構成

[~/Code/Project/proj_y2022/proj0102/jikken3]
% tree .
.
├── dbdata
│   └── setup.sql
└── main.go


1 directory, 2 files

Heroku上で実行されるサーバサイドのプログラム

main.go

今回注目するのはhellodb関数であり,これはURLで/dbのパスにアクセスしたときの処理について書いてある.ここで行なっているのは,テーブルから全てのレコードを読み取り,カラム(name)を改行で連結した文字列として出力する.

package main

import (
    "io"
    "net/http"
    "os"
    "log"
    "database/sql"
    _ "github.com/lib/pq"
    "strings"
)

var Db *sql.DB

func hello(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello World!!")
}

type member struct {
    NUM string
    NAME string
}

func hellodb(w http.ResponseWriter, r *http.Request) {
    Db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
    defer Db.Close()
    if err != nil {
        log.Fatalf("Error opening database: %q", err)
    }

    rows, err := Db.Query("SELECT * FROM members")
    defer rows.Close()
    if err != nil {
        io.WriteString(w, "Errorrr!")
        return
    }

    var out string
    var names []string
    for rows.Next() {
        var e member
        rows.Scan(&e.NUM, &e.NAME)
        names = append(names, e.NAME)
    }
    out = strings.Join(names, "\n")
    io.WriteString(w, out)
}

func main() {
    port := os.Getenv("PORT")
    http.HandleFunc("/", hello)
    http.HandleFunc("/db", hellodb)
    http.ListenAndServe(":"+port, nil)
}

setup.sql

上記のGo言語のプログラム(main.go)からアクセスされることになる,今回のサンプルとしてのテーブルを定義する.テーブル(members)は名前(name)と番号(id)をレコードとして保存する構成であり,2つのカラム(id, name)を持つ.

[~/Code/Project/proj_y2022/proj0102/jikken3]
% cat dbdata/setup.sql
CREATE TABLE members (
  id integer,
  name varchar(30)
);
INSERT INTO members VALUES (1, 'John Smith');
INSERT INTO members VALUES (2, 'Richard Roe');
INSERT INTO members VALUES (3, 'Baby Doe');

Herokuにデプロイするまでの流れ

流れとしては以下である.詳細は次になる.

  • (1) リポジトリを作る
  • (2) Heroku上にアプリを作成する
  • (3) Herou上のアプリにデータベースを追加する
  • (4) 作成したコードをHeroku上のリポジトリにpushする
  • (5) Heroku上のアプリのデータベースにテーブルを作成する

(1) リポジトリを作る

[~/Code/Project/proj_y2022/proj0102/jikken3]
% git init

同じディレクトリ内で以下を実行する.go mod initで作成されるgo.modファイルはGoモジュールの依存関係を記述したファイルであり,これはHeroku上でのビルドにおいて使用される.

% go mod init github.com/<ユーザ名>/jikken3
% go mod tidy
% go mod vendor

(2) Heroku上にアプリを作成する

heroku createコマンドでアプリの名前を指定できる.(とりあえずjohnapp47としておく)

[~/Code/Project/proj_y2022/proj0102/jikken3]
% heroku create johnapp47

(3) Herou上のアプリにデータベースを追加する

今回のコードはpostgresqlを使うので,以下のようにしてHerokuアプリにアドオンを追加する.

% heroku addons:create heroku-postgresql:hobby-dev --app johnapp47

(4) 作成したコードをHeroku上のリポジトリにpushする

[~/Code/Project/proj_y2022/proj0102/jikken3]
% git add -A .

% git commit -m "test commit"

% git push heroku  master

(5) Heroku上のアプリのデータベースにテーブルを作成する

Herokuアプリ(今回はjohnapp47)の上に展開された,Go言語のサーバーサイドプログラムから利用するデータベースに対して,先に挙げたsetup.sqlSQLを実行し,テーブルを作成する.

まず確認として,ファイル群は先ほどpushしたので,setup.sqlは以下のようにして認識されている.

[~/Code/Project/proj_y2022/proj0102/jikken3]
% heroku run bash --app johnapp47
Running bash on ⬢ johnapp47... up, run.8167 (Free)
~ $ ls
Procfile  bin  dbdata  go.mod  go.sum  main.go  vendor
~ $ ls dbdata
setup.sql
~ $

それでは,テーブルを作成するためにsetup.sqlの中身のSQL文をデータベース上で実行する. HerokuのPostgresサーバにのコンソールにアクセスするにはheroku pg:psql --app <アプリ名>と実行すればよい.

[~/Code/Project/proj_y2022/proj0102/jikken3]
% heroku pg:psql --app johnapp47
--> Connecting to postgresql-convex-34468
psql (14.1, server 13.5 (Ubuntu 13.5-2.pgdg20.04+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

johnapp47::DATABASE=>

ファイル(setup.sql)を読み込んで実行するコマンドは\i <ファイル名>

johnapp47::DATABASE=> \i dbdata/setup.sql
CREATE TABLE
INSERT 0 1
INSERT 0 1
INSERT 0 1
johnapp47::DATABASE=> \dt
             List of relations
 Schema |  Name   | Type  |     Owner
--------+---------+-------+----------------
 public | members | table | jutsizbfgorver
(1 row)

テーブルが作成されたのをselect文で確認.

johnapp47::DATABASE=> select * from members;
 id |    name
----+-------------
  1 | John Smith
  2 | Richard Roe
  3 | Baby Doe
(3 rows)


johnapp47::DATABASE=> \q

デプロイされたアプリにアクセスしてみる

https://johnapp47.herokuapp.com/dbにアクセスすると,以下のような表示を確認.

f:id:aster-wildcard:20220113203127p:plain

Go言語で書いたサーバプログラムがデータベースからレコードを読み込んでテーブル(members)から読んだ名前の一覧を表示できたことを確認できた.

おわりに

Heroku上でデータベースが連携するコードをデプロイする方法について理解できた.個人的にDockerを学び始めたこともあり,この後にDocker on Herokuなる存在を知ったので次はこっちも試してみようと思う.

参考資料