Go + MySQL + Dockerの構成でfly.ioにデプロイしてみる
こんにちはKabosです。
今回は私のPortfolioサイトのバックエンド(Golang + MySQL + Docker)をFly.ioにデプロイしたときのハマったところなどを備忘録的な感じで書いていきたいと思います。
基本的にFly Docsに書いてある通りです。
Fly.ioに登録する
こちらからFly.ioにアカウント登録します。
カード情報の入力を求められると思いますが、Fly.ioは従量課金制のサービスですが月5ドル分までは無料枠として利用できます。
1つのAppを1か月(30日)毎日24時間起動し続けても大体2.5ドルに行くか行かないかぐらいだそうなので、安心していただいて大丈夫です。
ただし5ドル分の無料枠を超過するとそこからは従量課金となるので注意してください。
flyctlを導入する
fly.ioではターミナルからAppの作成やデプロイなどができる便利なCLIが用意されています。
ご自身のOSに合わせて以下のコマンドを実行してflyctlをインストールしましょう。
macOS
$ brew install flyctl
OR
$ curl -L https://fly.io/install.sh | sh
Linux
$ curl -L https://fly.io/install.sh | sh
Windows
$ pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"
動作確認
$ flyctl version
でバージョンが表示されればインストール完了です。
flyctlのコマンドが見つからない場合
パスが通っていない可能性があります。
./bash_profile
などにexport FLYCTL_INSTALL="/home/{ご自身のユーザ名}/.fly"
,
export PATH="$FLYCTL_INSTALL/bin:$PATH"
を追記してください。
Appの作成(Go App)
flyctlをインストールできたところで、fly.io上にGoアプリケーションをデプロイするAppを作成します。
CLI上でログイン
$ flyctl auth login
このコマンドでfly.ioに登録したアカウントにログインします。
ログインを行わないとこの先の作業ができませんので必ず行ってください。
Appの作成
$ flyctl launch
このコマンドでAppの初期設定および作成を行います。
Scanning source code
Could not find a Dockerfile, nor detect a runtime or framework from source code. Continuing with a blank app.
Creating app in /home/user/yourapp
We're about to launch your app on Fly.io. Here's what you're getting:
Organization: user (fly launch defaults to the personal org)
Name: your_app_name (derived from your directory name)
Region: Tokyo, Japan (this is the fastest region for you)
App Machines: shared-cpu-1x, 1GB RAM (most apps need about 1GB of RAM)
Postgres: <none> (not requested)
Redis: <none> (not requested)
? Do you want to tweak these settings before proceeding? (y/N)
実行すると以上のように作成するAppの設定を行うか聞かれるのでCPUやPORT, メモリ, App名などを設定したい場合はy
を入力して設定画面へ移動します。
こんな画面に移動するので各々の設定をしましょう。
設定が終わるとfly.tomlなどが作成されターミナル上でデプロイが始まります。
こちらが完了すればGo APIのデプロイは完了です。
Dockerfileの記述
FROM golang:1.20-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
ARG MYSQL_ROOT_PASSWORD
ARG MYSQL_DATABASE
ARG MYSQL_USER
ARG MYSQL_PASSWORD
ARG MYSQL_HOST
ENV MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD \
MYSQL_DATABASE=$MYSQL_DATABASE \
MYSQL_USER=$MYSQL_USER \
MYSQL_PASSWORD=$MYSQL_PASSWORD \
MYSQL_HOST=$MYSQL_HOST
RUN CGO_ENABLED=0 go build -o main cmd/server/main.go
FROM alpine AS prod
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY /app/main /app/main
EXPOSE 8000
ENTRYPOINT [ "/app/main" ]
細かい説明は割愛しますが大雑把に説明すると、builderステージではgolang:1.20-alipine
イメージをベースに環境変数の設定、buildファイルの生成を行っています。
またprodステージではca証明書を追加し、builderステージから生成したbuildファイルをコピーし、8000ポートをエクスポートして/app/main
をエントリーポイントとしています。
MySQL Appの作成
Appの作成
$ mkdir myapp-db
$ cd myapp-db
$ fly launch
まず適当なディレクトリを作成し、移動します。
移動先のディレクトリでGo Appと同様にfly launch
でfly.io上にMySQLを動かすためのAppを作成します。
こちらも同様にfly.tomlなどが作成されます。
volumeの作成
$ fly volumes create mysqldata --size 10 # (GB)
上記のコマンドで先ほど作成したApp内にmysqldata
というvolumeを作成します。
Dockerで作るvolumeと一緒でDB保存されたデータの永続化を行います。
fly.tomlの変更
fly.toml(MySQL)
# fly.toml app configuration file generated for myapp-db on 2024-02-11T15:54:03+09:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = '{YOUR_DB_APP_NAME}'
primary_region = 'nrt'
kill_signal = 'SIGINT'
kill_timeout = 5
[processes]
app = "--datadir /data/mysql --default-authentication-plugin mysql_native_password --performance-schema=OFF --innodb-buffer-pool-size 64M"
[mounts]
source="{YOUR_VOLUME_NAME}"
destination="/data"
# MYSQL_DATABASE can name your database and will created aftering deploy
[env]
MYSQL_DATABASE = "{YOUR_DB_NAME}"
MYSQL_USER = "{YOUR_DB_USERNAME}"
[build]
image = "mysql:8.1"
[[vm]]
cpu_kind = 'shared'
cpus = 1
memory_mb = 1024
MySQLのfly.tomlはこんな感じになります。
[processes]
app = "--datadir /data/mysql --default-authentication-plugin mysql_native_password --performance-schema=OFF --innodb-buffer-pool-size 64M"
ここではMySQLを起動する際の設定を記述しています。
各optionごとの説明は以下の通りです。
Option | やってること |
---|---|
--datadir /data/mysql |
データを保存するディレクトリを指定 |
--default-authentication-plugin mysql_native_password |
MySQLのデフォルト認証方式をnative passwordに設定 |
--performance-schema=OFF |
ここではPerformance Schemaの使用をオフにしています |
--innodb-buffer-pool-size 64M |
innoDBのバッファプールサイズを64MBに設定しています |
[mounts]
[mounts]
source="{YOUR_VOLUME_NAME}"
destination="/data"
/data
ディレクトリに先ほど作成したvolumeをマウントしています。
やっていることはDockerにおけるvolumeのマウントと同じです。
[env]
[env]
MYSQL_DATABASE = "{YOUR_DB_NAME}"
MYSQL_USER = "{YOUR_DB_USERNAME}"
ここでは環境変数としてMYSQL_DATABASE
, MYSQL_USER
を設定しています。
MySQLに関する環境変数としてMYSQL_ROOT_PASSWORD
, MYSQL_PASSWORD
などがありますが、こちらに関してはsecretとしてデプロイするようにしましょう。
以下のコマンドでsecretとして値をSetすることができます
$ fly secrets set MYSQL_PASSWORD="test-pass"
[build]
[build]
image = "mysql:8.1"
buildイメージを指定しています。
ご自身の環境に合わせて指定しましょう。
[vm]
[[vm]]
cpu_kind = 'shared'
cpus = 1
memory_mb = 1024
VMに関する設定を記述しています。
ここで特筆すべき点はとくにはないです。
fly.toml (Go App)
# fly.toml app configuration file generated for kabos-dev-api on 2024-02-11T15:37:44+09:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = '{YOUR_APP_NAME}'
primary_region = 'nrt'
[build]
build-target='prod'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
cpu_kind = 'shared'
cpus = 1
memory_mb = 1024
[env]
# MYSQL_USREなどの変数を定義
[experimental]
auto_rollback = true
[[services]]
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
force_https = true
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
Goのfly.tomlはこんな感じになります。
[build]
[build]
build-target='prod'
build時のターゲットを指定しています。
今回はDockerfileで定義したprodステージをターゲットとしています。
[http_service]
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ['app']
ここではhttpサービスに関する設定を行っています。
設定項目 | 何をしているか |
---|---|
internal_port |
Goアプリケーションがリクエストを受け付ける内部portの設定 |
force_https |
trueにすることでhttps接続を強制します |
auto_stop_machines |
trueとするとアプリケーションの利用が一定時間ない場合に自動的にマシンを停止します |
auto_start_machines |
trueとするとアプリケーションの利用があった場合に自動的にmachineの起動を行います |
min_machines_running |
常に実行される最小マシン数を設定します。今回は0としているため呼び出しがない場合は自動的にマシンはストップされます |
processes |
アプリケーションのプロセスを指定しています |
[[vm]]
MySQLと同じなので割愛
[experimental]
[experimental]
auto_rollback = true
ここではauto_rollback
をtrueとしています。
Auto Rollbackとはfly.ioへのdeployがFailした際に自動的にアプリケーションのRollbackを行ってくれる機能です。
この機能のおかげでdeployがFailしたことによるサービスの停止を防ぐことができます。
[[services]]
[[services]]
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
force_https = true
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
ここではserviceに関する設定をしています。
services.concurrency
ではhard_limitやsoft_limitなど接続に関する制限の設定を行っています。
services.ports
は使用するポートの数だけ複数設定することができます。
ここでは80ポートでhttpリクエストを受け取り、自動的にhttpsにリダイレクトされ443ポートで処理を行っています。
Go AppからMySQL Appに接続する
GoアプリケーションからMySQLに接続する際は大体以下のようなコードになると思います。
package mysql
import (
"database/sql"
"fmt"
"os"
"github.com/go-sql-driver/mysql"
)
const (
DRIVER = "mysql"
NET = "tcp"
)
func ConnectDB() (*sql.DB, error) {
c := mysql.Config{
User: os.Getenv("MYSQL_USER"),
Passwd: os.Getenv("MYSQL_PASSWORD"),
Net: NET,
Addr: fmt.Sprintf("%s:%s", os.Getenv("MYSQL_HOST"), os.Getenv("MYSQL_PORT")),
DBName: os.Getenv("MYSQL_DATABASE"),
AllowNativePasswords: true,
ParseTime: true,
}
db, err := sql.Open(DRIVER, c.FormatDSN())
if err != nil {
return nil, err
}
return db, nil
}
基本的にはこの通りですが、DB_HOST
にセットする値は{MYSQL_APP_NAME}.internal
とすることに注意してください。
改めてデプロイする
ここまで出来たらそれぞれのfly.tomlが存在するディレクトリ内でfly deploy
を実行し改めて各Appにデプロイしましょう。
insertやfetchする処理をGoで書いて正しく実行できればokです。
まとめ
今回はGo + Docker + MySQLでfly.ioにデプロイする手順をまとめてみました。
ただ正直なところfly.ioではpostgresがサポートされているのでそっち使った方がこんな手順踏まなくてももっと楽に構築できるので、よほどのことがない限りはそちらをお勧めします。
備忘録的な感じて書いたのでちょいちょいテキトーになってしまった部分もあるかもしれませんが少しでも参考になれば幸いです。
Discussion