docker-compose で Go + PostgreSQL の環境構築をする
はじめに
docker-compose で Go + PostgreSQL の環境構築をしてみました。
なお、作成したものは以下のレポジトリで公開しています。
ディレクトリ構成
myapp
├── docker-compose.yml
├── Dockerfile
├── go.mod
├── go.sum
└── main.go
アプリの作成
検証のための簡単なアプリを作成します。
go mod init
を実行します。
module github.com/t-shimpo/go-postgres-docker
go 1.24.0
/
にアクセスすると Hello, World!
と表示されるプログラムを実装しました。
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Server running on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
go run main.go
を実行し、localhost:8080
にアクセスすると Hello World!
と表示されます。
Goコンテナを作成
続いて、GoをDockerで動かせるようにします。
以下のDockerfile
とdocker-compose.yml
を追加しました。
FROM golang:1.24
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
CMD ["go", "run", "main.go"]
COPY go.mod go.sum ./
について、後の実装でgo.sum
が更新されるのでgo.sum
も含めています。
ファイルがない場合エラーになるので、空ファイルでgo.sum
を追加しておきます。
services:
app:
build: .
container_name: app
ports:
- "8080:8080"
volumes:
- .:/app
以下のコマンドを実行して、アプリが実行できるか確認します。
$ docker-compose up -d
localhost:8080
にアクセスし Hello World!
と表示されることを確認します。
PostgreSQLコンテナを作成
続いて、PostgreSQLをDockerで動かせるようにします。
docker-compose.yml
を更新しました。
services:
app:
build: .
container_name: app
ports:
- "8080:8080"
volumes:
- .:/app
depends_on:
- db
environment:
DATABASE_URL: postgres://user:password@db:5432/mydb?sslmode=disable
db:
image: postgres
container_name: postgres
restart: always
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
今回は簡単に、DB接続用の情報をGoアプリでDATABASE_URL
として取得できるようにしています。
dockerを起動して、PostgreSQLのコンテナに入れるか確認します。
$ docker-compose up -d
以下のコマンドを実行して、PostgreSQLにログインできるか確認します。
※postgres
はコンテナ名です。
$ docker exec -it postgres psql -U user -d mydb
成功すればmydb=#
のようなプロンプトが表示されます。
補足
depends_on
以下の設定でdbコンテナが起動してからappコンテナを起動するようにしています。
depends_on:
- db
PostgreSQL の起動完了を保証するわけではないため、アプリ側でリトライ処理の実装や、healthcheckを追加しアプリがデータベースの準備完了を待つようにするのが望ましいです。
healthcheckの場合は以下のように設定できます。
services:
app:
//
depends_on:
+ db:
+ condition: service_healthy
db:
//
volumes:
- postgres-data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
+ interval: 10s
+ retries: 5
+ start_period: 30s
+ timeout: 10s
depends_on: - db
では、単にdbコンテナが起動するのを待つだけですが、service_healthy
を使うことで、dbが正常に応答できる状態になってからappを起動することができます。
📌 参考URL: https://docs.docker.com/compose/how-tos/startup-order/#example
volumes
以下の設定はデータの永続に関する設定です。
volumes:
postgres-data:
postgres-data
というボリュームを作成し、PostgreSQL のデータ(/var/lib/postgresql/data
)を永続化しています。
これにより、コンテナを削除してもデータが保持されます。
接続確認
GoアプリとpostgresDBの接続を確認します。
main.go
を以下のように更新しました。
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
_ "github.com/lib/pq"
)
func handler(w http.ResponseWriter, r *http.Request) {
dbURL := os.Getenv("DATABASE_URL")
if dbURL == "" {
http.Error(w, "DATABASE_URL not set", http.StatusInternalServerError)
return
}
db, err := sql.Open("postgres", dbURL)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = db.Ping()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer db.Close()
w.WriteHeader(http.StatusOK)
w.Write([]byte("Database connection successful"))
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Server running on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
github.com/lib/pq
はPostgreSQL用のドライバです。
PostgreSQLに接続するにはこちらが必要となります。
外部パッケージを追加したので、go mod tidy
も実行しておきます。
dockerを起動して動作確認します。
$ docker-compose up -d
localhost:8080
にアクセスし Database connection successful
と表示されれば成功です。
参考資料
Discussion
こんにちは!
healthcheckする手もありますね。
コメントいただきありがとうございます🙏
参考にさせていただき、記事を加筆しています!