今回の目標は、Cloud Foundry アプリと同様に、Bluemix k8sでロードバランサーによる負荷分散された高可用性環境にコンテナを配置して、Bluemixで自動付与されたドメイン名へHTTPSでアクセスできる様にすることです。
前提条件
k8sで、HTTPSを利用できる様にするためには、有償の標準クラスタを利用する必要あります。 このため、フリーアカウントでは利用できません。また、Bluemix Infrastrucre と連携ができない Bluemix アカウントでは利用できませんので、ご了承願います。
- k8s標準クラスタを作るために、クレジットカード登録または、IBM契約のあるBluemixのアカウント
- Vagrant + Virtual Box が動作するPC環境 (Windows PC, Mac, Linux PCのいづれか)
- インターネット接続 10Mbps以上
ローカル環境の構築
Cloud Foundry の場合には、コードをアップロードするだけで良かったのですが、k8sの場合は、ビルド済みのコンテナを利用します。 したがって、アプリを開発する場合には、Bluemix CLIコマンドやプラグイン以外に、Dockerのビルド環境が必要になります。
- Bluemix環境と連携させるために bxコマンド、および プラグイン
- k8sのマスターノードと連携するために、kubectl コマンド、
- アプリケーションをコンテナ化するために Docker環境
- プログラム言語の開発環境
Vagrant + Virtual Box を利用して開発環境を作っている方向けに、PHP,Ruby,Python,Node のバージョン管理、git, Bluemix CLI, kubectl などコマンドと前提パッケージを予めインストールした仮想サーバーを作るためのVagrantfile を作成してあります。
https://github.com/takara9/bluemix-dev
k8sクラスタの作成
ここからの操作は、前述のVagrantfileを利用して作成した仮想サーバー上での操作です。 それから、bx login を完了した状態から始めます。
k8sの標準クラスタを構築していきます。 この操作は、Bluemix の ウェブ画面からも同様の事が出来るのですが、この記事では、将来自動化しやすい様に、すべて、CLIで進めていきます。
最初に、どこのデータセンターにk8sのクラスタを構築するかを決めておきます。 現在k8sが利用出来るデータセンターを参照して決めると良いと思います。 近々、東京データセンターでも k8sクラスタが利用できる様になりますので、乞うご期待です。
この資料では、Bluemix Infrastructure の ダラス 第12データセンター DAL12を選択して進めていきます。 まず、コンテナのホストになるサーバーを決めて、Nameの欄の表記をメモしておきます。
$ bx cs machine-types dal12
OK
Name Cores Memory Network Speed OS Storage Server Type
u1c.2x4 2 4GB 1000Mbps UBUNTU_16_64 100GB virtual
b1c.4x16 4 16GB 1000Mbps UBUNTU_16_64 100GB virtual
b1c.16x64 16 64GB 1000Mbps UBUNTU_16_64 100GB virtual
b1c.32x128 32 128GB 1000Mbps UBUNTU_16_64 100GB virtual
b1c.56x242 56 242GB 1000Mbps UBUNTU_16_64 100GB virtual
ここでは、以前にDAL12を利用した事があり、VLANが割り当てられていれば表示されます。
$ bx cs vlans dal12
OK
ID Name Number Type Router
2114477 1441 private bcr01a.dal12
2114475 1286 public fcr01a.dal12
スタンダード・クラスタを作成します。 --name で指定する名前や --workers で指定するノードの数、 --machine-type で指定される サーバーのスペックなどは、目的に合わせて変更をお願いします。 また、 --public-vlan, --private-vlan の IDは省略すれば、自動割り当てとなります。
$ bx cs cluster-create --name mycluster3 --location dal12 --machine-type u1c.2x4 --private-vlan 2114477 --public-vlan 2114475 --workers 3
Creating cluster...
OK
2017年9月現在の実装状況では、DAL12で3ノードを指定した場合、上記コマンドを投入してから約30分でクラスタが完成しました。
$ bx cs clusters
OK
Name ID State Created Workers Datacenter
mycluster3 dc74ec9f2d9242ee824a2af3da894d31 normal 2017-09-09T15:11:17+0000 3 dal12
今回作成したクラスタの詳細を確認しておきます。 Ingress subdomein と Ingress secret は、後ほど利用しますのでメモしておきます。
$ bx cs cluster-get mycluster3
Retrieving cluster mycluster3...
OK
Name: mycluster3
ID: dc74ec9f2d9242ee824a2af3da894d31
State: normal
Created: 2017-09-09T15:11:17+0000
Datacenter: dal12
Master URL: https://169.47.70.10:21530
Ingress subdomain: mycluster3.us-south.containers.mybluemix.net
Ingress secret: mycluster3
Workers: 3
Log Space: 7e209692-4e0c-40b2-a7a6-6abb874dba8d
Addons
Name Enabled
customer-storage-pod true
storage-watcher-pod true
basic-ingress true
初期の頃に、k8sのクラスタを構築した場合、 Addonsが動作していない事がありました。 これらが Addons Enabked が false になっていた場合、関連する機能が働きません。 もし、false があれば、チケットに問い合わせるか、または、削除して再作成するのが良いです。
以上で、クラスタの準備ができましたので、次にアプリの開発へ触れていきます。
Node.js アプリの開発
この記事では、コンテナでアプリを開発する事が主眼ではないので、最も簡単と思う方法で、簡単なアプリを作ります。
開発用のディレクトリを作成して、そこへ移動します。
mkdir dev_app
cd dev_app
Node.js の Expressフレームワークのコード生成ツールをインストールします。
$ npm install express-generator -g
次のコマンドで、テンプレートアプリを作成します。
$ express --view=pug myapp
ローカル環境でのテストの為に、生成したアプリのディレクトリへ移動します。
$ cd myapp
アプリが実行に必要なモジュールをインストールします。
$ npm install
デバッグモードで起動してテストします。
$ DEBUG=myapp:* npm start
別のターミナル・ウィンドを立ち上げて、もう一つ、vagrant ssh でログインして、curlコマンドでアクセスして、下記のHTMLのレスポンスがあれば、アプリの準備完了です。
$ curl http://localhost:3000/
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>
アプリのコンテナ化
作成したアプリのディレクトリから、コンテナイメージを作成します。 この為に、ディレクトリを一つ上がって、dev_appに移動します。
$ cd ..
コンテナを作成する為に、Dockerfileの名前で、カレントディレクトリに次の内容のファイルを作成します。
FROM ubuntu
RUN apt-get update
RUN apt-get install curl -y
RUN curl -sL https://deb.nodesource.com/setup_6.x | /bin/bash -
RUN apt-get install nodejs -y
ADD ./myapp /app
ENV NODE_ENV development
EXPOSE 3000
CMD ["node", "/app/bin/www"]
Dockerfileの内容について、以下に列挙します。 詳しい説明説明は、Docker referenceにあります。
- FROM ベースになるOSを指定するもので、https://hub.docker.com/_/ubuntu/ のlatest バージョンになります
- RUN 初回だけ実行するコマンドです。 nodeの環境などをインストールしています
- ADD アプリのディレクトリ ./myapp を コンテナのファイルシステム上の/app へコピーします
- ENV 環境変数設定
- EXPOSE 外部へ開くポート番号
- CMD コンテナ起動時に毎回実行するコマンド
次のコマンドで、アプリケーションのディレクトリから、Dockerコンテナを作成します。
$ docker build -t express-app .
ビルドされたコンテナは、Dockerのローカル・リポジトリに登録されます。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
express-app latest 94ffae43dcae 11 seconds ago 265MB
ローカル・リポジトリのコンテナを起動します。 -p は外部に公開するポートで、外部公開ポート番号:コンテナのEXPOSEで指定したポート番号となります。 例えば、-p 4000:3000 とすれば、EXPOSE で指定したポート番号を 4000番に対応させて外部からアクセスできる様になります。 また -t は TTYからアクセスできる様にします。 もし、-t を忘れると Ctrl + Cで止める事ができなくなります。
$ docker run -t -p 3000:3000 express-app
上記と別のウィンドから、curl コマンドでアクセスして、コンテナの動作を確認します。 以下のHTMLが表示されていれば、コンテナの動作は予定通りとなります。
$ curl http://localhost:3000/
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>
レジストリへの登録
k8sで利用できる様にする為に、ローカルリポジトリに保存されている コンテナのイメージを Bluemix の レジストリサービスへ転送します。
この中で、registry.ng.bluemix.net までがリポジトリとなり、その後の takara の部分が、ネームスペースと呼ばれる利用者が作成する部分になります。 このネームスペースを作成する為には、bx cr namespace-add を実行します。 詳しくは、bx cr --help を参照ください。
$ docker tag express-app registry.ng.bluemix.net/takara/express-app
ローカルから、Bluemixへプッシュします。
$ docker push registry.ng.bluemix.net/takara/express-app
登録したイメージが存在するか確認します。 もし無ければ手順やネームスペースが存在しているかを確認します。
$ bx cr images
これで k8s のクラスタ環境へアプリを登録する準備ができました。 次に、k8s へデプロイしていきます。
k8sでの実行 (シングルでテスト)
まずは、コマンドラインだけで出来る方法で、k8sでコンテナを実行します。
$ kubectl run express-app --image=registry.ng.bluemix.net/takara/express-app
k8sのポッド、デプロイメント、サービス、レプリカセットなどの情報を確認します。 次の例では、AVAILABLE が 1となっており、動作している事が読み取れます。
$ kubectl get all
NAME READY STATUS RESTARTS AGE
po/express-app-2177018659-tqhzf 1/1 Running 0 38s
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/kubernetes 10.10.10.1 <none> 443/TCP 1h
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/express-app 1 1 1 1 38s
NAME DESIRED CURRENT READY AGE
rs/express-app-2177018659 1 1 1 38s
コンテナを外部へテスト公開 (分散クラスタ)
これだけでは、外部へ公開されないので、--type NodePortで公開します。 この方法は、クラスタとして公開するのではなく、コンテナが実行されているワーカー(ノード)のポートで、公開するというものです。
$ kubectl expose deployment express-app --port 3000 --type NodePort
expose を実行した後に、再び状態を確認すると svc/express-app が増えており、EXTERNAL-IPが となり、 PORT(S)が、3000:31980/TCP となっています。 これは、ワーカー(ノード)の Public IP で参照可能であり、 TCP3000番を31980で公開している事が読み取れます。
$ kubectl get all
NAME READY STATUS RESTARTS AGE
po/express-app-2177018659-tqhzf 1/1 Running 0 2m
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/express-app 10.10.10.217 <nodes> 3000:31980/TCP 6s
svc/kubernetes 10.10.10.1 <none> 443/TCP 1h
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/express-app 1 1 1 1 2m
NAME DESIRED CURRENT READY AGE
rs/express-app-2177018659 1 1 1 2m
そこで、ワーカーのPublic IPアドレスを次のコマンドで確認します。
vagrant@vagrant-ubuntu-trusty-64:~/k8s$ bx cs workers mycluster3
curl コマンドで、public IP と ポート番号を指定して、HTMLが表示されると、シングルでの確認は完了です。 この例では、ワーカーノードが3つあり、その何れかで稼働しています。 応答がなければ、IPアドレスを変えて試してみましょう。
$ curl http://xxx.xxx.211.236:31980/
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>
デプロイしたコンテナやサービスの削除
コンテナ単体の動作確認が完了したので、削除しておきます。 削除には以下2つが、必要です。
$ kubectl delete deployment express-app
$ kubectl delete service express-app
HTTPSとドメイン名でアクセスできる様にデプロイ
アプリを3つのワーカ(ノード)に展開して、暗号化可能なロードバランサーを実行するには、次の様にYAMLファイルを作成して設定を適用します。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: express-app
spec:
replicas: 3
template:
metadata:
labels:
app: express-app
spec:
containers:
- name: express-container
image: registry.ng.bluemix.net/takara/express-app
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: express-svc
spec:
type: NodePort
selector:
app: express-app
ports:
- protocol: TCP
port: 3000
nodePort: 31514
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: express-ingress
spec:
tls:
- hosts:
- mycluster3.us-south.containers.mybluemix.net
secretName: mycluster3
rules:
- host: mycluster3.us-south.containers.mybluemix.net
http:
paths:
- path: /
backend:
serviceName: express-svc
servicePort: 3000
最初に構成した3ノードのk8sクラスタに対して、このYAMLファイルで構築する構成を図にすると、次の様な内容になります。
このYAMLファイルの説明は、TLS 終端ありで IBM 提供ドメインを使用するを参照してください。
kubectl、コマンドで前述のYAMLファイルを適用します。
$ kubectl create -f publish.yml
deployment "express-app" created
service "express-svc" created
ingress "express-ingress" created
問題が無いか確認します。
$ kubectl get all
NAME READY STATUS RESTARTS AGE
po/express-app-1882574706-pfhjv 1/1 Running 0 8s
po/express-app-1882574706-pqmf2 1/1 Running 0 8s
po/express-app-1882574706-xlgr4 1/1 Running 0 8s
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/express-svc 10.10.10.57 <nodes> 3000:31514/TCP 7s
svc/kubernetes 10.10.10.1 <none> 443/TCP 1h
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/express-app 3 3 3 3 8s
NAME DESIRED CURRENT READY AGE
rs/express-app-1882574706 3 3 3 8s
ingressの動作を確認してみます。 ADDRESSが表示されないですが、Bluemixでは、これで正常な動作です。
$ kubectl get ing
NAME HOSTS ADDRESS PORTS AGE
express-ingress mycluster3.us-south.containers.mybluemix.net 80, 443 1d
それでは、https://mycluster3.us-south.containers.mybluemix.net/ でアクセスした結果が、次のスクリーン・ショットになります。
HTTPSとドメイン名でアクセスできる事がわかります。
トラブル・シューティング
今年の4月初めにオープンしたk8sの環境ですが、9月に入って、安定的に利用できる様になった様に思います。 トラブルの中で、とても困ったケースについて、症状と対策を記載しておきます。
Ingress のドメイン名でアクセスしても、タイムアウトになる
暗号化と負荷分散を受け持つ Ingress を設定しているのですが、
$ kubectl get ing
NAME HOSTS ADDRESS PORTS AGE
express-ingress mycluster-241320.us-south.containers.mybluemix.net 80, 443 15s
curl コマンドで、アクセスしても、応答がありません。
$ curl https://mycluster-241320.us-south.containers.mybluemix.net/
curl: (7) Failed to connect to mycluster-241320.us-south.containers.mybluemix.net port 443: Operation timed out
k8sの中を調べても、異常が見つかりません。
$ kubectl get all
NAME READY STATUS RESTARTS AGE
po/express-app-1065668149-14x76 1/1 Running 0 21s
po/express-app-1065668149-pzxjq 1/1 Running 0 21s
po/express-app-1065668149-sccx3 1/1 Running 0 21s
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/express-lb 10.10.10.216 169.48.212.10 80:31520/TCP 20s
svc/express-svc 10.10.10.86 <nodes> 3000:31514/TCP 20s
svc/kubernetes 10.10.10.1 <none> 443/TCP 16d
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/express-app 3 3 3 3 21s
NAME DESIRED CURRENT READY AGE
rs/express-app-1065668149 3 3 3 21s
Bluemix CLIで クラスタ状態を確認すると、Addonsの部分の Enabled が全て falseになっていました。
kubectl で操作できない為、本クラスタを破棄して、再作成した処、正常に動作する様になりました。
$ bx cs cluster-get mycluster
Retrieving cluster mycluster...
OK
Name: mycluster
ID: 852830a347cc4280b8985489fb883cbf
State: normal
Created: 2017-08-24T06:46:39+0000
Datacenter: dal12
Master URL: https://169.47.70.10:25428
Ingress subdomain: mycluster-241320.us-south.containers.mybluemix.net
Ingress secret: mycluster-241320
Workers: 3
Log Space: db39e8a1-5cb6-4fcc-83d2-3e8f0c8c259a
Addons
Name Enabled
storage-watcher-pod false
basic-ingress false
customer-storage-pod false
参考資料
(1) GitHub bluemix-dev https://github.com/takara9/bluemix-dev
(2) Ingress を使用してインターネットにアプリを公開する https://console.bluemix.net/docs/containers/cs_planning.html#cs_ingress
(3) IBM Bluemix Container Service での Kubernetes クラスターとデプロイメントのセキュリティー設定 https://console.bluemix.net/docs/containers/cs_security.html#cs_security_network_policies
(4) TLS 終端ありで IBM 提供ドメインを使用する https://console.bluemix.net/docs/containers/cs_apps.html#cs_apps_public_ingress
(5) Kubernetes and IBM Bluemix: How to deploy, manage, and secure your container-based workloads – Part 3 https://www.ibm.com/blogs/bluemix/2017/06/kubernetes-and-bluemix-container-based-workloads-part3/