docker
Bluemix
kubernetes
ibmcloud
IBM-Cloud

【IBM Cloud k8s検証メモ】アプリをHTTPSとドメイン名でアクセスする

More than 1 year has passed since last update.

今回の目標は、Cloud Foundry アプリと同様に、Bluemix k8sでロードバランサーによる負荷分散された高可用性環境にコンテナを配置して、Bluemixで自動付与されたドメイン名へHTTPSでアクセスできる様にすることです。

スクリーンショット 2017-09-11 10.01.59.png

前提条件

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の名前で、カレントディレクトリに次の内容のファイルを作成します。

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ファイルを作成して設定を適用します。

publish.yml
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ファイルで構築する構成を図にすると、次の様な内容になります。

スクリーンショット 2017-09-11 15.46.11.png

この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とドメイン名でアクセスできる事がわかります。

スクリーンショット 2017-09-10 1.48.52.png

トラブル・シューティング

今年の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/