Kubernetesで実践するクラウドネイティブDevOps -2章-

# 経緯
[Kubernetesで実践するクラウドネイティブDevOps](https://www.oreilly.co.jp/books/9784873119014/)という本を買ったので、実際に手を動かしていきたいと思います。

コンテナとかKubernetesを全然知らないわけではないのですが、雰囲気でやってる部分が多いので再確認する意味でもしっかりやっていきたいと思います。

1章も非常にエモくてよかったんですが、今回は実際に手を動かす2章からです。

# 環境

Hyper-V（win10Pro）に作ったUbuntu上でやってみます。Docker for Winでも十分できます。（個人的に手元の環境（特にレジストリ）に手を入れるのが好きではないので。。。）


* OS:Ubuntu 18.04.3 LTS
* MEM:5GB
* CPU:4Core
* SSD:20GB


# 2章 Kubernetes 最初の一歩

## Dockerのインストール
[公式手順](https://docs.docker.com/install/linux/docker-ce/ubuntu/)通りに入れます。

バージョンを確認

```bash
$ docker version
Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea838
 Built:             Wed Nov 13 07:29:52 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea838
  Built:            Wed Nov 13 07:28:22 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```


## コンテナイメージの実行

サンプルイメージを実行してみましょう

```bash
$ docker container run -p 9999:8888 --name hello cloudnatived/demo:hello

```

コンテナをフォアグラウンドで動かしているのでこれでOKです。ブラウザでlocalhost:9999を開いてみましょう。

![2020-02-23_17h47_40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151399/9a0f194d-4796-52a4-7b26-7270e2a21bfc.png)

無事動いてることを確認できました。Ctrl-Cでコンテナを停止できます。


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

先ほど動かしたアプリケーションの中を見てみましょう。

gitで手元にcloneします。

```bash
$ git clone https://github.com/cloudnativedevops/demo.git
Cloning into 'demo'...
remote: Enumerating objects: 808, done.
remote: Total 808 (delta 0), reused 0 (delta 0), pack-reused 808
Receiving objects: 100% (808/808), 312.19 KiB | 222.00 KiB/s, done.
Resolving deltas: 100% (324/324), done.
```

`demo/hello/main.go` をエディタで開いてみましょう。

```go:main.go
package main

import (
        "fmt"
        "log"
        "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, 世界")
}

func main() {
        http.HandleFunc("/", handler)
        log.Fatal(http.ListenAndServe(":8888", nil))
}
```


これは `8888/http` で待ち受け、HTTPリクエストに対して `"Hello, 世界"` という文字列を返すプログラムです。
シンプルなコードですが、立派にHTTPサーバとして機能します。

## コンテナのビルド

先ほどはコンテナイメージをダウンロードして実行しました。DVDを買ってきて再生したようなものです。

次は自分でイメージを作成してみます。まずは先ほど実行したサンプルアプリのDockerfileを見てみましょう。

`demo/hello/Dockerfile` がそれにあたります。

```dockerfile:Dockerfile
FROM golang:1.11-alpine AS build

WORKDIR /src/
COPY main.go go.* /src/
RUN CGO_ENABLED=0 go build -o /bin/demo

FROM scratch
COPY --from=build /bin/demo /bin/demo
ENTRYPOINT ["/bin/demo"]
```

『マルチステージビルド』という手法が使われています。Goコンテナでは標準的な手法だそうです。

まず最初のステージでは、先ほど紹介したmain.goを公式のgolangイメージを使って`/bin/demo`というバイナリにビルドします。

`AS build` の `build`部分がビルドステージの名前です。

次のステージでは、[Docker が準備した最小イメージ scratch](http://docs.docker.jp/engine/userguide/eng-image/baseimages.html#id4) を使って、`build`ステージで作成した`/bin/demo`を自身にコピーして実行しています。

## 最小限のコンテナイメージ

なんでそんなことするの？と思うかもしれませんが、最小イメージのコンテナを使うことで起動の速さや、脆弱性リスクの軽減などの効果が見込めます。

試しにCentOS、Ubuntu、Golangの公式コンテナイメージをPullしてサイズを確認してみました。

一番下の`cloudnatived/demo`が今回実行したコンテナイメージです。最小イメージを使うことで桁違いの軽さを実現できていることを確認できます。

```bash
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              72300a873c2c        2 days ago          64.2MB
golang              latest              297e5bf50f50        10 days ago         803MB
centos              latest              470671670cac        5 weeks ago         237MB
cloudnatived/demo   hello               eeb7d1c2e2b7        17 months ago       6.49MB
```

## docker image build の実行

それでは実際にbuildしてみましょう。helloディレクトリでやってます。

```bash
$ docker image build -t myhello .
Sending build context to Docker daemon  11.78kB
Step 1/7 : FROM golang:1.11-alpine AS build
1.11-alpine: Pulling from library/golang
9d48c3bd43c5: Pull complete
# 中略
Step 5/7 : FROM scratch
 --->
Step 6/7 : COPY --from=build /bin/demo /bin/demo
 ---> 1ada301d7816
Step 7/7 : ENTRYPOINT ["/bin/demo"]
 ---> Running in 34c534cf6e67
Removing intermediate container 34c534cf6e67
 ---> 7ab71d258907
Successfully built 7ab71d258907
Successfully tagged myhello:latest
```

docker images で確認しましょう

```bash
$ docker images myhello
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myhello             latest              7ab71d258907        2 minutes ago       6.5MB
```

ちゃんとありますね。

## イメージの命名

コマンドのオプションとして指定した`-t myhello`がコンテナイメージにつけた名前です。内部的にはImageIDという64桁のIDがそれぞれのコンテナに付与されています。[（参考：dockerコマンドでのフルImageID確認方法）](https://docs.docker.com/engine/reference/commandline/images/#list-the-full-length-image-ids)

先ほど作成した`myhello`イメージを実行してみましょう。

```bash
$ docker container run -p 9999:8888 myhello
```

ブラウザでlocalhost:9999を開いてみましょう。

![2020-02-24_12h17_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151399/832c938f-925d-9f13-1ae7-067e76f1a4c7.png)

一緒ですよね。

本書では『練習問題として任意の文字列を出してみましょう！』とあるので、文言変えたりしてやってみましょう。

Goをより深く学習するための対話型の[A Tour of Go](https://go-tour-jp.appspot.com/welcome/1)もおススメみたいです。

## イメージのプッシュ

作ったイメージを自分のリポジトリにプッシュしてみましょう。[Docker HUB](https://hub.docker.com/)でDockerIDを取得し、ローカルのDockerデーモンと自分のDockerIDを紐づけます。

```bash
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: <DockerID>
Password: <Password>

Login Succeeded
```

そしてプッシュします

```bash
$ docker image tag myhello <DockerID>/myhello
$ docker image push <DockerID>/myhello
The push refers to repository [docker.io/<DockerID>/myhello]
943af86c7619: Pushed
latest: digest: sha256:28ecda082644d329d63da7d9161c95452b508addd35531b6d5835c667f5ea5c4 size: 528
```

無事成功すると、インターネットにアクセスできる環境であれば、どこでも実行できるようになりました。

```
$docker container run -p 9999:8888 <DockerID>/myhello
```

Docker Hubの画面でも、イメージが登録されていることが確認できます。
![2020-02-24_12h59_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151399/dda1da20-fc24-70bd-7a85-5a899eb6549a.png)



## ようこそKubernetes

待ってましたKubernetes！

Ubuntuでやってるので、Minikubeを入れます。[公式手順](https://kubernetes.io/ja/docs/tasks/tools/install-minikube/)通りで大丈夫です。

さて実行

```bash
$ kubectl run demo --image=<DockerID>/myhello --port=8888 --labels app=demo
$ kubectl port-forward deploy/demo 9999:8888
Forwarding from 127.0.0.1:9999 -> 8888
Forwarding from [::1]:9999 -> 8888
Handling connection for 9999
E0224 13:20:25.161315    9671 portforward.go:400] an error occurred forwarding 9999 -> 8888: error forwarding port 8888 to pod 210dab08657b51a7685e7f65bac683738b4de979f2bd7f5b7592539531e3ebe8, uid : unable to do port forwarding: socat not found
Handling connection for 9999
E0224 13:20:32.063153    9671 portforward.go:400] an error occurred forwarding 9999 -> 8888: error forwarding port 8888 to pod 210dab08657b51a7685e7f65bac683738b4de979f2bd7f5b7592539531e3ebe8, uid : unable to do port forwarding: socat not found
Handling connection for 9999
```

こんな感じでエラーが出て表示されませんでした。どうも`socat not found`が原因のようです。socatはProxyツールなんですが、Port転送でKubernetesが使う？

ま、簡単に入るので大丈夫です。

```bash
$ sudo apt-get update
$ sudo apt-get install socat
```

インストールしてもう1度実行すると見慣れたが画面が返ってきました。

![2020-02-24_13h37_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151399/806caafb-d35e-df69-ac84-0e3abe8ad5c7.png)


## 所感

久しいぶりに手を動かしたので楽しかったです（小並

本の解説もしっかり丁寧の説明してくれて理解しやすかったです。

次は3章のKubernetes環境の選択です。

お読みいただきありがとうございました。