連載 [第5回] :
  Red Hat OpenShiftの全貌

OpenShift:アプリケーションの構成と運用

2019年3月28日(木)
連載5回目となる今回は、OpenShiftでデプロイしたアプリケーションの運用に関するトピックを紹介する。

今回は、デプロイしたアプリケーションの運用に関わる設定を中心に紹介していきたいと思います。デプロイメントのヘルスチェックの設定や環境依存情報の取り扱い、永続ストレージの設定など、デプロイメントの「チューニングポイント」といった内容です。今回紹介する機能のほとんどが、Kubernetesで実装されているものなので、すでにご存知の方もいるかもしれませんが、OpenShiftのコマンドを使った設定方法と一緒に確認していきましょう。

アプリケーションのヘルスチェック

Readiness Probe と Liveness Probe

Readiness ProbeとLiveness Probeは、OpenShift(Kubernetes)が提供しているアプリケーションへのヘルスチェックの機能です。2つのヘルスチェックの違いですが、Readinessには「準備」、Livenessには「生存」という意味があり、ヘルスチェックが失敗した際の挙動が異なります。Readiness Probeが失敗した場合には、そのPodへのトラフィックを流さなくなります。一方Liveness Probeが失敗した場合には、そのPodの再起動を行います。

Readiness ProbeとLiveness Probeのヘルスチェック失敗時の挙動

Readiness ProbeとLiveness Probeのヘルスチェック失敗時の挙動

またReadiness ProbeとLiveness Probeのいずれも、以下に示す3種類のヘルスチェックの方法を選択できます。

ヘルスチェックの方法

種類意味
execコマンドの実行結果によるヘルスチェック
tcpSocketTCPソケットによるヘルスチェック
httpGetHTTP Getリクエストによるヘルスチェック

これらの設定の詳細なオプションは、explainコマンドで確認が可能です。

oc explain によるliveness Probe設定値の確認コマンド

# oc explain pod.spec.containers.livenessProbe

Readiness ProbeとLiveness Probeのどちらにも、インターバルの値やタイムアウト値、失敗とみなす条件など、多くの設定項目がありますが、設定値に関する詳細な解説は省略します。まずは、実際の動きを確認してみましょう。

Liveness Probeの検証準備

最初は、前回の記事で紹介したビルドの方法のおさらいです。Dockerビルドストラテジーを使って、検証用アプリケーションのビルドとデプロイをしてみましょう。

リスト1:ディレクトリ内のファイルリスト

# ls
Dockerfile  main.go

リスト2:Dockerfile

FROM golang:alpine

WORKDIR /app
COPY main.go .

CMD go run main.go

リスト3:検証用アプリケーションのコード

package main

import (
    "fmt"
    "net/http"
    "time"
)

func rootHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World")
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("%v: request from: %v\n", time.Now(), r.RemoteAddr)
}

func main() {
    http.HandleFunc("/", rootHandler)
    http.HandleFunc("/health", healthHandler)
    http.ListenAndServe(":8080", nil)
}

アプリケーションと呼べるほどのものではありませんが、/healthにヘルスチェックを受け付けるエンドポイントを実装しています。今回は、検証目的のため、リクエストが発生した時刻を表示し、Liveness/Readiness Probeが定期的にヘルスチェックを実行していることを確認します。コード(main.go)とDockerfileの準備が整ったら、ビルドとデプロイを実行します。

ビルドとデプロイの手順

$ oc new-build --name=go-httpd --strategy=docker --binary
$ oc start-build go-httpd --from-dir=. --follow
$ oc new-app go-httpd

oc get podでPodのRunning状態が確認できれば、次からLiveness Probeの動きを確認していきます。

Liveness Probeの確認

Liveness Probeのテストを実施してみましょう。DeploymentConfigに書き込むこともできますが、oc set probeコマンドが用意されているので、ここではそれを使って設定してみます。

oc set probeコマンドの例

$ oc set probe dc/go-httpd --liveness --get-url=http://:8080/health

--livenessでLiveness Probeを指定し、--get-urlでヘルスチェックのリクエストを受け付けるアクセスポイントを指定しています。設定後、再デプロイされたPodのログを見てみましょう。デフォルトのヘルスチェック間隔である10秒ごとにリクエストが届いていれば正常です。

Podのログを確認

# oc logs go-httpd-4-vxwjp
2019-01-06 07:47:45.798994545 +0000 UTC m=+4.165327161: request from: 10.128.0.1:54808
2019-01-06 07:47:55.897736149 +0000 UTC m=+14.264069102: request from: 10.128.0.1:54878
2019-01-06 07:48:05.799532904 +0000 UTC m=+24.165865675: request from: 10.128.0.1:54988
2019-01-06 07:48:15.7983669 +0000 UTC m=+34.164699506: request from: 10.128.0.1:55078
2019-01-06 07:48:25.798541175 +0000 UTC m=+44.164873661: request from: 10.128.0.1:55142
2019-01-06 07:48:35.798272671 +0000 UTC m=+54.164605630: request from: 10.128.0.1:55244

正常時のヘルスチェックは問題がなさそうです。Liveness Probeはヘルスチェックが失敗すると、Podの再起動が実施されると前述しました。そこで、プロセスにSTOPシグナルを送って確認してみましょう。

コンテナ内のgoプロセスを停止させるコマンド:

goプロセスを停止してみる

$ oc exec go-httpd-4-vxwjp -- pkill -STOP go

Liveness Probeのログを確認する場合には、eventログを参照します。`oc get event`でイベントログを確認してみましょう。

eventログを確認

$ oc get event -w
0s        0s        1         go-httpd-4-k7kc7.157c67383af6b0b8   Pod       spec.containers{go-httpd}   Warning   Unhealthy   kubelet, knakayam-ose311-all   Liveness probe failed: Get http://10.128.1.138:8080/health: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
0s        10s       2         go-httpd-4-k7kc7.157c67383af6b0b8   Pod       spec.containers{go-httpd}   Warning   Unhealthy   kubelet, knakayam-ose311-all   Liveness probe failed: Get http://10.128.1.138:8080/health: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
0s        20s       3         go-httpd-4-k7kc7.157c67383af6b0b8   Pod       spec.containers{go-httpd}   Warning   Unhealthy   kubelet, knakayam-ose311-all   Liveness probe failed: Get http://10.128.1.138:8080/health: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
0s        0s        1         go-httpd-4-k7kc7.157c673d04c21ac6   Pod       spec.containers{go-httpd}   Normal    Killing   kubelet, knakayam-ose311-all   Killing container with id docker://go-httpd:Container failed liveness probe.. Container will be killed and recreated.
0s        58s       2         go-httpd-4-k7kc7.157c672f9672fdb6   Pod       spec.containers{go-httpd}   Normal    Pulling   kubelet, knakayam-ose311-all   pulling image "docker-registry.default.svc:5000/ch05/go-httpd@sha256:2643d3821c64bfd0a51d5f7753e4fa3f03cbe703e33058f354a3b7210da1a3a8"
0s        58s       2         go-httpd-4-k7kc7.157c672fb212efda   Pod       spec.containers{go-httpd}   Normal    Pulled    kubelet, knakayam-ose311-all   Successfully pulled image "docker-registry.default.svc:5000/ch05/go-httpd@sha256:2643d3821c64bfd0a51d5f7753e4fa3f03cbe703e33058f354a3b7210da1a3a8"
0s        58s       2         go-httpd-4-k7kc7.157c672fb75df174   Pod       spec.containers{go-httpd}   Normal    Created   kubelet, knakayam-ose311-all   Created container
0s        58s       2         go-httpd-4-k7kc7.157c672fccab6180   Pod       spec.containers{go-httpd}   Normal    Started   kubelet, knakayam-ose311-all   Started container

Liveness Probeが失敗して、期待通りPodが再起動されていることを確認できました。

init Containerを使った起動時の外部サービスチェック

さて、少し寄り道になりますが、Liveness Probe/Readiness Probeの紹介をしたついでに、init Containerの機能についても簡単に触れておきます。init Containerを設定した場合、アプリケーションコンテナは、init Containerの正常終了後に起動することが保証されます。この機能を利用することで、「別のサービスが起動していることを確認してから、アプリケーションを立ち上げる」といったことが可能になります。

例えば、myserviceというサービスへの名前解決ができるようになってから、アプリケーションPodを立ち上げる場合には、次のようにinit Containerを設定します。

リスト4:init Containerの設定例

  initContainers:
  - name: init-myservice
    image: busybox
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  containers:
    ...略 ...

上の例では、busyboxコンテナを立ち上げ、nslookupコマンドでmyserviceの名前解決を確認しています。Readiness Probeとは異なり、init Containerは起動時のみという制約もありますが、これでPodの別サービスへのアクセス確認などが可能です。

またinit Containerには、この他にもデータの初期化、ファイルや静的コンテンツのダウンロードといった使い道もあります。

永続ストレージの設定

さて、次の設定ポイントは永続ストレージです。まずは各ストレージの性質を理解し、ストレージの選定方法について話しをしてから、後半で設定方法を見ていきます。

永続ストレージの全体像

まず、OpenShiftで永続ストレージをする際の全体像を理解します。

OpenShiftでのストレージの要求フロー

OpenShiftでのストレージの要求フロー

永続ストレージを利用する際には、PVC(PersistentVolumeClaim)、PV(PersistentVolume)といったオブジェクトを作成します。PVCは、ストレージを要求するために利用するオブジェクトで、アプリケーション開発者が作成しPodと紐づけます。一方PVは、バックエンドのストレージを抽象化したオブジェクトで、クラスタ管理者側で作成します。アプリケーション開発者がPVCを作成することで、ストレージの要求が発生します。ストレージの要求が発生すると、OpenShiftがバインド可能なPVを見つけてバインドするという仕組みです。

ストレージの種類と選定

OpenShiftのPVで指定可能なストレージには、ブロックデバイスタイプのストレージとファイル共有タイプのストレージ(以下、簡単に「ブロックストレージ」「ファイル共有ストレージ」と表記)があります。ブロックストレージとは、例えばAmazon EBS、Gluster Block、iSCSIといったストレージです。一方ファイル共有ストレージは、NFSやGlusterFSといったストレージです。基本的にブロックストレージを利用した場合には、複数のコンテナからの同時書き込みができませんが、ファイル共有ストレージでは、それが可能です。OpenShiftやKubernetesでは、前者のことを「ReadWriteOnce(複数クライアントからの書き込みは不可で読み込みは可能な場合は、ReadOnlyMany)」、後者を「ReadWriteMany」と表記しています。ストレージを選択する際には、アプリケーションのストレージアクセスの性質やスケールアウトを想定しているかなど、性質を見極めることが必要です。

またストレージの選定には、ReadWrieManyやReadWriteOnceといった性質の他にも、デフォルトでStorageClassをサポートしているか否かも検討材料の一つになるかもしれません。StorageClassとは、PersistentVolumeで利用するボリュームをオンデマンドに切り出す仕組みです。StorageClassを利用すれば、クラスタの管理者がPersistentVolume用に予め準備しておく必要がなくなり、PVCが作成されたタイミングでPVがStorageClassによって作成されることになります。

StorageClassを利用した場合のストレージの要求フロー

StorageClassを利用した場合のストレージの要求フロー

永続ストレージの設定方法

それでは、実際にNFSをバックエンドにしたPV/PVCを利用してみます。今回はStorageClassを利用せず、PersistentVolumeを直接使います。上で述べた通り、PVの作成はクラスタ管理者の作業になりますので、PVがcreateできる権限、例えばcluster-admin権限が必要になります。

リスト5:NFSをバックエンドに使うPVの定義

cat <<EOF > ./pv1.yaml
apiVersion: "v1"
kind: "PersistentVolume"
metadata:
  name: "pv0001"
spec:
  capacity:
    storage: "1Gi"
  accessModes:
    - "ReadWriteMany"
  nfs:
    path: "/nfs"
    server: "192.168.133.4"  # ホスト名/IPはご利用の環境に合わせて変更する必要があります。
  persistentVolumeReclaimPolicy: "Recycle"
EOF

PVの作成コマンド

oc create -f pv1.yaml

今回はNFSを利用し複数クライアントからの書き込みができるので、上で触れたReadWriteManyをPVに設定しています。クラスタ管理者側でのPV準備作業が済んだら、次はアプリケーション開発者側の作業であるPVCを作成します。

リスト6:PVCの定義

cat <<EOF > ./pvc.yaml
apiVersion: "v1"
kind: "PersistentVolumeClaim"
metadata:
  name: "claim1"
spec:
  accessModes:
    - "ReadWriteMany"
  resources:
    requests:
      storage: "1Gi"
EOF

PVCの作成コマンド

oc create -f pvc.yaml

PVCがPVにバインドされたかどうかは、oc get pvcで確認ができます。STATUSがBoundとなっていることを確認してみましょう。

PVC確認コマンド

$ oc get pvc
NAME      STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
claim1    Bound     pv0001    1Gi        RWX                           2s

これで、PVC(claim1)が利用できる状態となりました。あとは、アプリケーションからこのclaim1を利用するだけです。oc set volumeコマンドを使って、DeploymentConfigを更新しましょう。

oc set volumeコマンドによるDeploymentConfigの更新

$ oc set volume dc/go-httpd --add -t pvc --name=test-vol --claim-name=claim1 --mount-path=/mnt

また、pod/<POD名>を引数にしてoc set volumeコマンドを実行してみましょう。mountされているvolumeとマウントポイントが確認できます。

oc set volumeコマンドによるマウントポイントの確認

$ oc set volume pod/go-httpd-5-5892n
deploymentconfigs/go-httpd
  pvc/claim1 (allocated 1GiB) as test-vol
    mounted at /mnt

さて、oc get podコマンドでPodがRunning状態になっていれば良いのですが、もし、Podがpending状態になってしまった場合には、oc get eventでイベントログを確認してみてください。

oc get eventコマンドによるイベントログの確認

$ oc get event

「マウントの実行に失敗している」というログがある場合には、OpenShiftのNodeにsshでログインして、ローカルでNFSがマウントできるかを確認してください。OpenShift Nodeは、内部でmountコマンドを発行してマウントを実行しています。まずは、ホスト上のマウントコマンドで同様のエラーが発生しないことを確認してみてください。

最後に、Podを削除しても永続ストレージ上のファイルが残っていることを確認しておきましょう。

永続ストレージ上のファイルは残ることを確認

# oc rsh go-httpd-5-mnkzk touch /mnt/test-file

# oc delete pod go-httpd-5-mnkzk
pod "go-httpd-5-mnkzk" deleted

# oc rsh go-httpd-5-z62nj ls /mnt
test-file

Podが再作成されても、/mnt以下にストレージがマウントされ、ファイルもそのまま残っているようです。以上が、永続ストレージの簡単な使い方でした。

連載バックナンバー

クラウド技術解説
第5回

OpenShift:アプリケーションの構成と運用

2019/3/28
連載5回目となる今回は、OpenShiftでデプロイしたアプリケーションの運用に関するトピックを紹介する。
クラウド技術解説
第4回

Projectとアプリケーションデプロイ

2019/1/18
連載4回目となる今回は、OpenShiftにおけるProjectの解説、イメージのビルドやデプロイについて学びます。
クラウド技術解説
第3回

開発環境の準備とOpenShiftへのアクセス

2018/12/20
連載3回目となる今回は、OpenShiftの学習と開発のための環境を準備する手順を紹介いたします。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています