IBM Cloud を利用した Go言語のアプリ開発入門（その４）


Go言語の初心者として今回は、Go言語のアプリを IBM Cloud コンテナサービス　すなわち Kubernetes（以下 k8s) のクラスタにデプロイするまでを考えます。順番としては、DockerコンテナをビルドするためのDockerファイルの開発、そして、ローカルのDocker環境でのテスト、それから、k8sへデプロイするためコンテナレジストリへの登録、そして、k8sへのデプロイの流れになります。

Cloud Foundry の手軽さからすると、k8s へのデプロイは面倒な感じがしますね。 Dockerコンテナをそのままデプロイできる処は素晴らしいですが、もっと、自動化したいものですが、今期は手作業で進めます。


Go言語アプリは、以下の３つのリポジトリから構成されるもので、一番目のリポジトリから、2番目を依存パッケージとして、3番目は2番目の依存パッケージとして構成しています。

1. ウェブコンテンツ、各種定義ファイル  https://github.com/takara9/go_webpages
1. Go言語で書いたシンプルなWebサーバー  https://github.com/takara9/go_webserver2
1. Go言語の共通モジュール https://github.com/takara9/go_util

上記の１番目のリポジトリの内容は、参考資料の3を参照してください。


# 手打ちでDockerコンテナをビルドして、Dockerfileに必要な材料を確認

その３で作成した リポジトリ https://github.com/takara9/go_webpages から、Go言語で書いたウェブサーバーのコンテナをビルドして、動作させるまでの、作業手順を試行錯誤で作ったものが、以下になります。 パッケージ・マネージャーの`godep restore`で前提パッケージを集めて、`go install `でビルドして、実行という流れです。

~~~
$ docker run -it golang bash
root@5d68e424048f:/go# echo $GOPATH
/go
rm -fr go
cd /
git clone https://github.com/takara9/go_webpages --recursive go
cd /go
go get github.com/tools/godep
godep restore
go install github.com/takara9/go_webserver2
bin/go_webserver2 
~~~


## コンテナ内でサーバーgo_webserver2を起動

手打ちで実行した go_webserver2が正しく動作しているか確認します。 ビルドでは、対話モードでコンテナに入って作業していたので、上記の稼働中のコンテナに別のターミナルから入って、curlコマンドでアクセスしてテストします。

以下は、上記と同じコンテナに入った処です。 go_webserver2が動作しているのが確認できます。

~~~
$ docker exec -it 4707e89a7800 bash
root@4707e89a7800:/go# ps -ax
  PID TTY      STAT   TIME COMMAND
    1 pts/0    Ss     0:00 bash
  509 pts/0    Sl+    0:00 bin/go_webserver2
  512 pts/1    Ss     0:00 bash
  516 pts/1    R+     0:00 ps -ax
~~~


## 同じコンテナに入ってcurlのアクセス・テスト

curlの実行結果です。 確かにHTMLコンテンツを送出している事が確認したできました。

~~~
root@4707e89a7800:/go# curl http://localhost:4040/
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Go言語で作ったウェブサーバー</title>
<meta charset="utf-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<H1>質問#1</H1>
<p>歌詞などで表現される「時計仕掛けの夜」とはなんだろう？</p>
<br>
<img src="gopher.png">
<br>
<a href="test_doc_1.html">答えページへ進む</a>


</body>
</html>
~~~



# コンテナをビルドして、ローカルでテスト

## Dockerfileの作成

上記の手打ちの結果をもとに、Dockerfileを作成したのが、以下のファイルです。


~~~Dockerfile
FROM golang:1.9.2

RUN rm -fr $GOPATH
RUN git clone https://github.com/takara9/go_webpages --recursive $GOPATH
WORKDIR $GOPATH
RUN go get github.com/tools/godep
RUN godep restore
RUN go install github.com/takara9/go_webserver2

EXPOSE 4040
ENTRYPOINT ["bin/go_webserver2"]
~~~


## ビルドの実行

上記のDockerfileを利用してローカルのMacの環境で、コンテナをビルドして、テストします。

~~~
vagrant@vagrant-ubuntu-trusty-64:~/go/go_webpages$ docker build -t go_webserver .
Sending build context to Docker daemon  423.9kB
Step 1/9 : FROM golang:1.9.2
 ---> 138bd936fa29
Step 2/9 : RUN rm -fr $GOPATH
 ---> Running in 68a725444541
 ---> fb59c95a1e5f
Removing intermediate container 68a725444541
Step 3/9 : RUN git clone https://github.com/takara9/go_webpages --recursive $GOPATH
 ---> Running in 2b5f79923ce9
Cloning into '/go'...
Submodule 'vendor/github.com/takara9/go_util' (https://github.com/takara9/go_util.git) registered for path 'vendor/github.com/takara9/go_util'
Submodule 'vendor/github.com/takara9/go_webserver2' (https://github.com/takara9/go_webserver2.git) registered for path 'vendor/github.com/takara9/go_webserver2'
Cloning into '/go/vendor/github.com/takara9/go_util'...
Cloning into '/go/vendor/github.com/takara9/go_webserver2'...
Submodule path 'vendor/github.com/takara9/go_util': checked out '38ff0221e206b57cd7ea242e4f1238fb8f613b8a'
Submodule path 'vendor/github.com/takara9/go_webserver2': checked out '25d39c2700995e71dbb9a3b9c80891cec437ffd5'
 ---> 756ad9a01728
Removing intermediate container 2b5f79923ce9
Step 4/9 : WORKDIR $GOPATH
 ---> eccea02c8da2
Removing intermediate container f1d8e8634a95
Step 5/9 : RUN go get github.com/tools/godep
 ---> Running in 0dc09366ed94
 ---> b1d4f2e4c65d
Removing intermediate container 0dc09366ed94
Step 6/9 : RUN godep restore
 ---> Running in 4f048faa37a3
godep: [WARNING]: godep should only be used inside a valid go package directory and
godep: [WARNING]: may not function correctly. You are probably outside of your $GOPATH.
godep: [WARNING]:	Current Directory: /go
godep: [WARNING]:	$GOPATH: /go
 ---> 29ee74bb965b
Removing intermediate container 4f048faa37a3
Step 7/9 : RUN go install github.com/takara9/go_webserver2
 ---> Running in dd5fbd7202ef
 ---> 4ad3472126d3
Removing intermediate container dd5fbd7202ef
Step 8/9 : EXPOSE 4040
 ---> Running in d556fe610c0c
 ---> 4cf9d6904ea9
Removing intermediate container d556fe610c0c
Step 9/9 : ENTRYPOINT bin/go_webserver2
 ---> Running in 48123c88fc70
 ---> a9b6339a9a56
Removing intermediate container 48123c88fc70
Successfully built a9b6339a9a56
Successfully tagged go_webserver:latest
~~~


コンテナ・イメージができていることを確認

~~~
vagrant@vagrant-ubuntu-trusty-64:~/go/go_webpages$ docker images
REPOSITORY     TAG     IMAGE ID            CREATED             SIZE
go_webserver   latest  a9b6339a9a56        7 seconds ago       751MB
~~~


## コンテナのテスト

コンテナをデーモンとして起動

~~~
vagrant@vagrant-ubuntu-trusty-64:~/go/go_webpages$ docker run -d -p 8080:4040 go_webserver
568cdb58537aa9947821ab2ca2faa3ea4cb76101481f44a2d259644bf456c7cc
~~~

curlでのアクセステストで、HTMLコンテンツの応答がきたので、テスト完了です。

~~~
vagrant@vagrant-ubuntu-trusty-64:~/go/go_webpages$ curl http://localhost:8080/
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Go言語で作ったウェブサーバー</title>
<meta charset="utf-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<H1>質問#1</H1>
<p>歌詞などで表現される「時計仕掛けの夜」とはなんだろう？</p>
<br>
<img src="gopher.png">
<br>
<a href="test_doc_1.html">答えページへ進む</a>
~~~


## k8sのコンテナ・レジストリへ登録

コンテナをK8sクラスタへデプロイするために、IBM Cloud レジストリサービスへコンテナを登録します。
東京DCのk8sクラスタを利用するために、ap-southのレジストリを利用します。　ネームスペースは既に作成しておいた `takara` を利用します。
ローカルのコンテナ・レジストリにタグを付けて、リモート・レジストリへプッシュする流れです。

~~~
$ bx cr region-set ap-south
$ bx cr login
$ bx cr namespace-list
<中略>
Namespace   
takara   

$ docker tag go_webserver:latest registry.au-syd.bluemix.net/takara/go_webserver:v1
$ docker push registry.au-syd.bluemix.net/takara/go_webserver:v1
~~~


リモート・レジストリ、すなわち、IBM Cloudのレジストリ・サービスに登録された事がわかります。　このレジストリは、プライベートなので、失敗した場合は、`bx cr login` を確認してください。

~~~
$ bx cr images
<中略>
NAMESPACE   TAG   DIGEST         CREATED         SIZE     VULNERABILITY STATUS   
registry.au-syd.bluemix.net/takara/go_webserver   takara      v1    ecfd885a3874   24 minutes ago   279 MB   Vulnerable   
~~~

これで 東京DC の k8s へデプロイする準備が整いました。 


# Kubernetesへのデプロイと公開

k8sへデプロイして、サービスとして内部公開、さらにIngressサービスで外部へ公開するために、３つのYAMLファイルを準備します。

次は、コンテナをポッドとして、デプロイするためのYAMLです。 imageのフィールドに、先に登録したリポジトリ名をセットします。


~~~k8s_deploy.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: go-web-deploy
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: go-web
    spec:
      containers:
      - name: go-web
        image: registry.au-syd.bluemix.net/takara/go_webserver:v1
        ports:
        - containerPort: 4040
~~~

次は、デプロイしたポッドが、k8sクラスタの他からアクセスできる様にするサービスを定義するものです。
これにより、ポッドのクラスタの代表アドレスが取得され、k8sのDNSサービスに登録されます。　これによって、Ingressから接続できる様になります。


~~~k8s_svc.yml
apiVersion: v1
kind: Service
metadata:
  name: go-web-svc
spec:
  type: ClusterIP
  selector:
    app: go-web
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 4040
~~~


次は、パブリック・ネットワークであるインターネットから HTTPでアクセスするために、設定する Igressサービスの定義です。
go-web-svcの8080ポートをプロキシーして、外部へ公開します。

~~~k8s_ingress.yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: go-web-ingress
spec:
  tls:
  - hosts:
    - mycluster3.jp-tok.containers.mybluemix.net
    secretName: mycluster3
  rules:
  - host: mycluster3.jp-tok.containers.mybluemix.net
    http:
      paths:
      - path: /
        backend:
          serviceName: go-web-svc
          servicePort: 8080
~~~


次のコマンドで、k8sクラスタへのデプロイを実行します。 このコマンドが実行できる様になるまでに、bx login、k8sクラスタのセットアップなどの作業がありますが、ここでは省略しています。ごめんなさい。

~~~
kubectl apply -f k8s_deploy.yml
kubectl apply -f k8s_svc.yml
kubectl apply -f k8s_ingress.yml
~~~


デプロイが成功したら、ブラウザからアクセスできる様になります。

![スクリーンショット 2018-01-17 20.27.52.png](https://qiita-image-store.s3.amazonaws.com/0/42829/9e798542-78cb-c0f8-6e44-f04965affe96.png "スクリーンショット 2018-01-17 20.27.52.png")


# まとめ

Go言語で開発したアプリをk8sクラスタで公開するまでの作業内容を確認してみました。 流れをまとめると、以下の様になると思います。

* Go言語で開発したサービスをコンテナ化
* Dockerfileを開発して、ビルドとローカルでのテスト
* k8sクラスタ環境のセットアップ（今回は省略)
* コンテナのレジストリサービスへ登録
* k8sのクラスタへデプロイするためのYAMLファイル作成
* kubectrlでデプロイ


Go言語だからといって、特別な事は無いのですが、GitHubとgodepの使い方が、重要な事が良く判りました。



# 参考資料

1. IBM Cloud を利用して Go言語のアプリ開発入門（その１） https://qiita.com/MahoTakara/items/70572f3f214f24ee452d
1. IBM Cloud を利用した Go言語のアプリ開発入門（その２） https://qiita.com/MahoTakara/items/76d5d7905d07c9452d06
1. IBM Cloud を利用した Go言語のアプリ開発入門（その３） https://qiita.com/MahoTakara/items/20dcb775f97eaf4a73dd
1. 今さらだけど、Go言語に入門するための情報源 https://qiita.com/MahoTakara/items/10fede35c03db1e3b849
1. Ingress のアノテーション IBM Cloud Docs https://console.bluemix.net/docs/containers/cs_annotations.html#service-rate-limit
1. Docker-docs-ja http://docs.docker.jp/engine/toc.html