LoginSignup
68
67

More than 5 years have passed since last update.

GoとDockerとMicroservices 最初の一歩

Last updated at Posted at 2015-12-09

この記事は Go その2 Advent Calendar 2015 10日目の記事です。

今回はgoとdockerを使ってMicroserviceっぽく、ウェブアプリケーションを作っていく時のさわりの部分を書いてみました。

Microservice Hello World

macでdocker toolboxインストールして、docker-machine start devとdocker-machineを立ち上げた後から。

Hello Worldを返すウェブサーバーのdockerイメージを用意してみます。

helloworld.go
package main

import "net/http"

func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello World"))
  })
  http.ListenAndServe(":8080", nil)
}

あらかじめビルド。

$ GOOS=linux GOARCH=amd64 go build helloworld.go

Dockerfileはこんな感じ。

Dockerfile
FROM busybox

COPY ./helloworld /helloworld
CMD ["/helloworld"]

docker build。

$ docker build -t helloworld .

あとはイメージを実行するだけです。

$ docker run -p "8080:8080" --rm helloworld
$ curl http://$(docker-machine ip dev):8080
Hello World

dockerイメージの容量を小さくする

dockerイメージはできるだけ小さくするようにするのが一つのプラクティスのようです。
上で使っているbusyboxは最小のパッケージですが、単純にgoを実行するだけならばこれで十分です。

dockerを使った運用を行う際に https://github.com/docker/distribution などに
イメージを登録したり、イメージを引っ張ってきたりするのですが、容量が大きいとそのぶん時間がかかります。

RUNなどをワンライナーで書いているのもビルドを早くしたり、容量を下げたりする方法のひとつです。

RUN echo '#!/bin/sh' > /usr/sbin/policy-rc.d \
    && echo 'exit 101' >> /usr/sbin/policy-rc.d \
    && chmod +x /usr/sbin/policy-rc.d \

少し油断すると、すぐに容量が肥大化するので注意しておくことのひとつになります。

参考: (より)小さいDockerイメージを作ろう

環境ごとに動作を変える

開発環境・ステージング環境・本番環境を用意する場合、その環境ごとにコンフィグの設定を用意して、起動時に引数などに設定して実行することがあります。
Railsの場合なら rails server -e production と環境を指定し、
config/environments/<ENV>.rbファイルを読んで環境ごとに設定を変更する、といったところです。

これは便利ではあるのですが、設定ファイルのルールをきちんと決めておかないと、各所に散乱してしまったり、設定の仕方がフレームワークに依存してしまったり、リポジトリ内に外部に公開できない情報が含まれてしまうという問題もあったりします。

特にマイクロサービスの場合、小さな機能を持つサービスをいくつも立ち上げることが前提です。設定ファイルに依存していると、まず元のリポジトリにファイルを追加をしてから、そのイメージをビルドして、デプロイ作業を何度もやらなくてはいけなくなります。

development がもうひとつ必要になったときに development002といった環境名を逐一つけなければいけないのも大変です。

そしてそのビルドされたイメージの設定がどうなっているかを知る術が欲しくなったりします。Goの場合は、ビルドした実行ファイルを置くだけで良かったのが環境ファイルもイメージに入れて実行時に参照など最初の理想から離れてしまいます。

そんな経緯があるのか http://12factor.net/config では、設定は環境変数に入れる方法を取り上げています。

サービス間の連携をする

マイクロサービスなので、サービス間で通信することになります。

例えば上で作成したhelloworldにアクセスするのに、
ひとつ間に挟む gateway というコンテナを挟んでみます。

Dockerfile
FROM busybox

COPY ./gateway /gateway

CMD ["/gateway"]
gateway.go
package main

import (
    "io/ioutil"
    "net/http"
    "os"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        urlStr := os.Getenv("API_URL")
        req, err := http.NewRequest(r.Method, urlStr, r.Body)
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }

        cli := http.Client{}
        resp, err := cli.Do(req)
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }
        defer resp.Body.Close()

        bs, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }

        w.WriteHeader(resp.StatusCode)
        w.Write(append([]byte("Gateway -> "), bs...))
    })
    http.ListenAndServe(":8080", nil)
}

gateway.goはhttpリクエストを受け取ったら、環境変数 API_URL にリクエストを送るというものです。helloworld.goの場合と同じように go buildをあらかじめして、それをDockerイメージの中に入れます。

そしたら、各イメージのコンテナを作成します。

$ docker run -d --name helloworld helloworld
$ docker run -d --name gateway \
    -p "8080:8080"      \
    --link="helloworld" \
    -e "API_URL=http://helloworld:8080/" \
    gateway

helloworldは名前をつけてコンテナ作成しています。

gatewayは、まず名前をつけて
外からのアクセスを受け付け、 (-p "8080:8080")
helloworldにlink設定し (--link="helloworld")
環境変数 API_URLに 接続先のURLを設定 ( -e "API_URL=http://helloworld:8080/")をしています。

これで http://$(docker-machine ip dev):8080にアクセスするとHello world を返すようになります。

このような感じでひとつひとつのサービスを紐付けていきます。

うまい具合に設計するために、などを眺めながら
どのサービスに責任を持つかみたいなことを考えながら行っていったりします。

また上の例はhttpですが、gRPCを使ったり、プロトコルをどうするかも考えて設計することになったりします。

参考

まとめ

あんまりGo関係なくなってしまいましたが、ここからコンテナの管理のためにKubernetesAmazon EC2 Container Serviceを使うことになったり、Docker Regstryを使ってイメージを管理したりしながら、マイクロサービスを作ったりまとめたりしながらひとつのサービスを作っていくことになっていくのかと思います。

Goのbuildは、クロスコンパイルができて、1つの実行ファイルになるので、余計なところでハマらずに済むのが本当に良いなっていうのが最近のGoを使っていての感想です。

68
67
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
67