kubernetes の Web Application 構成について

# はじめに
この記事は [GameWith Advent Calendar 2018](https://qiita.com/advent-calendar/2018/gamewith)の20日目です。

基本小心者なので、  packagist や npm に library 等を登録することは、あるものの記事を書くというのは初めてです。なので、お手柔らかに見ていただければと思います :pray:

今回は、 比較的ありがちな kubernetes の Web Application 構成についてお話しさせていただきます。

# 構成について

まず kubernetes で初めて設計する際に悩む箇所ですが、大雑把に2つのパターンになると思います。

### NGINX と PHP をまとめるパターン

こちらのケースの方は 1 Pod で済みますし、サンプルでも比較的多く見かける構成だと思います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/316627/799fe5cd-3775-a93d-e450-69033d160707.png)

### NGINX と PHP を分けるパターン

上の図のパターンと比べると複雑になりますが、 `NGINX` と `PHP` を分けるパターンです。
`Ingress` 経由で `NGINX Service` にアクセスし、 特定の `NGINX Pod` に処理を割り振ります。
`NGINX Pod` が `PHP Service` にアクセスし、 `PHP Pod` に処理を割り振ります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/316627/275bf564-fa22-e1b9-c96e-284f6604e504.png)

### 結局どっちが良いのか

**基本的には `NGINX と PHP` を分けるパターン** が良いかと思います。
Pod はアプリケーション単位で分けるのが基本と言われておりますが、運用や負荷テストをすると顕著に理解できます。
例えば以下のリソース使用量を見てください。

```
$ kubectl top pod
NAME                            CPU(cores)   MEMORY(bytes)
php-deployment-xxxxxxxx-yyyy1      34m          57Mi
php-deployment-xxxxxxxx-yyyy2      28m          53Mi
php-deployment-xxxxxxxx-yyyy3      32m          52Mi
nginx-deployment-xxxxxxxx-yyyy1     1m           5Mi
nginx-deployment-xxxxxxxx-yyyy2     1m           5Mi
```

上記のように `NGINX` と `PHP` ではリソースの消費量が大抵桁外れに違うので、スケールする単位・タイミングが違います。なので、 `NGINX` と `PHP` を一緒にすると、負荷の高い方に影響されてスケールするため、高負荷なサービスほど必要以上にリソースを消費してしまいます。以上の事から、リソースは分けるべきだと個人的に思います。

# 定義について
こちらはあくまでもサンプルです。図のイメージと定義を照らし合わせて頂ければ良いかなと思う程度で記載しています。コピペで動作はしないので予めご理解願います :bow:
hpa などの auto scale に関しては今回省略しています。また、 [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) や [Services](https://kubernetes.io/docs/concepts/services-networking/service/) などの詳細に関しましては公式を見て頂く方が確実かと思いますので、リンクを貼り付けさせていただきます。

### PHP の定義

```php-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-deployment
  labels:
    app: php
spec:
  replicas: 2
  selector:
    matchLabels:
      app: php
  template:
    metadata:
      labels:
        app: php
    spec:
      containers:
      - name: php
        image: hoge/dummy:commit_id
        ports:
        - containerPort: 9000
        workingDir: /app
      - name: cloudsql-proxy
        image: gcr.io/cloudsql-docker/gce-proxy:1.13
        command: ["/cloud_sql_proxy",
                  "-instances=hoge:fuga:piyo=tcp:3306",
                  "-credential_file=/secrets/cloudsql/credentials.json",
                  "-verbose=false"]
        volumeMounts:
          - name: cloudsql-oauth-credentials
            mountPath: /secrets/cloudsql
            readOnly: true
      volumes:
        - name: cloudsql-oauth-credentials
          secret:
            secretName: cloudsql-oauth-credentials
      restartPolicy: Always
```

```php-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: php-service
  labels:
    app: php-service
spec:
  type: ClusterIP
  ports:
    - port: 9000
  selector:
    app: php
```

### NGINX の定義

```nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: hoge/fuga:piyo
        ports:
          - containerPort: 80
        workingDir: /var/www/html
      restartPolicy: Always
```

```nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx-service
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: nginx
```

```nginx.conf
server {   
    ...

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        # fastcgi_pass に service (Cluster IP) を定義します
        fastcgi_pass php-service:9000;
        fastcgi_index index.php;
        include conf.d/common/fastcgi_params.conf;
    }
}
```

### Ingress (L7 LB) の定義

```ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: ingress-ip
spec:
   backend:
     serviceName: nginx-service
     servicePort: 80 
```

# 終わりに
次回書くことがありましたら、 `BAD Knowhow` でも書かせていただこうと思います :pray: