Open10

自作のGolang製プログラムをDockerで動かせるようになるまで

なかやばしなかやばし

モチベーション

  • Golangで作ったプログラムをDockerで動かせるようになりたい
  • ベースとなるイメージは軽いほうがよい
  • 重要度としては「イメージは軽いほうがよい」< 「とりあえず動く」
  • 開発環境でもPostgreSQLを動かしたいが、構築が面倒なのでDockerとcomposeでシュッと構築できてほしい

前提

  • PythonのプログラムをDockerで動かしたことがある程度
  • 自作したプログラム
    • Webアプリケーションのバックエンド
    • PostgreSQLをDBとして利用
      • docker composeでよしなにしたい
なかやばしなかやばし

やること

  • WSL上のdebianにDockerとdocker composeを入れる
  • Dockerfileを書く
  • compose.ymlを書く
なかやばしなかやばし

マルチステージングビルド

1つのDockerfileに複数回のFROMが使われること?

https://docs.docker.jp/develop/develop-images/multistage-build.html

アプリケーションをビルドする際に使うイメージと、アプリケーションを動作させる際に使うイメージを分割する。
アプリケーションをビルドする際に使うイメージを仮にbuilderとすると、実際にはFROM hogehoge AS builderと書くことになる。
ビルドされた成果物をbuilderからアプリケーションを動作させる際に使うイメージへ移すとなると、COPY --from=builder hogehoge ./みたいなことを書けばよいらしい

なかやばしなかやばし

使うコンテナを吟味する

アプリケーションをビルドする際に使うイメージ

cgoなどが使えたほうが後々よさそう。というのも、データベースドライバなどがcgoを利用していたりするし、後々で音声を取り扱うことを予定しているため、音声を取り扱うライブラリがC言語で書かれていたりするため。

最終的には公式イメージのgolang:1.22-bookwormを使うことに。

アプリケーションを動作させる際に使うイメージ

https://log.noid11.com/posts/distroless-and-go/

Googleが作っているdistrolessイメージが流行っているっぽい?

https://github.com/GoogleContainerTools/distroless

最近はalpine linuxを使うことは控えられてきている?代わりにdebianの-slimみたいなやつが使われてきている?
Golangのコンパイラは静的なシングルバイナリを出力するため、distrolessイメージのうち、basebase-nosslstaticが使えそう。

どれを使えばいいのかよくわからないので、このREADMEを読む。

https://github.com/GoogleContainerTools/distroless/blob/main/base/README.md

コンテナサイズも大事だがとりあえず動くことを目標としたいので、この中で一番安全そうなbase-debian12を使うことにする

なかやばしなかやばし

以上を踏まえて作ったDockerfileがこんな感じになった:

FROM golang:1.22-bookworm as builder
WORKDIR /app/
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY . .
RUN go build -o main ./bin/main.go

FROM gcr.io/distroless/base-debian12
WORKDIR /app/
COPY ./.config /app
COPY ./files /app
COPY --from=builder /app/main /app
CMD ["./main", "-config", "./.config/config.toml"]
なかやばしなかやばし

dockerfileをビルドする際はこんな感じにshellを叩く:

$ docker build -t {{imageの名前}} {{Dockerfileのあるディレクトリ}}
なかやばしなかやばし

compose.ymlはこんな感じ。misskeyのdocker-composeを参考に。

https://github.com/misskey-dev/misskey/blob/072f67d6e71af3d7fa6f5f4c73ae460d6844f511/docker-compose_example.yml

version: "3"
services:
  backend:
    build: .
    restart: always
    links:
      - db
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "3001:3001"
    env_file:
      - .config/docker.env
    volumes:
      - ./files:/app/files
      - ./.config:/app/.config:ro
  db:
    restart: always
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    env_file:
      - .config/docker.env
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
      interval: 5s
      retries: 20
なかやばしなかやばし

ここまで来て気がついたのだが、マイグレーションが走っていないような気がする。

今回はdbmateを使っているのでdbmate upをshellから実行すればよい。
https://github.com/amacneil/dbmate

しかし、使っているコンテナであるdistrolessでshellを実行するのには一癖がありそうである。

しかし、幸運なことにdbmateにはdocker imageを配布しているので、これを利用しようと思う。
これを利用すると、以下のように実行されれば起動することができそうである。

  1. postgresqlが起動する
  2. dbmateのコンテナを実行し、正常終了する
  3. 自作Webアプリケーションが起動する