Go言語で開発したWebアプリをGCP(GKE)へデプロイして公開する

#はじめに
Go言語で開発したWebアプリ(HTTPによって通信を行うプログラム)を
GCPに作成したGKEへデプロイするまでの手順を記載します。

手順実施後のGCPのアーキテクチャはこんな感じです。
![GCP_GKE_GO.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406684/b823716e-fa66-8abd-98ee-dc3958a95711.jpeg)

# GKE(Google Kubernetes Engine)とは
GCPで提供されている[Kubernetes(クーバネティス)](https://kubernetes.io/)ホスティングサービスです。
つまり、GCP上でKubernetesを使用することができます。

Kubernetesとは、1つ以上のコンテナ(独立したアプリケーションの実行環境)から
構成されるクラスターの自動的な管理やスケールを行う、
オーケストレーションツールです。

複数の小さなサービスの連携により１つのサービスを構成する、
いわゆるマイクロサービス環境を実現するにあたり、
Kubernetesはサービスの構成や各役割を持ったクラスターの定義を宣言し、
自動的に管理することが可能です。

#前提
[GCP](https://console.cloud.google.com/?hl=ja)が利用可能なこと。無料トライアルでもOK

#環境構築
まずはGKEへデプロイおよびインターネットへ公開するための環境を構築します。

##GKEを有効化
以下の手順でGCPのGKEを有効にします。

**■手順**
　1.GCPで[Kubernetes Engine](https://console.cloud.google.com/projectselector/kubernetes?hl=ja&_ga=2.158417016.-372559893.1556361553&_gac=1.162011470.1557844905.Cj0KCQjwzunmBRDsARIsAGrt4mv8ws1Tg2z4Tw3vSyAHZ5pML4LZ2CkieZazSwca5rcSHnQSocqZTI4aAg1TEALw_wcBページ)にアクセスします。
　2.プロジェクトを作成または選択します。
　3.Kubernetes Engineが有効になるのを待ちます。
　4.プロジェクトに対して課金が有効になっていることを確認します。
　　・[課金を有効にする方法について](https://cloud.google.com/billing/docs/how-to/modify-project?hl=ja)
　　　<font color="red">※課金を有効といいつつ、無料トライアル期間中は課金されません。
　　　　無料トライアル期間終了後に自動でクレジットカードに課金されてしまうこともありません。</font>
　　　　⇒ご参考：[無料試用期間中の課金](https://support.google.com/cloud/answer/7006543?hl=ja)

##ローカルシェルをインストール
ローカルでGCP用のコマンドを使用するために、
以下の手順でGoogle Cloud SDKをPCにインストールします。

gcloudコマンドラインツールはGCPへの主要なコマンドを提供し、
kubectlコマンドラインツールはKubernetesクラスタ用のコマンドを提供します。

**■手順**
　1.[Google Cloud SDKのクイックスタート](https://cloud.google.com/sdk/docs/quickstarts?hl=ja)に従い、Google Cloud SDKをインストールします。
　　・このSDKにgcloudコマンドラインツールが含まれています。
　2.次のコマンドを実行し、kubectlコマンドラインツールをインストールします。

```console:コマンドプロンプト
gcloud components install kubectl
```

##gcloudのデフォルト設定を構成
以下の手順でデフォルトのプロジェクトとコンピューティングゾーンの2つのデフォルト設定を構成します。
プロジェクトとはGCPに作成したプロジェクトで、
コンピューティングゾーンとはクラスタとそのリソースが有効になるリージョンのおおよその場所です。

**■手順**
　1.次のコマンドを実行し、デフォルトのプロジェクトを設定します。

```console:コマンドプロンプト
gcloud config set project [プロジェクト ID]
```
　・<font color="blue">[プロジェクト ID]</font>は実際のプロジェクトIDに置き換えます。
　　プロジェクトIDはGCPのホーム画面の左上に表示されています。

2.次のコマンドを実行し、デフォルトのコンピューティングゾーンの設定します。
　今回は「asia-northeast1-a(東京)」を設定します。

```console:コマンドプロンプト
gcloud config set compute/zone asia-northeast1-a
```

##クラスタを作成
[クラスタ](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture?hl=ja)を作成するために、コマンドプロンプトを起動して以下の手順を実施します。

GKEでは、クラスタは少なくとも1つの[クラスタマスター](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture?hl=ja#master)と、
1つ以上のノード([仮想マシン](https://cloud.google.com/compute/docs/instances/?hl=ja))で構成されます。
これらのクラスタマスターとノードが、Kubernetesクラスタのオーケストレーションシステムを実行します。

**■手順**
　1.次のコマンドを実行し、クラスタを作成する。

```console:コマンドプロンプト
gcloud container clusters create go-cluster --num-nodes=3
```
　　・```--num-nodes```パラメータでノード数を3つにしています。
　　・「go-cluster」はクラスタに付ける名前なので任意で命名できますが、
　　　英大文字やハイフン以外の記号は使えないです。

　2.次のコマンドを実行し、上記クラスタと通信するために必要な認証情報を取得します。

```console:コマンドプロンプト
gcloud container clusters get-credentials go-cluster
```
#WebアプリをDockerイメージ化
GKEは複数のDocker環境をクラスタ化する仕組みです。
GKEでWebアプリを動かすには、
Docker環境のWebアプリを動かすDockerイメージを用意する必要があります。

Dockerイメージとは、Dockerコンテナを作成する際に必要となるファイルシステムです。
DockerコンテナはDockerイメージを元に作成されます。

Go言語でWebアプリを開発し、それをDockerイメージにしてみます。
まずは、テキストを表示するだけの簡単なWebアプリを用意します。

##goファイルを準備
以下のgoファイルをローカルに準備します。
処理の詳細はコメントをご覧ください。

```golang:helloGke.go
// このソースファイルは「main」パッケージに属すことを指定しています。
// また、Go言語のソースファイルは必ず「package」文で始まります。
package main

// 一つの import ステートメントで複数のパッケージをインポートしています。
import (

	// フォーマットI/Oを扱うパッケージです。
	// C言語のprintfおよびscanfと似た関数を持ちます。
	"fmt"

	// ロギングを行うシンプルなパッケージです。
	// 出力をフォーマットするメソッドを持つLogger型が定義されています。
	"log"

	// HTTPを扱うパッケージです。
	// HTTPクライアントとHTTPサーバーを実装するために必要な機能が提供されています
	"net/http"
)

// 「func」が関数の宣言で、ここから「main」関数の記述が始まります。
// ※関数宣言のような波括弧「{}」は、開始波括弧「{」をfuncと同じ行に書く必要があります。
func main() {

	// 第一引数にURL、第二引数にハンドラを渡し、DefaultServeMuxに登録する。
	// つまり、「/」というURLがリクエストされた際に、handlerが起動する。
	//   ・URL            ：HTTPリクエストのパターン
	//   ・ハンドラ        ：リクエストに対する応答
	//   ・DefaultServeMux：パターンにマッチしたリクエストに対して、そのパターンを持つhandlerを返却
	http.HandleFunc("/", handle)

	// ポート番号8080で待ち受けるHTTPサーバを起動します。
	log.Fatal("[Fatal] [main] ", http.ListenAndServe(":8080", nil))
}

// ブラウザへメッセージを表示する関数
func handle(w http.ResponseWriter, r *http.Request) {

	// インポートした「fmt」パッケージ内の「Fprintf」関数を使用し、
	// 第一引数のレスポンスパラメータへ、第二引数で指定したメッセージを書き込みます。
	fmt.Fprintf(w, "Hello, Google Kubernetes Engine\n")
}

```

##Dockerfileファイルを準備
以下のDockerfileをローカルに準備します。
<font color="red">配置先のディレクトリはgoファイル「helloGke.go」と同じ場所にしてください。</font>
Dockerfileがあれば、イメージ・コンテナの作成が簡単にできるので、
イメージ・コンテナは使い捨てにできます。

```dockerfile:Dockerfile
# 「Dockerfile」は、プログラムのビルドでよく利用されるmakeツールの「Makefile」ファイルと同様に、
#  Dockerコンテナーの構成内容をまとめて記述するシンプルなテキスト形式のファイルです。
#
# 補足として、このファイルはマルチステージビルドという機能を利用しています。
# 「ビルド依存のライブラリ」と「ランタイム(プログラムの実行)依存のライブラリ」を分離できる機能です。
# これにより、Dockerイメージのサイズを大幅に削減できます。

# ステージ１
# golangのビルド用に golang:1.8-alpine でDockerイメージ(コンテナで利用するファイル・設定)を作成
FROM golang:1.8-alpine

# 作者
MAINTAINER melty_go

# カレントディレクトリをコンテナの「/go/src/hello-app」へ追加
ADD . /go/src/hello-app

# hello-appの実行ファイルを生成
RUN go install hello-app

# ステージ２
# Alpine Linux(セキュアで軽量な Linux ディストリビューション)ベースでDockerイメージを作成
FROM alpine:latest

# 「ステージ１」で生成された実行ファイルをこの新しいステージへコピー
COPY --from=0 /go/bin/hello-app .

# ポート番号を指定
ENV PORT 8080

# コンテナ内で実行するコマンドで実行ファイルを実行
CMD ["./hello-app"]

```

##WebアプリのDockerイメージ化を実施
以下の手順でDockerイメージを作成し、[Container Registry](https://cloud.google.com/container-registry/?hl=ja)にpushします。
Container Registryとは、GCPのサービスの一部として提供するプライベートなDocker registryです。

**■手順**
　1.次のコマンドを実行し、goファイルとDockerfileがあるカレントディレクトリへ移動する。

```console:コマンドプロンプト
cd C:\[goファイルとDockerfileがあるカレントディレクトリ]
```

　2.次のコマンドを実行し、DockerfileからDockerイメージを作成し、Container Registryにpushする。

```console:コマンドプロンプト
gcloud builds submit --tag gcr.io/[プロジェクト ID]/go-docker-image:1.0 .
```
　　【補足】
　　　`gcloud builds submit --tag [ホスト名]/[プロジェクト ID]/[イメージ名:タグ名] .`
　　　　・ホスト名：push先のリージョンです。gcr.ioは米国にホストされています。
　　　　・プロジェクト名：今回利用しているGCPのプロジェクト名です。
　　　　・イメージ名タグ名：任意の命名ができます。

　3.GCPからContainer Registry画面を開き、
　　Dockerイメージがリポジトリに作成されていることを確認します。
![go-docker-image.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406684/199031d8-1a09-5d4e-0b1d-950bdf588009.jpeg)

#GKEにデプロイ
GKEのクラスタで「helloGke」を実行するには、次のコマンドを実行します。
hello-server という名前の新しいDeploymentを作成します。
このDeploymentのポッドは、そのコンテナ内で「go-docker-image」イメージを実行します。

```console:コマンドプロンプト
kubectl run hello-server --image gcr.io/[プロジェクトID]/go-docker-image:1.0 --port 8080
```
　・`--image`はデプロイするコンテナ イメージを指定します。
　・`--port`はコンテナで公開するポートを指定します。

#インターネットに公開
アプリケーションをデプロイした後は、そのアプリケーションをインターネットに公開して
ユーザーがアクセスできるようにする必要があります。
次の手順でアプリケーションをインターネットに公開してアクセスしてみます。

**■手順**
　1.次のコマンドを実行し、アプリケーションをインターネットに公開する。

```console:コマンドプロンプト
kubectl expose deployment hello-server --type "LoadBalancer" --port 8080 --target-port 8080
```
　・`--type="LoadBalancer"`フラグを渡すと、
　　コンテナのCompute Engineロードバランサが作成されます。

　2.次のコマンドを実行し、「EXTERNAL-IP（公開されている外部IPアドレス）」を確認する。
　　EXTERNAL-IPが「\<pending\>」になっていましたら、数分後に再実行してください。

```console:コマンドプロンプト
kubectl get service hello-server
```

　3.ウェブブラウザで、「2」のEXTERNAL-IPにアクセスする。
　　http://<font color="blue">[EXTERNAL-IP]</font>:8080

　　**【アクセス結果】**
![hello_go.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406684/9521a5f2-3b74-53cb-0c52-5e5616a6ef1e.jpeg)

#クリーンアップ
インスタンスが起動したままだと、GCPの無料クレジットを使ってしまうので、
次の手順でサービスの停止とクラスターの削除を行います。

**■手順**
　1.次のコマンドを実行し、サービスを停止する。

```console:コマンドプロンプト
kubectl delete service hello-server
```

　2.次のコマンドを実行し、クラスターを削除する。

```console:コマンドプロンプト
gcloud container clusters delete go-cluster
```

#参考にしたサイト
[GKEのドキュメント](https://cloud.google.com/kubernetes-engine/docs/?hl=ja)
[GKEのクイックスタート](https://cloud.google.com/kubernetes-engine/docs/quickstart?hl=ja)
[コンテナ化されたアプリケーションのデプロイ](https://cloud.google.com/kubernetes-engine/docs/tutorials/hello-node?hl=ja)
[Go言語のOFFICIAL REPOSITORY](https://hub.docker.com/_/golang/)

#おわりに
WebアプリをGKEへデプロイして公開するまでの一連の流れについて理解することができました。
[Docker Community Edition（CE）](https://docs.docker.com/install/)があればローカルサーバでの動作確認ができたのですが、
私の環境では使用できないので割愛しました・・。

次回はGo言語の開発環境について投稿します。