KubernetesでDockerコンテナ「php:7.0-apache」を利用してみる

## 概要
Dockerコンテナ`php:7.0-apache`をminikubeを使って構築したkubernetes環境にdeployし、WEBブラウザからページが閲覧できるようにしてみる。

## 目次
- [Kubernetesハンズオン目次](https://qiita.com/Esfahan/items/4fcf51622fed20dffb88)

## 関連記事
- [Minikubeを使ってローカルにkubernetes環境を構築](https://qiita.com/Esfahan/items/f5c846088281c39f73a4)

## 環境
- CentOS 7.6
- Minikube v1.1.1
- Kubernetes v1.14.3

## minikube（クラスタ）起動
まずはクラスタ起動

```shell-session
$ sudo /usr/local/bin/minikube start --vm-driver=none
```

## DockerHubで公開されている`php:7.0-apache`をdeployする
### podを定義

```yaml:pod.yml
apiVersion: v1
kind: Pod
metadata:
  # pod名
  name: sample-web-pod
  labels:
    app: http-app
spec:
  containers:
    # Dockerコンテナ名
    - name: web-container
      # 利用するDockerイメージ
      image: php:7.0-apache
      # コンテナ内のport
      ports:
        - name: http-port
          containerPort: 80
      # mountするディレクトリ：コンテナ側の設定
      volumeMounts:
        - name: documentroot
          mountPath: /var/www/html
  # mountするディレクトリ：ホストOS側の設定
  volumes:
    - name: documentroot
      hostPath:
        path: /home/username/containers/web/html
```

### serviceを定義

```yaml:service.yml
kind: Service
apiVersion: v1
metadata:
  # service名
  name: http-service
  labels:
    app: http-app
spec:
  selector:
    app: http-app
  # 公開するport
  ports:
    - port: 8080
      targetPort: http-port
  type: NodePort
```

### pod、serviceを作成

```shell-session
$ sudo kubectl create -f pod.yml -f service.yml
```

### curlでwebアクセス
http-serviceにアクセスしてみる

```shell-session
$ curl $(sudo minikube service http-service --url)
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /
on this server.<br />
</p>
<hr>
<address>Apache/2.4.25 (Debian) Server at xxx.xxx.xxx.xxx Port 32016</address>
</body></html>
```

### ブラウザからアクセス
URLを取得

```shell-session
$ sudo minikube service http-service --url
http://xxx.xxx.xxx.xxx:32016
```

ApacheのForbiddenページが表示されればOK。

### index.phpを作成
mountした領域にindex.phpを作成

```php:/home/username/containers/web/html/index.php
<?php phpinfo(); ?>
```

URLを取得

```shell-session
$ sudo minikube service http-service --url
http://xxx.xxx.xxx.xxx:32016
```

ブラウザからhttp://xxx.xxx.xxx.xxx:32016 にアクセス
`<?php phpinfo(); ?>`の内容が表示されればOK。


### podの状態を確認

```shell-session
$ sudo kubectl describe pod sample-web-pod
# **snip**
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  2s    default-scheduler  Successfully assigned default/sample-web-pod to minikube
  Normal  Pulling    2s    kubelet, minikube  Pulling image "php:7.0-apache"
```

### serviceの状態を確認

```shell-session
$ sudo kubectl get services
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
http-service   NodePort    10.109.95.198   <none>        8080:32269/TCP   7m41s
kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP          7h4m
```

## localのDockerイメージを利用する場合
local(ホストOS上)のDockerイメージを利用しようとすると、イメージをpullすることができず、以下のエラーが出る。

```
$ sudo minikube logs -f
container start failed: ErrImagePull: rpc error: code = Unknown desc = Error response from daemon: repository my-web-server not found: does not exist or no pull access
```

イメージがpullできないということは、当然コンテナが立ち上がらないのでアクセスできない。

```shell-session
$ curl `sudo minikube service http-service --url`
curl: (7) Failed connect to xxx.xxx.xxx.xxx:31528; Connection refused
```

コンテナにも入れない。

```shell-session
$ sudo kubectl exec sample-web-pod /bin/sh
error: unable to upgrade connection: container not found ("web-container")
```

podをdescribeすることでもエラーを確認できる。

```shell-session
$ sudo kubectl describe pod sample-web-pod
# **snip**
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  10m                  default-scheduler  Successfully assigned default/sample-web-pod to minikube
  Normal   Pulling    9m4s (x4 over 10m)   kubelet, minikube  Pulling image "web-container:latest"
  Warning  Failed     9m2s (x4 over 10m)   kubelet, minikube  Failed to pull image "web-container:latest": rpc error: code = Unknown desc = Error response from daemon: repository web-container not found: does not exist or no pull access
  Warning  Failed     9m2s (x4 over 10m)   kubelet, minikube  Error: ErrImagePull
  Warning  Failed     8m38s (x6 over 10m)  kubelet, minikube  Error: ImagePullBackOff
  Normal   BackOff    32s (x41 over 10m)   kubelet, minikube  Back-off pulling image "web-container:latest"
```

### 解決策
[minikubeでローカルのdocker imageを使う](https://qiita.com/ocadaruma/items/efe720e46ae7ecb9ec25)

以下コマンドで、minikube VM上のDockerイメージを使うように設定できる。

```
$ eval $(sudo minikube docker-env)
```

これにより以下が設定される。

- DOCKER_TLS_VERIFY
- DOCKER_HOST
- DOCKER_CERT_PATH
- DOCKER_API_VERSION

### `--vm-driver=none`でminikubeを起動している場合
`sudo minikube docker-env`を実行すると以下のエラーが出る。

```
'none' driver does not support 'minikube docker-env' command
```

`--vm-driver=none`でminikubeを起動している場合、`$(minikube docker-env)`は利用できない。
そもそも、localにイメージが存在しているので、pullする必要がない。

参考：https://github.com/kubernetes/minikube/issues/2575#issuecomment-410145576
> your locally available images are also available to kubernetes with vm-driver=none.

> Not that I'm aware of. Note that you don't even have to pull them: with vm-driver=none it's the same docker daemon as on your desktop

pod.ymlのDockerイメージの定義部分で、`imagePullPolicy`を指定することで、localのDockerイメージを利用することができるようになる。

- `imagePullPolicy: IfNotPresent`
    - localイメージ利用。ただし、localに指定したイメージが存在しない場合はレポジトリを参照する。
- `imagePullPolicy: Never`
    - localイメージのみを利用。


### 自作Dockerイメージを作成
以上を踏まえて、自作Dockerイメージを作成し、pod、serviceを作成してみる。

```Dockerfile:Dockerfile
From php:7.0-apache
```

buildする。
タグを`latest`にすると、k8sはrepositoryからpullしようとしてしまうので、versionを明記する。

```shell-session
$ sudo docker build -t my-web-server:v1.0 ./
```

### pod.ymlを定義
今回は`imagePullPolicy: Never`を指定する。

```diff:pod.yml
apiVersion: v1
kind: Pod
metadata:
  # pod名
  name: sample-web-pod
  labels:
    app: http-app
spec:
  containers:
    # Dockerコンテナ名
    - name: web-container
      # 利用するDockerイメージ
-     image: php:7.0-apache
+     # localの自作イメージ
+     image: my-web-server:v1.0
+     imagePullPolicy: Never
+     #imagePullPolicy: IfNotPresent
      # コンテナ内のport
      ports:
        - name: http-port
          containerPort: 80
      # mountするディレクトリ：コンテナ側の設定
      volumeMounts:
        - name: documentroot
          mountPath: /var/www/html
  # mountするディレクトリ：ホストOS側の設定
  volumes:
    - name: documentroot
      hostPath:
        path: /home/username/containers/web/html
```

### service.yml
先程と同じ

### pod、serviceを作成

```shell-session
$ sudo kubectl create -f pod.yml -f service.yml
```

### podの状態を確認

```shell-session
$ sudo kubectl describe pod sample-web-pod
# **snip**
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  3s    default-scheduler  Successfully assigned default/sample-web-pod to minikube
  Normal  Pulled     2s    kubelet, minikube  Container image "my-web-server:v1.0" already present on machine
  Normal  Created    2s    kubelet, minikube  Created container web-container-name
  Normal  Started    2s    kubelet, minikube  Started container web-container-name
```

```shell-session
$ sudo kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
sample-web-pod   1/1     Running   0          23s
```

### webアクセス

```shell-session
$ curl $(sudo minikube service http-service --url)
# omitted...
# <?php phpinfo(); ?>の内容が表示されればOK
```

### ブラウザからアクセス
URLを取得

```shell-session
$ sudo minikube service http-service --url
http://xxx.xxx.xxx.xxx:30910
```

ブラウザから http://xxx.xxx.xxx.xxx:30910 にアクセスして、`<?php phpinfo(); ?>`の内容が表示されればOK

## Delete

```shell-session
$ sudo kubectl delete services http-service
$ sudo kubectl delete pods sample-web-pod
```

## 参考
- [kubernetesでapache-phpのpodを作成する](https://qiita.com/k_shiota/items/57032f807f6c13894c07)
- [Kubernetes チュートリアル](https://kubernetes.io/ja/docs/tutorials/hello-minikube/)
- [[Docker For Mac]KubernetesでManifestfileを使ってNginxを立ち上げてみた](https://blog.adachin.me/archives/7591)
- [HTTPプロキシ経由のMinikube利用](https://kubernetes.io/ja/docs/setup/minikube/#http%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7%E7%B5%8C%E7%94%B1%E3%81%AEminikube%E5%88%A9%E7%94%A8)
- [kubenetesでローカルコンテナイメージからコンテナを作成する方法](https://www.opensourcetech.tokyo/entry/20190223/1550908537)
- [DeploymentとServiceをyamlファイルで定義する](https://reiki4040.hatenablog.com/entry/2017/04/16/225428)