OpenShift と Kubernetes の違いを理解するためのサンプル演習

最近、OpenShift と Kubernetes の違いを尋ねられた私は、Kubernetes にとっての Red Hat OpenShift は、Linux カーネルにとっての Red Hat Enterprise Linux に例えられると思いつきました。これらのテクノロジーに根本的な違いはありません。むしろ、大体において一方のテクノロジーはもう一方のテクノロジーに包含されます。

具体的に言うと、いくつかの重要で有用な違いがあるものの、Red Hat OpenShift は分散型の Kubernetes とみなせます。このチュートリアルではこうした違いを指摘して説明している優れたブログ記事を蒸し返すのではなく (例えば、「10 most important differences between OpenShift and Kubernetes」、「What’s the difference between Kubernetes and OpenShift?」といったブログ記事があります)、いくつかの相違点を際立たせるような演習を行います。これらの相違点とは、ルートとルーター、そしてシンプルながらも有用なタッチレス・パイプラインを可能にする BuildConfig/ImageStream/DeploymentConfig の三要素です。このチュートリアルの例では Red Hat OpenShift on IBM Cloud を使用します。

前提条件

このチュートリアルでは、読者に Kubernetes の基礎知識があり、OpenShift クラスターを作成済みであることを前提とします。チュートリアルの例で使用するクラスターは Red Hat OpenShift on IBM Cloud 上に作成されているものです。また、サンプル・アプリケーションを構成するファイルが GitHub リポジトリー内に保管されていて、Webhook を追加できることも前提とします。さらに、コマンド・ライン・インターフェースからターゲット OpenShift クラスターを制御できるよう、ローカル環境に Calico をインストールできることも前提条件となります。

まず、アプリケーションをデプロイする OpenShift クラスターを作成しておいてください。このチュートリアルには、1 つのゾーン内で 3 つのワーカー・ノードを使用できる基本的なクラスターが作成されていれば十分です。クラスターにアクセスした後、OpenShift Web コンソールのボタン (右上にある青色のボタン) をクリックすると、クラスターの Web コンソールが開くはずです。以下に、Red Hat OpenShift on IBM Cloud で開いたクラスター Web コンソールのスクリーン・キャプチャーを示します。

OpenShift Web コンソール・ボタンを使って開いたクラスター Web コンソールのスクリーン・キャプチャー

クラスター Web コンソールの「Access (アクセス)」タブに、OpenShift CLI ツールのセットアップ方法に関する情報と手順も記載されています。OpenShift CLI ツールは、クラスターにログオンする際や、クラスターの機能を制御する各種のコマンドを実行する際に使用します 。

所要時間

「前提条件」に記載されている OpenShift クラスターの作成、GitHub リポジトリーの準備、Calico のインストールのすべてが完了していれば、このチュートリアルの手順は約 30 分で完了できます。チュートリアルを読むだけであれば、所要時間は 10 ~ 15 分です。

シンプルなパイプラインを作成する

サンプル・アプリケーションの単純なシナリオで、ルートとルーターの仕組みと、OpenShift の BuildConfig/ImageStream/DeploymentConfig 機能を説明します。このシナリオで必要となるのは、GET REST リクエストを取り、シンプルなメッセージで応答するシンプルな HTTP サーバーです。この HTTP サーバーには外部からパブリック URL でアクセスできる必要があります。また、このサーバーの Git リポジトリーに変更をプッシュするだけで、サーバーのコードを更新して実行中のインスタンスに反映できるようでなければなりません。

このチュートリアルでは、サーバーのコードが格納されている Git リポジトリーが準備されていることを前提とします。リポジトリーに格納するファイルは少なくとも 3 つあります。ファイルのうちの 1 つは、以下に示すようなサーバーの実際のコードが含まれているものです。

サーバーのサンプル・コードが含まれるファイル

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Refreshes the k8s config, obtains the id-token, and automates the following:
# curl -X POST -H "Kube-Id-Token: <token> -d payload --resolve httpserver.npspoc.com:443:169.48.64.46 <url> --cacert <sslcertfile>
# Notice that as long as the host/ip-address pair is in /etc/hosts, --resolve is not needed, and requests does not need to deal with it

import logging
import argparse
import json
import subprocess
import requests

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('--url', default='https://simple-http-server-route-default.voc-sandbox-cluster-7d4bdc08e7ddc90fa89b373d95c240eb-0001.us-east.containers.appdomain.cloud', help='url is set to (default: %(default)s)')
    parser.add_argument('--sslcertfile', default='/Users/isilval/Devt/ssl-cert/CertBundle.p7b', help='sslcertfile is set to (default: %(default)s)')
    return parser.parse_args()

if __name__ == '__main__':
    config = vars(parse_args())
    logger.info(json.dumps(config))

    headers = {'Content-Type' : 'application/json'}
    response = requests.get(config['url'], headers=headers, verify=False)

    logger.info('response code: {}, message: {}'.format(str(response.status_code), response.text))

他の 2 つのファイルには、OpenShift がアプリの Docker イメージを作成する際に使用する Dockerfile、HTTP サーバーを起動するスクリプトをそれぞれ含めます。以下に例を示します。

FROM python:3

COPY simple_http_server.py .
COPY start_simple_http_server.sh .
COPY ssl ./ssl
CMD [ "sh", "./start_simple_http_server.sh" ]
#!/usr/bin/sh

python simple_http_server.py

アプリを管理するパイプラインは、Git リポジトリーから OpenShift の Kubernetes ポッド内で実行中のインスタンスにシームレスにコードを渡す必要があります。このパイプラインを作成するには、まず、リポジトリー内の 3 つのファイルを基に Docker イメージをビルドする BuildConfig を作成します。

BuildConfig では各種のトリガー (Generic、GitHub、ConfigChange など) を 1 つ以上使用することができます。このチュートリアルで必要となるのは、GitHub トリガーです。GitHub トリガーは、リポジトリーに対して (プッシュなどによって) 新しいコミットが行われるたびに、イメージのビルドを開始します 。この例では、GitHub サーバー、プロジェクト、アプリケーションのコードがあるリポジトリーを指定する BuildConfig オブジェクトのソースも Git です。

OpenShift が Git リポジトリーに接続するために必要となる認証情報を格納する sourceSecret も用意する必要があります。認証情報一式を指定するには BuildConfig タイプの .yaml ファイルを使用できます。それとは別に、OpenShift CLI では、必要となる重要な値を取って .yaml ファイルを作成する new-build コマンドも使用できます。このコマンドの例を以下に示します。

oc new-build --name=simple-http-server-bc git@github.ibm.com:MarketingSystems/OpenShiftTest.git --source-secret gitsecret

上記のコマンドに含まれる gitsecret は、以下のコマンドを使用して作成します。

oc create secret generic gitsecret --from-file=ssh-privatekey=<ssh-private-key-file> --type=kubernetes.io/ssh-auth

GitHub が実際に BuildConfig に通知して目的のビルドをトリガーさせるには、GitHub に Webhook も用意する必要があります。この Webhook を使用して、OpenShift と BuildConfig への接続を確立するという仕組みです。Webhook 内で使用する特定の URL は、BuildConfig に対応する OpenShift コンソール・ページから取得できます (以下の例を参照)。

HTTP サーバーを起動するサンプル・スクリプト

作成される BuildConfig からは、ImageStream が出力されます。デフォルトのタグは、new-build コマンドで BuildConfig に指定した名前です。もう 1 つ注目する点として、OpenShift は BuildConfig のストラテジーを dockerStrategy に定義するために、ベース・イメージのソースとして、Dockerfile で指定されている ImageStream を使用します。この追加の ImageStream も作成されて、Dockerfile 内のベース (つまり FROM) イメージの名前でタグ付けされます。作成された ImageStreams イメージを確認するには、CLI を使用することができます (以下の例を参照)。

CLI を使用して作成された ImageStream を確認する画面のスクリーン・キャプチャー

使用される Docker リポジトリーは、OpenShift によって管理される、クラスター内部のリポジトリーです。

ここまでのところで、ImageStream が何を表すのかについて概要をつかめたことでしょう。ImageStream は内部 Docker レジストリーまたはリポジトリーに格納された一連のイメージ・バージョンであり、BuildConfig でも DeploymentConfig でもシンクおよびソースとして使用できます。パイプラインの 2 番目のステップはこのオブジェクトによって処理されるため、明示的に作成しなければならないものはありません。

パイプラインの 3 番目のステップでは、管理可能なポッドにアプリをデプロイして実行します。ただし、通常の Kubernetes でのように通常の Deployment オブジェクトを使用してポッドへのアプリのデプロイを管理するのではなく、OpenShift では DeploymentConfig タイプのオブジェクトを使用できるようになっています。Tomasz Cholewa 氏が指摘しているように、「OpenShift では Kubernetes とは異なるデプロイメントの管理方法を選択」し、「コントローラーではなく、プロセス全体を制御する専用のポッドに基づく高度なロジックによって」その管理方法を実装しています。

YAML 内で指定される DeploymentConfig は、通常の Deployment とかなり似ていて、同じく containers セクション内でアプリのイメージへの Docker レジストリーでのパスを指定します。このアプリのイメージへの Docker レジストリーでのパスを指定するのは、BuildConfig の出力として作成された ImageStream に含まれる Docker リポジトリー・アイテムです。ただし、DeploymentConfig 内の triggers 要素には重要な違いがあります。トリガーには ConfigChangeImageChange といったさまざまなタイプがありますが、このチュートリアルで重要となるトリガー・タイプは ImageChange です。

DeploymentConfig に含まれる要素のうち、重要なのは labels 要素です (メタデータの一部となっています)。この要素は、Deployment を含む他のアーティファクト・タイプにも共通します。どのラベルを使用するかに関わらず、そのラベルが、アプリを公開するために使用する Service に対して DeploymentConfig を特定する際の鍵となります。このチュートリアルでは例として、以下の DeploymentConfig を使用します。

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  name: simple-http-server-dep
  namespace: default
  labels:
    app: simple-http-server-dep
spec:
  template:
    metadata:
      labels:
        name: simple-http-server-dep
        app: simple-http-server-dep
    spec:
      containers:
        - name: simple-http-server-ctr
          image: 'docker-registry.default.svc:5000/default/simple-http-server-bc:latest'
          ports:
            - containerPort: 80
              protocol: TCP
  replicas: 1
  triggers:
    - type: "ConfigChange"
    - type: "ImageChange"
      imageChangeParams:
        automatic: true
        containerNames:
          - "simple-http-server-ctr"
        from:
          kind: "ImageStreamTag"
          name: "simple-http-server-bc:latest"
  strategy:
    type: Rolling
  paused: false
  revisionHistoryLimit: 2
  minReadySeconds: 0

以下のコマンドを使用して、DeploymentConfig ファイルを適用します。

oc apply -f simple-http-server-dc.yaml

このコマンドによって、アプリ用の実行中コンテナーを格納するポッドが作成されます。作成されたポッドを確認するには、デプロイメントのレプリカに関するページの下部を見てください。

Red Hat OpenShift on IBM Cloud 内のデプロイメントのレプリカに関するページのスクリーン・キャプチャー

Red Hat OpenShift on IBM Cloud 内のデプロイメントのレプリカに関するページのスクリーン・キャプチャー

アプリを外部に公開する

ここまでの手順でシンプルな HTTP サーバーが稼働中の状態になりました。けれどもこのサーバーには、サーバーのコンテナーを実行するポッドに接続されたターミナルから内部でアクセスすることしかできません。

アプリを外部に公開するのは同じく簡単なことです。Service を使用してアプリを公開し、実行中のコンテナーにトラフィックを転送するルーターにアプリを接続するルートを作成するだけで、外部からでもアプリに接続できるようになります。Service を作成するには、通常の Kubernetes で行う場合と同じ方法で YAML ファイルを定義できます。この YAML ファイルは、以下のような内容になります。

apiVersion: v1
kind: Service
metadata:
  name: simple-http-server-service
  namespace: default
  labels:
    name: simple-http-server-service
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8089
  selector:
    app: simple-http-server-dep

ここには注目すべき重要なアイテムがいくつかあります。具体的には、外部ポートを定義する ports 要素と、アプリがコンテナー内で listen する targetPort 要素です。また、実行中のアプリを管理する DeploymentConfig を宣言型で参照する selector 要素も重要です。この selector は、DeploymentConfig のメタデータ内のラベルと、名前および値の両方で一致していなければなりません。以下の例に、単一のラベルを示します。

app: simple-http-server-dep

この Service を使用して、トラフィックを転送するルートを作成できます。ルートにはルーターを割り当てる必要があるので、そのルーターも作成する必要があります。(このチュートリアルの単純な例と同じような) デフォルトのプロジェクトまたは名前空間を使用するのでかまわなければ、OpenShift に用意されているデフォルトのルーターを使用できるので、作成する必要はありません。

補足すると、このチュートリアルでは プロジェクト名前空間 を同義語として使用しています。これも OpenShift と Kubernetes の相違点の 1 つですが、OpenShift でのプロジェクトは基本的に、いくつかのアドオンを追加した名前空間です。プロジェクトと名前空間の違いについて詳しくは、セクション「9. OpenShift projects are more than Kubernetes namespaces」を参照してください。

ルートの作成に話を戻すと、デフォルト・ルーターのページを使用することもできます。

ルーターのページの例を示す画面のスクリーン・キャプチャー

Create route (ルートを作成) 」をクリックし、ルートの名前、アプリの外部 URL のホスト名とパスを入力し、アプリを公開するために定義した Service を選択します。ホスト名を入力するには、2 つの方法があります。1 つは、フィールドを空白のままにして、ルートに指定した名前、プロジェクトの名前、クラスター名、生成された一意の ID に基づいて OpenShift にホスト名を生成させることです。 このチュートリアルの例では、以下のホスト名が生成されます。

simple-http-server-route-default.voc-sandbox-cluster-7d4bdc08e7ddc90fa89b373d95c240eb-0001.us-east.containers.appdomain.cloud

長々としたホスト名であることは認めますが、もう一方の方法では、OpenShift に構成する独自のホスト名を入力してから、そのホスト名を適用するように DNS 構成に関するすべての作業を行わなければなりません。このチュートリアルの単純な例には、生成されたホスト名を使用します。こうすれば、ホスト名を有効にするために必要な作業がなくなります。

ルートの定義に指定できるアイテムには、ルートのセキュリティー・レベルもあります。「Secure route (セキュア・ルート)」チェックボックスをオンにすると、TLS レベルのセキュリティーを有効にして、URL で HTTPS プロトコルを使用できるようになります。このチュートリアルではエッジや再暗号化ではなく、パススルー・レベルの TLS 終端を使用して、OpenShift にデフォルトの証明書を使用させます。

OpenShift のルートは Kubernetes の Ingress や Istio ゲートウェイにかなり似ています。

アプリへの外部アクセスを可能にするために最後に必要な作業は、適切なファイアウォール・フローを開始することです。インストール済み環境内で Calico を使用してファイアウォール・フローを管理します。Calico がインストールされていれば、OpenShift CLI で以下のコマンドを使用することによって、対応する Calico 構成を生成できます。

ibmcloud oc cluster config --cluster <cluster-id> --admin --network

<cluster-id> は、メインの OpenShift クラスターの「Overview (概要)」タブで確認できます。

Calico でクラスターの構成を指すために、以下のようにして対応するファイルを移動します。

mv ~/.bluemix/plugins/container-service/clusters/<cluster-id>-admin/calicoctl.cfg /etc/calico

これで、YAML ファイルを使用して Calico フローを定義できます。以下に例を示します。

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: allow-simple-http-server
spec:
  applyOnForward: true
  ingress:
  - action: Allow
    destination:
      nets:
      - 169.63.135.10/32
      ports:
      - 443
    protocol: TCP
    source: {}
  preDNAT: true
  selector: ibm.role=='worker_public'
  order: 1800
  types:
  - Ingress

この例での nets IP アドレスは、前に記載したデフォルト・ルーターのページ内に示されている「Ingress Points (Ingress ポイント)」項目から取得できます。443 ポートを使用している点に注目してください。慣習として、HTTPS トラフィックには、このポートを使用します。

この YAML を適用するには、OpenShift CLI ではなく Calico CLI を使用して、以下のコマンドを実行します。

calicoctl apply -f allow-http-simple-server.yaml

これで、生成されたホスト名を含む URL を使用するだけで、curl を使ってアプリを呼び出すことができます。

まとめ

GET REST リクエストを受け取ってシンプルなメッセージで応答するシンプルな HTTP サーバーが完成し、この HTTP サーバーにパブリック URL を使用して外部からアクセスできるようになりました。

また、サーバーの Git リポジトリーに変更をプッシュするだけで、サーバーのコードを更新して実行中のインスタンスに反映できることも確認できます。

私が作成した動画「OpenShift exercise recording」で、ローカルでコードを変更して Git にプッシュする方法をデモしてあり、以降の手順の流れを確認できます。前に実行したのと同じ curl コマンドを実行すると、新しい出力メッセージが表示されることを確認できます。この動画を参考に、独自の環境でこの手順を試してください。