Data Technology Lab Guide

https://travis-ci.org/NetAppJpTechTeam/DataTechnologyLab.svg?branch=master

はじめに

目的

  • DXを支える根幹技術(3rd Platform)とNetApp製品を組み合わせることによる価値を触りながら体験
  • 特定のシナリオに沿った関連技術の組み合わせ検証
  • Labを通じての技術者コミュニティの創出

連絡方法

連絡先は以下を準備しています。

Contents

Level 0: 環境の確認・基本操作

目的・ゴール: ラボを実施する環境の確認

本ラボではkubernetesクラスタへの接続確認と稼働確認を行うことが目的です。

ガイドの中では以下を確認しています。

  • ラボを実施する環境の構成理解
  • 環境への接続確認
  • kubernetesの基本操作を確認

流れ

  1. ユーザIDの確認
  2. 環境へログイン
  3. 基本コマンド確認、k8s へアプリケーションデプロイ

kubernetes環境へのログイン

各自配布されている接続先情報にログイン出来るかを確認してください。

kubernetesにデプロイ

kubernetes基本操作

必要となるコマンドラインツールがインストールされていることを確認します。

$ kubectl version

Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.6", GitCommit:"9f8ebd171479bec0ada837d7ee641dec2f8c6dd1", GitTreeState:"clean", BuildDate:"2018-03-21T15:21:50Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.6", GitCommit:"9f8ebd171479bec0ada837d7ee641dec2f8c6dd1", GitTreeState:"clean", BuildDate:"2018-03-21T15:13:31Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}

次にクラスタを形成するノードを確認します。

$ kubectl get nodes

NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    6d        v1.9.6
node0     Ready     <none>    6d        v1.9.6
node1     Ready     <none>    6d        v1.9.6
node2     Ready     <none>    6d        v1.9.6
デプロイメント

kubernetesクラスタに作成したコンテナアプリケーションをデプロイするためには 「Deployment」を作成します。 kubectlを使用して、アプリケーションをデプロイします。

以下では kubectl run を実行すると「Deployment」が作成されます。

$ kubectl run 任意のデプロイメント名 --image=nginx --port=80

deployment "nginxweb" created

デプロイが完了したら以下のコマンドで状況を確認します。

$ kubectl get deployments

NAME                                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginxweb                              1         1         1            1           53s

デプロイしたアプリケーションのサービスを確認します。 まだこの状態ではデプロイしたアプリケーションのサービスは存在しない状況です。

$ kubectl get services

NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   8s
外部向けに公開

外部向けにサービスを公開します。 公開後、再度サービスを確認します。

$ kubectl expose deployment/上記のデプロイメント名 --type="NodePort" --port 80

service "nginxweb" exposed

kubectl expose コマンドで外部へ公開しました。

サービス一覧から公開されたポートを確認します。

$ kubectl get services

NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        5d
nginxweb     NodePort    10.103.136.206   <none>        80:30606/TCP   1m

PORT 列を確認します。上の実行例でいうと「30606」ポートの部分を確認します。

--type="NodePort" を指定すると各ノード上にアプリケーションにアクセスするポート(標準で30000–32767)を作成します。 ノードにアクセスしポッドが動いていれば、そのままアクセスします。 ノードにポッドがなければ適切なノード転送される仕組みを持っています。 そのためマスターノードにアクセスすればk8sが適切に転送するという動作をします。

ホストのIPを確認します。

$ ifconfig -a | grep 192.168.*

  inet addr:192.168.10.10  Bcast:192.168.10.255  Mask:255.255.255.0

上記の情報を元にIPを生成してアクセスします。

  • http://確認したIP:確認したポート番号/

アクセス時に以下の画面が表示されれば稼働確認完了です。

_images/nginx.png

状態を確認します。

$ kubectl describe deployment nginxweb

Name:                   nginxweb
Namespace:              default
CreationTimestamp:      Tue, 20 Mar 2018 13:44:08 +0900
Labels:                 run=nginxweb
Annotations:            deployment.kubernetes.io/revision=1
Selector:               run=nginxweb
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:
  Labels:  run=nginxweb
  Containers:
   nginxweb:
    Image:        nginx
    Port:         80/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginxweb-78547ccd78 (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  15m   deployment-controller  Scaled up replica set nginxweb-78547ccd78 to 1

Replicas の項目で 1 available となっていればデプロイメント成功です。

問題発生時のログの確認方法

デプロイに失敗するようであれば以下のコマンドで状態を確認します。

ポッドの状態を確認するコマンド

$ kubectl logs ポッド名

デプロイメントの状態を確認するコマンド

$ kubectl describe deployments デプロイメント名

他にも以下のようなコマンドで状態を確認することができます。 デプロイ時のYAMLファイル単位や、定義しているラベル単位でも情報を確認できます。

$ kubectl describe -f YAML定義ファイル
$ kubectl describe -l ラベル名

よく使うコマンドや問題発生時の確認方法については次のページにまとめました。 今後のラボでうまくいかない場合いはぜひ参考にしてください。

コマンドリファレンス

クリーンアップ

コマンドラインの操作は完了です。 今までデプロイしたアプリケーションを削除します。

$ kubectl delete deployments デプロイメント名
$ kubectl delete services サービス名

まとめ

このラボではこの先のラボを行うための基本となる操作及び環境の確認を実施しました。

この先は各自ガイドを見ながら進めてください。

ここまでで Level0 は終了です。

Level 1: アプリケーションをコンテナ化する

目的・ゴール: アプリケーションをコンテナ化する

今回はコンテナに適したアーキテクチャへ変更するまえの段階として、 オンプレミスで仮想マシンで動いているアプリケーションについてコンテナ化をしていきます。

コンテナ技術のDockerを使うことでクラウド、オンプレミス、PCなどどのような環境でもアプリケーションを稼働させることができます。

具体的にはコンテナイメージをpullし、実行します。 その結果、アプリケーションがコンテナとして起動します。

このレベルではこのあとのラボで使用するKubernetes上で稼働させるアプリケーションのコンテナイメージを作成するのがの目標です。

流れ

  1. Dockerfileを作成する。
  2. ビルドを行いDockerイメージを作成
  3. 作成したDockerイメージをDocker Registryに登録
  4. どこからでもpull可能に(デプロイが可能に)

コンテナ化の準備

本ラボでは以下のミドルウェアやスタックを使ったアプリケーションを想定しています。 基本的にはアプリケーションをコンテナ化する際にはDockerHubで作成済みのイメージを使用することで効率よくコンテナ化することができます。

Web/AP レイヤー

  • nginx
  • apache
  • tomcat

Databaseレイヤー

  • mySQL
  • Postgress
  • Oracle
  • MongoDB

コンテナイメージの作成

このステップはアプリケーションを持ち込みの場合や複雑なスタックをコンテナ化する際に行うステップです。
選択したアプリケーションによっては不要なステップになるのでやるべきかどうかを確認してください。

その場合、 アプリケーションのマニフェストファイルを作成してデプロイ からはじめてください。

想定するアプリケーションのコンテナイメージを作成します。

Dockerfile のリファレンス Dockerfile Reference ファイル

留意点としては以下の通りです。

  • アプリケーションの配置をDockerfile内に配置

  • 基本となるコンテナイメージについてはDockerHubで探してベースイメージとする

  • 静的な構成となっていないか(IPパスワードのべた書きなど)

    • 環境変数で設定出来るよう設計する。のちほどk8sのSecretなどでパスワードを保存
  • 冪等性はコンテナイメージ側で対応する。責任範囲を明確にしてイメージを作成

  • ステートフルなものについてはコンテナに適したものにする

ヒント

記述例を提示します。このままビルドしてもイメージは作成されませんのであくまで記述例としてみてください。 どうしても進まない場合は サンプル: Dockerfile記述例 をクリックしてください。

コンテナイメージのビルド

作成した Dockerfileをビルドしてイメージを作成します。

バージョニングを意識してコンテナイメージを作成します、コンテナイメージに明示的にバージョンを指定します。

$ docker build -t 生成するコンテナイメージ名:タグ名 Dockerファイルのパス

Dockerイメージの生成方法は複数の手法があります。 例えば、普通のOSイメージを起動して、ログインしパッケージなどのインストールを行っていく手法があります。 メリットとしてはオペレーションで作成したものをイメージとして登録できるため、Dockerfileを作成しなくても良いといメリットがある一方で、 コンテナイメージの作成方法が不透明となる可能性もあります。

イメージレポジトリに登録

今回はイメージレポジトリにDockerHubを使用します。

DockerHub へログイン

DockerHubにアカウントがあることが前提です。

$ docker login

  ユーザ名、パスワードを入力

$ docker image push アカウント名/コンテナイメージ名:タグ名

アプリケーションのマニフェストファイルを作成してデプロイ

Level 0: 環境の確認・基本操作 ではコマンドラインで作成してきましたがYAMLファイルで1サービスをまとめてデプロイ出来るようにします。

ファイルのセクション構成としては以下の通りです。

  • Service
  • PersistentVolumeClaim
  • Deployment

サンプルファイルを準備しましたのでそれぞれの項目の意味を考え作成してみましょう。

(https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/ を参考としています。)

アプリケーションをデプロイするマニフェストファイルの例 deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: サービス名
  labels:
    app: 任意のラベル名
spec:
  ports:
    - port: ポート番号
  selector:
    app: アプリケーション名
    tier: ティア名
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: デプロイメント名
  labels:
    app: アプリケーション名
spec:
  selector:
    matchLabels:
      app: アプリケーション名
      tier: ティア名
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: アプリケーション名
        tier: ティア名
    spec:
      containers:
      - image: イメージ名:タグ
        name: アプリケーション名
        env:
        - name: PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-pass
              key: password
        ports:
        - containerPort: ポート番号
          name: アプリケーション名
kubectlの操作を容易にする

kubectlのオペレーションの簡易化のためlabelをつけることをおすすめします。

kubectl get pods -l app=nginx などのようにlabelがついているPod一覧を取得といったことが簡単にできます。 ほかにも以下の様なことが可能となります。

  • kubectl delete deployment -l app=app_label
  • kubectl delete service -l app=app_label
  • kubectl delete pvc -l app=wordpress
kubectlを使ってアプリケーションをデプロイ

以下のコマンドを実行してデプロイしましょう。

$ kubectl create -f deployment.yaml

アプリケーションの稼働確認

デプロイしたアプリケーションにアクセスし正常稼働しているか確認します。

アクセスするIPについてはサービスを取得して確認します。

$ kubectl get svc

結果として以下のような出力が得られます。

今回はServiceのtypeをNodePortで指定しているため、PORT(S)の」:」で区切られた右側のポート(以下の例だと32048)にアクセスしてみましょう。

NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes        ClusterIP   10.96.0.1      <none>        443/TCP        6d
wordpress         NodePort    10.98.247.58   <none>        80:32048/TCP   2h
wordpress-mysql   ClusterIP   None           <none>        3306/TCP       2h

注釈

kubectl引数の省略系について

今回はServiceの確認をする際に svc という省略形でコマンドを実行しました。 他のオブジェクトも同様に省略形があります。コマンド入力を省力化したい場合は省略形も使ってみましょう。

kubectl --helpkubectl XX --help コマンドで確認できます。

まとめ

kubectlやYAMLで記載するマニフェストファイルを使ってk8sへのデプロイが体感できたかと思います。 実運用になるとこのYAMLをたくさん書くことは負荷になることもあるかもしれません.

その解決のためにパッケージマネージャーHelm 等を使ってデプロイすることが多いかと思います。 このラボでは仕組みを理解していただき、応用出来ることを目的としています。

ここまでで Level1 は終了です。

Level 2: ステートフルコンテナの実現

目的・ゴール: アプリケーションのデータ永続化を実現

アプリケーションは永続化領域がないとデータの保存ができません。 KubernetesではStatic provisioningとDynamic provisioningの2つの永続化の手法があります。

このレベルではDynamic provisioningを実現するためDynamic provisonerであるTridentをインストールし、 マニフェストファイルを作成しデータの永続化をすることが目標です。

流れ

  1. Dynamic storage provisioningを実現(Tridentのインストール)

  2. StorageClassの作成

  3. PVCをkubernetesマニフェストファイルに追加

    1. 作成したStorageClassを使用する
    2. PVCをkubernetesにリクエストした時点で動的にストレージがプロビジョニングされる
  4. アプリケーションを稼働させて永続化ができていることを確認

コンテナでの永続データのカテゴライズ

コンテナ化されたアプリケーション、環境での永続データは 以下のように分類して考え必要な物をリストアップしました。

  • データベースのデータファイル、ログファイル
  • 各サーバのログファイル
  • 設定ファイル
  • 共有ファイル

Dynamic provisioning

ステートフルコンテナを実現する上でストレージは重要なコンポーネントになります。

Dynamic volume provisiong はオンデマンドにストレージをプロビジョニングするためのものです。

Static provisioning、Dynamic provisioning それぞれを比較します。

Static provisioningの場合、クラスタの管理者がストレージをプロビジョニングして、PersitentVolumeオブジェクトを作成しkubernetesに公開する必要があります。

Dynamic provisioningの場合、Static provisioningで手動で行っていたステップを自動化し、管理者がおこなっていたストレージの事前のプロビジョニング作業をなくすことができます。

StorageClassオブジェクトで指定したプロビジョナを使用し、動的にストレージリソースをプロビジョニングすることができます。

StorageClassには様々なパラメータを指定することができアプリケーションに適したストレージカタログ、プロファイルを作成することができ、物理的なストレージを抽象化するレイヤとなります。

ネットアップはDynamic provisioningを実現するためのNetApp Tridentというprovisionerを提供しています。

このレベルではTridentでDynamic provisioningを行い、アプリケーションのデータ永続化を実現します。

NetApp Tridentのインストール

Dynamic storage provisioningを実現するためNetApp Tridentを導入します。 TridentはPodとしてデプロイされ通常のアプリケーションと同様に稼働します。

Tridentインストール事前準備

Trident のインストールでk8sクラスタの管理者権限が必要になります。

$ kubectl auth can-i '*' '*' --all-namespaces

バックエンドに登録するマネジメントIPにk8sクラスタのコンテナから疎通が取れるかを確認します。

$ kubectl run -i --tty ping --image=busybox --restart=Never --rm --  ping [ipアドレス]

Tridentインストール

バイナリをダウンロードしてインストールします。 バックエンドストレージのための setup/backend.json を編集します。以下はサンプルとなります。

$ wget https://github.com/NetApp/trident/releases/download/v18.01.0/trident-installer-18.01.0.tar.gz
$ tar xzf trident*.tar.gz && cd trident-installer
$ cp sample-input/backend-ontap-nas.json setup/backend.json
backend.jsonの設定パラメータ
パラメータ名 説明 設定内容
managementLIF ONTAPのクラスタ管理LIFまたはSVM管理LIFを設定 192.168.XX.200
dataLIF データ通信LIF 192.168.XX.200
svm tridentから使用するSVM svmXX
username/password クラスタ管理者またはSVM管理者のクレデンシャル 今回SVM管理者を設定: vsadmin/netapp123

「XX」はユーザ環境番号になります。

編集後は以下の通りとなります。 疎通が取れないIPを設定するとtridentデプロイが失敗します。

$ cat setup/backend.json

{
    "version": 1,
    "storageDriverName": "ontap-nas",
    "managementLIF": "192.168.XX.200",
    "dataLIF": "192.168.XX.200",
    "svm": "svmXX",
    "username": "vsadmin",
    "password": "netapp123"
}

$ ./install_trident.sh -n trident

インストールの進捗を確認します。

$ kubectl get pod -n trident -aw

NAME                READY     STATUS    RESTARTS   AGE
trident-ephemeral   1/1       Running   0          58s

``-aw``オプションをつけることでPodの動きに変化があれば自動的にリロードしてくれます。

上記の状態で止まってしまう場合は、 trident-installer/ 配下に tridentctl というtridentのコマンドラインユーティリティが同梱されています。 このツールを使って状況を確認します。

tridentctlはパスの通った場所に配置します。

$ sudo cp tridentctl /usr/local/bin

以下のようにtridentに関するログをまとめて確認することが出来るようになります。

$ tridentctl -n trident logs

time="2018-02-15T03:32:35Z" level=error msg="API invocation failed. Post https://10.0.1.146/servlets/netapp.servlets.admin.XMLrequest_filer: dial tcp 10.0.1.146:443: getsockopt: connection timed out"
time="2018-02-15T03:32:35Z" level=error msg="Problem initializing storage driver: 'ontap-nas' error: Error initializing ontap-nas driver. Could not determine Data ONTAP API version. Could not read ONTAPI version. Post https://10.0.1.146/servlets/netapp.servlets.admin.XMLrequest_filer: dial tcp 10.0.1.146:443: getsockopt: connection timed out" backend= handler=AddBackend
time="2018-02-15T03:32:35Z" level=info msg="API server REST call." duration=2m10.64501326s method=POST route=AddBackend uri=/trident/v1/backend

Tridentへバックエンドストレージの登録

インストールが完了したことを確認します。

$ tridentctl -n trident version

+----------------+----------------+
| SERVER VERSION | CLIENT VERSION |
+----------------+----------------+
| 18.01.0        | 18.01.0        |
+----------------+----------------+

バージョンが表示されていればインストール成功です。 作成した setup/backend.json を指定し作成します。

$ tridentctl -n trident create backend -f setup/backend.json

+-------------------------+----------------+--------+---------+
|          NAME           | STORAGE DRIVER | ONLINE | VOLUMES |
+-------------------------+----------------+--------+---------+
| ontapnas_192.168.10.200 | ontap-nas      | true   |       0 |
+-------------------------+----------------+--------+---------+

(Troubleshooting) Tridentをアンインストールする

trident-installer にアンインストール用のシェルスクリプトが入っています。 以下の用に -a オプションを付与して実行すると生成した管理用のetcdのデータなどすべてを削除した上でアンインストールします。

$ ./uninstall_trident.sh -n trident -a

インストール時にうまくいかずに試行錯誤した際には一度クリーンアップすることをおすすめします。

例えば、v18.01のTridentでは以下の項目をStorageClassを作成するときに設定できます。

  • 性能に関する属性: メディアのタイプ、プロビジョニングのタイプ(シン・シック)、IOPS
  • データ保護・管理に関する属性:スナップショット、クローニング、暗号化の有効・向こう
  • バックエンドのストレージプラットフォーム

全てのパラメータ設定については以下のURLに記載があります。

StorageClassの定義

StorageClassを定義して、ストレージのサービスカタログを作りましょう。

  • DB 用の高速領域: SSD を使ったストレージサービス
  • Web コンテンツ用のリポジトリ: HDDを使ったストレージサービス

ストレージ構成は以下の通りです。 今回、意識する必要があるところは異なるメディアタイプ(HDDとSSD)のアグリゲートを保有しているところです。

  • ONTAP 9.3

  • 各SVMにHDD, SSDのアグリゲートを割り当て済み

    • aggr1_01:SSDのアグリゲート
    • aggr2_01:HDDのアグリゲート

StorageClassの作成方法のサンプルは以下の通りです。

高速ストレージ用のマニフェストファイル例 StorageClassFastest.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ontap-gold
provisioner: netapp.io/trident
parameters:
  backendType: "ontap-nas"
  media: "ssd"
  provisioningType: "thin"
  snapshots: "true"

ストレージクラスを作成します。

$ kubectl create -f StorageClassFastest.yml

storageclass "ontap-gold" created

$ kubectl get sc

NAME         PROVISIONER         AGE
ontap-gold   netapp.io/trident   10s

Persistent Volume Claimの作成

アプリケーションで必要とされる永続化領域の定義をします。 PVCを作成時に独自の機能を有効化することができます。

reclaimPolicy によってポッドがなくなった際のデータの保管ポリシーの設定ができます。 他にもデータ保護、SnapShotの取得ポリシーなどを設定できます。

一覧については以下のURLに記載があります。

デプロイ用のマニフェストファイルににPVCを追加

Level1で作成したマニフェストファイルにPVCの項目を追加し、ダイナミックプロビジョニングで永続化出来るアプリケーションを定義します。

高速ストレージ用の定義ファイルの例 PVCFastest.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sample-pv-claim
  labels:
    app: アプリケーション名
  annotations:
    trident.netapp.io/reclaimPolicy: "Retain"
    trident.netapp.io/exportPolicy: "default"
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: ontap-gold

デプロイメント実施

アプリケーションから何かしらのデータを保存するようにします。

  • アプリケーションからデータを記録
  • シンプルにnginxのアクセスログファイルを永続化

アプリケーションの停止

永続化されていることを確認するため、一度アプリケーションを停止します。 可能であればアプリケーションのバージョンアップを行ってみましょう。

Deploymentで必ず1つのポッドは起動するような設定になっているため、 簡単に実施するためにはポッドを削除する手段がとれます。 DeploymentによってPodの起動数は管理されるため新たにポッドが起動します。

再デプロイメント

再起動したPodに対してボリュームがマウントされていることを確認することも可能です。 容易に行える操作としてはDeployment配下にあるPodを削除し、Deploymentによって起動し直させるといったやり方です。

  • アプリケーションであれば再度ログインし、保存したデータを確認します。
  • 通常運用のリリースに想定するオペレーションをして、外部ストレージにデータ永続化されていることを確認します。

動的にボリュームが作成されていることを確認します。

$ ssh vsadmin@192.168.20.20 vol show

Password:
Vserver   Volume       Aggregate    State      Type       Size  Available Used%
--------- ------------ ------------ ---------- ---- ---------- ---------- -----
tridentsvm root        aggr1        online     RW          1GB    972.2MB    5%
tridentsvm trident_trident aggr1    online     RW       1.86GB     1.77GB    5%
tridentsvm trident_trident_basic_f4048 aggr1 online RW     1GB    972.4MB    5%
3 entries were displayed.

Tridentの特徴的な機能: Fast Cloning

Tridentには特徴的な機能であるクローニングの機能が存在します。

**巨大なボリュームでも容量消費せずに超高速にデータをコピーする**クローニングテクノロジーがkubernetesから使用可能となります。

ユーザーが既存のボリュームを複製することによって新しいボリュームをプロビジョニングできる機能を提供しています。 PVCアノテーションである、trident.netapp.io/cloneFromPVC を介してクローン機能を利用できます。

クローニングのマニフェストファイルの例 pvccloning.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: basicclone
  annotations:
    trident.netapp.io/cloneFromPVC: database
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: ontap-gold
クローニング技術によって実現可能なこと

クローニング技術はシンプルですが非常に多く用途で使用することができます。 例としてあげられるのが以下の通りのことです。

  • プレビルド環境の高速展開
  • 本番環境に影響せずに大規模な並列テスト
  • 運用時のデータリストアの高速化、瞬時に論理障害を戻す

まとめ

アプリケーションに対して動的に永続化領域をプロビジョニングしデータの永続化を実現しました。

今回はStorageClassの作成からアプリケーションにPersistentVolumeを割り当てるところまでを一連の流れで実現しました。

本来であればそれぞれで役割がことなるため以下のような分担になるかと思います。

  • StorageClassの作成: インフラ・kubernetesクラスタの管理者
  • PersistentVolumeClaimの作成: 利用者

今後障害時の動作が気になると思いますが、 Level 4: 運用編 での検討事項とします。

ここまでで Level2 は終了です。

Level 3: CI/CDパイプラインを構築

目的・ゴール: コンテナ化したアプリケーションのCICDを実現する

アプリケーションをコンテナ化したら、常にリリース可能な状態、自動でデプロイメントを出来る仕組みをつくるのが迅速な開発をするために必要になります。

そのためのCI/CDパイプラインを作成するのがこのレベルの目標です。

_images/cicd_pipeline.png

本ラボでは Level1, Level2 で行ったオペレーションをベースにCI/CDパイプラインを構築します。

Gitにソースがコミットされたら自動でテスト・ビルドを実現するためのツール(Jenkins)をkubernetes上へデプロイ、及び外部公開をします。 そして、Jenkinsがデプロイできたら実際にアプリケーションの変更を行い自動でデプロイするところまでを目指します。

流れ

  1. Jenkins をインストールする
  2. Jenkins 内部でジョブを定義する。
  3. あるアクションをトリガーにビルド、テストを自動実行する。
  4. 自動でk8sクラスタにデプロイメントできるようにする。

CI/CDパイプラインの定義

このラボでのCI/CDパイプラインの定義は以下を想定しています。

  • テスト実行
  • アプリケーションビルド
  • コンテナイメージのビルド
  • レジストリへコンテナイメージのpush
  • k8sへアプリケーションデプロイ

Gitは共有で準備しています。

ここではJenkinsをkubernetes上にデプロイしてみましょう。 Git自体も併せてデプロイしてみたいということであればGitLabをデプロイすることをおすすめします。 GitLabを使えばコンテナのCI/CDパイプライン、構成管理、イメージレジストリを兼ねて使用することができます。

Jenkinsのデプロイ方法について

CI/CDパイプラインを実現するためのツールとしてJenkinsが非常に有名であることは周知の事実です。 このラボではJenkinsを使用しCI/CDを実現します。

まずは、各自Jenkinsをデプロイします。

方法としては3つ存在します。

  1. Helm Chartでデプロイする方法 (手軽にインストールしたい人向け)
  2. Level1,2と同じようにyamlファイルを作成し、デプロイする方法(仕組みをより深く知りたい人向け)
  3. Kubernetes用にCI/CDを提供するJenkins Xをデプロイする方法(新しい物を使いたい人向け)

今回は最初のHelmでデプロイするバージョンを記載しました。 好みのもの、挑戦したい内容に沿って選択してください。

オリジナルでyamlファイルを作成する場合は以下のサイトが参考になります。

Helmを使ってJenkinsをデプロイ

Helmの初期化

Helmを使用する事前の設定をします。 Helmの初期化、RBACの設定を実施します。

$ helm init
$ kubectl create clusterrolebinding add-on-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default
Helmの基本

基本的なHelmの使い方は以下の通りです。

$ helm install stable/helm-chart名
Helmチャートのインストール・Jenkinsのカスタマイズ

今回はJenkinsを導入するにあたり環境に併せてカスタマイズを行います。 Helmは以下のURLに様々なものが公開されています。パラメータを与えることである程度カスタマイズし使用することができます。 Helm chartと同等のディレクトリにvalues.yamlというファイルが存在し、これを環境に併せて変更することでカスタマイズしデプロイできます。

今回のJenkinsのデプロイでは2つの公開方法が選択できます。

1つ目が今までのレベルと同様に ServicetypeNodePort として公開する方法です。これは今まで通りの疎通確認が可能です。

2つ目が Ingress を使った公開です。IngressをJenkinsのHelmチャートを使ってデプロイするためには「Master.Ingress.Annotations」、「Master.ServiceType」を変更しデプロイします。 また、このvalues.yamlでは永続化ストレージが定義されていないため、Level2で作成したStorageClassを使用し動的にプロビジョニングをするように変更しましょう。

簡易的にデプロイをためしてみたい方は1つ目の NodePort を使ったやり方を実施、新しい概念であるIngressを使った方法を実施したい方は2つ目を選択しましょう。

どちらの方法の場合も、以下のvalues.yamlをカスタマイズしてデプロイします。

Helm設定用のvalues.yaml
# Default values for jenkins.
# This is a YAML-formatted file.
# Declare name/value pairs to be passed into your templates.
# name: value

## Overrides for generated resource names
# See templates/_helpers.tpl
# nameOverride:
# fullnameOverride:

Master:
  Name: jenkins-master
  Image: "jenkins/jenkins"
  ImageTag: "lts"
  ImagePullPolicy: "Always"
# ImagePullSecret: jenkins
  Component: "jenkins-master"
  UseSecurity: true
  AdminUser: admin
  # AdminPassword: <defaults to random>
  Cpu: "200m"
  Memory: "256Mi"
  # Environment variables that get added to the init container (useful for e.g. http_proxy)
  # InitContainerEnv:
  #   - name: http_proxy
  #     value: "http://192.168.64.1:3128"
  # ContainerEnv:
  #   - name: http_proxy
  #     value: "http://192.168.64.1:3128"
  # Set min/max heap here if needed with:
  # JavaOpts: "-Xms512m -Xmx512m"
  # JenkinsOpts: ""
  # JenkinsUriPrefix: "/jenkins"
  # Set RunAsUser to 1000 to let Jenkins run as non-root user 'jenkins' which exists in 'jenkins/jenkins' docker image.
  # When setting RunAsUser to a different value than 0 also set FsGroup to the same value:
  # RunAsUser: <defaults to 0>
  # FsGroup: <will be omitted in deployment if RunAsUser is 0>
  ServicePort: 8080
  # For minikube, set this to NodePort, elsewhere use LoadBalancer
  # Use ClusterIP if your setup includes ingress controller
  ServiceType: LoadBalancer
  # Master Service annotations
  ServiceAnnotations: {}
  #   service.beta.kubernetes.io/aws-load-balancer-backend-protocol: https
  # Used to create Ingress record (should used with ServiceType: ClusterIP)
  # HostName: jenkins.cluster.local
  # NodePort: <to set explicitly, choose port between 30000-32767
  ContainerPort: 8080
  # Enable Kubernetes Liveness and Readiness Probes
  HealthProbes: true
  HealthProbesTimeout: 60
  SlaveListenerPort: 50000
  # Kubernetes service type for the JNLP slave service
  # SETTING THIS TO "LoadBalancer" IS A HUGE SECURITY RISK: https://github.com/kubernetes/charts/issues/1341
  SlaveListenerServiceType: ClusterIP
  SlaveListenerServiceAnnotations: {}
  LoadBalancerSourceRanges:
  - 0.0.0.0/0
  # Optionally assign a known public LB IP
  # LoadBalancerIP: 1.2.3.4
  # Optionally configure a JMX port
  # requires additional JavaOpts, ie
  # JavaOpts: >
  #   -Dcom.sun.management.jmxremote.port=4000
  #   -Dcom.sun.management.jmxremote.authenticate=false
  #   -Dcom.sun.management.jmxremote.ssl=false
  # JMXPort: 4000
  # List of plugins to be install during Jenkins master start
  InstallPlugins:
    - kubernetes:1.1
    - workflow-aggregator:2.5
    - workflow-job:2.15
    - credentials-binding:1.13
    - git:3.6.4
  # Used to approve a list of groovy functions in pipelines used the script-security plugin. Can be viewed under /scriptApproval
  # ScriptApproval:
  #   - "method groovy.json.JsonSlurperClassic parseText java.lang.String"
  #   - "new groovy.json.JsonSlurperClassic"
  # List of groovy init scripts to be executed during Jenkins master start
  InitScripts:
  #  - |
  #    print 'adding global pipeline libraries, register properties, bootstrap jobs...'
  # Kubernetes secret that contains a 'credentials.xml' for Jenkins
  # CredentialsXmlSecret: jenkins-credentials
  # Kubernetes secret that contains files to be put in the Jenkins 'secrets' directory,
  # useful to manage encryption keys used for credentials.xml for instance (such as
  # master.key and hudson.util.Secret)
  # SecretsFilesSecret: jenkins-secrets
  # Jenkins XML job configs to provision
  # Jobs: |-
  #   test: |-
  #     <<xml here>>
  CustomConfigMap: false
  # Node labels and tolerations for pod assignment
  # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
  # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature
  NodeSelector: {}
  Tolerations: {}

  Ingress:
    Annotations:
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"

    TLS:
    # - secretName: jenkins.cluster.local
    #   hosts:
    #     - jenkins.cluster.local

Agent:
  Enabled: true
  Image: jenkins/jnlp-slave
  ImageTag: 3.10-1
# ImagePullSecret: jenkins
  Component: "jenkins-slave"
  Privileged: false
  Cpu: "200m"
  Memory: "256Mi"
  # You may want to change this to true while testing a new image
  AlwaysPullImage: false
  # You can define the volumes that you want to mount for this container
  # Allowed types are: ConfigMap, EmptyDir, HostPath, Nfs, Pod, Secret
  # Configure the attributes as they appear in the corresponding Java class for that type
  # https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes
  volumes:
  # - type: Secret
  #   secretName: mysecret
  #   mountPath: /var/myapp/mysecret
  NodeSelector: {}
  # Key Value selectors. Ex:
  # jenkins-agent: v1

Persistence:
  Enabled: true
  ## A manually managed Persistent Volume and Claim
  ## Requires Persistence.Enabled: true
  ## If defined, PVC must be created manually before volume will be bound
  # ExistingClaim:

  ## jenkins data Persistent Volume Storage Class
  ## If defined, storageClassName: <storageClass>
  ## If set to "-", storageClassName: "", which disables dynamic provisioning
  ## If undefined (the default) or set to null, no storageClassName spec is
  ##   set, choosing the default provisioner.  (gp2 on AWS, standard on
  ##   GKE, AWS & OpenStack)
  ##
  # StorageClass: "-"

  Annotations: {}
  AccessMode: ReadWriteOnce
  Size: 8Gi
  volumes:
  #  - name: nothing
  #    emptyDir: {}
  mounts:
  #  - mountPath: /var/nothing
  #    name: nothing
  #    readOnly: true

NetworkPolicy:
  # Enable creation of NetworkPolicy resources.
  Enabled: false
  # For Kubernetes v1.4, v1.5 and v1.6, use 'extensions/v1beta1'
  # For Kubernetes v1.7, use 'networking.k8s.io/v1'
  ApiVersion: extensions/v1beta1

## Install Default RBAC roles and bindings
rbac:
  install: false
  serviceAccountName: default
  # RBAC api version (currently either v1beta1 or v1alpha1)
  apiVersion: v1beta1
  # Cluster role reference
  roleRef: cluster-admin

実行イメージとしては以下の通りです。

$ helm --namespace jenkins --name jenkins -f ./jenkins-values.yaml install stable/jenkins --debug
    LAST DEPLOYED: Tue Apr 24 12:47:12 2018
    NAMESPACE: jenkins
    STATUS: DEPLOYED

    RESOURCES:
    ==> v1/Secret
    NAME     TYPE    DATA  AGE
    jenkins  Opaque  2     8m

    ==> v1/ConfigMap
    NAME           DATA  AGE
    jenkins        3     8m
    jenkins-tests  1     8m

    ==> v1/PersistentVolumeClaim
    NAME     STATUS  VOLUME                 CAPACITY  ACCESS MODES  STORAGECLASS  AGE
    jenkins  Bound   jenkins-jenkins-2c478  8Gi       RWO           ontap-gold    8m

    ==> v1/ServiceAccount
    NAME     SECRETS  AGE
    jenkins  1        8m

    ==> v1/Service
    NAME           TYPE       CLUSTER-IP   EXTERNAL-IP  PORT(S)         AGE
    jenkins-agent  ClusterIP  10.98.21.68  <none>       50000/TCP       8m
    jenkins        NodePort   10.96.24.25  <none>       8080:31050/TCP  8m

    ==> v1beta1/Deployment
    NAME     DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
    jenkins  1        1        1           1          8m

    ==> v1beta1/Ingress
    NAME     HOSTS                              ADDRESS  PORTS  AGE
    jenkins  jenkins.user21.web.service.consul  80       8m

    ==> v1/Pod(related)
    NAME                      READY  STATUS   RESTARTS  AGE
    jenkins-578686f98d-6pbx9  1/1    Running  0         8m

    ==> v1beta1/ClusterRoleBinding
    NAME                  AGE
    jenkins-role-binding  8m


    NOTES:
    1. Get your 'admin' user password by running:
      printf $(kubectl get secret --namespace jenkins jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo

    2. Visit http://jenkins.user21.web.service.consul

    3. Login with the password from step 1 and the username: admin

    For more information on running Jenkins on Kubernetes, visit:
    https://cloud.google.com/solutions/jenkins-on-container-engine
    Configure the Kubernetes plugin in Jenkins to use the following Service Account name jenkins using the following steps:
      Create a Jenkins credential of type Kubernetes service account with service account name jenkins
      Under configure Jenkins -- Update the credentials config in the cloud section to use the service account credential you created in the step above.

「NOTES」欄に記載の通りadminパスワードを取得します。

$ printf $(kubectl get secret --namespace jenkins jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo

    sShJg2gig9

以上で、Jenkinsのデプロイが完了しました。

Helmが生成するマニフェストファイル

Helmを使いvalues.yamlを定義するとどのようなマニフェストファイルが生成されるかが予測しづらいこともあります。

その場合には --dry-run--debug を付与することでデプロイメントされるYAMLファイルが出力されます。

helm –namespace jenkins –name jenkins -f ./values.yaml install stable/jenkins –dry-run –debug

インストールが上手くいかない場合は?

values.yamlを試行錯誤しながら設定していくことになると思います。 一度デプロイメントしたHelmチャートは以下のコマンドで削除することができます。

$ helm del --purge チャート名

Helm以外でJenkinsをデプロイした場合

本セクションに記載してあることはオプションです。

必要に応じて実施してください。

外部にアプリケーションを公開する方法として Ingress があります。 Helmを使ってJenkinsをインストールした場合は自動でIngressが作成されます。 それ以外の手法を取った場合は、kubernetesクラスタ外のネットワークからアクセスできるようにIngressを作成してみましょう。

注意
Helm chart を使ってインストールした場合は自動でIngressが導入されています。
そのため、以下の手順はHelmで実施した人は不要です。

Ingressの導入についてはこちらに Ingressを導入する まとめました。

ServiceをDNSへ登録する

HelmでデプロイしたJenkinsにはIngress経由でアクセスします。 そのためホスト名を使用してアクセスします。

注釈

なぜそのような仕組みになっているかを知りたい方はJenkinsのHelmチャートをご確認ください。 https://github.com/kubernetes/charts/tree/master/stable/jenkins

今回は名前解決にConsulを使います。

登録用JSONは以下の通りです、TagsとNameでdnsに問い合わせる名前が決まります。 今回はドメインを service.consul を使用します。

このラボでは命名規則を定義します。

  • ID, Tags: アプリケーション識別子.環境番号
  • Name: web固定
  • Address: 各環境のマスタのIP

アプリケーションにアクセスする際に jenkins.user10.web.service.consul というFQDNでアクセスしたい場合は以下のjsonファイルを作成します。 ファイル名はwebservice.jsonとします。ポート番号はアプリケーションで使用しているものに変更してください。

{

  "ID": "jenkins.user10",
  "Name": "web",
  "Tags": [ "jenkins.user10" ],
  "Address": "192.168.XX.10",
  "Port": 80
}

ファイルを作成したら以下のコマンドで登録します。

$ curl -i -s --request PUT --data @webservice.json http://infra1:8500/v1/agent/service/register

HTTP/1.1 200 OK
Date: Wed, 11 Apr 2018 05:31:37 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8

登録が完了したら名前解決ができるか確認します。

$ nslookup jenkins.user10.web.service.consul

Jenkinsの設定をする

Gitリポジトリに変更があったら自動でテストを実行するジョブを定義します。 このテストは任意で作成してください。

ここでやりたいことは該当リポジトリにコミットがあり、リリースタグが付与された場合に自動でビルド・デプロイをする流れを作成することです。 そのためにはまずJenkinsでGitリポジトリに操作があった場合の動作を定義します。

定義出来る動作としては以下の単位が考えられます。 細かく設定することも可能です。運用に合わせた単位で設定します。

  • pull request 単位
  • release tag 単位
  • 定期実行

前述した以下の項目を盛り込みCI/CDパイプラインを作成しましょう。 以下のようなタスクを組み込んだパイプラインを作成します。シンプルなパイプラインからはじめ、必要に応じてステージを追加していきましょう。

  • テスト実行
  • アプリケーションビルド
  • コンテナイメージのビルド
  • レジストリへコンテナイメージのpush
  • アプリケーションデプロイ

上記のようなパイプラインを作成にはJenkins pipeline機能が活用できます。

アプリケーションの変更を検知してデプロイメント可能にする

CI/CDのパイプラインを作成したら実際にアプリケーションの変更をトリガーに(ソースコードの変更、Gitリポジトリへのpush等)k8sへアプリケーションをデプロイします。

ポリシーとして大きく2つに別れます、参考までに以下に記載いたします。

  • デプロイ可能な状態までにし、最後のデプロイメントは人が実施する(クリックするだけ)
  • デプロイメントまでを完全自動化する

実際にkubernetes環境へのデプロイができたかの確認とアプリケーションが稼働しているかを確認します。

Helm ChartでCI/CD

個別のアプリケーションデプロイメントからHelm Chartを使ったデプロイメントに変更します。

作成したコンテナをHelm Chartを使ってデプロイするようにします。

Helm Chartの開発ガイドは以下のURLを確認ください。

デプロイメントのさらなる進化

CI/CDプロセスを成熟させていくと常にリリース可能な状態となっていきます。 そのような状態になると本番環境へのデプロイを迅速にし、ダウンタイムを最小化するための方法が必要になってきます。 元々存在するプラクティスや考え方となりますがコンテナ技術、kubernetesのスケジューラー機能を使うことで今までの環境とくらべて実現がしやすくなっています。

Blue/Greenデプロイメント, Canary リリースというキーワードで紹介したいと思います。

Blue/Greenデプロイメント

従来のやり方では1つの環境にデプロイし何かあれば戻すという方法をほとんどのケースで採用していたかと思いますが、さらなる進化として常に戻せる環境を準備し迅速にロールバック 新バージョン、旧バージョンをデプロイしたままルータで切り替えるようになります。

様々な企業で行き着いている運用でもあるかと思いますが、2010年にBlueGreenデプロイメントという名称で説明しています。

実現方法、切り替えのタイミングなどあり、BlueGreenの実装の決定的なものはなく、1つのプラクティスとして存在しています。

2つの環境を準備し、どこかのタイミングで切り替えを行うためDBのマイグレーションの方法などを検討する必要はでてきます。

Canary

Canary リリースは BlueGreen デプロイメントと類似したデプロイメントになります。 Blue/Green デプロイメントはすぐに古いバージョンにもどせるように仕組みを整えたものですが、Canaryリリースは新しいバージョン、旧バージョンにアクセスする比率を決めてデプロイするプラクティスです。

こちらは2つの環境ではなく、1環境に複数バージョンのアプリケーションが存在することになります。そのためDBのデータをどのように取り扱うかは検討が必要となります。

まとめ

このラボではコンテナ化したアプリケーションのCI/CDパイプラインの構築に挑戦しました。 CI/CDパイプラインを作成するためのJenkins/GitLabをインストールするために必要なHelmの使い方、アプリケーションを外部に公開するためのkubernetesオブジェクトのIngressも併せて使えるようになりました。

ここまでで Level3 は終了です。

Level 4: 運用編

目的・ゴール: ラボを実施する環境の確認

本番運用になった場合に必要となるアプリケーションの可用性、インフラの可用性、非機能要件の実現について検討します。

Level3まででアプリケーションを迅速にリリースする仕組みを作成しました。ここからはマスタのHA化やバックアップをどうするかを検討します。

流れ

本レベルでは運用時に課題となり、解決が必要となる項目についてリストしました。 現在の環境に対して変更をして見てください位。ここでは実際に手をうごかしてもらってもいいですし、 チーム内でディスカッションの材料にしてください。

アプリケーションの可用性を向上させる

アプリケーションの可用性を挙げるためWorkload APIを使用する

すでに Deployment で使われているかもしれませんが、replica数などを定義できます。一般的なアプリケーションデプロイに使用します。

各ノードでコンテナを稼働させる DaemonSet があります。ログ収集用のデーモンやメトリクス収集などのユースケースがあります。

レプリカ生成時の順序制御、各ポッドにPVを割り当てることができる StatefulSet があります。主にクラスタ、分散環境におけるユースケースで使用するものです。

kubernetes上のオブジェクト名は以下の通りです。

  • ReplicaSet
  • DaemonSet
  • StatefulSet
ローリングアップデート

Deployment``Pod template``部分に変更があった場合に自動でアップデートできます。

$ kubectl set image deployment/DEPLOYMENT CONTAINER=IMAGE_NAME:TAG

``–record``オプションをつけるとアノテーションが付与されます。

参考: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

リリース後に問題発生、アプリケーションを戻す

アプリケーションは Deployment でリビジョン管理しています。

rollout history でリビジョンを確認できます。

$ kubectl rollout history deployment/デプロイメント名

deployments "nginx-deployment"
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

各リビジョンの詳細については --revision=N を付与することで詳細を確認できます。

$ kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" with revision #2
Pod Template:
  Labels:       app=nginx
        pod-template-hash=1520898311
  Containers:
   nginx:
    Image:      nginx:1.9.1
    Port:       80/TCP
    Environment:        <none>
    Mounts:     <none>
  Volumes:      <none>

アプリケーションは Deployment でリビジョン管理しており、ロールバック機能も提供しています。 rollout undo で直前のリビジョンに戻ります。``–to-revision``を指定することで任意のリビジョンに戻すことも可能です。

$ kubectl rollout undo deployment/nginx-deployment [--to-revision=N]

保存されるリビジョンは revisionHistoryLimit で定義できるため、運用に合わせた数にしましょう。

Helmを使った場合にも同様のことが実現可能です。

アプリケーション負荷に応じたスケールアウト・イン

Horizontal Pod Autoscaler を使用してアプリケーションの負荷に応じてスケールアウトすることができます。

事前定義としてアプリケーションの負荷情報をheapsterで収集しておく必要があります。 以下の例はすべてのポッドのCPU使用率の平均が50%を超えた場合にレプリカを最大10まで増やす動作をします。

$ kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10

上記の例では、CPU使用率をメトリクスとしていますが複数のメトリクスをしようしたり、カスタマイズすることも可能です。

参考: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/

インフラの可用性を向上させる

k8s Master の冗長化

API受付をするマスタ系のノードやetcdやkubernetesサービスの高可用性も担保しましょう。

また、障害設計をどの単位(DC単位、リージョン単位)で行うかも検討をしましょう。

ログの確認、デバッグ方法

標準のkubectlだとログがおいづらいときがあるため以下のツールの検討をします。

  • kubernetesホストの/var/log/containerにログは保管。(systemd系の場合)
  • sternなどのログ管理ツールを活用する
  • fluetdを使いログを集約する。
コンテナクラスタの監視

監視する対象として、メトリクス監視、サービス呼び出し、サービスメッシュなど分散環境になったことで従来型のアーキテクチャとは違った監視をする必要があります。 簡単にスケールアウトできる=監視対象が動的というような考え方をします。

また、分散環境では1つのアプリケーションでも複数のサービス呼び出しがおこなわれるため、どのようなサービス呼び出しが行われているかも確認できる必要があります。

  • (heapster|Prometheus) + Grafana + InfluxDB を使いコンテナクラスタの監視。
  • 分散環境に於けるサービスの呼び出しを可視化 Traces = Zipkin
  • ServiceGraph Graphbiz & Prometeus
  • ServiceMesh
バックアップはどうするか?

大きく以下のバックアップ対象が存在。

  • etcd のバックアップ戦略
  • コンテナ化されたアプリケーションの永続化データ
  • コンテナイメージ
セキュリティアップグレード

例えば、脆弱性があった場合の対処方法はどうすればよいか。

  • ノードごとにバージョンアップするため、ある程度の余力を見込んだ設計とする。
  • kubectl drain を使いノードで動いているPodを別ノードで起動、対象ノードをアップデートし、ポッドを戻す。
DRをどうするか?

アプリケーションのポータビリティはコンテナで実現。 別クラスタで作成されたPVはそのままは参照できないので以下の方法を検討する。

  • Cluster federation
  • CSI (Container Storage Interface)の既存ボリュームのインポートに対応をまつ
  • Heptio ark: https://github.com/heptio/ark + SnapMirror

このチャプターはドラフト段階です。

Level 5: Microservice化

目的・ゴール: Microserviceを支えるインフラの技術・テクノロジースタックを活用

Level4までで既存のアプリケーションをコンテナ化して本番運用可能かつ迅速にリリースできる仕組みを作りました。 このレベルではアプリケーションをコンポーネント単位に分割していった際に必要となるインフラの仕組みを適用します。

流れ

  1. Microservice化していくにあたって発生する課題
  2. 解決する技術の1つ 「Istio」

Microservice化していくにあたって発生する課題

マイクロサービス化が進むと1つのシステムを複数の細かいコンポーネントにわけ、独立したサービスとして迅速にデプロイできるようになり、

その反面、モノリスなアプリケーションでは問題とならなかったサービス間の接続、モニタリング、通信の制御、エンドツーエンドの認証などが課題として顕在化してきます。

マイクロサービスを実現するためには上記の課題を解決する必要があります。

解決する技術の1つ 「Istio」

ここでは上記の課題を解決するための」Istio」について紹介します。

モノリスなアプリケーションから分散型・コンポーネントに分割されたアプリケーションへコンポーネント分割際に発生する課題に対して有効です。

最初はモノリスなアプリケーションをそのままコンテナ化し徐々にコンポーネントに分けていく、そしてサービス間通信をうまくやるためのServiceMeshとして開発されました。

ポイントはアプリケーションに影響なくIstioを導入できます。

さらなる進化

  • マルチクラウド化
  • ハイブリッドクラウド化

References

コマンドリファレンス

kubectlの使い方・本家へのリンク

公式ガイドへのリンクです。 詳細や使い方等については以下ページを見ることをおすすめします。 このページではよく使うコマンドについてユースケースでまとめました。

デプロイメントの実施

kubectl create/apply/patch/replaceを使用します。

それぞれ便利な点・留意する点があります。

kubectl create デプロイメントの基本系、マニフェストファイルを指定してデプロイし、新規に行う場合に使用します。

$ kubectl create -f deployment.yaml

kubectl applyはcreate/replaceを包含できます。差分反映のアルゴリズムを理解して利用しましょう。 applyの動きとしてはすでにデプロイされていれば更新を行い、デプロイされていなければ新規作成の動きをします。

$ kubectl apply -f deployment.yaml

kubectl replace は稼働中のアプリケーションに対して動的に定義を反映する。

$ kubectl apply -f deployment.yaml

kubectl patch は稼働中のアプリケーションに対して、一部のフィールドを書き換える用途に使用。

状況確認

基本形としては kubectl get オブジェクト名kubectl describe オブジェクト名 になります。 以下は kubectl get ですが、getdescribe に変更することで詳細な情報が確認できるようになります。

よく使うものとしては以下の通りです。

$ kubectl get pod

NAME                               READY     STATUS    RESTARTS   AGE
wordpress-mysql-58cf8dc9f9-t2wnr   1/1       Running   0          2d
$ kubectl get svc

NAME              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes        ClusterIP   10.96.0.1    <none>        443/TCP    10d
wordpress-mysql   ClusterIP   None         <none>        3306/TCP   2d
$ kubectl get deployment

NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
wordpress-mysql   1         1         1            1           2d

ネームスペースを指定する場合は -n オプション、または --all-namespaces で全ネームスペースのオブジェクトを確認できます。

$ kubectl get all -n ネームスペース名

マニフェストファイルを使用している場合は get の引数に -f マニフェストファイル を指定すると関連するオブジェクトをすべて表示してくれます。

$ kubectl get -f deployment.yaml

現状のオブジェクトをすべて確認する場合はオブジェクトを指定する箇所に all を設定するとすべてのオブジェクトを確認できます。

$ kubectl get all [-n ネームスペース名]

すべてのネームスペースのすべてのオブジェクトを確認したい場合は以下のとおりです。

$ kubectl get all --all-namespaces

マニフェストファイルを使用したオブジェクトの確認もできます。

-f オプションを使用してデプロイ時に使用したマニフェストファイルを指定すると関連するオブジェクトをすべて表示します。

$ kubectl get -f wordpress-mysql-deploy.yaml
NAME                  TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
svc/wordpress-mysql   ClusterIP   None         <none>        3306/TCP   2d

NAME                 STATUS    VOLUME                         CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc/mysql-pv-claim   Bound     default-mysql-pv-claim-b5e95   20Gi       RWO            ontap-gold     2d

NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/wordpress-mysql   1         1         1            1           2d
問題の特定方法について

マニフェストを

kubectl getkubectl describe, kubectl logs を組み合わせて問題箇所を特定していきます。

よく使うコマンド
  • kubectl describe オブジェクト名
  • kubectl describe -f deployment.yaml
トラブルシュートの流れ
  1. 問題箇所の特定

    1. kubectl get -f deployment.yaml で予期しない動作をしている箇所を発見
    2. kubectl describe -f deployment.yaml
  2. うまく行っていない箇所が分かれば該当のPodを確認する

    1. kubectl logs pod ポッド名
    2. 3rd party製の stern というツールもあります。こちらは複数のPodに対して kubectl logs を実行する動きをします。非常に便利なものになります。
  3. 取得できた情報を元に対応実施

    1. マニフェストファイルの修正

このドキュメントは整備中です。

用語集

本ラボで出てくる単語集です。

おもにk8s関連の用語になります。 (version1.9時点)

kubernetes、ネットアップの用語集
用語 略称 分類 説明
Deployment deploy kubernetes アプリケーションをデプロイする時に使うもの。デプロイの管理やレプリカの管理を行う。
Service svc kubernetes アプリケーションをkubernetesクラスタ外に公開するときに使用する。
Ingress ing kubernetes アプリケーションをkubernetesクラスタ外に公開するときに使用する。Serviceとの違いはIngressは、Serviceの前段に配置されServiceへのアクセスルールを定義する。
NodePort 特になし kubernetes アプリケーションをkubernetesクラスタ外に公開するときに使用する。各k8sノードでポートを解放しアクセスできるようする。接続したノードにポッドがなければ適切なノードに転送してアクセス可能にする。
LoadBalancer 特になし kubernetes アプリケーションをkubernetesクラスタ外に公開するときに使用する。デプロイ時にロードバランサーサービスに自動登録します。クラウドサービス、オンプレミスでは一部のプラットフォームで対応しています。
ClusterIP 特になし kubernetes アプリケーションをkubernetesクラスタ内に公開するときに使用する。
  • Pod
  • PersistentVolume
  • PersistentVolumeClaim
  • StorageClass
  • Provisioner
  • DevicePlugin
  • ContainerStorageInterface(CSI)
  • ContainerNetworkInterface(CNI)
  • ConfigMap
  • Secret

NetApp用語

  • StorageVirtualMachine(SVM)
  • Logical interface(LIF)
  • vsadmin SVM管理者

References

Kubernets Network (Expose application for external)

Ingressを導入する

Level1,2ではデプロイしたアプリケーションが配置されているノードのIPに対してアクセスして稼働を確認していました。 ここからは外部にアプリケーションを公開しアクセスする方法を使用します。

具体的にはServiceを定義する際に指定する「type」が複数提供されています。

  1. ClusterIP
  2. NodePort
  3. LoadBalancer

今回はServiceのtypeをNodePortとして、Serviceの前段にIngressを配置する構成とします。 Ingressを使用してアプリケーションを外部に公開します。 IngressはL7ロードバランサーのような動きをします。

Ingress用のネームスペースを作成

Nginx Ingressをデプロイするネームスペースを作成します。

Nginx Ingressをデプロイするネームスペース用マニフェストファイル
kind: Namespace
apiVersion: v1
metadata:
  name: ingress

以下のコマンドでネームスペースを作成します。

$ kubectl create -f ingress-ns.yaml

  namespace "ingress" created

Nginx Ingressのデプロイメント

helm chartを使ったNginx Ingressのデプロイメントです。

–dry-run を付与してhelmを実行することでドライランモードで実行することが可能です。

$ helm install stable/nginx-ingress --name nginx-ingress --set rbac.create=true --namespace ingress

NAME:   nginx-ingress
LAST DEPLOYED: Mon Apr  9 13:58:29 2018
NAMESPACE: ingress
STATUS: DEPLOYED

RESOURCES:
==> v1/ServiceAccount
NAME           SECRETS  AGE
nginx-ingress  1        0s

==> v1beta1/ClusterRoleBinding
NAME           AGE
nginx-ingress  0s

==> v1beta1/Role
NAME           AGE
nginx-ingress  0s

==> v1beta1/RoleBinding
NAME           AGE
nginx-ingress  0s

==> v1/Service
NAME                           TYPE          CLUSTER-IP     EXTERNAL-IP  PORT(S)                     AGE
nginx-ingress-controller       LoadBalancer  10.96.106.165  <pending>    80:32065/TCP,443:32049/TCP  0s
nginx-ingress-default-backend  ClusterIP     10.101.0.249   <none>       80/TCP                      0s

==> v1beta1/Deployment
NAME                           DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
nginx-ingress-controller       1        1        1           0          0s
nginx-ingress-default-backend  1        1        1           0          0s

==> v1/ConfigMap
NAME                      DATA  AGE
nginx-ingress-controller  1     0s

==> v1beta1/PodDisruptionBudget
NAME                           MIN AVAILABLE  MAX UNAVAILABLE  ALLOWED DISRUPTIONS  AGE
nginx-ingress-controller       1              N/A              0                    0s
nginx-ingress-default-backend  1              N/A              0                    0s

==> v1/Pod(related)
NAME                                           READY  STATUS             RESTARTS  AGE
nginx-ingress-controller-5475585cc9-q5ckc      0/1    ContainerCreating  0         0s
nginx-ingress-default-backend-956f8bbff-5znnc  0/1    ContainerCreating  0         0s

==> v1beta1/ClusterRole
NAME           AGE
nginx-ingress  0s


NOTES:
The nginx-ingress controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl --namespace ingress get services -o wide -w nginx-ingress-controller'

An example Ingress that makes use of the controller:

  apiVersion: extensions/v1beta1
  kind: Ingress
  metadata:
    annotations:
      kubernetes.io/ingress.class: nginx
    name: example
    namespace: foo
  spec:
    rules:
      - host: www.example.com
        http:
          paths:
            - backend:
                serviceName: exampleService
                servicePort: 80
              path: /
    # This section is only required if TLS is to be enabled for the Ingress
    tls:
        - hosts:
            - www.example.com
          secretName: example-tls

If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:

  apiVersion: v1
  kind: Secret
  metadata:
    name: example-tls
    namespace: foo
  data:
    tls.crt: <base64 encoded cert>
    tls.key: <base64 encoded key>
  type: kubernetes.io/tls

Ingressを作成するサンプルです。

L7ロードバランス的なもの
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: mynginx-ingress
spec:
  rules:
    - host: user10.netapp.local
      http:
        paths:
          - backend:
              serviceName: mynginx
              servicePort: 80
            path: /

上記のマニフェストファイルをインプットとして、Ingressを作成します。

$ kubectl create -f ingress-controller.yaml

ingress.extensions "mynginx-ingress" created

$ kubectl get ing

NAME              HOSTS                 ADDRESS   PORTS     AGE
mynginx-ingress   user10.netapp.local             80        51s

Ingressが作成されると、「spec - rules - host」で指定したホスト名でアクセス出来るようになります。 以下の確認では簡易的にcurlコマンドでipとホスト名をマッピングしていますが、通常はDNSへAレコードを登録します。

$ curl -L --resolve user10.netapp.local:80:10.244.0.3 http://user10.netapp.local

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

今回のサンプルではDNS登録することとの違いがわからないかもしれませんが、複数のサービスのエンドポイントを統一出来るようになります。

インストールするもの・あると便利なもの

Installed apps

kubernetes 関係

  • kubectl
  • helm
  • stern
  • tridentctl (ラボの内部でインストール実施)

やるべきこと一覧

このドキュメント全体のTODOをまとめたもの

ToDos

課題

jenkinsのyaml作成

(元のエントリ は、 /home/docs/checkouts/readthedocs.org/user_builds/datatechnologylab/checkouts/v18.04/docs/source/Level3/yaml-install.rst の 20 行目です)

DesignDoc

Design: kubernetes クラスタの設計要素について

ラボ環境を作成する際に検討したことやなぜそのようなジャッジをしたかのメモを残す場所

ネットワーク設計

マルチクラスタ構成時のネットワーク構成について マルチテナントはどうする?

ストレージ設計

  • 管理ポートのネットワークはどうするか?

  • アーキテクチャとして、cluster管理 LIF を公開するか?それともSVM管理LIFか?

    • マルチテナントを構成するのであれば k8s クラスタ単位にSVMを割り当てるデザインとする。マネジメントもSVMをユーザに渡す。
  • StorageClass までを管理者側で作成

  • PersistentVolumeClaim は開発者が作成するが、ある程度のパターンはカタログ化して提供する。

  • 無制限に作られてしまうと困るので、k8s 側で Storage Quota を設定、Namespace 毎に指定。ストレージ側では設定なし。 https://kubernetes.io/docs/concepts/policy/resource-quotas/