NetApp Digital Transformation Lab (NDX) (formerly Data Technology Lab Guide)

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

はじめに

目的

  • デジタルトランスフォーメーションを支える根幹技術とNetApp製品を組み合わせることによる価値を触りながら体験
  • 特定のシナリオに沿った関連技術の組み合わせ検証
  • Labを通じての技術者コミュニティの創出

連絡方法

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

Contents

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

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

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

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

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

流れ

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

kubernetes環境へのログイン

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

kubernetesにデプロイ

kubernetes基本操作

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

$ kubectl version

Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.2", GitCommit:"bb9ffb1654d4a729bb4cec18ff088eacc153c239", GitTreeState:"clean", BuildDate:"2018-08-07T23:17:28Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.2", GitCommit:"bb9ffb1654d4a729bb4cec18ff088eacc153c239", GitTreeState:"clean", BuildDate:"2018-08-07T23:08:19Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}

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

$ kubectl get nodes

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

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にpushすることもできます。

DockerHub へログイン

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

$ docker login

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

$ docker image push アカウント名/コンテナイメージ名:タグ名
Private registry (GitLab) を使う場合

private registry を使う場合はGitLabへログインし、プロジェクトを作成します。

GitLab URL: http://gitlab.gitlab.ndxlab.net

プロジェクトを作成するとコンテナイメージのレジストリも使用できるようになります。

_images/gitlab_docker_image_registry.png

Dockerfileを含んだソースをgitリポジトリにpushすると自動で以下のようなビルド、レジストリへのpushの手順が提示されます。 プライベートレジストリのURLも手順内に記載されます。

_images/gitlab_docker_push.png

Dockerイメージのビルド、pushのサンプルは以下の通りです。

$ docker build -t レジストリURL/アカウント名/コンテナイメージ名:タグ名
$ docker push レジストリURL/アカウント名/コンテナイメージ名:タグ名

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

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 provisionerである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インストール

バイナリをダウンロードしてインストールします。(例はバージョン18.07) バックエンドストレージのための setup/backend.json を編集します。

$ wget https://github.com/NetApp/trident/releases/download/v18.07.0/trident-installer-18.07.0.tar.gz

$ tar xzf trident*.tar.gz && cd trident-installer

$ cp sample-input/backend-ontap-nas.json setup/backend.json
backend.jsonの設定パラメータ (NFS ONTAPバックエンド)
パラメータ名 説明 設定内容
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"
}

tridentctl ユーティリティではドライランモードとデバッグモードがオプションで指定できます。 2つを設定し、実行すると以下のように必要事項を事前チェックし、その内容をすべて標準出力にプリントします。

まずは、ドライランモードで実行し問題ないことを確認します。以下の出力結果はユーザ14で実施した場合です。

$ ./tridentctl install --dry-run -n trident -d

DEBU Initialized logging.                          logLevel=debug
DEBU Running outside a pod, creating CLI-based client.
DEBU Initialized Kubernetes CLI client.            cli=kubectl flavor=k8s namespace=default version=1.11.0
DEBU Validated installation environment.           installationNamespace=trident kubernetesVersion=
DEBU Parsed requested volume size.                 quantity=2Gi
DEBU Dumping RBAC fields.                          ucpBearerToken= ucpHost= useKubernetesRBAC=true
DEBU Namespace does not exist.                     namespace=trident
DEBU PVC does not exist.                           pvc=trident
DEBU PV does not exist.                            pv=trident
INFO Starting storage driver.                      backend=/home/localadmin/manifest/trident/trident-installer/setup/backend.json
DEBU config: {"backendName":"NFS_ONTAP_Backend","dataLIF":"192.168.14.200","managementLIF":"192.168.14.200","password":"netapp123","storageDriverName":"ontap-nas","svm":"svm14","username":"vsadmin","version":1}
DEBU Storage prefix is absent, will use default prefix.
DEBU Parsed commonConfig: {Version:1 StorageDriverName:ontap-nas BackendName:NFS_ONTAP_Backend Debug:false DebugTraceFlags:map[] DisableDelete:false StoragePrefixRaw:[] StoragePrefix:<nil> SerialNumbers:[] DriverContext:}
DEBU Initializing storage driver.                  driver=ontap-nas
DEBU Addresses found from ManagementLIF lookup.    addresses="[192.168.14.200]" hostname=192.168.14.200
DEBU Using specified SVM.                          SVM=svm14
DEBU ONTAP API version.                            Ontapi=1.130
WARN Could not determine controller serial numbers. API status: failed, Reason: Unable to find API: system-node-get-iter, Code: 13005
DEBU Configuration defaults                        Encryption=false ExportPolicy=default FileSystemType=ext4 NfsMountOptions="-o nfsvers=3" SecurityStyle=unix Size=1G SnapshotDir=false SnapshotPolicy=none SpaceReserve=none SplitOnClone=false StoragePrefix=trident_ UnixPermissions=---rwxrwxrwx
DEBU Data LIFs                                     dataLIFs="[192.168.14.200]"
DEBU Found NAS LIFs.                               dataLIFs="[192.168.14.200]"
DEBU Addresses found from hostname lookup.         addresses="[192.168.14.200]" hostname=192.168.14.200
DEBU Found matching Data LIF.                      hostNameAddress=192.168.14.200
DEBU Configured EMS heartbeat.                     intervalHours=24
DEBU Read storage pools assigned to SVM.           pools="[aggr1_01 aggr2_01]" svm=svm14
DEBU Read aggregate attributes.                    aggregate=aggr1_01 mediaType=ssd
DEBU Read aggregate attributes.                    aggregate=aggr2_01 mediaType=hdd
DEBU Storage driver initialized.                   driver=ontap-nas
INFO Storage driver loaded.                        driver=ontap-nas
INFO Dry run completed, no problems found.

ドライランモードで実施すると最後に問題ない旨(INFO Dry run completed, no problems found.) が表示されれば、インストールに必要な事前要件を満たしていることが確認できます。

上記の状態まで確認できたら実際にインストールを実施します。

$ ./tridentctl install -n trident -d

DEBU Initialized logging.                          logLevel=debug
DEBU Running outside a pod, creating CLI-based client.
DEBU Initialized Kubernetes CLI client.            cli=kubectl flavor=k8s namespace=default version=1.11.0
DEBU Validated installation environment.           installationNamespace=trident kubernetesVersion=
DEBU Parsed requested volume size.                 quantity=2Gi
DEBU Dumping RBAC fields.                          ucpBearerToken= ucpHost= useKubernetesRBAC=true
DEBU Namespace does not exist.                     namespace=trident
DEBU PVC does not exist.                           pvc=trident
DEBU PV does not exist.                            pv=trident
INFO Starting storage driver.                      backend=/home/localadmin/manifest/trident/trident-installer/setup/backend.json
DEBU config: {"backendName":"NFS_ONTAP_Backend","dataLIF":"192.168.14.200","managementLIF":"192.168.14.200","password":"netapp123","storageDriverName":"ontap-nas","svm":"svm14","username":"vsadmin","version":1}
DEBU Storage prefix is absent, will use default prefix.
DEBU Parsed commonConfig: {Version:1 StorageDriverName:ontap-nas BackendName:NFS_ONTAP_Backend Debug:false DebugTraceFlags:map[] DisableDelete:false StoragePrefixRaw:[] StoragePrefix:<nil> SerialNumbers:[] DriverContext:}
DEBU Initializing storage driver.                  driver=ontap-nas
DEBU Addresses found from ManagementLIF lookup.    addresses="[192.168.14.200]" hostname=192.168.14.200
DEBU Using specified SVM.                          SVM=svm14
DEBU ONTAP API version.                            Ontapi=1.130
WARN Could not determine controller serial numbers. API status: failed, Reason: Unable to find API: system-node-get-iter, Code: 13005
DEBU Configuration defaults                        Encryption=false ExportPolicy=default FileSystemType=ext4 NfsMountOptions="-o nfsvers=3" SecurityStyle=unix Size=1G SnapshotDir=false SnapshotPolicy=none SpaceReserve=none SplitOnClone=false StoragePrefix=trident_ UnixPermissions=---rwxrwxrwx
DEBU Data LIFs                                     dataLIFs="[192.168.14.200]"
DEBU Found NAS LIFs.                               dataLIFs="[192.168.14.200]"
DEBU Addresses found from hostname lookup.         addresses="[192.168.14.200]" hostname=192.168.14.200
DEBU Found matching Data LIF.                      hostNameAddress=192.168.14.200
DEBU Configured EMS heartbeat.                     intervalHours=24
DEBU Read storage pools assigned to SVM.           pools="[aggr1_01 aggr2_01]" svm=svm14
DEBU Read aggregate attributes.                    aggregate=aggr1_01 mediaType=ssd
DEBU Read aggregate attributes.                    aggregate=aggr2_01 mediaType=hdd
DEBU Storage driver initialized.                   driver=ontap-nas
INFO Storage driver loaded.                        driver=ontap-nas
INFO Starting Trident installation.                namespace=trident
DEBU Created Kubernetes object by YAML.
INFO Created namespace.                            namespace=trident
DEBU Deleted Kubernetes object by YAML.
DEBU Deleted cluster role binding.
DEBU Deleted Kubernetes object by YAML.
DEBU Deleted cluster role.
DEBU Deleted Kubernetes object by YAML.
DEBU Deleted service account.
DEBU Created Kubernetes object by YAML.
INFO Created service account.
DEBU Created Kubernetes object by YAML.
INFO Created cluster role.
DEBU Created Kubernetes object by YAML.
INFO Created cluster role binding.
DEBU Created Kubernetes object by YAML.
INFO Created PVC.
DEBU Attempting volume create.                     size=2147483648 storagePool=aggr1_01 volConfig.StorageClass=
DEBU Creating Flexvol.                             aggregate=aggr1_01 encryption=false exportPolicy=default name=trident_trident securityStyle=unix size=2147483648 snapshotDir=false snapshotPolicy=none snapshotReserve=0 spaceReserve=none unixPermissions=---rwxrwxrwx
DEBU SVM root volume has no load-sharing mirrors.  rootVolume=svm_root
DEBU Created Kubernetes object by YAML.
INFO Created PV.                                   pv=trident
INFO Waiting for PVC to be bound.                  pvc=trident
DEBU PVC not yet bound, waiting.                   increment=282.430263ms pvc=trident
DEBU PVC not yet bound, waiting.                   increment=907.038791ms pvc=trident
DEBU PVC not yet bound, waiting.                   increment=1.497234254s pvc=trident
DEBU PVC not yet bound, waiting.                   increment=1.182346358s pvc=trident
DEBU PVC not yet bound, waiting.                   increment=3.794274009s pvc=trident
DEBU Logged EMS message.                           driver=ontap-nas
DEBU PVC not yet bound, waiting.                   increment=2.554707984s pvc=trident
DEBU Created Kubernetes object by YAML.
INFO Created Trident deployment.
INFO Waiting for Trident pod to start.
DEBU Trident pod not yet running, waiting.         increment=481.632837ms
DEBU Trident pod not yet running, waiting.         increment=848.840617ms
DEBU Trident pod not yet running, waiting.         increment=1.171028148s
DEBU Trident pod not yet running, waiting.         increment=871.68468ms
DEBU Trident pod not yet running, waiting.         increment=2.784723303s
DEBU Trident pod not yet running, waiting.         increment=3.037298468s
DEBU Trident pod not yet running, waiting.         increment=7.540652793s
DEBU Trident pod not yet running, waiting.         increment=12.611925219s
DEBU Trident pod not yet running, waiting.         increment=18.389729895s
INFO Trident pod started.                          namespace=trident pod=trident-6946fdf6d8-8cb8q
INFO Waiting for Trident REST interface.
DEBU Invoking tunneled command: kubectl exec trident-6946fdf6d8-8cb8q -n trident -c trident-main -- tridentctl -s 127.0.0.1:8000 version -o json
INFO Trident REST interface is up.                 version=18.07.0
INFO Trident installation succeeded.

「INFO Trident installation succeeded.」が出力されればインストール成功です。

また、問題が発生した場合には tridentctl を使用して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へバックエンドストレージの登録

インストールが完了したらtridentのバージョンを確認します。

$ ./tridentctl  version -n trident

+----------------+----------------+
| SERVER VERSION | CLIENT VERSION |
+----------------+----------------+
| 18.07.0        | 18.07.0        |
+----------------+----------------+

バージョンが表示されていればインストール成功です。 作成した定義ファイル、 setup/backend.json を使用し、バックエンド登録を実行します。 まずは NFS ストレージバックエンドであるONTAPを登録します。

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

+-------------------+----------------+--------+---------+
|       NAME        | STORAGE DRIVER | ONLINE | VOLUMES |
+-------------------+----------------+--------+---------+
| NFS_ONTAP_Backend | ontap-nas      | true   |       0 |
+-------------------+----------------+--------+---------+

つづいて、iSCSI ブロック・ストレージバックエンドのSolidFireを登録します。

NFSバックエンドストレージと同様に setup ディレクトリに solidfire-backend.json を作成します。

基本的な設定項目としては以下の表の通りです。

solidfire-backend.jsonの設定パラメータ (iSCSI SolidFire バックエンド)
パラメータ名 説明 設定内容
Endpoint SolidFire の管理用IPを設定(MVIP)、URL先頭にユーザーIDとパスワードを付与 10.128.223.240
SVIP データ通信のIPを設定(クラスタで1つ) 192.168.0.240:3260
TenantName 任意の名称を設定、SolidFire側でのテナントとなる。 今回は環境番号とする(userXX)
Types ストレージカタログとしてのQoSのリストを指定 1つ以上のminIOPS, maxIOPS, burstIOPSを指定

テンプレートとなるSolidFireのバックエンド定義ファイルは以下の通りです。

{
    "version": 1,
    "storageDriverName": "solidfire-san",
    "Endpoint": "https://ユーザ名:パスワード@マネジメント用IP/json-rpc/8.0",
    "SVIP": "ストレージアクセス用IP:3260",
    "TenantName": "ユーザ環境番号",
    "backendName": "iSCSI_SF_Backend",
    "InitiatorIFace": "default",
    "UseCHAP": true,
    "Types": [
        {
            "Type": "Bronze",
            "Qos": {
                "minIOPS": 1000,
                "maxIOPS": 3999,
                "burstIOPS": 4500
            }
        },
        {
            "Type": "Silver",
            "Qos": {
                "minIOPS": 4000,
                "maxIOPS": 5999,
                "burstIOPS": 6500
            }
        },
        {
            "Type": "Gold",
            "Qos": {
                "minIOPS": 6000,
                "maxIOPS": 8000,
                "burstIOPS": 10000
            }
        }
    ]
}

同様にバックエンド登録を実施します。

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

+------------------+----------------+--------+---------+
|       NAME       | STORAGE DRIVER | ONLINE | VOLUMES |
+------------------+----------------+--------+---------+
| iSCSI_SF_Backend | solidfire-san  | true   |       0 |
+------------------+----------------+--------+---------+

今までに登録したストレージバックエンドを確認します。

$ ./tridentctl get backend -n trident

+-------------------+----------------+--------+---------+
|       NAME        | STORAGE DRIVER | ONLINE | VOLUMES |
+-------------------+----------------+--------+---------+
| NFS_ONTAP_Backend | ontap-nas      | true   |       0 |
| iSCSI_SF_Backend  | solidfire-san  | true   |       0 |
+-------------------+----------------+--------+---------+

注釈

tridentctl ユーティリティにはアンインストール用のサブコマンドがあります。

以下のように -a オプションを付与して実行すると生成した管理用のetcdのデータなどすべてを削除した上でアンインストールします。 インストール実行時に失敗したときなど、クリーンに再インストールしたい場合に使います。

$ ./tridentctl uninstall -n trident -a

StorageClassの定義

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

Trident v18.07 ではStorageClassを作成するときに以下の属性を設定できます。 これらの属性のパラメータを組み合わせてストレージサービスをデザインします。

StorageClass の parameters に設定可能な属性
設定可能な属性
性能に関する属性 メデイアタイプ(hdd, hybrid, ssd)、プロビジョニングのタイプ(シン、シック)、IOPS
データ保護・管理に関する属性 スナップショット有無、クローニング有効化、暗号化の有効化
バックエンドのストレージプラットフォーム属性 ontap-nas, ontap-nas-economy, ontap-nas-flexgroup, ontap-san, solidfire-san, eseries-iscsi

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

NFSバックエンドのONTAPでのStorageClass

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

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

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

以下のようなイメージでStoageClassを作成しましょう。

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

以下は上記の「DB 用の高速領域」の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

同様にブロックデバイスバックエンドとして設定したSolidFireに対応するStorageClassを作成します。

バックエンド登録時に3つの性能別のQoSを作成しました。

それぞれに該当するStoageClassを作成します。StorageClassで指定されたIOPSを実現できるバックエンドのQoSがボリューム作成時に自動設定されます。

1000IOPSの性能が出せるStorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: solidfire-bronze
provisioner: netapp.io/trident
parameters:
  backendType: "solidfire-san"
  IOPS: "1500"
  fsType: "ext4"
4000IOPSの性能が出せるStoageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: solidfire-silver
provisioner: netapp.io/trident
parameters:
  backendType: "solidfire-san"
  IOPS: "4000"
  fsType: "ext4"
8000IOPSの性能が出せるStoageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: solidfire-gold
provisioner: netapp.io/trident
parameters:
  backendType: "solidfire-san"
  IOPS: "8000"
  fsType: "ext4"

以降のセクションではここまでで作成したStorageClassを適切に使い分けてすすめましょう。

注釈

デフォルトのStorageClassの設定

StorageClassは記載がないときに使用するStorageClassを指定できます。

kubectl patch storageclass ストレージクラス名 -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

Persistent Volume Claimの作成

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

データの保管ポリシー、データ保護ポリシー、SnapShotの取得ポリシー、クローニングの有効化、暗号化の有効化などを設定できます。

一覧については以下のURLに記載があります。 metadata.annotation 配下に記述することで様々な機能を使用することが可能となります。

デプロイ用のマニフェストファイルに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

デプロイメント実施

上記のPVCの設定が終わったら再度アプリケーションをデプロイします。

その後、アプリケーションからデータを保存するようオペレーションを行います。 WordPressであれば記事を投稿することで簡単に確認ができます。

アプリケーションの停止・起動

永続化されていることを確認するため、一度アプリケーションを停止します。

Deploymentで必要となるポッドは起動するような設定になっているため、 簡単にアプリケーションの停止・起動を行う方法として Deployment 配下の Pod を削除する方法がとれます。

$ kubectl delete pod -l "ラベル名"

$ kubectl get deploy

実行例は以下の通りです。

$ kubectl delete pod -l app=wordpress

pod "wordpress-5bc75fd7bd-kzc5l" deleted
pod "wordpress-mysql-565494758-jjdl4" deleted

$ kubectl get deploy

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

DeploymentによってPodの起動数は管理されるため新たにPodが起動します。 AVAILABLE の数が正常になるまで待ちましょう。

$ kubectl get deploy

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

再デプロイメント後の確認

再起動したPodに対して永続化されたデータが使用されていることを確認します。 2つの視点から確認したいと思います。

  1. アプリケーションであれば再度ログインして保存したデータを確認します。
  2. バックエンドストレージに動的にボリュームが作成されていることを確認します。
$ ssh vsadmin@192.168.XX.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 を介してクローン機能を利用できます。

引数にPVC名を指定します。

クローニングのマニフェストファイルの例 pvccloning.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: basicclone
  annotations:
    trident.netapp.io/cloneFromPVC: database (<-ここにクローンしたい既存のPVC名(ボリューム名)を記述)
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: ontap-gold

ここではサンプルでPVC Cloning を活用したOracle Databaseを複数デプロイするデモ動画をご覧ください。

クローニング技術によって実現可能なこと

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

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

Tridentの18.07でのアップデート: CSI (Container Storage Interface)への対応

最新のTridentではCSIモードでのデプロイが可能となっています。(インストール時に --csi を付与する) CSIは仕様自体がまだαステージということもあり実験的なモードですが、いち早くCSIをお試しいただくことが可能となっています。

CSI自体についてはこちら - https://kubernetes.io/blog/2018/01/introducing-container-storage-interface/

注釈

理論的にはCSIの仕様でドライバを実装すれば、そのドライバはkubernetes、Mesos, Docker, Cloud Foundryなど CSIを実装したコンテナオーケストレーターから使用できるようになります。

まとめ

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

今回は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はGitLabを共有で準備していますが、使いなれているサービス(GitHub等)があればそちらを使って頂いても構いません。 まずは、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つ目が、今回の環境では ServicetypeLoadBalancer としてしてデプロイすると external-ipが付与される環境となっています。(MetalLBをデプロイ済みです。)

2つ目が Ingress を使った公開です。IngressをJenkinsのHelmチャートを使ってデプロイするためには「Master.Ingress.Annotations」、「Master.ServiceType」を変更しデプロイします。

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

どちらの方法の場合も、以下のvalues.yamlをカスタマイズしてデプロイします。 このレベルではJenkinsをデプロイするのが目的ではなくCI/CDパイプラインを作成するのが目的であるため、デプロイ用のyamlファイルを準備しました。

また、このvalues.yamlでは永続化ストレージが定義されていないため、Level2で作成したStorageClassを使用し動的にプロビジョニングをするように変更しましょう。

StorageClassには環境に作成したStorageClassを設定します。このサンプルでは "ontap-gold"を設定してあります。

また、Kubernetes上でCI/CDパイプラインを作成するため Kubernetes-plugin も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>
  resources:
    requests:
      cpu: "50m"
      memory: "256Mi"
    limits:
      cpu: "2000m"
      memory: "2048Mi"
  # 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"
  # Enable pod security context (must be `true` if RunAsUser or FsGroup are set)
  UsePodSecurityContext: true
  # 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
  # Enable Kubernetes Liveness and Readiness Probes
  # ~ 2 minutes to allow Jenkins to restart when upgrading plugins. Set ReadinessTimeout to be shorter than LivenessTimeout.
  HealthProbes: true
  HealthProbesLivenessTimeout: 90
  HealthProbesReadinessTimeout: 60
  HealthProbeLivenessFailureThreshold: 12
  SlaveListenerPort: 50000
  DisabledAgentProtocols:
  - JNLP-connect
  - JNLP2-connect
  CSRF:
    DefaultCrumbIssuer:
      Enabled: true
      ProxyCompatability: true
  CLI: false
  # 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.12.3
  - workflow-job:2.24
  - workflow-aggregator:2.5
  - credentials-binding:1.16
  - git:3.9.1
  - blueocean:1.8.2
  - ghprb:1.40.0

  # 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: {}
  PodAnnotations: {}

  Ingress:
    ApiVersion: extensions/v1beta1
    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
  resources:
    requests:
      cpu: "200m"
      memory: "256Mi"
    limits:
      cpu: "200m"
      memory: "256Mi"
  # You may want to change this to true while testing a new image
  AlwaysPullImage: false
  # Controls how slave pods are retained after the Jenkins build completes
  # Possible values: Always, Never, OnFailure
  PodRetention: Never
  # 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>ls
  ## 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: "ontap-gold"

  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 v1, v1beta1, or v1alpha1)
  apiVersion: v1
  # Role reference
  roleRef: cluster-admin
  # Role kind (RoleBinding or ClusterRoleBinding)
  roleBindingKind: ClusterRoleBinding

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

$ helm --namespace jenkins --name jenkins -f ./jenkins-values.yaml install stable/jenkins --debug
[debug] Created tunnel using local port: '44511'

[debug] SERVER: "127.0.0.1:44511"

[debug] Original chart version: ""
[debug] Fetched stable/jenkins to /home/localadmin/.helm/cache/archive/jenkins-0.16.20.tgz

[debug] CHART PATH: /home/localadmin/.helm/cache/archive/jenkins-0.16.20.tgz

NAME:   jenkins
REVISION: 1
RELEASED: Mon Aug 27 23:54:09 2018
CHART: jenkins-0.16.20
USER-SUPPLIED VALUES:
Agent:
  AlwaysPullImage: false
  Component: jenkins-slave
  Enabled: true
  Image: jenkins/jnlp-slave
  ImageTag: 3.10-1
  NodeSelector: {}
  PodRetention: Never
  Privileged: false
  resources:
    limits:
      cpu: 200m
      memory: 256Mi
    requests:
      cpu: 200m
      memory: 256Mi
  volumes: null
Master:
  AdminUser: admin
  CLI: false
  CSRF:
    DefaultCrumbIssuer:
      Enabled: true
      ProxyCompatability: true
  Component: jenkins-master
  CustomConfigMap: false
  DisabledAgentProtocols:
  - JNLP-connect
  - JNLP2-connect
  HealthProbeLivenessFailureThreshold: 12
  HealthProbes: true
  HealthProbesLivenessTimeout: 90
  HealthProbesReadinessTimeout: 60
  Image: jenkins/jenkins
  ImagePullPolicy: Always
  ImageTag: lts
  Ingress:
    Annotations: null
    ApiVersion: extensions/v1beta1
    TLS: null
  InitScripts: null
  InstallPlugins:
  - kubernetes:1.12.3
  - workflow-job:2.24
  - workflow-aggregator:2.5
  - credentials-binding:1.16
  - git:3.9.1
  - blueocean:1.4.1
  - ghprb:1.40.0
  LoadBalancerSourceRanges:
  - 0.0.0.0/0
  Name: jenkins-master
  NodeSelector: {}
  PodAnnotations: {}
  ServiceAnnotations: {}
  ServicePort: 8080
  ServiceType: LoadBalancer
  SlaveListenerPort: 50000
  SlaveListenerServiceAnnotations: {}
  SlaveListenerServiceType: ClusterIP
  Tolerations: {}
  UsePodSecurityContext: true
  UseSecurity: true
  resources:
    limits:
      cpu: 2000m
      memory: 2048Mi
    requests:
      cpu: 50m
      memory: 256Mi
NetworkPolicy:
  ApiVersion: extensions/v1beta1
  Enabled: false
Persistence:
  AccessMode: ReadWriteOnce
  Annotations: {}
  Enabled: true
  Size: 8Gi
  StorageClass: ontap-gold
  mounts: null
  volumes: null
rbac:
  apiVersion: v1
  install: false
  roleBindingKind: ClusterRoleBinding
  roleRef: cluster-admin
  serviceAccountName: default

COMPUTED VALUES:
Agent:
  AlwaysPullImage: false
  Component: jenkins-slave
  Enabled: true
  Image: jenkins/jnlp-slave
  ImageTag: 3.10-1
  NodeSelector: {}
  PodRetention: Never
  Privileged: false
  resources:
    limits:
      cpu: 200m
      memory: 256Mi
    requests:
      cpu: 200m
      memory: 256Mi
  volumes: null
Master:
  AdminUser: admin
  CLI: false
  CSRF:
    DefaultCrumbIssuer:
      Enabled: true
      ProxyCompatability: true
  Component: jenkins-master
  CustomConfigMap: false
  DisabledAgentProtocols:
  - JNLP-connect
  - JNLP2-connect
  HealthProbeLivenessFailureThreshold: 12
  HealthProbes: true
  HealthProbesLivenessTimeout: 90
  HealthProbesReadinessTimeout: 60
  Image: jenkins/jenkins
  ImagePullPolicy: Always
  ImageTag: lts
  Ingress:
    Annotations: null
    ApiVersion: extensions/v1beta1
    TLS: null
  InitScripts: null
  InstallPlugins:
  - kubernetes:1.12.3
  - workflow-job:2.24
  - workflow-aggregator:2.5
  - credentials-binding:1.16
  - git:3.9.1
  - blueocean:1.4.1
  - ghprb:1.40.0
  LoadBalancerSourceRanges:
  - 0.0.0.0/0
  Name: jenkins-master
  NodeSelector: {}
  PodAnnotations: {}
  ServiceAnnotations: {}
  ServicePort: 8080
  ServiceType: LoadBalancer
  SlaveListenerPort: 50000
  SlaveListenerServiceAnnotations: {}
  SlaveListenerServiceType: ClusterIP
  Tolerations: {}
  UsePodSecurityContext: true
  UseSecurity: true
  resources:
    limits:
      cpu: 2000m
      memory: 2048Mi
    requests:
      cpu: 50m
      memory: 256Mi
NetworkPolicy:
  ApiVersion: extensions/v1beta1
  Enabled: false
Persistence:
  AccessMode: ReadWriteOnce
  Annotations: {}
  Enabled: true
  Size: 8Gi
  StorageClass: ontap-gold
  mounts: null
  volumes: null
rbac:
  apiVersion: v1
  install: false
  roleBindingKind: ClusterRoleBinding
  roleRef: cluster-admin
  serviceAccountName: default

HOOKS:
---
# jenkins-ui-test-1g5nb
apiVersion: v1
kind: Pod
metadata:
  name: "jenkins-ui-test-1g5nb"
  annotations:
    "helm.sh/hook": test-success
spec:
  initContainers:
    - name: "test-framework"
      image: "dduportal/bats:0.4.0"
      command:
      - "bash"
      - "-c"
      - |
        set -ex
        # copy bats to tools dir
        cp -R /usr/local/libexec/ /tools/bats/
      volumeMounts:
      - mountPath: /tools
        name: tools
  containers:
    - name: jenkins-ui-test
      image: jenkins/jenkins:lts
      command: ["/tools/bats/bats", "-t", "/tests/run.sh"]
      volumeMounts:
      - mountPath: /tests
        name: tests
        readOnly: true
      - mountPath: /tools
        name: tools
  volumes:
  - name: tests
    configMap:
      name: jenkins-tests
  - name: tools
    emptyDir: {}
  restartPolicy: Never
MANIFEST:

---
# Source: jenkins/templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: jenkins
  labels:
    app: jenkins
    chart: "jenkins-0.16.20"
    release: "jenkins"
    heritage: "Tiller"
type: Opaque
data:

  jenkins-admin-password: "N3EwZWtydDAyQg=="

  jenkins-admin-user: "YWRtaW4="
---
# Source: jenkins/templates/config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: jenkins
data:
  config.xml: |-
    <?xml version='1.0' encoding='UTF-8'?>
    <hudson>
      <disabledAdministrativeMonitors/>
      <version>lts</version>
      <numExecutors>0</numExecutors>
      <mode>NORMAL</mode>
      <useSecurity>true</useSecurity>
      <authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy">
        <denyAnonymousReadAccess>true</denyAnonymousReadAccess>
      </authorizationStrategy>
      <securityRealm class="hudson.security.LegacySecurityRealm"/>
      <disableRememberMe>false</disableRememberMe>
      <projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
      <workspaceDir>${JENKINS_HOME}/workspace/${ITEM_FULLNAME}</workspaceDir>
      <buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
      <markupFormatter class="hudson.markup.EscapedMarkupFormatter"/>
      <jdks/>
      <viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
      <myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
      <clouds>
        <org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud plugin="kubernetes@1.12.3">
          <name>kubernetes</name>
          <templates>
            <org.csanchez.jenkins.plugins.kubernetes.PodTemplate>
              <inheritFrom></inheritFrom>
              <name>default</name>
              <instanceCap>2147483647</instanceCap>
              <idleMinutes>0</idleMinutes>
              <label>jenkins-jenkins-slave</label>
              <nodeSelector></nodeSelector>
                <nodeUsageMode>NORMAL</nodeUsageMode>
              <volumes>
              </volumes>
              <containers>
                <org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate>
                  <name>jnlp</name>
                  <image>jenkins/jnlp-slave:3.10-1</image>
                  <privileged>false</privileged>
                  <alwaysPullImage>false</alwaysPullImage>
                  <workingDir>/home/jenkins</workingDir>
                  <command></command>
                  <args>${computer.jnlpmac} ${computer.name}</args>
                  <ttyEnabled>false</ttyEnabled>
                  # Resources configuration is a little hacky. This was to prevent breaking
                  # changes, and should be cleanned up in the future once everybody had
                  # enough time to migrate.
                  <resourceRequestCpu>200m</resourceRequestCpu>
                  <resourceRequestMemory>256Mi</resourceRequestMemory>
                  <resourceLimitCpu>200m</resourceLimitCpu>
                  <resourceLimitMemory>256Mi</resourceLimitMemory>
                  <envVars>
                    <org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar>
                      <key>JENKINS_URL</key>
                      <value>http://jenkins:8080</value>
                    </org.csanchez.jenkins.plugins.kubernetes.ContainerEnvVar>
                  </envVars>
                </org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate>
              </containers>
              <envVars/>
              <annotations/>
              <imagePullSecrets/>
              <nodeProperties/>
              <podRetention class="org.csanchez.jenkins.plugins.kubernetes.pod.retention.Default"/>
            </org.csanchez.jenkins.plugins.kubernetes.PodTemplate></templates>
          <serverUrl>https://kubernetes.default</serverUrl>
          <skipTlsVerify>false</skipTlsVerify>
          <namespace>jenkins</namespace>
          <jenkinsUrl>http://jenkins:8080</jenkinsUrl>
          <jenkinsTunnel>jenkins-agent:50000</jenkinsTunnel>
          <containerCap>10</containerCap>
          <retentionTimeout>5</retentionTimeout>
          <connectTimeout>0</connectTimeout>
          <readTimeout>0</readTimeout>
          <podRetention class="org.csanchez.jenkins.plugins.kubernetes.pod.retention.Never"/>
        </org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud>
      </clouds>
      <quietPeriod>5</quietPeriod>
      <scmCheckoutRetryCount>0</scmCheckoutRetryCount>
      <views>
        <hudson.model.AllView>
          <owner class="hudson" reference="../../.."/>
          <name>All</name>
          <filterExecutors>false</filterExecutors>
          <filterQueue>false</filterQueue>
          <properties class="hudson.model.View$PropertyList"/>
        </hudson.model.AllView>
      </views>
      <primaryView>All</primaryView>
      <slaveAgentPort>50000</slaveAgentPort>
      <disabledAgentProtocols>
        <string>JNLP-connect</string>
        <string>JNLP2-connect</string>
      </disabledAgentProtocols>
      <label></label>
      <crumbIssuer class="hudson.security.csrf.DefaultCrumbIssuer">
        <excludeClientIPFromCrumb>true</excludeClientIPFromCrumb>
      </crumbIssuer>
      <nodeProperties/>
      <globalNodeProperties/>
      <noUsageStatistics>true</noUsageStatistics>
    </hudson>
  jenkins.model.JenkinsLocationConfiguration.xml: |-
    <?xml version='1.1' encoding='UTF-8'?>
    <jenkins.model.JenkinsLocationConfiguration>
      <adminAddress></adminAddress>
      <jenkinsUrl>http://jenkins:8080</jenkinsUrl>
    </jenkins.model.JenkinsLocationConfiguration>
  jenkins.CLI.xml: |-
    <?xml version='1.1' encoding='UTF-8'?>
    <jenkins.CLI>
      <enabled>false</enabled>
    </jenkins.CLI>
  apply_config.sh: |-
    mkdir -p /usr/share/jenkins/ref/secrets/;
    echo "false" > /usr/share/jenkins/ref/secrets/slave-to-master-security-kill-switch;
    cp -n /var/jenkins_config/config.xml /var/jenkins_home;
    cp -n /var/jenkins_config/jenkins.CLI.xml /var/jenkins_home;
    cp -n /var/jenkins_config/jenkins.model.JenkinsLocationConfiguration.xml /var/jenkins_home;
    # Install missing plugins
    cp /var/jenkins_config/plugins.txt /var/jenkins_home;
    rm -rf /usr/share/jenkins/ref/plugins/*.lock
    /usr/local/bin/install-plugins.sh `echo $(cat /var/jenkins_home/plugins.txt)`;
    # Copy plugins to shared volume
    cp -n /usr/share/jenkins/ref/plugins/* /var/jenkins_plugins;
  plugins.txt: |-
    kubernetes:1.12.3
    workflow-job:2.24
    workflow-aggregator:2.5
    credentials-binding:1.16
    git:3.9.1
    blueocean:1.4.1
    ghprb:1.40.0
---
# Source: jenkins/templates/test-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: jenkins-tests
data:
  run.sh: |-
    @test "Testing Jenkins UI is accessible" {
      curl --retry 48 --retry-delay 10 jenkins:8080/login
    }
---
# Source: jenkins/templates/home-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: jenkins
  labels:
    app: jenkins
    chart: "jenkins-0.16.20"
    release: "jenkins"
    heritage: "Tiller"
spec:
  accessModes:
    - "ReadWriteOnce"
  resources:
    requests:
      storage: "8Gi"
  storageClassName: "ontap-gold"
---
# Source: jenkins/templates/jenkins-agent-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: jenkins-agent
  labels:
    app: jenkins
    chart: "jenkins-0.16.20"
    component: "jenkins-jenkins-master"
spec:
  ports:
    - port: 50000
      targetPort: 50000

      name: slavelistener
  selector:
    component: "jenkins-jenkins-master"
  type: ClusterIP
---
# Source: jenkins/templates/jenkins-master-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: jenkins
  labels:
    app: jenkins
    heritage: "Tiller"
    release: "jenkins"
    chart: "jenkins-0.16.20"
    component: "jenkins-jenkins-master"
spec:
  ports:
    - port: 8080
      name: http
      targetPort: 8080

  selector:
    component: "jenkins-jenkins-master"
  type: LoadBalancer

  loadBalancerSourceRanges:
    - 0.0.0.0/0
---
# Source: jenkins/templates/jenkins-master-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: jenkins
  labels:
    heritage: "Tiller"
    release: "jenkins"
    chart: "jenkins-0.16.20"
    component: "jenkins-jenkins-master"
spec:
  replicas: 1
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      component: "jenkins-jenkins-master"
  template:
    metadata:
      labels:
        app: jenkins
        heritage: "Tiller"
        release: "jenkins"
        chart: "jenkins-0.16.20"
        component: "jenkins-jenkins-master"
      annotations:
        checksum/config: f1949fdff0e0d3db7c6180357f63c007db61b13e5c107e5980a5eac982863c21
    spec:
      securityContext:
        runAsUser: 0
      serviceAccountName: "default"
      initContainers:
        - name: "copy-default-config"
          image: "jenkins/jenkins:lts"
          imagePullPolicy: "Always"
          command: [ "sh", "/var/jenkins_config/apply_config.sh" ]
          resources:
            limits:
              cpu: 2000m
              memory: 2048Mi
            requests:
              cpu: 50m
              memory: 256Mi

          volumeMounts:
            -
              mountPath: /var/jenkins_home
              name: jenkins-home
            -
              mountPath: /var/jenkins_config
              name: jenkins-config
            -
              mountPath: /var/jenkins_plugins
              name: plugin-dir
            -
              mountPath: /usr/share/jenkins/ref/secrets/
              name: secrets-dir
      containers:
        - name: jenkins
          image: "jenkins/jenkins:lts"
          imagePullPolicy: "Always"
          args: [ "--argumentsRealm.passwd.$(ADMIN_USER)=$(ADMIN_PASSWORD)",  "--argumentsRealm.roles.$(ADMIN_USER)=admin"]
          env:
            - name: JAVA_TOOL_OPTIONS
              value: ""
            - name: JENKINS_OPTS
              value: ""
            - name: ADMIN_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: jenkins
                  key: jenkins-admin-password
            - name: ADMIN_USER
              valueFrom:
                secretKeyRef:
                  name: jenkins
                  key: jenkins-admin-user
          ports:
            - containerPort: 8080
              name: http
            - containerPort: 50000
              name: slavelistener
          livenessProbe:
            httpGet:
              path: "/login"
              port: http
            initialDelaySeconds: 90
            timeoutSeconds: 5
            failureThreshold: 12
          readinessProbe:
            httpGet:
              path: "/login"
              port: http
            initialDelaySeconds: 60
          # Resources configuration is a little hacky. This was to prevent breaking
          # changes, and should be cleanned up in the future once everybody had
          # enough time to migrate.
          resources:

            limits:
              cpu: 2000m
              memory: 2048Mi
            requests:
              cpu: 50m
              memory: 256Mi


          volumeMounts:
            -
              mountPath: /var/jenkins_home
              name: jenkins-home
              readOnly: false
            -
              mountPath: /var/jenkins_config
              name: jenkins-config
              readOnly: true
            -
              mountPath: /usr/share/jenkins/ref/plugins/
              name: plugin-dir
              readOnly: false
            -
              mountPath: /usr/share/jenkins/ref/secrets/
              name: secrets-dir
              readOnly: false
      volumes:
      - name: jenkins-config
        configMap:
          name: jenkins
      - name: plugin-dir
        emptyDir: {}
      - name: secrets-dir
        emptyDir: {}
      - name: jenkins-home
        persistentVolumeClaim:
          claimName: jenkins
LAST DEPLOYED: Mon Aug 27 23:54:09 2018
NAMESPACE: jenkins
STATUS: DEPLOYED

RESOURCES:
==> v1/Secret
NAME     TYPE    DATA  AGE
jenkins  Opaque  2     0s

==> v1/ConfigMap
NAME           DATA  AGE
jenkins        5     0s
jenkins-tests  1     0s

==> v1/PersistentVolumeClaim
NAME     STATUS   VOLUME      CAPACITY  ACCESS MODES  STORAGECLASS  AGE
jenkins  Pending  ontap-gold  0s

==> v1/Service
NAME           TYPE          CLUSTER-IP     EXTERNAL-IP     PORT(S)         AGE
jenkins-agent  ClusterIP     10.109.172.86  <none>          50000/TCP       0s
jenkins        LoadBalancer  10.97.161.136  192.168.10.210  8080:30376/TCP  0s

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

==> v1/Pod(related)
NAME                     READY  STATUS   RESTARTS  AGE
jenkins-965668c95-7tzmc  0/1    Pending  0         0s


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. Get the Jenkins URL to visit by running these commands in the same shell:
  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        You can watch the status of by running 'kubectl get svc --namespace jenkins -w jenkins'
  export SERVICE_IP=$(kubectl get svc --namespace jenkins jenkins --template "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}")
  echo http://$SERVICE_IP:8080/login

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

「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のTry & Error: インストールが上手くいかない場合は?

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

$ helm del --purge チャート名

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

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

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

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

Ingressの導入についてはLevel4 運用編の Ingressを導入 にまとめました。

Jenkinsの設定をする

Gitリポジトリに変更があったら自動でテストを実行するpipelineを定義します。 そのためにはまずJenkinsでGitリポジトリに操作があった場合の動作の定義とKubernetesとの接続の設定をします。

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

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

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

Jenkins AgentをKubernetes上で実行できるようにする

Jenkinsからkubernetes上でJenkins agentを実行する場合にはJenkins kubernetes-plugin の導入が必要です。 通常はソースコードの取得から実施することになります。gitを使う場合であればgitのjenkins-pluginが必要です。

本ガイドで準備した values.yaml を使用している場合にはすでにどちらも導入されている状態となります。

ここでは Jenkins から kubernetesへ接続できるようにする設定を提示いたします。

Jeninsログイン後、クレデンシャルを事前に作成します。

_images/Kubernetes-Credentials.png

jenkins 導入済みのネームスペースにサービスアカウントを作成します。

kubectl create clusterrolebinding jenkins --clusterrole cluster-admin --serviceaccount=jenkins:default

Configurationから「Kubernetes設定」を選択します。

_images/jenkins-configuration.jpg

ここでは必要となるパラメータを設定していきます。

  • kubernetes URL: マスタのIP, 192.168.XX.10
  • Kubernetes Namespace: Jenkinsをインストールしたnamespace名
  • kubernetes certificate key: /etc/kubernetes/pki/apiserver.crtの内容をペースト
  • Credentials: Secret を選択

Jenkins Pipelineの作成

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

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

ここではテンプレートを準備しました、上記の様なパイプラインを実装してみましょう。 Jenkins ではパイプラインを構築するために2つの記述方法があります。

それぞれの違いついてはこちら。

Jenkins pipelineのフォーマット
pipeline {
    agent {
        kubernetes {
            label 'jenkins-ci'
            defaultContainer 'jnlp'
            yamlFile 'KubernetesPod.yaml'
        }
    }

    stages {

        stage('Pre Build Check') {
            steps {
                script {
                    echo "Pre Build staget implement!"
                }
                container('busybox') {
                    sh 'echo Hello Container World in Pre Build Check'
                }
            }
        }

        stage('Build') {
            steps {
                echo "Build staget implement!"
                script {
//                    Dockerfile 内部でdockerを導入する
//                    docker.build("nodejs-build-${env.BUILD_ID}").inside() {
//                        node -v
//                    }
                }
            }
        }

        stage('Test') {
            steps {
                echo 'Test Stage implement!'
                container('kubectl') {
                    sh 'kubectl version'
                }
            }
        }

        stage('Deploy') {
            steps {
                echo 'printenv'
                container('helm') {
                    sh 'helm version'
                }
            }
        }
    }
}
Jenkins pipelineをkubernetesで動作させるコンテナのテンプレートを定義
metadata:
  labels:
    some-label: jenkins-ci
spec:
  containers:
  - name: jnlp
    env:
    - name: CONTAINER_ENV_VAR
      value: jnlp
  - name: busybox
    image: busybox
    command:
    - cat
    tty: true
    env:
    - name: CONTAINER_ENV_VAR
      value: busybox
  - name: kubectl
    image: lachlanevenson/k8s-kubectl:v1.8.8
    command:
    - cat
    tty: true
    env:
    - name: CONTAINER_ENV_VAR
      value: kubectl
  - name: helm
    image: lachlanevenson/k8s-helm:latest
    command: 
    - cat
    tty: true
    env:
    - name: CONTAINER_ENV_VAR
      value: helm

Jenkins pipeline の作成が完了したら任意のGitリポジトリにpushします。 以降のJenkins Pipelineの実行にJenkinsfileを使用します。

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

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

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

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

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

今回はサンプルとしてJenkinsのBlueOcean pluginを使用してPipelineを作成します。

_images/jenkins_blueocean.png

BlueOcean plugin を使用するとウィザード形式でPipelineを作成することができます。

各入力値については以下のURLにてどのような形式で入力されるかの記載があります。

コンテナをCI/CDする方法 Helmを使ってみる

コンテナのCI/CDではいくつか方法があります。 ここではコンテナをCI/CDするために必要な検討事項を記載するとともに

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

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

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

他にも以下のようなCI/CDを行いやすくする構成管理・パッケージマネジメントのツールが存在しています。

  • Kustomize
  • Draft
  • GitKube
  • Skaffold

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

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が使えるようになりました。

本ラボでは簡易的なパイプラインを実際に構築しました。パイプライン内の処理については個々で実装したものから発展させ様々な処理を追加することができます。

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

Level 4: 運用編

目的・ゴール: 運用を行うにあたり必要となる検討事項を把握する

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

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

流れ

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

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

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

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

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

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

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

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

DeploymentPod 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/

アプリケーション負荷に応じたスケールアップ

Horizontal Pod AutoScaler に対して Vertical Pod AutoScaler があります。

完全互換ではありませんが、Vertical Pod AutoScalerというものが k8s 1.9でalpha versionとして提供されています。 従来型のアプリケーションではスケールアウトより、スケールアップのほうが行いやすいのが一般的です。

https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler

アプリケーションの監視

kubernetsで監視すべき項目としてはクラスタ全体の監視とアプリケーションごとの監視になります。

  • クラスタ全体の監視については後述します。
  • 稼働しているアプリケーションの監視(Pod の監視)

Kubernetes クラスタの操作性を向上させる

Rancher でできること

Rancherは様々な環境のkubernetesクラスタを管理するとともに、アプリケーションの管理も行うことができます。

ここではRancherの導入から、アプリケーションのデプロイまでを簡単に実施します。

ここでいうアプリケーションとは kubernetes 上で動くものすべてです。

例えば、kubernetes クラスタを監視するソフトウェアスタック(Prometheus+Grafana+InfluxDB)をkubernetes上で簡単に起動することが可能です。

Rancher を導入する

課題

コンテンツ記載

アプリケーションをデプロイ

課題

コンテンツ記載

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

k8s Master の冗長化

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

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

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

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

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

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

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

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

Helmで提供されているGrafana+Prometheusをデプロイし監視することにチャレンジしてみましょう。 完成図としては以下のようなイメージです。

バックアップはどうするか?

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

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

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

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

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

  • CSI (Container Storage Interface)の既存ボリュームのインポートに対応をまつ
  • Heptio ark: https://github.com/heptio/ark + SVM-DR
Podが停止した場合のアプリケーションの動作

Dynamic ProvisioningされたPVCのPod障害時の動作については以下のような動作になります。 PVCはTridentを使ってデプロイしたものです。

  • 停止したPodは別ノードで立ち上がる
  • Podからマウントしていたボリュームは再度別ノードでもマウントされデータの読み書きは継続可能

Stateful Set を使い、MongoDBを複数ノードで構成し上記の検証を行った結果が以下のリンク先で確認できます。

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

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. マニフェストファイルの修正
オペレーション簡易化のためデフォルトストレージクラスを設定

サンプルで公開されているマニフェストを試したいときに以下の設定をしておくと簡単に起動できるようになります。

  • デフォルトのストレージクラスを設定
  • external ip が付与できるようにするような仕組みを導入する
kubectl patch storageclass [StorageClass名] -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

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

用語集

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

おもに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をデプロイするネームスペースを作成します。

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

$ 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を作成するサンプルです。

上記のマニフェストファイルをインプットとして、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>

1つのエンドポイントから複数のサービス呼びだしを実現することができるようになりました。

DNS登録実施

Ingress経由でアクセスするにはホスト名を使用してアクセスします。 本ラボではDNSサーバを共通で準備しています。

Aレコードのエントリは以下のようなスクリプトを準備することで各ユーザが自身で登録することが可能なしくみです。

#!/bin/bash

ETCD="http://192.168.1.1:2379,http://192.168.1.2:2379,http://192.168.1.3:2379"

ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints $ETCD "$@"

以下のようにしてDNSに登録可能です(jenkins.user1x.ndxlab.net で 192.168.1x.10 を登録します)

$ etcdctl put /dns/net/ndxlab/user1X/jenkins '{"host":"192.168.1x.10"}'

名前解決ができているか確認します。

$ nslookup app-name.user1X.ndxlab.net

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

Installed apps

kubernetes 関係

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

やるべきこと一覧

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

ToDos

課題

コンテンツ記載

(元のエントリ は、 Level4/rancher/index.rst の 15 行目です)

課題

コンテンツ記載

(元のエントリ は、 Level4/rancher/index.rst の 20 行目です)

課題

コンテンツ記載

(元のエントリ は、 /home/docs/checkouts/readthedocs.org/user_builds/datatechnologylab/checkouts/stable/docs/source/Level4/rancher/index.rst の 15 行目です)

課題

コンテンツ記載

(元のエントリ は、 /home/docs/checkouts/readthedocs.org/user_builds/datatechnologylab/checkouts/stable/docs/source/Level4/rancher/index.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/