Angular Universal なアプリケーションを +nginx して GKE で公開する

* **(12/8に修正)**  
upstream でアプリケーションサーバーを指定すると、nginx のコンテナが~~エラーを吐くため。~~起動しないため。
* **(12/9に修正)**  
上記だけでは解消しないので、proxy_pass の指定を変数に。アプリケーションサーバーが起動するまえだと nginx が起動しない。



----

HI 👀✨

Angular v8.2.14 で Angular Universal を使ってサーバーサイドレンダリングするアプリケーションを GKE で公開するための設定などを解説する記事です。

Angular のアプリケーションを nginx を介して表示させるような構成です。

http から https へのリダイレクト、gzip圧縮などを nginx で行えるのが便利です。

## Angular の Docker コンテナを作る

まず、`npm run build:ssr` でアプリケーションをビルドして、以下の Dockerfile でコンテナを作成します。

Angular でビルドした時に出力される dist ディレクトリをコンテナの /app/augular-app/dist に配置しています。

コンテナの作成は `docker build ./ -t angular-app --no-cache -f ./Dockerfile` のようなコマンドです。

```dockerfile:angular-app/Dockerfile
FROM node:10-alpine

ENV APP_HOME /app/angular-app
ENV APP_DIST /app/angular-app/dist
RUN mkdir -p $APP_HOME

WORKDIR $APP_HOME
COPY dist $APP_DIST

CMD ["node", "dist/server"]
```


## nginx コンテナを作る

以下のような、3つのファイルを用意します。

* Dockerfile
* nginx.conf
* default.conf

そして、`docker build ./ -t angular-app-nginx --no-cache` のようにコンテナを作成します。

httpsへのリダイレクトについては、[Kubernetes Engineでhttpからhttpsにリダイレクトする](https://qiita.com/subscrec/items/fa5d536bd5ba201f763c)という記事を書いているのでご覧ください。

```dockerfile:nginx/Dockerfile
FROM nginx:mainline-alpine

COPY nginx.conf /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf
```

```nginx:default.conf
# これは不要(12/8修正)
# upstream angular {
#  server localhost:4000;
# }

server {
  listen 80;
  # server_name angular; 下記に修正(12/8)
  server_name example.com;

  set $redirect_to_https "";

  if ($http_x_forwarded_proto) {
    set $redirect_to_https A;
  }

  if ($http_x_forwarded_proto != https) {
    set $redirect_to_https "${redirect_to_https}B";
  }

  if ($redirect_to_https = AB) {
    return 301 https://$host$request_uri;
  }

  gzip on;
  gzip_types text/css application/javascript application/json application/font-woff application/font-tff image/gif image/png image/jpeg application/octet-stream image/svg+xml image/x-icon;
  gzip_proxied any;
  gzip_min_length 1000;
  gunzip on;

  location / {
    proxy_read_timeout 300;
    proxy_connect_timeout 300;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # proxy_pass http://angular; 下記に修正(12/8)
    # proxy_pass http://localhost:4000; さらに修正(12/9)
    # APPサーバーが起動する前でも nginx が起動するように
    resolver 127.0.0.1;
    set $upstream 127.0.0.1:4000;
    proxy_pass http://$upstream;
  }
}
```

```nginx:nginx.conf
user nginx;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid       /var/run/nginx.pid;

events {
  worker_connections 1024;
}

http {
  include      /etc/nginx/mime.types;
  default_type application/octet-stream;

  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

  access_log /var/log/nginx/access.log  main;

  sendfile on;
  keepalive_timeout 65;
  include /etc/nginx/conf.d/*.conf;
}
```

## GKE の Deployment について

```nginx
# upstream angular {
#   server localhost:4000;
# }
proxy_pass http://localhost:4000
```

nginx の設定ファイルでこのように `localhost:4000` としているため、Angular アプリケーションと nginx のコンテナを同じ Pod 内で動かしています。そのような設定は以下。

```yaml:deployment.yaml
apiVersion: "extensions/v1beta1"
kind: "Deployment"
metadata:
  name: "angular-app"
  namespace: "default"
  labels:
    app: "angular-app"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: "angular-app"
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: "angular-app"
    spec:
      containers:
      - name: "angular-app-nginx"
        image: "<IMAGE>"
        livenessProbe:
          httpGet:
            scheme: HTTP
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 10
      - name: "angular-app"
        image: "<IMAGE>"
```

おしまい。

## Angular Universal に関するおすすめ記事など

* [Server-side Rendering (SSR): An intro to Angular Universal](https://angular.io/guide/universal)  
  公式ドキュメント。これを読めば大体動かせる👀✨
* [SSR の知識ゼロから始める Angular Universal](https://qiita.com/MasanobuAkiba/items/7adcfd5050150ac9ba36)  
  大体動いたあとに、つまづくことがあったらこちらの記事を参考に。