MEAN StackをKubernetesで動かす(その1)
前回はMEAN StackをDocker Composeで動かしてみました。
hi1280.hatenablog.com
今回はMEAN StackをKubernetesで動かします。
Dockerに対応したので、Kubernetesでも動くようになっているはずです。試してみます。
なお、今回のKubernetesの環境はGKE(Google Kubernetes Engine)を使用します。
手軽にKubernetes環境が用意できるので、試しに使ってみるには良いと思います。
無料のトライアル期間もあります。
MEAN Stackのプログラム一式はこちら
github.com
Docker Composeの時と似たような構成でAngularとExpressとMongoDBを動かします。
実運用に耐えうることを想定して、以下の内容を含めます。
- MongoDBは可用性を高めるためにReplica Setで構成する
- 外部公開を考慮して、HTTPS対応を行う
HTTPS対応の話は次回に行います。今回はHTTPでアクセスするところまでです。
事前準備
GKEを利用するために、GCP(Google Cloud Platform)用のコマンドラインツール(gcloud)をインストールします。
Quickstarts | Cloud SDK | Google Cloud
kubectlコマンドをインストールします。
Quickstart | Kubernetes Engine Documentation | Google Cloud
gcloudコマンドでKubernetesクラスタを構築します。
$ gcloud container clusters create mean-example
MongoDBのセットアップ
MongoDBを作成する前にデータを保存するディスクを準備する必要があります。
ディスクの設定
何のディスクを利用するのかを決めるためにStorageClassを作成します。
ここでは、GCE(Google Compute Engine)のディスクを利用するように設定します。
gce-storageclass.yml
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: slow provisioner: kubernetes.io/gce-pd parameters: type: pd-standard
$ kubectl apply -f gce-storageclass.yml
MongoDBの作成
Replica Setで認証を行うためにkeyfileを用意します。
KubernetesのSecretに登録します。
$ TMPFILE=$(mktemp) $ /usr/bin/openssl rand -base64 741 > $TMPFILE $ kubectl create secret generic shared-bootstrap-data --from-file=internal-auth-mongodb-keyfile=$TMPFILE $ rm $TMPFILE
Replica Setにおける認証の参考情報
Internal Authentication — MongoDB Manual
MongoDB用のStatefulSetとServiceを作成します。
mongodb-service.yml
apiVersion: v1 kind: Service metadata: name: mongodb-service labels: name: mongo spec: ports: - port: 27017 targetPort: 27017 clusterIP: None selector: role: mongo --- apiVersion: apps/v1 kind: StatefulSet metadata: name: mongod spec: selector: matchLabels: role: mongo serviceName: mongodb-service replicas: 3 template: metadata: labels: role: mongo environment: test replicaset: MainRepSet spec: terminationGracePeriodSeconds: 10 volumes: - name: secrets-volume secret: secretName: shared-bootstrap-data defaultMode: 256 containers: - name: mongod-container image: mongo:3.4.16-jessie command: - "mongod" - "--bind_ip" - "0.0.0.0" - "--replSet" - "MainRepSet" - "--auth" - "--clusterAuthMode" - "keyFile" - "--keyFile" - "/etc/secrets-volume/internal-auth-mongodb-keyfile" - "--setParameter" - "authenticationMechanisms=SCRAM-SHA-1" ports: - containerPort: 27017 volumeMounts: - name: secrets-volume readOnly: true mountPath: /etc/secrets-volume - name: mongodb-persistent-storage-claim mountPath: /data/db volumeClaimTemplates: - metadata: name: mongodb-persistent-storage-claim annotations: volume.beta.kubernetes.io/storage-class: "slow" spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 10Gi
ServiceはHeadless Serviceで作成します。
この後にReplica Setの設定を行う際に、PodのIPアドレスを知る必要があるためです。
StatefulSetではMongoDBコンテナが起動する3つのPodを作成します。
3つのPodがReplica Setで利用されます。
mongod
コマンドで、事前に作成したSecretを利用して認証を有効にします。
volumeClaimTemplates
で先ほどのStorageClassを指定しています。
Pod毎に10Gのディスクが設定されます。
$ kubectl apply -f mongodb-service.yml
MongoDBの設定
PrimaryになるMongoDBに接続します。
$ kubectl exec -it mongod-0 -c mongod-container bash $ mongo
Replica Setを設定します。
> rs.initiate({_id: "MainRepSet", version: 1, members: [ { _id: 0, host : "mongod-0.mongodb-service:27017" }, { _id: 1, host : "mongod-1.mongodb-service:27017" }, { _id: 2, host : "mongod-2.mongodb-service:27017" } ]})
ユーザを作成します。
> db.getSiblingDB("admin").createUser({ user : "xxx", pwd : "xxx", roles: [ { role: "root", db: "admin" } ] }) > use admin > db.auth("xxx","xxx") > db.getSiblingDB("my-heroes").createUser({ user : "xxx", pwd : "xxx", roles: [{role:"readWrite", db: "my-heroes"}] })
ユーザ、パスワードは適宜設定する。
Angular+Expressの作成
Expressを作成します。
webapp-backend-service.yml
apiVersion: v1 kind: Service metadata: name: webapp-backend-service labels: name: webapp-backend spec: type: ClusterIP ports: - protocol: 'TCP' port: 3000 targetPort: 3000 selector: app: webapp-backend --- apiVersion: apps/v1 kind: Deployment metadata: name: webapp-backend spec: replicas: 1 selector: matchLabels: app: webapp-backend template: metadata: labels: app: webapp-backend spec: containers: - name: webapp-backend image: asia.gcr.io/arched-photon-204013/mean-example_backend:latest ports: - containerPort: 3000 env: - name: PORT value: '3000' - name: MONGODB_URI value: 'mongodb://xxx:xxx@mongod-0.mongodb-service:27017,mongod-1.mongodb-service:27017,mongod-2.mongodb-service:27017/my-heroes?replicaSet=MainRepSet'
コンテナレジストリにはGCR(Google Container Registry)を利用しています。
前回のExpress実行用のDockerfileから作成したイメージです。
Replica SetのMongoDBに接続するため、3つのMongoDBをホスト名で指定しています。
$ kubectl apply -f webapp-backend-service.yml
Angularを作成します。
webapp-frontend-service.yml
apiVersion: v1 kind: Service metadata: name: webapp-frontend-service labels: app: webapp-frontend spec: type: NodePort ports: - name: "http-port" protocol: "TCP" port: 80 targetPort: 80 selector: app: webapp-frontend --- apiVersion: apps/v1 kind: Deployment metadata: name: webapp-frontend spec: replicas: 1 selector: matchLabels: app: webapp-frontend template: metadata: labels: app: webapp-frontend spec: containers: - name: webapp-frontend image: asia.gcr.io/arched-photon-204013/mean-example_frontend:latest ports: - containerPort: 80 env: - name: APP_HOST value: 'webapp-backend-service' - name: APP_PORT value: '3000' command: ['/bin/sh'] args: ['-c', "envsubst '$$APP_HOST$$APP_PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"]
HTTPSに対応するためにIngressを利用することになります。Ingressを利用するためには、事前にNodePortのServiceを作成しておく必要があります。
コンテナイメージに関しては、前回のAngularアプリ実行用のDockerfileから作成したイメージです。
$ kubectl apply -f webapp-frontend-service.yml
外部からの疎通が可能なネットワークの設定
Ingressを作成します。
ingress.yml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: webapp-service labels: app: webapp spec: backend: serviceName: webapp-frontend-service servicePort: 80
backend
では先ほど作成したNodePortのServiceを指定しています。
$ kubectl apply -f ingress.yml
Ingressを作成することで、GCLB(Google Cloud Load Balancer)が設定されます。
$ kubectl get ingress NAME HOSTS ADDRESS PORTS AGE webapp-service * xxx.xxx.xxx.xxx 80 5m
5分ほどで静的IPアドレスが割り当てられて、アクセスできるようになります。
HTTPS対応の話は次回に。
参考にした資料
Kubernetesの基礎情報 thinkit.co.jp
MongoDBのKubernetes対応 pauldone.blogspot.com
MEAN StackをDocker Composeで動かす
前回はMEAN StackをHeroku上で動かしてみました。
hi1280.hatenablog.com
今回はMEAN Stackアプリに手を加えて、Dockerに対応させます。
Dockerにすれば、他の環境への移行も楽になるのではと思います。
Docker Composeを使って、nginx+Node.js+MongoDBの環境でMEAN Stackアプリを動かします。
プログラム一式はこちら
github.com
Docker Composeを使って、以下の3つのコンテナで構成します。
- nginxでAngularを動かす(フロントエンドアプリ)
- Node.jsでExpressを動かす(バックエンドアプリ)
- MongoDBを動かす(DB)
Angularビルド環境用のDockerfile
AngularビルドのためにAngular CLI環境のDockerfileを作ります。
Dockerfile
FROM node:8.11.3-alpine LABEL authors="hi1280" RUN npm install -g @angular/cli@6.0.8 RUN npm cache clean --force
Node.jsの環境にAngular CLIをインストールしているだけというシンプルなモノです。
Angularアプリ実行用のDockerfile
Angularをビルドして、それをそのままDockerコンテナに含めると容量がかなり大きくなってしまいます。
そのため、Angularビルドとnginx上でのAngularアプリの実行を分けます。
これはDockerのマルチステージビルドの機能を使えば、実現できます。
Use multi-stage builds | Docker Documentation
マルチステージビルドを用いたDockerfileを作ります。
Dockerfile
# Angular Build FROM hi1280/angular-cli:latest as build-stage WORKDIR /usr/src/app COPY ["package.json", "package-lock.json*", "./"] RUN npm install --production --silent COPY . . RUN npm run build # nginx FROM nginx:alpine RUN rm -rf /usr/share/nginx/html/* COPY --from=build-stage /usr/src/app/dist/mean-example /usr/share/nginx/html COPY ./nginx /etc/nginx/conf.d CMD ["nginx", "-g", "daemon off;"]
COPY --from=build-stage
でビルド後の成果物のみをコピーしています。
これにより、最小限のファイルサイズになります。
AngularでビルドしたHTMLリソースをnginxで動かすDockerコンテナになります。
Express実行用のDockerfile
Expressを動かすDockerfileを作ります。
Dockerfile
FROM node:8.11.3-alpine WORKDIR /usr/src/app COPY ["server.js", "/src/server/package.json", "/src/server/package-lock.json", "./"] COPY ["/src/server", "./src/server"] RUN npm install --production --silent EXPOSE 3000 ENV NODE_ENV production CMD [ "node", "server.js" ]
Node.jsの環境を公式のイメージから取得して、Expressを動かすだけのDockerコンテナになります。
Docker Composeで各コンテナを連携
これまでに用意した各Dockerコンテナを連携します。
docker-compose.yml
version: '2.1' services: frontend: build: context: . dockerfile: Dockerfile-frontend ports: - 80:80 environment: - APP_HOST=backend - APP_PORT=3000 command: /bin/sh -c "envsubst '$$APP_HOST$$APP_PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'" depends_on: - backend backend: build: context: . dockerfile: Dockerfile-backend ports: - 3000:3000 depends_on: - db environment: - MONGODB_URI=mongodb://db:27017/my-heroes db: image: mongo:3.4.16-jessie restart: always volumes: - /data/db:/data/db
URLにサービス名を指定することで、各サービスにアクセスすることができます。
mongodb://db:27017/my-heroes
といった形です。
depends_on
を指定することで、サービスを立ち上げるために事前に起動が必要なサービスを指定できます。
これにより、サービスを起動する順番を制御することができます。
Docker Composeにおけるnginxの設定変更
環境変数を用いて動的にnginxの設定を変更しています。
nginxの公式Dockerイメージにそのやり方が記載されています。(Using environment variables in nginx configurationという箇所です)
https://hub.docker.com/_/nginx/
こちらも参考にしました。
Docker上のNginxのconfに環境変数(env)を渡すたったひとつの全く優れてない方法(修正:+優れている方法)
default.conf.template
server { listen 80; server_name localhost; location /api/ { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://${APP_HOST}:${APP_PORT}/api/; } location / { root /usr/share/nginx/html; index index.html index.htm; } }
${APP_HOST}と${APP_PORT}が環境変数で定義した値に変わります。
Expressのサービス名にアクセスする設定になります。
まとめ
公式のDockerイメージを活用すれば、プログラムをコピーして、起動コマンドを実行するといった形で、Dockerコンテナ化できるので難しいことはあまりないかなと思います。
ハマったら、コンテナのシェルに入ってデバッグするといったことができると良いです。
MEAN StackをHeroku上で動かす
MEAN Stackを運用するサービスを検討していましたが、Herokuが良いだろうという結論になり、実際に使ってみました。
Herokuを選んだ決め手は以下のとおりです。
- Node.jsに対応している
- MongoDBがアドオンで簡単に使える
Herokuのドキュメントを参考にして、自分なりにやってみました。
Create a Web App and RESTful API Server Using the MEAN Stack | Heroku Dev Center
MEAN Stackの構成
MEAN Stackのアプリを用意しました。
github.com
Node.jsでExpressのAPIサーバとAngularで作成したフロントのコンテンツの両方を動かす形です。
これをHerokuでそのまま動かそうという算段です。
プログラムの参考元はこちら github.com
Herokuへのデプロイ
Heroku CLIをインストールします。
The Heroku CLI | Heroku Dev Center
Herokuのアプリ環境を作成します。
$ heroku create ... Creating app... done, ⬢ secret-scrubland-78144 https://secret-scrubland-78144.herokuapp.com/ | https://git.heroku.com/secret-scrubland-78144.git
gitの設定にherokuというリポジトリが追加されています。
$ git config --list ... remote.heroku.url=https://git.heroku.com/secret-scrubland-78144.git remote.heroku.fetch=+refs/heads/*:refs/remotes/heroku/*
デプロイを行うには、このリポジトリにPushします。
$ git push heroku master
MongoDBの追加
HerokuにMongoDBを作成するには、mLabのアドオンを追加します。
$ heroku addons:create mongolab
DBの接続情報はMONGODB_URI
という環境変数で取得できます。
今回のプログラムでは既にこの環境変数を使用して、MongoDBに接続するようになっています。
Heroku環境にアクセス
$ heroku open
こちらが今回作成したデモ環境です。
https://secret-scrubland-78144.herokuapp.com/
まとめ
Herokuを使えばMEAN Stackの運用環境を簡単に作成できることが分かりました。