20200907のGoに関する記事は7件です。

Golang チュートリアル その1

概要

「処理が速い」「並行処理」が得意ということから、
今後大規模webサービスなどで重宝されると思いましたので、
学習していきたいと思います。

参考サイト

以下を実施してみました。

Welcome to a tour of Go

環境

GoogleChrome
※ブラウザ操作だけで学習できるため、エディタを用意する必要がありません。
※CentOS8へのGoインストール手順です。よろしければ、ご活用ください。
CentOS8へのGo言語インストール手順

基本操作

Welcome!

Hello Worldの実行手順です。

基本的な書き方

Packages, variables, and functions.

Go プログラムの基本的なコンポーネントを学びます。

  • 「package main」から始まる。

すべてはここから始まる

  • 関数について

引数に設定する関数には、型名を書く必要がある

//xとyが該当
func add(x int, y int) int {
    return x + y
}

ただし、関数の2つ以上の引数が同じ型である場合には、最後の型を残して省略して記述できる。

//xとyが該当
func add(x, y int) int {
    return x + y
}
  • 関数は複数の戻り値を返すことができる
func.go
package main

import "fmt"

//関数側
func swap(x, y string) (string, string) {
        return y, x
}

//呼び出す側
func main() {
        a, b := swap("hello", "world")
        fmt.Println(a, b)
}

//実行
go run func.go

//実行結果
world hello
  • 戻り値となる変数に名前をつける

戻り値となる変数に名前をつけることができる。
戻り値に名前をつけると、関数の最初で定義した変数名として扱われます。

名前をつけた戻り値の変数を使うと、
return ステートメントに何も書かずに戻すことができる。
※長い関数で使うと読みやすさ( readability )に悪影響があるため、
 注意が必要。

returnValueName.go
package main

import "fmt"

//(x, y int)の部分が名前付き変数
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

func main() {
    fmt.Println(split(17))
}

//実行
go run returnValueName.go

//実行結果
7 10

変数

  • 変数の宣言

「var」ステートメントで変数の宣言を行う。
パッケージ、または、関数で利用できる。

var.go
package main

import "fmt"

//グローバルに宣言 ※boolが型
var c, python, java bool

func main() {
    var i int
    fmt.Println(i, c, python, java)
}

//実行
go run var.go

//実行結果
0 false false false
  • 変数に初期値を与える

初期値が与えられている場合、型を省略できる。
その変数は初期値が持つ型になります。
※例: 数字->int , 文字列->stringなど

giveVarInitialValue.go
package main

import "fmt"

var i, j int = 1, 2

func main() {
    //c->bool ,python->bool ,java->string
    var c, python, java = true, false, "no!"
    fmt.Println(i, j, c, python, java)
}

//実行
go run giveVarInitialValue.go

//実行結果
1 2 true false no!
  • 変数の宣言を省略

関数の中では、 var 宣言の代わりに、短い := の代入文を使い、
暗黙的な型宣言ができる。

※関数の外では、キーワードではじまる宣言( var, func, など)が必要で、 := での暗黙的な宣言は利用できない。

varDeclarationOmitted.go
package main

import "fmt"

func main() {
    var i, j int = 1, 2
    //暗黙的宣言
    k := 3
    c, python, java := true, false, "no!"

    fmt.Println(i, j, k, c, python, java)
}

//実行
go run varDeclarationOmitted.go

//実行結果
1 2 3 true false no!
  • 基本の型

次のとおりです。

//型の紹介のみ

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // uint8 の別名

rune // int32 の別名
     // Unicode のコードポイントを表す

float32 float64

complex64 complex128
  • 変数に初期値を与えずに宣言

その場合、ゼロ値が与えられます。

ゼロ値は型によって以下のように与えられる。

数値型(int,floatなど): 0
bool型: false
string型: "" (空文字列( empty string ))

varNoInitialVal.go
package main

import "fmt"

func main() {
    var i int
    var f float64
    var b bool
    var s string
    fmt.Printf("%v %v %v %q\n", i, f, b, s)
}

//実行
go run go:varNoInitialVal.go

//実行結果
0 0 false ""
  • 型変換

少数点→整数の変換について
※数値から文字列への変換に関しては、また別処理が必要。。
golang 文字列→数値、数値→文字列変換

typeConv.go
package main

import (
    "fmt"
    "math"
)

func main() {
    var x, y int = 3, 4
    var f float64 = math.Sqrt(float64(x*x + y*y))
        var z uint = uint(f)
    fmt.Println(x, y, z)
}

//実行
go run typeConv.go

//実行結果
3 4 5
  • 型推論

明示的な型を指定せずに変数を宣言する場合( := や var = のいずれか)、
変数の型は右側の変数から型推論される。
※右側の変数が型を持っている場合、左側の新しい変数は同じ型になる。

typeInference.go
// i はint型のため、vもint型になる。
package main

import "fmt"

var i int

func main() {
    v := i
    fmt.Printf("v is of type %T\n", v)
}

//実行
go run typeInference.go

//実行結果
v is of type int

定数

定数は、 constを使って変数と同じように宣言する。
定数は、文字(character)、文字列(string)、boolean、数値(numeric)のみで使える。
※定数は := を使って宣言できない。

constant.go
package main

import "fmt"

const Pi = 3.14

func main() {
    const World string = "世界"
    fmt.Println("Hello", World)
    fmt.Println("Happy", Pi, "Day")

    const Truth = true
    fmt.Println("Go rules?", Truth)
}

//実行
go run constant.go

//実行結果
Hello 世界
Happy 3.14 Day
Go rules? true

まとめ

基本的なところをみていきました。
次回は、「For」や「if」といったところをみていきます。

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

vim-goimportsが効かない時の対処法。

 はじめに

Vimに vim-goimports を追加してみたものの効かなかったので対処法を記しておきます。

対処法

なぜ効かなかったかといいますと、~/.zshrc に$GOBINを通していなかったからです。
ということで、~/.zshrcに以下を書くだけです。

.zshrc
export GOBIN="$GOPATH/bin"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GoでVercel Serverless Functions

githubリポジトリ

ホスティングサービスをNetlifyからVercelに乗り換えたついでにServerless Functionsが気になったので試してみました。

Vercel

Next.jsの運営元であるVercel(元Zeit)のWebアプリケーションホスティングサービスです。ちょうどNetlifyとかと同じポジションですね。

前準備

アカウント作成

Vercelのトップページの右上にあるSignUpボタンからGithub, GitLab, BitBucketのアカウントを使用してアカウント作成をします。
vercelsignup.PNG

Vercel CLI

npmパッケージとして提供されているVercel CLIをインストールします。
これは主にローカルからデプロイを行う際に使用します。

npm
npm i -g vercel
yarn
yarn global add vercel

インストール後

vercel login

を実行し、上で登録したemailアドレスの入力と確認を済ませます。

サーバーレス関数の作成

プロジェクトフォルダの直下にindex.goを作成します。
このindex.gohttp.ResponseWriter*http.Requestを引数として持つ関数が存在する必要があります。
まずはVercel Serverless Functions Go Exampleというテキストを返すだけの関数を実装します。

index.js
package main

import (
  "fmt"
  "net/http"
)

func Handler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Vercel Serverless Functions Go Example")
}

vercel.jsonの作成

vercel.json
{
  "version": 2,
  "name": "go",
  "builds": [
    { "src": "*.go", "use": "@vercel/go" }
  ]
}

ルーティング

日時を返すエンドポイントを作成していきます。
今回はわかりやすく下記のようにエンドポイントごとにフォルダを分けて作成します。

root
├ /date
│ └ index.go
└ index.go

型の作成

date/index.go
type ResDate struct {
    Date string `json:"date"`
}

リクエストハンドラの実装

w.Header().Set("Content-Type", "application/json")Content-Typeの指定を行います。

date/index.go
func Handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    d := ResDate{ time.Now().Format(time.RFC850) }
    bytes, _ := json.Marshal(d)
    fmt.Fprintf(w, string(bytes))
}

date/index.go

date/index.go
package date

import (
    "fmt"
    "net/http"
    "time"
    "encoding/json"
)

type ResDate struct {
    Date string `json:"date"`
}

func Handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    d := ResDate{ time.Now().Format(time.RFC850) }
    bytes, _ := json.Marshal(d)
    fmt.Fprintf(w, string(bytes))
}

vercel.jsonでルーティングの設定

ルーティングの設定を行うためにvercel.jsonroutesの要素を追加します。

routes
  "routes": [
    { "src": "/date", "dest": "/date" }
  ]

加えて、ビルド対象フォルダに先程作成したdateフォルダ内のgoファイルを追加します。

builds
  "builds": [
    { "src": "*.go", "use": "@vercel/go" },
    { "src": "/date/*.go", "use": "@vercel/go" }
  ]
vercel.json
{
  "version": 2,
  "name": "go",
  "builds": [
    { "src": "*.go", "use": "@vercel/go" },
    { "src": "/date/*.go", "use": "@vercel/go" }
  ],
  "routes": [
    { "src": "/date", "dest": "/date" }
  ]
}

デプロイ

vercel

これだけです。

本番デプロイをするのであれば--probオプションをつけるだけです。

vercel --prob

デプロイ後の以下のURLにアクセスすると
https://go-vercel-function-example.mugi111.vercel.app
一番最初に作成した関数通りVercel Serverless Functions Go Exampleと表示されているのが確認できました。
vercelroot.PNG

dateエンドポイントも正常に動作していることが確認できました。
https://go-vercel-function-example.mugi111.vercel.app/date
verceldate.PNG

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

CentOS8にGolangをインストールする

概要

Golang学習用備忘録です。
今回以下URLを参考にさせていただきました。

VMwareにCentOS8を構築

以下に記載しておりますので、
よろしければ、ご覧ください。

Windows10にVMwareで仮想サーバを構築し、Dockerを使ってみる

Golangインストール/動作確認

  • Goインストール
ターミナル
#Golangインストール
dnf install -y epel-release
dnf install -y golang

#Golangバージョン確認
go version

→「go version go1.13.4」と表示される
  • 動作確認(Goファイル作成)
ターミナル
#動作確認用Goファイル作成
vim hello.go

#以下を追記
package main

import "fmt"

func main(){
        fmt.Println("hello world")
}

#動作確認
go run hello.go

#以下のように表示されること
hello world

まとめ

次回は、Goのチュートリアルについてまとめていきたいと思います。

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

【GCP】Cloud BuildでCI/CDパイプラインを構築する。

はじめに

Cloud BuildはGCP上でCI/CDを行うためのサービスです。
特にコンテナを扱ったソリューションと相性がよく、
テストやビルドはGCP上にプロビジョニングされたコンテナ内で行われ、
Cloud FunctionsやCloud Runといった様々なコンピューティングサービス上にデプロイすることができます。

本記事では、

スクリーンショット 2020-09-04 18.08.30.png

の全てGCP内で完結するリソースを使ってコンテナアプリケーションにおけるCI/CD環境を構築したいと思います!
本記事で使用したソースコードは、こちらにあります。

開発環境

  • macOS Catalina 10.15.6
  • Docker Desktop stable 2.3.0.4
  • Google Cloud SDK 308.0

登場人物たち

Cloud Source Repositories

プライベートなGitリポジトリをホスティングできるサービスです。
https://cloud.google.com/source-repositories/docs?hl=ja

Container Registry

プライベートなDockerイメージをホスティングできるサービスです。
https://cloud.google.com/container-registry/docs?hl=ja

Cloud Run

マネージドなコンテナ実行プラットフォームです。
コンテナを手軽にサービスとしてデプロイ、公開可能です。
https://cloud.google.com/run/docs?hl=ja

Cloud Build

本記事の主役です。
冒頭にもあるように、マネージドなCI/CD環境を構築できます。
詳しくは後の項で説明しますが、ビルドコンテナ内でgcloudコマンド叩けたりと柔軟なシナリオが作成可能です。
https://cloud.google.com/cloud-build/docs?hl=ja

作ってみよう!

ローカルにGitリポジトリを作成

適当な空のディレクトリを作成し、git initを行います。

$ mkdir cloudbuild-sample && cd cloudbuild-sample
$ git init

サンプルアプリケーションの作成

Cloud Runにデプロイするアプリケーションを作成します。
AとBの和を返す簡単なアプリを考えます。

$ mkdir workspace
cloudbuild-sample/workspace/go.mod
module mymod

go 1.15
cloudbuild-sample/workspace/main.go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "mymod/utils"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        j := json.NewDecoder(r.Body)
        var req struct {
            A int `json:"a"`
            B int `json:"b"`
        }

        j.Decode(&req)
        fmt.Fprintf(w, "%d\n", utils.Add(req.A, req.B))
    })

    log.Println("start server...")
    http.ListenAndServe(":8080", mux)
}

足し算の関数を定義するサブパッケージを作成します。

$ mkdir workspace/utils
cloudbuild-sample/workspace/utils/add.go
package utils

// Add return a + b
func Add(a int, b int) int {
    return a + b
}
cloudbuild-sample/workspace/utils/add_test.go
package utils

import "testing"

func TestSuccessAdd(t *testing.T) {
    expect := 3
    result := Add(2, 1)

    if expect != result {
        t.Fatalf("result is expected %d, got %d ", expect, result)
    }
}

func TestFailureAdd(t *testing.T) {
    t.Fatal("FAILURE!!")
}

CI実行時にテストの失敗を考慮されるかを確認するため、意図的に失敗するテストコードを作成しました。

さらに、公開用のDockerイメージファイルも作成します。

cloudbuild-sample/Dockerfile
FROM golang:1.15-alpine as builder

WORKDIR /opt/build

COPY go.* ./
RUN go mod download

COPY . ./

RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -v -o app


FROM alpine:3.12
ENV PORT 8080

COPY --from=builder /opt/build/app /opt/app

WORKDIR /opt
CMD ["./app"]

お疲れ様です!
上記のアプリは、

$ curl -X POST -H "Content-Type: application/json" -d '{"a":41, "b":10}' http://localhost:8080
51

のようなリクエストに対して、aとbの和を返してくれます。

gcloudコマンドの下準備

前提として、既にGCPプロジェクトが作成済みでgcloudコマンドが使用できることを確認してください。
ここではパイプライン環境構築のためのGCPプロジェクト側での設定を行います。

まずは利用するサービスのAPIを有効化しましょう!

$ gcloud services enable sourcerepo.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
containerregistry.googleapis.com

次にSource Repositoriesにリポジトリを作成し、リモートにセットします。

$ gcloud source repos create cloudbuild-sample
$ git remote add google https://source.developers.google.com/p/{YOUR_PROJECT_ID}/r/cloudbuild-sample

さらにリポジトリに対し認証情報を付与します。

$ git config --local credential.https://source.developers.google.com.helper gcloud.sh

ここまでのコードをpushできるか確認してましょう。

$ git add .
$ git commit -m 'first commit'
$ git push google master

次にCloud Buildのサービスアカウントに対しCloud Runへデプロイするための役割を付与します。

$ gcloud projects add-iam-policy-binding {YOUR_PROJECT_ID} \
--member serviceAccount:{YOUR_PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
--role roles/run.admin

$ gcloud projects add-iam-policy-binding {YOUR_PROJECT_ID} \ 
--member serviceAccount:{YOUR_PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
--role roles/iam.serviceAccountUser

お疲れ様です!
これで下準備は完了です。

Cloud Buildの構成ファイルを作成する。

Cloud Buildではテストやビルドの手順をYAMLファイルとして定義します。
簡単な例として、前項で作成したサンプルアプリケーションのテストを実行し、
成功したらデプロイ用のイメージをビルドする手順書を作成してみます。

cloudbuild-sample/cloudbuild.yaml
steps: # steps下にテスト〜デプロイまでの各手順を定義します。
  - name: 'golang:1.15-alpine' # コマンドを実行するコンテナイメージ
    id: 'do testing' # 各ステップを表す識別子
    entrypoint: '/bin/sh' # コンテナが実行される際のエントリーポイント
    env: # コンテナが実行される際に渡される環境変数
    - 'CGO_ENABLED=0'
    dir: 'workspace' # エントリーポイントが実行されるディレクトリ
    args: # エントリーポイントに対する引数
    - '-c'
    - |
        go mod download \
        && go test mymod/...
  - name: 'gcr.io/cloud-builders/docker' # 注1
    id: 'do building'
    args: # エントリーポイントは docker です。
    - 'build'
    - '-t'
    - 'gcr.io/$PROJECT_ID/sample_app:$SHORT_SHA' # 注2
    - '-f'
    - './Dockerfile'
    - './workspace'

設定しているオプションをよく見てみると、
普段のdocker runコマンドを構成するオプションと似ていることが確認できるかと思います。

注1
nameオプションに設定できるコンテナイメージですが、
クラウドビルダーと呼ばれるGCP側で用意された特別なコンテナイメージも利用可能です。
クラウドビルダーコンテナにはIAM等が適切に割り当てられた環境でgcloudコマンド等が実行できるため、
柔軟なパイプラインを作成することが可能になります。
利用可能なクラウドビルダーについては
https://cloud.google.com/cloud-build/docs/cloud-builders?hl=ja
を確認してみてください。

注2
$PROJECT_ID$SHORT_SHAといった、いくつかの変数はCloud Build側で適切な値に自動で置換されます。
今回の例で使用した
$PROJECT_IDは、GCPプロジェクトのID
$SHORT_SHAは、ビルドをトリガーしたリポジトリへのコミットID
です。
変数値の置換については、
https://cloud.google.com/cloud-build/docs/configuring-builds/substitute-variable-values?hl=ja
を確認してみてください。

それでは完全なCloud Buildの構成ファイルを作成してみましょう!
下記の構成ファイルでは、

  1. go testを実行しテストが全てパスされるかを確認
  2. デプロイ用のコンテナイメージをビルド
  3. ビルドされたコンテナイメージをGCRへpush
  4. GCRへプッシュされたイメージを使い、Cloud Runへサービスをデプロイ

を行います。

cloudbuild-sample/cloudbuild.yaml
steps:
  - name: 'golang:1.15-alpine'
    id: 'do testing'
    entrypoint: '/bin/sh'
    env:
    - 'CGO_ENABLED=0'
    dir: 'workspace'
    args:
    - '-c'
    - |
        go mod download \
        && go test mymod/...
  - name: 'gcr.io/cloud-builders/docker'
    id: 'do building'
    args:
    - 'build'
    - '-t'
    - 'gcr.io/$PROJECT_ID/sample_app:$SHORT_SHA'
    - '-f'
    - './Dockerfile'
    - './workspace'
  - name: 'gcr.io/cloud-builders/docker'
    id: 'do pushing image'
    args:
      - 'push'
      - 'gcr.io/$PROJECT_ID/sample_app:$SHORT_SHA'
  - name: 'gcr.io/cloud-builders/gcloud'
    id: 'do deploying'
    args:
    - 'run'
    - 'deploy'
    - 'sample-app'
    - '--image=gcr.io/$PROJECT_ID/sample_app:$SHORT_SHA'
    - '--region=asia-northeast1'
    - '--platform=managed'
    - '--allow-unauthenticated'

お疲れ様です!
パプリックなコンテナイメージとクラウドビルダーを使い分け様々なシナリオに対応できることが実感できたかと思います。
構成ファイルに関する正確な情報はこちらをご確認ください。
https://cloud.google.com/cloud-build/docs/build-config?hl=ja

Source Repositoriesへのpushでビルドをトリガーする。

最後に、Source Repositoriesへコードがプッシュされたときに自動的にCloud Buildが走るようトリガーを設定します。

$ gcloud beta builds triggers create cloud-source-repositories \
--repo cloudbuild-sample \
--branch-pattern="master" \
--build-config cloudbuild.yaml

cloudbuild-sampleリポジトリのmasterブランチにコードがpushされた際に、
cloudbuild.yamlに定義された内容のパイプラインが実行されます。

お疲れ様です!
作業は以上となります。
それではビルドの様子を確認していきたいと思います。

ビルドを実行してみよう!

これまでの内容をpushし、Cloud Buildが実行されるか確認してみます。

$ git add .
$ git commit -m 'second commit'
$ git push google master

下記のURLからビルドの履歴を確認できます。
https://console.cloud.google.com/cloud-build/builds?hl=ja

今回のビルドではgo testに失敗し、ビルドが途中で中断されていることが確認できるかと思います。

テストコードを修正し、もう一度ビルドを実行しましょう。

cloudbuild-sample/workspace/utils/add_test.go
package utils

import "testing"

func TestSuccessAdd(t *testing.T) {
    expect := 3
    result := Add(2, 1)

    if expect != result {
        t.Fatalf("result is expected %d, got %d ", expect, result)
    }
}

func TestFailureAdd(t *testing.T) {
    // t.Fatal("FAILURE!!") # コメントアウトしました。
}

再びpushします。

$ git add .
$ git commit -m 'fix test code'
$ git push google master

ビルドが正常に完了したことを確認します。
https://console.cloud.google.com/cloud-build/builds?hl=ja

さらに、正常にCloud Runへデプロイされたかを確認しましょう!
https://console.cloud.google.com/run?hl=ja

サービスのURLを確認し、curlを叩いてみます。

$ curl -X POST -H "Content-Type: application/json" -d '{"a":41, "b":10}' YOUR_CLOUD_RUN_URL
51

お疲れ様でした!

終わりに

本記事ではCloud Buildを使ってGo言語アプリケーションのCI/CDパイプラインを構築しました。
テストやビルドには様々なコンテナイメージを使えるためGo言語に限らず、柔軟なパイプラインを構築できるかと思います!
ありがとうございました!

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

[自分用メモ] はじめてのGoogle Cloud Functions(Go言語編)

注意

ここに書かれていることは、すべて下記の公式ドキュメントに書かれているものです。このブログは、あくまで自分用メモになります。

公式ドキュメント → https://cloud.google.com/functions/docs

Google Cloud FunctionsとGo

Goが使えるようになったのは2019年1月(参考記事)。

2020年9月現在、使えるGoのバージョンは1.11と1.13のみ(参考記事)。

関数を作る

ローカルの開発環境に、適当に次のような関数を作る。

hello.go
package hello

import (
    "fmt"
    "net/http"
)

func Hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello World!")
}

外部ライブラリとかを使いたければ、go.modを書けば良い。

GCPプロジェクト作成

適当にプロジェクトを作っておく。

ここに書いてある「APIの有効化」とかが必要かも。

gcloudコマンドでデプロイ

事前準備
# ログイン
$ gcloud auth login
# 操作対象のプロジェクトを設定
$ gcloud config set project プロジェクトID
関数のデプロイ
$ gcloud functions deploy Hello --runtime go113 --trigger-http --allow-unauthenticated

Hello の部分は、Goで書いた関数名と揃える必要があるっぽい。

実行

先ほどの gcloud functions コマンドの結果に、次のようなものが含まれている。このURLにアクセスすればよい。

httpsTrigger:
  url: https://us-central1-my-first-function-xxxxxx.cloudfunctions.net/Hello

スクリーンショット 2020-09-07 16.02.36.png

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

Goプロジェクトのはじめかたとおすすめライブラリ8.5選。ひな形にも使えるサンプルもあるよ。

(記事を読んで参考になったなとかストックしとこ、って思って頂けたらぜひLGTMもして頂けると喜びます?)

はじめに

最近イケイケなGo!
でもベターなGoプロジェクトのはじめかたっていまいち調べてもわからないですよね。
自分は最初色々迷いました。。

まずパッケージ管理ツール。
godepだったりGo Modulesだったり、少し情報がとっちらかっている印象です。

後はどのディレクトリにプロジェクトを配置した方がいいのかもわかりづらい。
importの仕方も相対と絶対のどちらがいいのか迷う。

というわけで以下の4点に絞って解説していきたいと思います!

  • パッケージ管理ツール
  • プロジェクトの配置場所
  • 自作パッケージのimportの仕方
  • おすすめライブラリ

この記事を読めば正しくGoプロジェクトが作成できます!(たぶん)

【追記】
インストール方法とVSCodeの設定に関しても簡潔にまとめたので、良かったら参考にしてください。
【超簡単】GoのインストールとVSCode設定方法

いきなりまとめ

プロジェクト作成

新規にGoプロジェクトを作成したい場合は以下の通りにコマンドを実行すればOKです。

$ cd (任意のディレクトリ)
$ mkdir (YOUR_PROJECT)
$ cd (YOUR_PROJECT)
$ go mod init github.com/(GitHubユーザ名)/(GitHubリポジトリ名)
# 後はどんどこファイル作成!
$ vim main.go
# あるいは必要なパッケージをインストール
$ go get ~~~~~
...

おすすめライブラリインストール

go mod init後に必要に応じて実行。
あるいは各ソースコードのimport文にライブラリのパスを記載しておくと、go runの時に自動でインストールしてくれます。

go get -u github.com/labstack/echo/...
go get -u github.com/joho/godotenv
go get -u github.com/sirupsen/logrus
go get -u github.com/valyala/fasthttp
go get -u github.com/golang/mock/gomock
go install github.com/golang/mock/mockgen
go get -u github.com/google/wire/cmd/wire
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql # mysqlを使う場合
go get -u github.com/oxequa/realize

サンプル

あくまでも簡易的なものです。。
こちら(GitHub)です。

パッケージ管理ツール

簡単に説明

現在はGo Modulesが標準なのでこちらの機能を使いましょう。
Go Modulesが標準で使えるようになる前は、godepなどが使われていたらしいです。

Go言語の依存モジュール管理ツール Modules の使い方

Modules は、依存モジュール管理ツールです。
Go言語 1.11 から標準で使えるようになりました。
以下のような機能を持っています。

・依存モジュールの自動検知
・依存モジュールのバージョン固定、バージョンアップ検知
・依存モジュールの情報は go.mod と go.sum という名前のファイルに記載されます。

使い方

新規にディレクトリを作成し、go mod initと打つだけで、Go Modulesを使えるようになります。

$ mkdir (YOUR_PROJECT)
$ cd (YOUR_PROJECT)
$ go mod init github.com/(GitHubユーザ名)/(GitHubリポジトリ名)
go: creating new go.mod: module

そうすると、go.modファイルが(YOUR_PROJECT)直下に作成されます。

go.mod
module github.com/(GitHubユーザ名)/(GitHubリポジトリ名)

go 1.15

ライブラリをインストールすると、そのファイル内にパッケージの依存情報が記載されます。
かんたん便利!

モジュール名

go mod initでモジュール名(プロジェクト名)を指定する時は、慣習的にgitリポジトリのドメインから記載するようです。
色々漁ってみたんですけどソースはいまいちわかりませんでした。。
ただ、The Go Blog - Using Go Modulesでもそのように紹介されていました。

例えば私だと以下の通りです。

$ go mod init github.com/yagi-eng/go-pj-template

リポジトリURLはこちら。
https://github.com/yagi-eng/go-pj-template

GitHub以外、例えばCodeCommitだとこうなるらしい。

$ go mod init git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/hello.git

【参考】Go で Lambda 書いたときの覚え書き

プロジェクトの配置場所

簡単に説明

Go Modulesの登場により、どこでも配置できるようになりました。
以上です。

以前は

Go Modulesの登場以前は$GOPATH/src(※)配下に配置する必要がありました。
他言語からくると、置く場所が決まっている点に少し戸惑いますが、解消されたようです。

$GOPATHはデフォルトだと$HOME/go

どこでも配置できるようになったけど、なんかGoっぽいので自分は以下の例の通りに配置しています。

具体例

$GOPATH
├─ bin/
├─ pkg/
└─ src/
  └─ github.com/
    └─ GitHubユーザ名/
      └─ GitHubリポジトリ名/
        ├─ main.go
        └─ などなど。気の向くままに配置。

自分の場合はこんな感じです。

$GOPATH
├─ bin/
├─ pkg/
└─ src/
  └─ github.com/
    └─ yagi-eng/
      └─ go-pj-template/
        ├─ main.go
        └─ などなど。気の向くままに配置。
$GOPATH/src/github.com/yagi-eng/go-pj-templete

自作パッケージのimportの仕方

簡単に説明

import "(モジュール名)/(自作パッケージ名)"

相対パスではなく、モジュール名から始まる絶対パスで指定します。
具体例は次の通りです。

具体例

モジュール名: github.com/yagi-eng/hoge-pj
自作パッケージ名: hoge

ディレクトリ構成
hoge-pj/
├─ hoge/ #自作パッケージ
| └─ hoge.go
├─ main.go
└─ go.mod
go.mod
module github.com/yagi-eng/hoge-pj

go 1.15
hoge.go
package hoge // 自作パッケージ

import "fmt"

func PrintHoge() {
    fmt.Println("hogehoge")
}

main.go
package main

import "github.com/yagi-eng/hoge-pj/hoge" // こんな感じでimport!

func main() {
    hoge.PrintHoge()
}

こんな感じでimportすれば実行できます。

$ go run main.go
hogehoge

おすすめパッケージ

インストールしておくと捗りそうなパッケージを紹介します。

パッケージのインストール方法

以下のコマンドを実行するだけです。Go Modulesがいい感じにインストールしてくれます。

$ go get -u (インストールしたいパッケージ名)

# 例 echoフレームワークをインストールする場合
$ go get -u github.com/labstack/echo/...

echo

Goの軽量フレームワーク。
簡単にルーティングやサーバ実行、ロギング、CORS設定などをすることができます。

公式HPはこちら

$ go get -u github.com/labstack/echo/...

godotenv

.envファイルを使用するためのライブラリ。

使い方は以下の記事が参考になります。
【Go】.envファイルをGolangでも使用するためのライブラリ「godotenv」

$ go get -u github.com/joho/godotenv

logrus

logの外部パッケージ。
Go標準パッケージのlogはシンプル過ぎるので、ログレベルなど使いたい時に便利です。

【Go×ログ】logrusの使い方を簡単に分かりやすくまとめてみた

$ go get -u github.com/sirupsen/logrus

【追記】zap

こちらもlogの外部パッケージ。
記事を読んで頂いた方に紹介頂きました!

logrusより高速なようです。
こっちを使った方が良さそうですね。

$ go get -u go.uber.org/zap

fasthttp

net/httpとfasthttpの対応表

go言語でHTTP通信を行うためのライブラリです.

go言語の標準パッケージではnet/httpがすでに用意されているのですが,
fasthttpでは標準パッケージを凌駕する処理の速さで一時期有名になりました.
公式のベンチマークでは,従来に比べ10倍の差がでたようです.

$ go get -u github.com/valyala/fasthttp

gomock

mock生成ライブラリ。
簡単にモックを作成できるので、テストを実装する時などに便利です。

Goでメソッドを簡単にモック化する【gomock】

$ go get -u github.com/golang/mock/gomock
$ go install github.com/golang/mock/mockgen

wire

DIライブラリ。いい感じにDIできます。

GoのDIライブラリgoogle/wireの使い方

$ go get -u github.com/google/wire/cmd/wire

gorm

GoのORマッパー。
2020年8月にVersion2.0がリリースされて、使い勝手が向上したみたいです。

$ go get -u gorm.io/gorm
$ go get -u gorm.io/driver/mysql # mysqlを使う場合

realize

hot reloadライブラリ。
Goはコンパイル言語なので、PHPのように変更が即反映されず、変更のたびにプロセスをキルしてgo runし直す必要があります。
その点を解消してくれます。

ただし、Go Modulesを使っている場合はそのままだと動作しません。
ワークアラウンドをあてる必要があります。

$ go get -u github.com/oxequa/realize

補足

realizeはファイルが更新された際に、今動いているアプリを停止しないまま更新後のアプリを起動させようとします。
そのため、ポート番号を指定してGoサーバを起動していると、以下のようなエラーが出てうまくhot reloadされません。

{"time":"2020-09-04T15:29:06.6074496+09:00",
"level":"FATAL","prefix":"echo","file":"server.go","line":"41",
"message":"listen tcp :8080: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted."}

こちらもワークアラウンドが出てます。
Realize is not killing the current app

reflex

こちらもhot reloadライブラリ。
2つ目のhotreloadライブラリなので0.5扱いです。
realizeがダメだったら試してみるといいかも。
ただし、Windowsには対応していません。

goのhot reloadingにはreflexが便利だった

$ go get -u github.com/cespare/reflex

【追記】air

こちらもhot reloadライブラリ。
記事を読んで頂いた方に紹介頂きました!
自分はrealizereflexも使えなかったので結局こちらを使っています。

cosmtrek/air

$ go get -u github.com/cosmtrek/air

サンプル

こちら(GitHub)です。

はじめかた

# MySQL準備
$ docker-compose up -d
# migration実行
$ go run tools/migrate.go
# アプリ実行
$ go run server.go

説明

Goをいい感じに始められるサンプル・ひな形です。
Goがインストールしてあることを前提としています。
簡単なAPIサーバを立てる時や、Goを始めたばかりでどうプロジェクトを作成していけばいいかわからない時に有効だと思います。

本格的なGoプロジェクトを作る時には不向きです。
コードを読むとおわかり頂けるのですが、db *gorm.DBを引きずりまわしているのでその点が辛みです。
本格的なプロジェクトを作成する際は、wireなどのDIライブラリを使うと捗ると思います。

go.modに記載されているライブラリに関しては、以下のQiitaを参照ください。
wiregomockなどはインポートはしましたが、サンプルでは使っていません。
記念に入れておいただけなので、go mod tidyすると消えます。

サンプルに含まれる内容

  • gormを使ったMySQL接続
  • gormを使ったDB migrationのサンプル
  • gormを使ったDB Read/Writeのサンプル
  • echoを使ったルーティング、ロギング、CORS設定
  • godotenvを使った.envファイルの読み込み

本格的なGoプロジェクトに興味のある方は。。

手前みそですが、こちらの記事が参考になるかと思います。
Goとクリーンアーキテクチャで飲食店検索ができるLINE BOT作ってみた

以下のように、結構ゴリゴリつくりました笑

|--.env
|--docker-compose.yml
|--go.mod // Goのバージョン管理
|--Procfile // Herokuデプロイ
|--server.go // main関数
|--wire.go // DI用
|--domain
|  |--model
|  |--repository // databaseのinterface
|--infrastructure
|  |--database
|  |--mysql.go
|  |--router.go // Routing
|--interfaces
|  |--controllers
|  |--gateway // 外部API通信
|  |--presenter // LINE BOT出力
|--mock // 各層のmock
|  |--gateway
|  |--presenter
|  |--repository
|--tools // DBマイグレーション
|--usecases
|  |--dto // usecase用のDTO
|  |  |--favoritedto
|  |  |--googlemapdto
|  |  |--searchdto
|  |--igateway // interfaces/gatewayのinterface
|  |--interactor
|  |  |--usecase // usecases/interactorのinterface
|  |--ipresenter // interfaces/presenterのinterface

さいごに

Twitterの方でも、モダンな技術習得やサービス開発の様子を発信したりしているので良かったらチェックしてみてください!

参考記事

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