20200919のMySQLに関する記事は7件です。

Lambda上でmysqlclientを動かす

概要

LambdaにてPythonを使用時、MySQLクライアントとしてmysqlclientを使うもの(SQLAlchemy等)を利用する場合、いろいろ準備が必要。

  • mysqlclient1.4.6 を使う
  • AmazonLinux2から libmysqlclient.so.18 を拾ってくる
  • libmysqlclient.so.18 をLambdaへ上げた際に、Lambda上でライブラリ用パスへ配置する

mysqlclient1.4.6 を使う

pip install mysqlclient===1.4.6

細かい原理は不明だが 2.0.1 を使おうとすると、'_mysql'がないと怒られる。。

libmysqlclient.so.18を拾ってくる

AmazonLinux2用のlibmysqlclient.so.18が必要になるのでビルドする。

コンテナ起動

docker run -it amazonlinux:2 bash

コンテナ内

libmysqlclient.so.18を入れる。

yum update -y
yum install -y gcc mysql-devel mysql-libs # /lib64/mysql/libmysqlclient.so.18 が入る

ローカルPC

docker cp等でlibmysqlclient.so.18をローカルへコピーする。

docker cp container:/lib64/mysql/libmysqlclient.so.18.0.0 local_path

libmysqlclient.so.18をLambdaに配置する際の注意

Lambdaのコンテナ上のライブラリのパスが、↓になっているので、

LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib

libmysqlclient.so.18 がLambdaコンテナに配置された際に上記のパスに配置されるようにする必要がある。

例:レイヤーとしてアップした場合

レイヤーのファイルは /opt 内に展開されるが、/opt/lib の位置に .so が来る必要がある。

venv/lib/site-packages 内をレイヤーとして上げる場合、venv/lib/site-packages/lib.so を入れる。

例:Lambda用のソースに紛れ込ませる場合

/var/task に展開されるので、 libを作って、その中に .so を入れる。

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

Go(Echo)×MySQL×Docker×Clean ArchitectureでAPIサーバー構築してみた

はじめに

タイトルにもある通り、Go×MySQL×DockerでAPIサーバーを作ってみました。
はじめは、

・Goで簡単なREST APIを作ってみる
・Dockerで開発環境を立てる

を目的として作っていたのですが、ディレクトリ構成をどうしようか悩んでいろいろ調べているとGo×クリーンアーキテクチャでAPIサーバーを作っている記事をたくさん発見したので、クリーンアーキテクチャやってみよう、ということでやってみました。

ということでこの記事は、

・GoでAPIを書いてみたい
・Dockerで開発環境をたてたい
・その際にクリーンアーキテクチャを採用してみたい

ぐらいの温度感の方におすすめです。

逆に、がっつりクリーンアーキテクチャを勉強したいという方にはあまり参考にならないかもしれません。。

なお、
フレームワークにEcho (https://echo.labstack.com/)
DBへのアクセスにはGorm (https://github.com/go-gorm/gorm)
を使用しています。

リポジトリはこちら↓

準備編

開発環境

Dockerで開発環境を構築しました。

Dockerfile

基本的なDockerfileの書き方は Dockerfile 公式リファレンス が参考になります。

では、さらっとですが順番に見ていきます。

まずはGoです。

docker/api/Dockerfile
FROM golang:1.14

# go moduleを使用
ENV GO111MODULE=on 

# アプリケーションを実行するディレクトリを指定
WORKDIR /go/src/github.com/Le0tk0k/go-rest-api

# 上記のディレクトリにgo.modとgo.sumをコピー
COPY go.mod go.sum ./
# 上記のファイルに変更がなければキャッシュ利用できる
RUN go mod download

COPY . .
RUN go build .

RUN go get github.com/pilu/fresh

EXPOSE 8080

# freshコマンドでサーバーを起動
CMD ["fresh"]

github.com/pilu/fresh でホットリロードできるようにしました。
他は特に変わったところはありません。

参考
Using go mod download to speed up Golang Docker builds
Go v1.11 + Docker + fresh でホットリロード開発環境を作って愉快なGo言語生活

次にMySQLです。

docker/mysql/Dockerfile
FROM mysql

EXPOSE 3306

# MySQL設定ファイルをイメージ内にコピー
COPY ./docker/mysql/my.cnf /etc/mysql/conf.d/my.cnf

CMD ["mysqld"]

MySQLの設定ファイルは以下のようになりました。
MySQL8.0以降では接続時の認証方式が変更になっているため、2行目の記述が必要になるらしい。
あと、文字化けを防ぐために文字コードを変更しています。

docker/mysql/my.cnf
[mysqld]
default_authentication_plugin=mysql_native_password
character-set-server=utf8mb4

[client]
default-character-set=utf8mb4

/docker-entrypoint-initdb.d/ というディレクトリ内に初期化用のSQLやスクリプトを置いておくと、最初にimageを起動したときにデータの初期化を自動的に行えます。

ということで、テーブルを作成します。

docker/mysql/db/init.sql
CREATE DATABASE IF NOT EXISTS go_rest_api;
USE go_rest_api;

CREATE TABLE IF NOT EXISTS users (
  id          INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
  name        VARCHAR(256) NOT NULL,
  age         INT NOT NULL,
  created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  updated_at  TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

参考
Docker + MySQLで開発環境用DBの作成

docker-compose.yml

こちらも Compose ファイル・公式リファレンス が参考になります。

docker-compose.yml
version: '3'
services: 
  db:
    build: 
      # Dockerfileのあるディレクトリのパスを指定
      context: .
      dockerfile: ./docker/mysql/Dockerfile
    ports: 
      # 公開ポートを指定
      - "3306:3306"
    volumes: 
      # 起動時にデータの初期化を行う
      - ./docker/mysql/db:/docker-entrypoint-initdb.d
      # mysqlの永続化
      - ./docker/mysql/db/mysql_data:/var/lib/mysql
    environment: 
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: go_rest_api
      MYSQL_USER: user
      MYSQL_PASSWORD: password

  api:
    build:
      context: .
      dockerfile: ./docker/api/Dockerfile
    volumes: 
      - ./:/go/src/github.com/Le0tk0k/go-rest-api
    ports: 
      - "8080:8080"
    depends_on:
      # dbが先に起動する
      - db

mysqlの永続化をしているので、コンテナを立ち上げ直した場合も、以前のデータが残ったままになります。

また、apiにdepend_onを指定することで、先にdbが起動し、その次にapiが起動するようになります。
しかし、depend_onは起動する順番を指定するだけで、出来上がるまでは待ってくれないので、別で対策が必要です。
なお、対策については実装編で紹介しています。

参考

クリーンアーキテクチャ

クリーンアーキテクチャは、The Clean Architecture で提唱されているアーキテクチャです。

僕はそもそも、アーキテクチャ?何それ?状態だったのですが、以下の記事を読んで上澄み1mmぐらいは理解できた気がします。他にもたくさん記事があるのでぜひ調べてみてください!

参考 (クリーンアーキテクチャ以外の記事もあります)

実装編

ディレクトリ構成

ディレクトリ構成は以下の通りです。

├── docker
│   ├── api
│   │   └── Dockerfile
│   └── mysql
│       ├── Dockerfile
│       ├── db
│       │   ├── init.sql
│       │   └── mysql_data
│       └── my.cnf
├── docker-compose.yml
├── domain
│   └── user.go
├── infrastructure
│   ├── router.go
│   └── sqlhandler.go
├── interfaces
│   ├── controllers
│   │   ├── context.go
│   │   ├── error.go
│   │   └── user_controller.go
│   └── database
│       ├── sql_handler.go
│       └── user_repository.go
|── usecase
│   ├── user_interactor.go
│   └── user_repository.go
├── server.go
├── go.mod
├── go.sum

domain    →  Entities層
infrastructure → Frameworks & Drivers層
interfaces   → Interface層
usecase   → Use cases層

に対応しています。

Domain ( Entities層 )

最も内側にある層で、どこにも依存しない層となります。
Userモデルを定義していきます。

なお、 json:"-" については、- と書くと出力されなくなります。

domain/user.go
package domain

import "time"

type User struct {
    ID        int       `gorm:"primary_key" json:"id"`
    Name      string    `json:"name"`
    Age       int       `json:"age"`
    CreatedAt time.Time `json:"-"`
    UpdatedAt time.Time `json:"-"`
}

type Users []User

Interfaces/database, Infrastructure (Interface層, Frameworks & Driver層)

次に、データベース周辺を実装していきます。
データベースの接続は外部との接続となるので、一番外側のInfrastructure層に定義します。

infrastructure/sqlhandler.go
package infrastructure

import (
    "fmt"
    "time"

    "github.com/Le0tk0k/go-rest-api/interfaces/database"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

type SqlHandler struct {
    Conn *gorm.DB
}

func NewMySqlDb() database.SqlHandler {

    connectionString := fmt.Sprintf(
        "%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=Local",
        "user",
        "password",
        "db",
        "3306",
        "go_rest_api",
    )

    conn, err := open(connectionString, 30)
    if err != nil {
        panic(err)
    }

    //接続できているか確認
    err = conn.DB().Ping()
    if err != nil {
        panic(err)
    }

    // ログの詳細を出力
    conn.LogMode(true)
    // DBのエンジンを設定
    conn.Set("gorm:table_options", "ENGINE=InnoDB")

    sqlHandler := new(SqlHandler)
    sqlHandler.Conn = conn

    return sqlHandler
}

// MySQLの立ち上がりを確認してからapiコンテナが立ち上がるようにする
func open(path string, count uint) (*gorm.DB, error) {
    db, err := gorm.Open("mysql", path)
    if err != nil {
        if count == 0 {
            return nil, fmt.Errorf("Retry count over")
        }
        time.Sleep(time.Second)
        count--
        return open(path, count)
    }
    return db, nil
}

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

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

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

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

opan()では、dbコンテナが立ち上がるまで待ってからapiコンテナが立ち上がるように設定しています。
今回は、Goのコードでこの機能を実装しましたが、シェルスクリプトで実装する方がいいのでしょうか?ベストプラクティスがわかりません、、
参考
- docker-compose upでMySQLが起動するまで待つ方法(2種類紹介)

なお、infrastructure層はdb接続のみにして、実際の処理はinterfaces/database層に実装します。

interfaces/database/user_repository.go
package database

import (
    "github.com/Le0tk0k/go-rest-api/domain"
)

type UserRepository struct {
    SqlHandler
}

func (userRepository *UserRepository) FindByID(id int) (user domain.User, err error) {
    if err = userRepository.Find(&user, id).Error; err != nil {
        return
    }
    return
}

func (userRepository *UserRepository) Store(u domain.User) (user domain.User, err error) {
    if err = userRepository.Create(&u).Error; err != nil {
        return
    }
    user = u
    return
}

func (userRepository *UserRepository) Update(u domain.User) (user domain.User, err error) {
    if err = userRepository.Save(&u).Error; err != nil {
        return
    }
    user = u
    return
}

func (userRepository *UserRepository) DeleteByID(user domain.User) (err error) {
    if err = userRepository.Delete(&user).Error; err != nil {
        return
    }
    return
}

func (userRepository *UserRepository) FindAll() (users domain.Users, err error) {
    if err = userRepository.Find(&users).Error; err != nil {
        return
    }
    return
}

UserRepositoryにSqlHandlerを埋め込んでいます。
domain層をインポートしていますが、これは最も内側にある層なので、依存関係は守れています。

しかし、SqlHandlerは1番外側のInfrastructure層に定義したので、依存関係が内から外に向いており、ルールを守れていないのでは?という疑問が生まれますが、実はここで呼んでいるSqlHandlerはInfrastructure層に定義した構造体ではなく、同階層であるinterface/database層に定義したインターフェースなのです。
これをDIP(依存関係逆転の原則)といいます。

どういうことかといいますと、interfaces/database/user_repository.goでは、Infrastructure層で定義されている処理を呼んでしまうと依存関係が内→外になってしまい依存関係を守れていないことになります。それを避けるために同階層にDBとのやりとりをinterfaceで定義しておき、それを呼び出しましょうということです。
これで、実際にはInfrastructure層で処理を行っているが、interfaces/database/user_repository.goではSqlHandlerインターフェースを呼び出しているので依存関係は守れていることになります。

そしてGoでは、型TがインターフェースIで定義されているメソッドを全て持つとき、インターフェースIを実装していることになるので、Infrastructure.SqlHandler構造体にinterfaces/databases/sql_handerのSqlHandlerインターフェースで定義されているメソッドを定義しています。

interfaces/database/sql_handler.go
package database

import "github.com/jinzhu/gorm"

type SqlHandler interface {
    Find(interface{}, ...interface{}) *gorm.DB
    Create(interface{}) *gorm.DB
    Save(interface{}) *gorm.DB
    Delete(interface{}) *gorm.DB
}

Usecase ( Use Case層 )

Usecase層では、interfaces/database層から情報を受け取り、interfaces/controller層へと情報を渡す役割を持ちます。

usecase/user_interactor.go
package usecase

import "github.com/Le0tk0k/go-rest-api/domain"

type UserInteractor struct {
    UserRepository UserRepository
}

func (interactor *UserInteractor) UserById(id int) (user domain.User, err error) {
    user, err = interactor.UserRepository.FindByID(id)
    return
}

func (interactor *UserInteractor) Users() (users domain.Users, err error) {
    users, err = interactor.UserRepository.FindAll()
    return
}

func (interactor *UserInteractor) Add(u domain.User) (user domain.User, err error) {
    user, err = interactor.UserRepository.Store(u)
    return
}

func (interactor *UserInteractor) Update(u domain.User) (user domain.User, err error) {
    user, err = interactor.UserRepository.Update(u)
    return
}

func (interactor *UserInteractor) DeleteById(user domain.User) (err error) {
    err = interactor.UserRepository.DeleteByID(user)
    return
}

UserInteractorはUserRepositoryを呼び出していますが、これもInterfase層から呼び出してしまうと内→外への依存となり依存関係を守れなくなってしまうので、同階層にuser_repository.goを作成し、インターフェースを実装してDIP(依存関係逆転の原則)を利用して依存関係を守ります。

usecase/user_repository.go
package usecase

import "github.com/Le0tk0k/go-rest-api/domain"

type UserRepository interface {
    FindByID(id int) (domain.User, error)
    Store(domain.User) (domain.User, error)
    Update(domain.User) (domain.User, error)
    DeleteByID(domain.User) error
    FindAll() (domain.Users, error)
}

Interfaces/controllers, Infrastructure (Interface層, Frameworks & Driver層)

そして、コントローラーとルーターを実装していきます。

interfaces/controllers/user_controller.go
package controllers

import (
    "strconv"

    "github.com/Le0tk0k/go-rest-api/domain"
    "github.com/Le0tk0k/go-rest-api/interfaces/database"
    "github.com/Le0tk0k/go-rest-api/usecase"
)

type UserController struct {
    Interactor usecase.UserInteractor
}

func NewUserController(sqlHandler database.SqlHandler) *UserController {
    return &UserController{
        Interactor: usecase.UserInteractor{
            UserRepository: &database.UserRepository{
                SqlHandler: sqlHandler,
            },
        },
    }
}

func (controller *UserController) CreateUser(c Context) (err error) {
    u := domain.User{}
    c.Bind(&u)
    user, err := controller.Interactor.Add(u)

    if err != nil {
        c.JSON(500, NewError(err))
        return
    }
    c.JSON(201, user)
    return
}

func (controller *UserController) GetUsers(c Context) (err error) {
    users, err := controller.Interactor.Users()

    if err != nil {
        c.JSON(500, NewError(err))
        return
    }
    c.JSON(200, users)
    return
}

func (controller *UserController) GetUser(c Context) (err error) {
    id, _ := strconv.Atoi(c.Param("id"))
    user, err := controller.Interactor.UserById(id)

    if err != nil {
        c.JSON(500, NewError(err))
        return
    }
    c.JSON(200, user)
    return
}

func (controller *UserController) UpdateUser(c Context) (err error) {
    id, _ := strconv.Atoi(c.Param("id"))
    u := domain.User{ID: id}
    c.Bind(&u)

    user, err := controller.Interactor.Update(u)

    if err != nil {
        c.JSON(500, NewError(err))
        return
    }
    c.JSON(201, user)
    return
}

func (controller *UserController) DeleteUser(c Context) (err error) {
    id, _ := strconv.Atoi(c.Param("id"))
    user := domain.User{ID: id}

    err = controller.Interactor.DeleteById(user)
    if err != nil {
        c.JSON(500, NewError(err))
        return
    }
    c.JSON(200, user)
    return
}

echoはecho.Contextを使用するので、今回利用するメソッドのインターフェイスを定義します。

interfaces/controllers/context.go
package controllers

type Context interface {
    Param(string) string
    Bind(interface{}) error
    JSON(int, interface{}) error
}
interfaces/controllers/error.go
package controllers

type Error struct {
    Message string
}

func NewError(err error) *Error {
    return &Error{
        Message: err.Error(),
    }
}

ルーティングはechoを使用している (外部パッケージを使用している)ので、Infrastructure層に実装します。
なお、uesrController.関数(c)で引数にecho.Context型を渡せているのは、echo.Contextがinterfaces/controllers/context.goのcontextインターフェースを満たしているからです。
(つまり、echo.Context型はcontextインターフェースのメソッドをすべて持っている。)

infrastructure/router.go
package infrastructure

import (
    "github.com/Le0tk0k/go-rest-api/interfaces/controllers"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

func Init() {
    e := echo.New()

    userController := controllers.NewUserController(NewMySqlDb())

    // アクセスログのようなリクエスト単位のログを出力する
    e.Use(middleware.Logger())
    // アプリケーションのどこかで予期せずにpanicを起こしてしまっても、サーバは落とさずにエラーレスポンスを返せるようにリカバリーする
    e.Use(middleware.Recover())

    e.GET("/users", func(c echo.Context) error { return userController.GetUsers(c) })
    e.GET("/users/:id", func(c echo.Context) error { return userController.GetUser(c) })
    e.POST("/users", func(c echo.Context) error { return userController.CreateUser(c) })
    e.PUT("/users/:id", func(c echo.Context) error { return userController.UpdateUser(c) })
    e.DELETE("/users/:id", func(c echo.Context) error { return userController.DeleteUser(c) })

    e.Logger.Fatal(e.Start(":8080"))
}

最後に、server.goから呼び出します。

server.go
package main

import "github.com/Le0tk0k/go-rest-api/infrastructure"

func main() {
    infrastructure.Init()
}

実行編

では、完成したAPIを叩いてみましょう。
今回はPostmanを使用してみたいと思います。 (https://www.postman.com/downloads/)

まずHeadersのContent-Typeをapplication/jsonにします。
スクリーンショット 2020-09-19 16.09.22.png

GetUsers

全ユーザーを取得してみます。
/usersにGETリクエストを送ります。
スクリーンショット 2020-09-19 16.14.07.png

Getuser

任意のユーザーを取得してみます。
/users/:idにGETリクエストを送ります。
スクリーンショット 2020-09-19 16.16.59.png

CreateUser

まずはUserを作成してみます。
/usersにPOSTリクエストを送ります。
スクリーンショット 2020-09-19 16.11.47.png

UpdateUser

任意のユーザーを更新してみます。
/users/:idにPUTリクエストを送ります。
スクリーンショット 2020-09-19 16.18.02.png

DeleteUser

任意のユーザーを削除してみます。
/users/:idにDELETEリクエストを送ります。
スクリーンショット 2020-09-19 16.19.14.png

GETで確認してみると、しっかり削除されています。
スクリーンショット 2020-09-19 16.19.24.png

しっかり機能してます!

さいごに

Dockerで開発環境を立ててREST APIを作るだけだったはずが、クリーンアーキテクチャまで勉強することになり大変でしたが、アーキテクチャを知るとても良い機会になったと思います。
今回ぐらいの小さいAPIだと逆に複雑になった感はありますが、大規模だったら良さが発揮されるのかなといった感想を持ちました。
実際のところ、あまり理解できていないので)また設計についても勉強していきたいと思います!

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

Local by FlywheelのMySQLにゲスト側ではなくホスト側からWP-CLIなどで接続する

Local by Flywheelなどの仮想環境のDBへホスト側のWP-CLIからアクセスしようとしてもうまくいきません。

$ wp post list
Error: Error establishing a database connection.

これは wp-config.php に記載されているDB接続情報が仮想環境間での接続情報となっているからです。

Local by Flywheelに記載されているDBの接続情報のうち、Socketを調べます。

Screen Shot 2020-09-19 at 13.27.20.png

この文字列をコピーし、wp-config.php に下記のように localhost: に続けて記載します。

app/public/wp-config.php
/** MySQL hostname */
define( 'DB_HOST', 'localhost:/Users/yousan/Library/Application Support/Local/run/_BXXXXXXX/mysql/mysqld.sock' );

これでつながるようになります。便利!

$ wp post list
+----+--------------+-------------+---------------------+-------------+
| ID | post_title   | post_name   | post_date           | post_status |
+----+--------------+-------------+---------------------+-------------+
| 1  | Hello world! | hello-world | 2020-09-19 01:32:34 | publish     |
+----+--------------+-------------+---------------------+-------------+
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go(Gin)+MySQLなDocker環境でpanic: dial tcp 127.0.0.1:3306: connect: connection refusedに苛まれた

症状

タイトルの通り。

解決策

こちらに見事なまでにおんなじ症状の方がいて、言われるがままにMySQLの接続情報を修正したところ無事直りました。以下Before/Afterです。

Before

docker-compose.yml
version: "3"
services:
  app:
    build: .
    depends_on:
      - db
    volumes:
      - ./:/go/src/app
    ports:
      - 3000:3000
    environment:
      MYSQL_DATABASE: go_app_dev
      MYSQL_USER: docker
      MYSQL_PASSWORD: password

  db:
    image: mysql:5.7
    container_name: dockerMySQL
    volumes:
      - ./storage/mysql_data:/var/lib/mysql
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: go_app_dev
      MYSQL_USER: docker
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password
db/db.go
package db

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

var (
    db  *gorm.DB
    err error
)

func Init() {
    user = os.Getenv("MySQL_USER")
    pass = os.Getenv("MYSQL_PASSWORD")
    dbname := os.Getenv("MYSQL_DATABASE")
    connection := fmt.Sprintf("%s:%s@/%s?charset=utf8&parseTime=True&loc=Local", user, pass, dbname)

    db, err := gorm.Open("mysql", connection)

    if err != nil {
        panic(err)
    }
}

After

docker-compose.yml
version: "3"
services:
  app:
    build: .
    depends_on:
      - db
    volumes:
      - ./:/go/src/app
    ports:
      - 3000:3000
    environment:
      MYSQL_DATABASE: go_app_dev
      MYSQL_HOST: dockerMySQL  # 追加!!
      MYSQL_USER: docker
      MYSQL_PASSWORD: password

  db:
    image: mysql:5.7
    container_name: dockerMySQL  # 追加!!
    volumes:
      - ./storage/mysql_data:/var/lib/mysql
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: go_app_dev
      MYSQL_USER: docker
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password
db/db.go
// ...略

func Init() {
    user = os.Getenv("MySQL_USER")
    pass = os.Getenv("MYSQL_PASSWORD")
    host = os.Getenv("MYSQL_HOST")  // ココ!!
    dbname := os.Getenv("MYSQL_DATABASE")
    connection := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", user, pass, host, dbname)  // 修正!!

    db, err := gorm.Open("mysql", connection)

    if err != nil {
        panic(err)
    }
}

決め手はgorm.Openの第二引数で接続情報を指定する際に、MySQLを動かしているDockerコンテナのコンテナ名で接続するという点でした。そもそもBeforeのコードでMySQLに接続できなかったのはMySQLがlocalhost,あるいはTCPの3306番ポートでListenしていなかったからです。そしてdocker-composeで起動しているコンテナ達はdocker networkを介して通信しているため、ネットワーク名の代わりにコンテナ名で接続することが可能であり、Afterに載せたような接続情報でappコンテナからdbコンテナに接続できるということらしいです。このような理解で合っているかは少々自信がありませんがw

参考

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

MySQLのデータベース操作

ターミナルでSQLを実行し、データベースの操作を行う際の備忘メモ。

ログイン

command
% mysql -u root

「-u root」というオプションで、MySQLであらかじめ用意されているユーザーでログインする。

データベースの作成

command
mysql> CREATE DATABASE データベース名;

「CREATE」は、データベースやテーブルを作成できるSQLの文。

データベースの一覧表示

command
mysql> SHOW DATABASES;

「SHOW」は、データベースやテーブルを一覧表示できるSQLの文。

データベースの削除

command
mysql> DROP DATABASE データベース名;

「DROP」は、データベースやテーブルを削除できるSQLの文。

テーブルの構造確認

command
mysql> SHOW columns FROM テーブル名;

「FROM」は、対象となるテーブルを指定する際に使用するSQLの句。

カラムの追加

command
mysql> ALTER TABLE テーブル名 ADD (カラム名 カラムの型, ……);

「ALTER」は、データベースやテーブルを編集できるSQLの文。

カラムの変更

command
mysql> ALTER TABLE テーブル名 CHANGE 古いカラム名 新しいカラム名 新しいカラムの型;

カラムの削除

command
mysql> ALTER TABLE テーブル名 DROP カラム名;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[laravel]Mysqlとの接続メモ

前提条件

Mysqlはインストール済、dbをcreate済、環境はMacOS

設定

config/database.php

return [

    'default' => env('DB_CONNECTION', 'mysql'),

    'connections' => [

        'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', '※接続するデータベース名'),
            'username' => env('DB_USERNAME', '※ユーザ名'),
            'password' => env('DB_PASSWORD', '※パスワード'),
        ],
]

.env

DB_USERNAME=※ユーザ名
DB_PASSWORD=※パスワード

接続確認

$ php artisan serve

正しく接続してなければエラーが返ってくる。

所感

.envはdatabase.phpを上書きするようだ。本番環境に置く時に.envでパラメータ上書きして使うのかな?

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

C#でMySQLを使う - 1.Visual Studio 2019+MySQL 5.7 Connectorsインストール編

前提と準備

C#の記事

最近私がプライベートで、三浦半島の一眼レフ写真や小さなアトラクションを通し、多くの方が自然と歴史などを通して感じていかれるWebサイトを制作しておりまして、ロリポップ!レンタルサーバーにPHP+MySQLを使用しています(*´꒳`*)

そのMySQLのデータ登録ですが、インターネットでPHPからMySQLを登録できるとなると、PHP側で登録用のアカウントを作らなければならなかったり、ログインなどのセキュリティを強固にする必要があっても、やはりインターネット上である以上は限度があるため、ローカルでMySQLにデータ登録し、その生成したSQLパッチを運用中のサーバーに適用しています。そうなるとインターネット上に管理画面を作る必要がなく、多少はマシになるものの、ローカルでMySQLデータ登録のプログラムを作成しなければなりません。

そこで、私の場合は、WindowsプログラムをC#で制作し、そこからMySQLに接続し、SQLパッチをテキストとして生成するというやり方を採っています。その際にどうしても必要なのは、C#(.NET)からMySQLに接続できるための外部ライブラリを使い、Visual Studioには標準で用意されていないため、サードパーティー製のコネクタを用意する必要があったので、インストールしました(˶ ・ᴗ・ )੭

環境

  • OS:Windows 10 Pro (インストール再現のためWindowsサンドボックス使用)
  • 開発環境:Visual Studio 2019
  • データベース:MySQL 5.7

前提

特になし

作業手順

今回は、開発環境側でインストールをしてみます。私のメインの環境にすでにC#でMySQLが使える状態だったので、インストールはWindowsサンドボックスで再現することにしました(* ॑꒳ ॑* )⋆*

Visual StudioとMySQLのインストール

Visual Studio 2019のインストール

Visual Studioは短期間に2015→2017→2019…とサイクル期間が短いようですが、たぶん他のVisual Studioのバージョンでもいけると思いますが、私は2019を使っています

Visual Studio 2019

そのままデフォルトでインストールしました( ˙꒳​˙ᐢ )
ちなみにC#を使う項目はすべてインストール選択しています

MySQLのインストール

MySQL 8.0はLinuxでサーバー構築で使いまくったけど、Windows用に置いてあるのが5.7だったので、5.7の方を使いました

MySQL 5.7

「Developer Default」を選択してインストール。開発向けデフォルトでは、画面のように「MySQL Connectors」が項目としてピックアップされていて、Javaや.NET、C/C++などに対応しています。

Visual Studioで使用する

参照を追加してみる

Visual Studioを起動して、新規のC#フォームアプリケーションプロジェクトを作成。
そうすると、ソリューションエクスプローラーに「参照」があるので、参照を追加して「参照マネージャー」を開く。

MySql.Data

「アセンブリ」の中に実際自動で認識してくれるので、参照マネージャーで、右上の検索画面に「mysql」を入力すると、↑の画面のように「MySql.Data」が何行も出てくるので、とりあえず1つだけ、どれでもいいので選択して(行左にマウスを当てると出てくるチェックボックスをON)「OK」を選択すると…

MySQLを使う

MySql.Data」が追加されました(*˘꒳˘*)
この中にMySQLを扱うC#オブジェクトが入っているのです。

ソースで記述する

実際にソースで使うには、最初に「using」で指定しておくと便利だったので、私はそうしました

sample1.cs
using System;
using System.text;
(中略)

using MySql.Data.MySqlClient;   // これがMySQLを扱うためのオブジェクトを含んでいる

namespace WindowsForms1
{
    class sample1
    {
        public void sampleMethod()
            try {
                // 接続情報を文字列で与える
                string connStr = "server=127.0.0.1;user id=test;password=test1;database=manutest";
                MySqlConnection conn = new MySqlConnection(connStr);

                // 接続を開く
                conn.Open();

                // ここにSQLの処理を書く

                // 接続を閉じる
                conn.Close();
            }
            catch (MySqlException mse)
            {
                // MySqlの接続エラー処理をここに書く
            }
        }
    }
}

こういう流れでソースを書いていくもので、実際にMySQL ConnectorsのC#に関する公式の根拠が出回っていなかったので、第三者の情報と実際Visual Studioのコーディングでポップアップされるトピックを頼りにするしかなかったのです…( ´ •̥ ̫ •̥ ` )

using

MySQL接続オブジェクトが認識できた

実際エラーがなく基本的なコーディングの流れはつかめた(˶ ・ᴗ・ )੭⚐⚑

次回

Visual StudioにMySQLを導入できたので、次は実際にMySQLに接続して、データのやりとりも記事にまとめる予定です٩(.› ‹.♡)۶

参考文献

 1. MySQL Connector/NETを使ってみよう
 2. C#からMySQLに接続する - 電脳産物
 3. 【Visual Studio】C#からMySqlの使い方 接続するには?SQL文を実行するには? - 困ったー
 4. C#でMySQLからSELECTした結果を取り出したい

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