20201006のGoに関する記事は2件です。

【Go + Gin】Docker環境を構築してみた

はじめに

GoとGinを使用する際のDokcerを用いた開発環境を構築しました。docker-composeを使用しています。
使用するDBはmysqlで、ORマッパーはGormの使用を想定しています。

TL;DR

下記のディレクトリ構成にしています。
Dockerfile、docker-compose.ymlにしています。

ディレクトリ構成

├── docker
│   ├── docker.mysql.env
│   └── go
│       └── Dockerfile
├── docker-compose.yml
└── src
    └── app

docker/go/Dockerfile

FROM golang:1.10.3-alpine3.8

COPY src/app /go/src/app/

WORKDIR /go/src/app/

RUN apk update \
  && apk add --no-cache git \
  && go get github.com/gin-gonic/gin \
  && go get github.com/jinzhu/gorm \
  && go get github.com/go-sql-driver/mysql

EXPOSE 8080

docker/docker.mysql.env

MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=go-gin-app
MYSQL_USER=root
MYSQL_PASSWORD=password

docker-compose.yml


services:
  app:
    container_name: app
    build:
      context: .
      dockerfile: ./docker/go/Dockerfile
    ports:
      - 3000:3000
    links:
      - database
    tty:
      true
    volumes:
      - ./src/app:/go/src/app

  database:
    restart: always
    image: mysql:5.7
    ports:
      - 3308:3306
    volumes:
      - mysql-datavolume:/var/lib/mysql
    env_file:
      - docker/docker.mysql.env

volumes:
  mysql-datavolume:
    driver: local

解説

Dockerfile

今回はGoのDockerfileのみを作成し、mysqlはベースイメージを使用するようにdocker-compose.ymlで指定します。

FROM golang:1.10.3-alpine3.8

COPY src/app /go/src/app/

WORKDIR /go/src/app/

RUN apk update \
  && apk add --no-cache git \
  && go get github.com/gin-gonic/gin \
  && go get github.com/jinzhu/gorm \
  && go get github.com/go-sql-driver/mysql

EXPOSE 8080

src/app直下のファイルをボリュームに指定しています。

また、aliphineのベースイメージを指定しているので、apkでGin、Gorm関連のパッケージをインストールしています。

docker-compose.yml

services:
  app:
    container_name: app
    build:
      context: .
      dockerfile: ./docker/go/Dockerfile
    ports:
      - 3000:3000
    links:
      - database
    tty:
      true
    volumes:
      - ./src/app:/go/src/app

  database:
    restart: always
    image: mysql:5.7
    ports:
      - 3307:3306
    volumes:
      - mysql-datavolume:/var/lib/mysql
    env_file:
      - docker/docker.mysql.env

volumes:
  mysql-datavolume:
    driver: local

mysqlのenvファイルを別で作成したので、env_fileで指定します。
ポート 3306はローカルのmysqlでよく使用するので、3307を指定しています。

動作確認

src/app直下にmain.goを作成し、おなじみのhello worldを出力するようにコードを記述します。

package main

import "fmt"

func main() {
        fmt.Println("Hello, world")
}

そして、下記コマンドでhello worldを出力させます。

docker-compose exec app go run main.go

hello worldが出力されたでしょうか。

終わりに

意外と簡単にGo+Ginの環境構築を行うことができました。

やはりDockerを使用すると環境構築が楽になりますね、!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Go】DBへのRead/Writeでエンドポイントを分けたい

はじめに

レプリケーションに対応するため、DBのエンドポイントをRead/Writeで分けることにしました。
Go/Gorm2でどのように対応するか検討しました。

利用環境

  • Go: 1.15.2
  • Gorm2: v1.20.1
  • Echo: v4.1.17

結論から

Endpoint別に複数のConnectionを持ったTypeに、Gormの各Methodをラップした。
こうすることで、connectionを利用する側はendpointを意識せずにDBへの操作を可能にしています。

db/connect.go
package db

import (
    "os"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

type SqlHandler struct {
    ReadConn  *gorm.DB
    WriteConn *gorm.DB
}

func Connect() *SqlHandler {
    readConn, err := gorm.Open(GetDBConfig("read"))
    if err != nil {
        panic(err.Error())
    }

    writeConn, err := gorm.Open(GetDBConfig("write"))
    if err != nil {
        panic(err.Error())
    }

    sqlHandler := SqlHandler{
        ReadConn:  readConn,
        WriteConn: writeConn,
    }

    return &sqlHandler
}

func GetDBConfig(endpoint string) (gorm.Dialector, *gorm.Config) {
    USER := "myUser"
    PASS := "hoge1234"
    PROTOCOL := "tcp(" + endpoint + ":5050)"
    DBNAME := "myDB"
    OPTION := "charset=utf8&parseTime=True&loc=Local"

    mysqlConfig := mysql.Config{
        DSN: USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?" + OPTION,
    }

    config := &gorm.Config{}

    return mysql.New(mysqlConfig), config
}

func (handler *SqlHandler) Find(out interface{}, where ...interface{}) *gorm.DB {
    return handler.ReadConn.Find(out, where...)
}

func (handler *SqlHandler) Exec(sql string, values ...interface{}) *gorm.DB {
    return handler.WriteConn.Exec(sql, values...)
}

func (handler *SqlHandler) First(out interface{}, where ...interface{}) *gorm.DB {
    return handler.ReadConn.Find(out, where...)
}

func (handler *SqlHandler) Raw(sql string, values ...interface{}) *gorm.DB {
    return handler.WriteConn.Raw(sql, values...)
}

func (handler *SqlHandler) Create(value interface{}) *gorm.DB {
    return handler.WriteConn.Create(value)
}

func (handler *SqlHandler) Save(value interface{}) *gorm.DB {
    return handler.WriteConn.Save(value)
}

func (handler *SqlHandler) Delete(value interface{}) *gorm.DB {
    return handler.WriteConn.Delete(value)
}

func (handler *SqlHandler) Where(query interface{}, args ...interface{}) *gorm.DB {
    return handler.WriteConn.Where(query, args...)
}

レプリケーション

is 何

レプリケーション

気になる所

func Execfunc Rawなど、queryそのものが発行できる部分については、read/write双方の可能性があり、どちらに振り分けるか悩むところです。
今回はwriteに振り分けました。なるべく利用者はendpointを意識せずに使えるようにしたい...

おわりに

CQRSを利用することを決めており、そもそもSQLベタ書きが増えそうな気がしています。
Gormを使う意味が少し薄れますが、やはりORMとしてのTypeへのマッピングを考えると欲しいところ。
マッピング部分だけ自作したりすると良いのかなと思います!!

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む