> Source: https://www.ymotongpoo.com/works/goblog-ja/docker/


# GoサーバーをDockerでデプロイする

[Deploying Go servers with Docker](https://go.dev/blog/docker) by Andrew Gerrand

## はじめに

今週、Dockerは[Go言語や他の主要言語向けの公式ベースイメージを発表](https://blog.docker.com/2014/09/docker-hub-official-repos-announcing-language-stacks/)し、
プログラマがGoプログラム用のコンテナを構築するための信頼できる簡単な方法を提供しました。

この記事では、単純なGoのWebアプリケーション用のDockerコンテナを作成し、そのコンテナをGoogle Compute Engineに
デプロイする一連の手順を見ていきます。Dockerに馴染みがない方は、読み進める前に
[Understanding Docker](https://docs.docker.com/engine/understanding-docker/)を読んでおくとよいでしょう。

## デモアプリケーション

デモには、[Goのサンプルリポジトリ](https://cs.opensource.google/go/x/example)にある
[outyet](https://pkg.go.dev/golang.org/x/example/outyet)というプログラムを使います。これは、
次のバージョンのGoがリリースされたかどうかを報告する単純なWebサーバーです
([isgo1point4.outyet.org](http://isgo1point4.outyet.org/)のようなサイトを支えるために設計されたものです)。
標準ライブラリ以外への依存はなく、実行時に追加のデータファイルも必要としません。
Webサーバーとしては、これ以上ないほど単純なものです。

「go get」を使って、outyetを[ワークスペース](/doc/code.html#Workspaces)に取得してインストールしましょう。

```shell-session
$ go get golang.org/x/example/outyet
```

## Dockerfileを書く

`outyet` ディレクトリの中に、`Dockerfile` という名前のファイルを次の内容で作成します。

```
# Debianイメージをベースに、最新版のGoがインストールされ、
# /go にワークスペース(GOPATH)が設定された状態から始める
FROM golang

# ローカルのパッケージファイルをコンテナのワークスペースにコピーする。
ADD . /go/src/golang.org/x/example/outyet

# コンテナの中でoutyetコマンドをビルドする。
# (依存パッケージの取得や管理は、ここで手動で、あるいは"godep"のような
# ツールを使って行うこともできる。)
RUN go install golang.org/x/example/outyet

# コンテナ起動時にデフォルトでoutyetコマンドを実行する。
ENTRYPOINT /go/bin/outyet

# このサービスがポート8080で待ち受けることを明示する。
EXPOSE 8080
```

この `Dockerfile` は、`outyet` を実行するコンテナをどのように構築するかを指定しています。
基本的な依存関係(GoがインストールされたDebianシステム。[公式の `golang` Dockerイメージ](https://registry.hub.docker.com/_/golang/)です)
から始めて、`outyet` パッケージのソースを追加し、それをビルドし、最後にそれを実行します。

`ADD`、`RUN`、`ENTRYPOINT` のステップは、どのGoプロジェクトにも共通する作業です。
これを簡略化するために、`golang` イメージには
[`onbuild` バリアント](https://github.com/docker-library/golang/blob/9ff2ccca569f9525b023080540f1bb55f6b59d7f/1.3.1/onbuild/Dockerfile)というものが用意されており、
パッケージのソースを自動的にコピーし、アプリケーションの依存関係を取得し、プログラムをビルドし、
起動時に実行されるように設定してくれます。

`onbuild` バリアントを使うと、`Dockerfile` はずっとシンプルになります。

```
FROM golang:onbuild
EXPOSE 8080
```

## イメージをビルドして実行する

`outyet` パッケージのディレクトリからDockerを呼び出し、`Dockerfile` を使ってイメージをビルドします。

```shell-session
$ docker build -t outyet .
```

これにより、Docker Hubから `golang` ベースイメージが取得され、パッケージのソースがその中にコピーされ、
パッケージがその中でビルドされ、出来上がったイメージには `outyet` というタグが付けられます。

出来上がったイメージからコンテナを実行するには、次のようにします。

```shell-session
$ docker run --publish 6060:8080 --name test --rm outyet
```

`--publish` フラグは、コンテナのポート `8080` を外部ポート `6060` として公開するようDockerに指示します。

`--name` フラグは、コンテナに扱いやすい名前を付けます。

`--rm` フラグは、outyetサーバーが終了したときにコンテナイメージを削除するようDockerに指示します。

コンテナが起動している状態で、Webブラウザで `http://localhost:6060/` を開くと、次のような画面が表示されるはずです。

![outyet](./outyet.png)

(Dockerデーモンが別のマシン(あるいは仮想マシン)上で動いている場合は、`localhost` の部分をそのマシンの
アドレスに置き換えてください。OS XやWindowsで[boot2docker](http://boot2docker.io/)を使っている場合は、
`boot2docker ip` でそのアドレスを確認できます。)

これでイメージが動作することを確認できたので、別のターミナルウィンドウから実行中のコンテナをシャットダウンしましょう。

```shell-session
$ docker stop test
```

## Docker Hubにリポジトリを作成する

先ほど `golang` イメージを取得した先であるコンテナレジストリの[Docker Hub](https://hub.docker.com/)には、
GitHubやBitBucketのリポジトリからイメージをビルドしてくれる[Automated Builds](http://docs.docker.com/docker-hub/builds/)という機能があります。

[Dockerfileをリポジトリにコミット](https://go.googlesource.com/example/+/refs/heads/master/outyet/)し、
それに対して[automated build](https://registry.hub.docker.com/u/adg1/outyet/)を作成しておけば、
Dockerがインストールされている環境であれば誰でも、たった1つのコマンドでこのイメージをダウンロードして
実行できるようになります。(この利点は次の節で確認します。)

Automated Buildを設定するには、Dockerfileを[GitHub](https://github.com/)や[BitBucket](https://bitbucket.org/)上の
自分のリポジトリにコミットし、Docker Hubにアカウントを作成した上で、
[Automated Buildの作成手順](http://docs.docker.com/docker-hub/builds/)に従ってください。

設定が完了したら、automated buildの名前を使ってコンテナを実行できます。

```shell-session
$ docker run goexample/outyet
```

(`goexample/outyet` の部分は、自分で作成したautomated buildの名前に置き換えてください。)

## コンテナをGoogle Compute Engineにデプロイする

Googleは、任意のDockerコンテナを実行する仮想マシンを簡単に立ち上げられる
[コンテナ最適化されたGoogle Compute Engineイメージ](https://developers.google.com/compute/docs/containers/container_vms)を提供しています。
インスタンスの起動時には、そのインスタンス上で動作するプログラムが設定ファイルを読み込み、
実行するコンテナを特定した上でコンテナイメージを取得して実行します。

実行するDockerイメージと公開するポートを指定した[containers.yaml](https://cloud.google.com/compute/docs/containers/container_vms#container_manifest)
ファイルを作成します。

```yaml
version: v1beta2
containers:
- name: outyet
  image: goexample/outyet
  ports:
  - name: http
    hostPort: 80
    containerPort: 8080
```

(ここでは、コンテナのポート `8080` を、HTTPトラフィックを扱うデフォルトのポートである外部ポート `80` として
公開している点に注意してください。また、`goexample/outyet` の部分は自分のAutomated Buildの名前に
置き換える必要があります。)

コンテナを実行するVMインスタンスを作成するには、[gcloudツール](https://cloud.google.com/sdk/#Quick_Start)を使います。

```shell-session
$ gcloud compute instances create outyet \
    --image container-vm-v20140925 \
    --image-project google-containers \
    --metadata-from-file google-container-manifest=containers.yaml \
    --tags http-server \
    --zone us-central1-a \
    --machine-type f1-micro
```

最初の引数(`outyet`)はインスタンス名を指定しており、管理上のわかりやすいラベルとして使われます。

`--image` と `--image-project` フラグは、使用する特別なコンテナ最適化システムイメージを指定します
(これらのフラグはそのままコピーしてください)。

`--metadata-from-file` フラグは、作成した `containers.yaml` ファイルをVMに渡します。

`--tags` フラグは、VMインスタンスにHTTPサーバーというタグを付け、パブリックなネットワークインターフェースの
ポート80を公開するようファイアウォールを調整します。

`--zone` と `--machine-type` フラグは、VMを実行するゾーンとマシンの種類を指定します
(マシンの種類とゾーンの一覧を見るには `gcloud compute machine-types list` を実行してください)。

コマンドの実行が完了すると、gcloudコマンドはインスタンスに関する情報をいくつか出力します。
その出力の中の `networkInterfaces` セクションを見つけて、インスタンスの外部IPアドレスを確認してください。
数分もすればそのIPアドレスにWebブラウザからアクセスでき、「Has Go 1.4 been released yet?」というページが
表示されるはずです。

(新しいVMインスタンスで何が起きているかを見るには、`gcloud compute ssh outyet` でsshログインできます。
そこで `sudo docker ps` を実行すると、どのDockerコンテナが動作しているかを確認できます。)

## もっと知りたい方へ

これはまだほんの入り口にすぎません。Go、Docker、そしてGoogle Compute Engineを使ってできることは、
まだまだたくさんあります。

Dockerについてもっと知りたい方は、[詳細なドキュメント](https://docs.docker.com/)を参照してください。

DockerとGoについてもっと知りたい方は、[公式の `golang` Docker Hubリポジトリ](https://registry.hub.docker.com/_/golang/)や、
Kelsey Hightowerによる[Optimizing Docker Images for Static Go Binaries](https://medium.com/@kelseyhightower/optimizing-docker-images-for-static-binaries-b5696e26eb07)を参照してください。

Dockerと[Google Compute Engine](http://cloud.google.com/compute)についてもっと知りたい方は、
[Container-optimized VMsのページ](https://cloud.google.com/compute/docs/containers/container_vms)や
[google/docker-registry Docker Hubリポジトリ](https://registry.hub.docker.com/u/google/docker-registry/)を参照してください。

By Andrew Gerrand

