6
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

マイクロサービスの作成

マイクロサービスやってますか?
私は今イギリスの会社でソフトウェアエンジニアとして日々マイクロサービス開発をしています。
イギリスで働くことになった経緯などはこちらに書き残したのでもし興味があればのぞいてみてください。

弊社では3年前からKubernetesを使ったマイクロサービス開発を行なっており、本番で運用するための知見が多くあります。今回、アドベントカレンダーというイベントを通して海外でのマイクロサービス開発の一事例を紹介していけたらなと思います。

まっさらな状態から徐々にサービスを育てていく形式で進めていこうと思います。
ノリで区切っていくので25日より短いかもしれないし、25日では足りないかもしれません。笑
どうか温かく見守っていただければと思います。

アプリケーションのレポジトリはこちらです。
ステップごとにコミットしていくのでバージョンを切り替えながら見てください。

では早速サービスを作っていきましょう。

リポジトリ作成

クリーンなリポジトリを作成したら以下の作業をします。

  • go mod init
  • main.goファイルの作成

go buildでバイナリが作成できるか試してみましょう。

Hello, world

今回は jawher/mow.cli ライブラリを使ってアプリを書いていきます。
シンプルで使いやすいためオススメです。

Hello, worldを出力するコードはこんな感じになります。

package main

import (
    cli "github.com/jawher/mow.cli"

    "log"
    "os"
)

const (
    appName = "qiita-advent-calendar-2019"
    appDesc = "The micro service sample app"
)

func main() {
    app := cli.App(appName, appDesc)

    app.Action = func() {
        log.Println("Hello, world")
    }

    if err := app.Run(os.Args); err != nil {
        log.Fatal(err)
    }
}

早速ビルドして走らせてみましょう。

$ go build
$ ./qiita-advent-calendar-2019 
2019/11/14 14:44:24 Hello, world

バイナリにgithashの値を埋め込む

githashをコンパイル時に埋め込み、コード内からアクセスできるようにしておきます。何か問題が発生した際などにどのバージョンのアプリなのかすぐわかるようできるなど便利なことがあります。
ついでに sirupsen/logrus ライブラリを使ってログをいい感じにします。

package main

import (
    "os"

    cli "github.com/jawher/mow.cli"
    log "github.com/sirupsen/logrus"
)

var gitHash = "overriden at compile time"

const (
    appName = "qiita-advent-calendar-2019"
    appDesc = "The micro service sample app"
)

func main() {
    app := cli.App(appName, appDesc)

    app.Action = func() {
        log.WithField("git_hash", gitHash).Println("Hello, world")
    }

    if err := app.Run(os.Args); err != nil {
        log.WithError(err).Fatal("app run")
    }
}

ビルドコマンドはgithashを埋め込む記述が入るためこうなります。

$ go build -ldflags "-X main.gitHash=$(git rev-parse HEAD)"
$ ./qiita-advent-calendar-2019 
INFO[0000] Hello, world                                  git_hash=2e94c00b197a3b1e131c09ce56545e56527a1ba0

logrusライブラリでは出力形式をjsonにすることもできますし、ログレベルの設定も簡単です。

サーバープロセスの開始

では、サーバープロセスを走らせてみましょう。

新しいプロセスはgoルーティンで開始し、メインのルーティンでは子ルーティンからのエラーメッセージを待ち受ける形で同期します。

  • プロセスの立ち上げ

errCh := make(chan error, 1)
go func() {
    http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, world")
    })
    if err := http.ListenAndServe(net.JoinHostPort("", strconv.Itoa(*srvPort)), nil); err != nil {
        errCh <- errors.Wrap(err, "server")
    }
}()
  • 子ルーティンの待ち受け
select {
    case err := <-errCh:
        log.Println(err)
}

なお、サーバのポート番号は変更可能なように環境変数として受け取れるようにします。mow.cliライブラリではこんな感じに記述します。

srvPort := app.Int(cli.IntOpt{
    Name:   "srv-port",
    Desc:   "http server port",
    EnvVar: "SRV_PORT",
    Value:  8080,
})

では、実行してcurlしてみましょう。

$ go build -ldflags "-X main.gitHash=$(git rev-parse HEAD)"
$ ./qiita-advent-calendar-2019                             
INFO[0000] Hello, world                                  git_hash=86bd5b5d84ec8b02329d80e62c5c76ecf2ded099
$ curl localhost:8080/bar
Hello, world

さいごに

シグナルを受けとった際にも安全に終了できるように、select句で同時に待ち受けておきましょう。

sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

select {
    case err := <-errCh:
        log.Println(err)
    case <-sigCh:
        log.Println("termination signal received. attempt graceful shutdown")
}
$ ./qiita-advent-calendar-2019 
INFO[0000] Hello, world                                  git_hash=d2cf7e593de77bc49a98a76fd1ee25f49bdaf795
^CINFO[0004] termination signal received. attempt graceful shutdown 
INFO[0004] bye

明日は、アプリケーションをデプロイしていきたいと思います。

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
Sign upLogin
6
Help us understand the problem. What are the problem?