はじめに
Single Page Applicationのフレームワーク Angular をコンテナで実行するまでを勉強したので、備忘録として残しておく。
1. node.jsのバージョン管理ツール nvm のセットアップ
macOSで、node.jsのバージョンを切り替えられるように、nvmをインストールして、その上でnode.jsを動作させ、Angularのコマンドを利用する。nvmのセットアップは、参考リンクを読めば解るので、補足はしない。
2. 最新バージョンのnode.jsをインストール
バージョンの指定無しでインストールを実行すれば、その時点の最新バージョンのnodeがインストールされる。
$ nvm install
$ node --version
3. TypsScript コマンドのインストール
TypeScriptのスニペットのテストを実施するために、TypeScript から JavaScript への変換コマンド tsc をインストールする。TypeScriptをブラウザ上で実行するときは、JavaScriptへ変換されたコードが実行される。TypeScriptからnode.jsのモジュールを利用するなどもあるので、TypeScriptで書いたコードが、JavaScriptに変換されたときの内容が解るtscコマンドは重要なデバックツールになる。
$ npm install -g typescript
4. Angular のコマンドをインストール
Angularのコマンドをインストールする。このコマンドは、Angularのアプリケーション開発の足がかりとなるコードを生成する。また、JavaScriptへ変換するビルドを実施することができる。
$ npm install -g @angular/cli
Angularの ngコマンドのオプションなどは、参考URLで解説されているので、ブックマークしておくと重宝する。
- 参考URL https://angular.io/cli
5. Angular のサンプルアプリを作成してみる
Anglularでアプリケーションを開発するときは、最初に、最小コードやディレクトリ構造をngコマンドで生成してから、足していくのが良い。次のコマンドは、SPAのコード TestAppの足場となるコードを生成してくれる。
$ ng new TestApp
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? SCSS [ https://sass-lang.com/documentation/syntax#scss ]
<中略>
✔ Packages installed successfully.
Successfully initialized git.
以下はアプリケーションのディレクトリTestApp 以下に生成されたファイル群だ。アプリケーションのコードは、以下に付け足していく形で、開発を推進することができる。
$ tree TestApp/
TestApp/
├── README.md
├── angular.json
├── karma.conf.js
├── package.json
├── src
│ ├── app
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ └── app.module.ts
│ ├── assets
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ └── test.ts
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
4 directories, 20 files
参考URL:
6. Angular のテスト実行
上記のコマンド実行で、必要なコードを生成してくれるので、ディレクトリを移動して、'ng serve'を実行すると、HTTPサーバーが起動して、Angularのコードを、ブラウザ上で実行できる準備が整う。
$ cd TestApp
$ ng serve
ターミナルにアクセスするべきURLは表示されるので、curlコマンドやブラウザを使って、動作や画面を確認することができる。
参考URL https://angular.io/cli/serve
7. コンテナのビルド準備
コンテナ化する場合でも 'ng serve' をWebサーバーとして利用することも出来なくはないが、本番用ではないため、脆弱性やパフォーマンスの懸念がある。そこで、Nginxのコンテンツとして、AngularのSPAをサーブする。そのためのDockerfileが以下だ。ここでのポイントは、ビルドとNginxのコンフィグの消去である。
# Stage 1 ビルドステージ
FROM node:latest as node
WORKDIR /app
COPY ./TestApp /app
RUN npm install
RUN npm run build --prod # TypeScriptからJavaScriptへのビルド
# Stage 2 Nginxとの統合
FROM nginx:alpine
RUN rm /etc/nginx/conf.d/default.conf # コンフィグの消去
COPY --from=node /app/dist/test-app /usr/share/nginx/html
ビルドでは、TypeScriptのソースコードから、ブラウザで実行するためのJavaScriptのコードを生成する。生成されたJavaScriptのコートは人が読むためのコードではなく、ブラウザで実行するためのコードなので、名称が変更されるなどが実施される。
Nginxとの統合では、Kubernetesにデプロイすることを想定して、TLSのための証明書、コンフィグファイルを、それぞれ、Secret, ConfigMapで外部から与えられるようにするために、default.confを消去しておく。そして、conf.d 以下を外部のボリュームから提供できるようにする。
ビルドステージで生成されたコードは、ディレクトリ dist/test-appに作成されるので、そのディレクトリ以下を、Nginxのドキュメントルート以下に、コピーする。
8. コンテナのビルドとローカルテスト
ここからは、Dokcerコマンドの使い方になる。必要となるオプションとパラメーターの意味は以下になる。
export TAG=1.0
docker build -t <レジストリURL>/<ユーザーID>/<コンテナイメージ名>:$TAG .
筆者のローカルのラボ環境に、コンテナレジストリでCNCF の Harborを立てている場合は、タグを初めから設定しておくと便利だ。
export TAG=1.0
docker build -t harbor.labo.local/tkr/spa:$TAG .
ビルドが完了したら、実行してアクセステストを実施する。同様にオプションは以下である。
docker run --rm --name spa -p <公開ポート>:<コンテナポート> -v <ホストの絶対パス>:<コンテナ上のマウント位置> <レジストリURL>/<ユーザーID>/<コンテナイメージ名>:$TAG
環境依存なので、参考程度に残しておく。 「-p」では、ポート番号はHTTPとHTTPSの両方のポートを開く。「-v」では、TLSのためのサーバー証明書と、Nginxのconf.d以下を、外部から与える。
docker run --rm --name spa -p 5080:5080 -p 5443:5443 -v `pwd`/certs:/certs -v `pwd`/nginx:/etc/nginx/conf.d harbor.labo.local/tkr/spa:$TAG
9. レジストリへ登録
Kubernetesでコンテナを実行するためには、コンテナをレジストリに登録しなければならない。以下は、レジストリに登録するには、アカウントを保有しており、そのアカウントでログインしていなければならない。この例では、コンテナレジストリ Harbor にログインしておき、登録するときのコマンド例だ。
docker login -u tkr harbor.labo.local
docker push harbor.labo.local/tkr/spa:$TAG
10. Kubernetesへのデプロイ
アプリケーション専用の名前空間を作成しておき、公開用IPアドレスからDNSを登録するために、ロードバランサーをデプロイする。
# 名前空間の作成
kubectl create ns spa
# ロードバランサーの設定、VIPの獲得
kubectl apply -f yamls/service.yaml -n spa
作業の前提として、以下の3点がある。特に、サーバー証明書のドメイン名が、DNS登録名と一致している昼用があるので、面倒だけど、外せない。
- コンテナはレジストリへ登録しておく。
- プライベートCAでサーバー証明書を作成しておく。
- プライベートDNSにドメイン名を登録しておく。
TLS用のサーバー証明書は、Secret spa.tlsに保存、そして、 Nginxのコンフィルは、Configmap nginx-configへ保存する。
# サーバー証明書をデプロイ
kubectl create secret tls spa.tls \
--cert=certs/tls.crt \
--key=certs/tls.key \
-n spa
# Nginxの設定ファイルをデプロイ
kubectl create configmap nginx-config --from-file=nginx -n spa
最後にKubernetesへデプロイする。
# コンテナのデプロイ、ロードバランサーの設定
kubectl apply -k yamls -n spa
これで、kubectl get po -n spa
や kubectl get svc -n spa
で確認すれば良い。
11. プロジェクトのファイル構成
コンテナやKubernetesのYAMLなどを一箇所に集約して、プロジェクト用のディレクトリを作成しておくとと便利だ。nginx と yamlsの部分の内容は、後述する。
$ tree angular-apl
angular-apl
├── Dockerfile
├── README.md
├── TestApp
│ ├── README.md
│ <以下省略 前述に同じため>
│
├── certs
│ ├── tls.crt
│ └── tls.key
├── nginx
│ ├── default.conf
│ └── ssl-server.conf
└── yamls
├── deployment.yaml
├── kustomization.yaml
└── service.yaml
12. Nginxのコンフィルファイル
'/etc/nginx/conf.d' に配置する HTTPの設定ファイルである。 OpenShift環境へのデプロイも考慮して、特権ポートを避ける設定にする。
server {
listen 5080;
server_name spa.labo.local;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
こちらは、TLSで暗号化するWebサーバーの設定である。証明書と鍵は/certsの下にマウントする想定だ。
server {
listen 5443 ssl http2;
#listen [::]:5443 ssl http2;
server_name spa.labo.local;
root /usr/share/nginx/html;
ssl_certificate /certs/tls.crt;
ssl_certificate_key /certs/tls.key;
ssl_session_timeout 1d;
ssl_session_cache shared:SharedNixCraftSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
}
13. Kubernetes の YAMLファイル
ロードバランサーのIPアドレスは、アサインされるので、先にデプロイしておいて、IPアドレスをDNSへ登録するのが良いと思う。クラウドやKubernetesクラスタの構成により変わるので、参考程度にみて欲しい。
apiVersion: v1
kind: Service
metadata:
name: spa
spec:
selector:
app: spa
group: spa-group
ports:
- name: https
protocol: TCP
port: 443
targetPort: 5443
- name: http
protocol: TCP
port: 80
targetPort: 5080
type: LoadBalancer
予めレジストリへ登録しておいたコンテナ harbor.labo.local/tkr/spa:1.0 を指定して、
ConfigMapとSecretをボリュームとしてマウントする。
apiVersion: apps/v1
kind: Deployment
metadata:
name: spa
spec:
replicas: 1
selector:
matchLabels:
app: spa
group: spa-group
template:
metadata:
labels:
app: spa
group: spa-group
spec:
#imagePullSecrets:
#- name: regcred
containers:
- name: spa
image: harbor.labo.local/tkr/spa:1.0
imagePullPolicy: Always
volumeMounts:
- name: tls
mountPath: /certs
- name: config
mountPath: /etc/nginx/conf.d
volumes:
- name: tls
secret:
secretName: spa.tls
- name: config
configMap:
name: nginx-config
さらにYAMLファイルが増える場合は、適用の順序が保証されるので、作成しておくと便利だ。
resources:
- service.yaml
- deployment.yaml
以上