🦐

herokuにechoサーバデプロイを試した

2025/01/07に公開

概要

DB操作するGoによるサーバをなる早で作成したかったため、
herokuでechoサーバ(golang)をデプロイしてみた

herokuについて

アプリケーションのデプロイ、実行、管理を簡単に行えるPaaS。
従来の無料プランは2022年11月に終了
エコプラン($5/月)から利用可能
https://jp.heroku.com/pricing
DBもherokuで作成出来るため、postgresをherokuで作成する
(最低金額~$0.007/hour, max $5/month)

herokuを選んだ理由

その他無料でやるとするとlambda+APIGatewayが思いつくが、herokuがこういったPaaSでは有名なので、リソースもすぐ消すため、herokuにした

コード作成

go init api-post

ディレクトリ構成例

internal/
  domain/
    post.go
  handler/
    post_handler.go
  infra/
    db.go
  router/
    router.go
.env
docker-compose.yml
Dockerfile
go.mod
go.sum
heroku.yml
main.go

主要な記述のみ抜き出して載せる

main.go

herokuは環境変数PORTでPORT番号を指定するため、解放portは環境変数PORTを参照する。

package main

import (
	"api-post/internal/router"
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "github.com/lib/pq"
)

func main() {
	db, err := initializeDB()
	if err != nil {
		log.Fatal("Failed to initialize:", err)
	}
	defer db.Close()

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	e := router.NewEchoRouter(db)
	log.Printf("Server starting on port: %s", port)
	e.Logger.Fatal(e.Start(":" + port))
}

func initializeDB() (*sql.DB, error) {
	dsn := os.Getenv("DATABASE_URL")
	if dsn == "" {
		dsn = fmt.Sprintf(
			"postgres://%s:%s@%s:%s/%s?sslmode=require",
			os.Getenv("DB_USER"),
			os.Getenv("DB_PASSWORD"),
			os.Getenv("DB_HOST"),
			os.Getenv("DB_PORT"),
			os.Getenv("DB_NAME"),
		)
	}

	db, err := sql.Open("postgres", dsn)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to database: %w", err)
	}

	if err := db.Ping(); err != nil {
		return nil, fmt.Errorf("failed to ping database: %w", err)
	}

	return db, nil
}

heroku.yml

web: go run main.goだと動かなかったため、以下のように記述した。

build:
  docker:
    web: Dockerfile

run:
  web: /main

Dockerfile

FROM golang:1.22.2-alpine3.19 as builder

RUN apk update \
  && apk add --no-cache git curl

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod tidy

COPY . .

RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" -o /main ./cmd/server

FROM alpine:3.12

COPY --from=builder /main .

EXPOSE 8080
ENTRYPOINT ["/main web"]

.env

例(ローカル、heroku以外のDBに接続する時用)

PORT=8080
DB_HOST=xxxx
DB_USER=xxxx
DB_PORT=xxxx
DB_NAME=xxxx
DB_PASSWORD=xxxx
DB_SSLMODE=require

herokuでdeploy

#herokuをインストール(mac)
brew install heroku/brew/heroku
#herokuにログインする
heroku login
#プロジェクト作成, push
heroku create api-post
heroku git:remote -a api-post
git push heroku main 

# データベース追加
heroku addons:create heroku-postgresql:essential-0
heroku pg:psql postgresql-[database-name] < init.sql

# 環境変数の設定
heroku config:set PORT=80
## herokuのdb以外に接続する場合
heroku config:set DB_USER=xxxx                               
heroku config:set DB_PASSWORD=xxxx
heroku config:set DB_HOST=xxxx
heroku config:set DB_PORT=xxxx
heroku config:set DB_NAME=xxxx
heroku config:set DB_SSLMODE=require

# ログの確認
heroku logs --tail

URLにアクセス->ルーティングの通りに値が返却される

はまったポイント

  • heroku.ymlのentrypointにgo run main.goが使えない

    • 以下のようなエラーになった
    2024-12-24T06:00:12.843863+00:00 app[web.1]: /bin/bash: line 1: go: command not found
    
    • 結果Dockerfileでbuildするやり方に変更した
  • DBが繋がらない(heroku起因でない)

    • 最初SupabaseのDBと繋げようとした際に、Direct connectionに向き先を向けていた。IPv6しか追加設定を入れない限り対応していないため、Session poolerに変更したら、接続出来るようになった

データの削除

#アプリ名確認
heroku apps
#アプリ削除
heroku apps:destroy --app <アプリ名> --confirm <アプリ名>

Discussion