Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What is going on with this article?
@akidon0000

Go RestAPI のあれこれ ついでにDockerも

今回できたこと

Goを使用してREST APIを作成した。
その際,Dockerを使用し,フレームワークには軽量なechoを使用した。
また,SSL化も行い,Let`s Encriptを使用した。

最初に知っておきたいコマンド
(全削除)
(コンテナ)$ docker rm -f `docker ps -a -q`
(イメージ)$ docker rmi $(docker images -q) -f

(build)
$ docker-compose up -d --build

(コンテナ起動)
$ docker-compose exec golang bash

(動いているdocker一覧表示)
$ docker-compose ps

(作成した全てのdocker一覧表示)
$ docker-compose ps -a 

(コンテナ内に入る)
$ docker-compose exec golang bash
$ docker-compose exec mysql bash

(MySQL接続)
root@コンテナ番号:/# mysql -u root -p -h 127.0.0.1

mysql> show databases;
mysql> use golang;
mysql> show tables;
mysql> SELECT * FROM `users`;

全容

初期設定

docker, docker-compose インストール済み
Docker のインストール手順
Docker Compose のインストール

フォルダは以下を作る

└─プロジェクト名
  ├─server
  │ ├─docker
  │ │ ├─golang
  │ │ │ └─Dockerfile
  │ │ ├─mysql
  │ │ │ ├─Dockerfile 
  │ │ │ └─db
  │ │ │   └─init.sql
  │ │ ├─src
  │ │ │ ├─crt    <-Dockerで自動生成(証明書が入る予定)
  │ │ │ ├─databases
  │ │ │ │ └─main.go
  │ │ │ ├─routing
  │ │ │ │ └─main.go
  │ │ │ ├─go.mod <-自動生成
  │ │ │ ├─go.sum <-自動生成
  │ │ │ └─main.go
  │ │ ├─docker-compose.yml
  │ │ └─api_v1.yaml <-必要に応じて
  │ └─Server.md     <-必要に応じて
  └─README.md       <-必要に応じて

証明書作成

無料の認証局を利用する
Let`s Encrypt
certbot

証明書が生成されているか確認する

$ ls /etc/letsencrypt/live/<自分のドメイン名>
>> README  cert.pem  chain.pem  fullchain.pem  privkey.pem

プロジェクトを書き込み

DockerComposeの設定

docker-compose.yml
version: "3"
services:
  golang:
    build: ./golang/
    image: image_golang
    container_name: container_api
    volumes:
      - ./src:/go/src
      # 証明書を/go/src/crtへ
      - /etc/letsencrypt:/go/src/crt
    ports: 
      - "443:443"
    tty: true

  mysql:
    # platform: linux/x86_64 #AppleSiliconを使用時,必要
    build: ./mysql/
    image: image_mysql
    container_name: container_db
    volumes:
      - ./mysql/db:/docker-entrypoint-initdb.d  #初期データをマウントする場所
    environment: # コンテナ内のMySQLを起動する際のパスワードを設定
      - MYSQL_DATABASE=golang      # 作成するデータベース名
      - MYSQL_USER=akidon          # 作業ユーザー名(任意の値)
      - MYSQL_PASSWORD=12345       # 作業ユーザーのパスワード(任意の値)
      - MYSQL_ROOT_PASSWORD=root   # rootユーザーのパスワード(デフォルトでは「root」)
    ports:
      - "3306:3306"
    tty: true

説明

versionは3系が最新版で、versionによって書き方が異なる

build: Dockerfileが存在する場所
image: イメージの名前
container_name: コンテナの名前
volumes: 「./a:/b」 aはホスト側のフォルダを bのdockerコンテナ側へ
ports: ポート
environment: 環境変数
tty: true コンテナ起動永続化
links: 他のコンテナの接続?

AppleSiliconを使用している場合は17行目のリマークを外す


DockerFileの設定

golang/Dockerfile.
# golang/Dockerfile

FROM golang:1.14

WORKDIR /go/src
mysql/Dockerfile.
# mysql/Dockerfile

FROM mysql

MySQLの初期設定

init.sql
-- MySQL dump 10.13  Distrib 5.1.51, for pc-linux-gnu (i686)
--
-- Host: 127.0.0.1    Database: world
-- ------------------------------------------------------
-- Server version       5.1.51-debug-log

/*以下Databaseの内容が記載される*/

DROP SCHEMA IF EXISTS golang;
CREATE SCHEMA golang;
USE golang;

DROP TABLE IF EXISTS golang;

CREATE TABLE users
(
  id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
  name                VARCHAR(40),
  age                 INT(10),
  created_at          TIMESTAMP DEFAULT NULL,
  updated_at          TIMESTAMP DEFAULT NULL,
  deleted_at          TIMESTAMP DEFAULT NULL
);

INSERT INTO users (id, name, age) VALUES (1, "testKun", 21);

説明

created_at
updated_at
deleted_at
はGormを使用する際,追加,更新,削除で必要になる要素

Goの設定

databases/main.go
package databases

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

// SQLConnect DB接続
func GormConnect() (database *gorm.DB) {
    DBMS := "mysql"                   // MySQL
    PROTOCOL := "tcp(container_db)"   // MySQLコンテナ名
    DBNAME := "golang"                // テーブル名
    USER := "akidon"                  // MySQLユーザー名
    PASS := "12345"                   // パスワード

    CONNECT := USER + ":" + PASS + "@" +PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=True&loc=Local"  // 修正!!
    db, err := gorm.Open(DBMS, CONNECT)
    if err != nil {
        panic(err.Error())
    } else {
        fmt.Println("DB接続成功")
    }
    return db
}

説明

PROTOCOL := "tcp(container_db)" // MySQLコンテナ名
のcontainer_dbはDockerComposeのcontainer_name:によるもの
間違っていれば接続できない

routing/main.go
package routing

import (
    "fmt"
    "github.com/labstack/echo"

    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"

    "src/databases"
)

type User struct {
    gorm.Model
    Name     string `json:"name"`
    Age      int `json:"age"`
}

func (u User) String() string {
    return fmt.Sprintf("Name:%s \n Age:%d \n ",
        u.Name,
        u.Age)
}

// ユーザー情報取得
func BaseAPI_GET() echo.HandlerFunc {
    return func(c echo.Context) error {
        db := databases.GormConnect()
        defer db.Close()

        text1 := search(db)
        return c.JSON(200, text1)
    }
}

// ユーザーを登録,更新
func BaseAPI_POST() echo.HandlerFunc {
    return func(c echo.Context) error {
        db := databases.GormConnect()
        defer db.Close()

        //追加・更新
        user := new(User)
        if err := c.Bind(user); err != nil {
            return err
        }

        user1 := User{
            Name:     user.Name,
            Age:      user.Age,
        }

        insertUsers := []User{user1}
        insert(insertUsers, db)

        // update(user1, db)

        return c.JSON(200, "追加完了")
    }
}

func insert(users []User, db *gorm.DB) {
    for _, user := range users {
        db.NewRecord(user)
        db.Create(&user)
    }
}

func update(users User, db *gorm.DB) {
    var user User
    db.Model(&user).Where("id = ?", 1).Update(map[string]interface{}{"name": users.Name, "age": users.Age})
}

func search(db *gorm.DB) []User {
    var user []User
    // 同じ位置にいるリストを取得
    db.Raw("SELECT * FROM users").Scan(&user)
    return user
}

説明
routing/main.go
type User struct {
    gorm.Model
    Name     string `json:"name"`
    Age      int `json:"age"`
}

User(単数形)はMySQLのテーブル名「Users(複数形)」単数名にする必要がある
Name,Ageは先頭大文字である必要がある。
json:"name"はリクエストボディに関連させる名前。


main.go
package main

import (
    "github.com/labstack/echo"
    "src/routing"
)

func main() {
    e := echo.New()
    // routing
    e.GET("/user",routing.BaseAPI_GET())
    e.POST("/user",routing.BaseAPI_POST())

    e.Logger.Fatal(e.StartTLS(":443", "/crt/live/<あなたのドメイン名>/fullchain.pem", "/crt/live/<あなたのドメイン名>/privkey.pem"))
}

HTTPでよいなら
docker-compose.yml
version: "3"
services:
  golang:
    build: ./golang/
    image: image_golang
    container_name: container_api
    volumes:
      - ./src:/go/src
      # 証明書を/go/src/crtへ
      # - /etc/letsencrypt:/go/src/crt  <- 削除
    ports:
      # - "443:443"  <- 削除
      - "8080:8080"  <- 追加
    tty: true

  mysql:
    # platform: linux/x86_64 #AppleSiliconを使用時,必要
    build: ./mysql/
    image: image_mysql
    container_name: container_db
    volumes:
      - ./mysql/db:/docker-entrypoint-initdb.d  #初期データをマウントする場所
    environment: # コンテナ内のMySQLを起動する際のパスワードを設定
      - MYSQL_DATABASE=golang      # 作成するデータベース名
      - MYSQL_USER=akidon          # 作業ユーザー名(任意の値)
      - MYSQL_PASSWORD=12345       # 作業ユーザーのパスワード(任意の値)
      - MYSQL_ROOT_PASSWORD=root   # rootユーザーのパスワード(デフォルトでは「root」)
    ports:
      - "3306:3306"
    tty: true
main.go
// e.Logger.Fatal(e.StartTLS(":443", "/crt/live/<あなたのドメイン名>/fullchain.pem", "/crt/live/<あなたのドメイン名>/privkey.pem"))
e.Logger.Fatal(e.Start(":8080")) // <- 追加

オレオレ証明書では

crtに
・ myself.csr
・ myself.key
・ myself.crt
がある場合
main.go
// e.Logger.Fatal(e.StartTLS(":443", "./crt/live/<あなたのドメイン名>/fullchain.pem", "./crt/live/<あなたのドメイン名>/privkey.pem"))
e.Logger.Fatal(e.StartTLS(":443", "crt/myself.crt", "crt/myself.key")) // <- 追加

動かす

$ cd ../プロジェクト名/server/docker

(再ビルドしても変更されない場合はキャッシュが残っている可能性がある為,全削除)
(注意) $ docker rm -f `docker ps -a -q`

(ビルドする)
$ docker compose up -d --build  

(コンテナ内に入る)**1分ぐらい待つ(MySQLの初期設定の完了に時間がかかる)**
$ docker-compose exec mysql bash
root@コンテナ番号:# exit

$ docker-compose exec golang bash

(go mod init フォルダ名)
root@コンテナ番号:/go/src# go mod init src
root@コンテナ番号:/go/src# go run main.go

MySQLの中を覗く

% docker-compose exec mysql bash

root@コンテナ番号:/# mysql -u root -p -h 127.0.0.1

(パスワードの入力を求められるので「root」を入力)
-> root

(現在存在するアカウントの確認)
(hostに「%」がついてるか確認)
mysql> select host,user from mysql.user;
+-----------+------------------+
| host      | user             |
+-----------+------------------+
| %         | akidon           |
| %         | root             |
| localhost | mysql.infoschema |
| localhost | mysql.session    |
| localhost | mysql.sys        |
| localhost | root             |
+-----------+------------------+

(ついていない場合は以下コードで追加できたはず)
mysql> grant all privileges on golang.* to 'akidon'@'%';


mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| golang             |  <- 作成できてるか確認
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.06 sec)


mysql> use golang;
mysql> show tables;
+------------------+
| Tables_in_golang |
+------------------+
| users            |   <- 作成できてるか確認
+------------------+
1 row in set (0.01 sec)


mysql> SELECT * FROM `users`;
+----+---------+------+------------+------------+------------+
| id | name    | age  | created_at | updated_at | deleted_at |
+----+---------+------+------------+------------+------------+
|  1 | testKun |   21 | NULL       | NULL       | NULL       |
+----+---------+------+------------+------------+------------+
1 row in set (0.00 sec)

この時点でエラーが起きた場合

・src/main.go 27行目のcontainer_dbはdocker-composeで指定したコンテナ名を指定しているか。
・ポート解放してるか
・データベース(テーブル)が正しく作成されているか(今回ならgolang)
・大文字小文字の区別を無視していないか
・go mod init src したか?(この辺は理解不足で間違っている場合がある。src以下のファイルをサブパッケージとして参照できるようにする??)

・docker起動できているか以下コマンドで確認

# 動いているdocker一覧表示
docker-compose ps
# 作成した全てのdocker一覧表示
docker-compose ps -a 

豆知識

main.goの内容を変更してもDocker再buildする必要はない

SequelAce

スクリーンショット 2021-04-13 15.46.15.png

GoENV
GO111MODULE=""
GOARCH="arm64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/root/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_arm64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/go/src/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build147035606=/tmp/go-build -gno-record-gcc-switches"

参考記事

docker-compose upって何?
Go言語の依存モジュール管理ツール Modules の使い方
他多数

0
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
akidon0000
四国の大学生、専門分野:化学

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
0
Help us understand the problem. What is going on with this article?