ヤフー株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。LINEヤフー Tech Blog

テクノロジー

OpenStackのコントローラをKubernetes上にデプロイする

はじめに

OpenStackの運用エンジニアをしている木下です。
今回はマルチプラットフォームCI環境に関する連載の第5弾として、OpenStack上へKubernetesをデプロイする取り組みについて紹介したいと思います。
マルチプラットフォームCI環境の目的は、イメージビルドによってさまざまなプラットフォーム上でサービスを迅速に稼働させる事にあります。そして、連載の中ではプラットフォームとしてKubernetesを中心に紹介をしてきました。
本エントリーでは、OpenStackを一つのアプリケーションと考えて、Kubernetes上へデプロイしようとする試みに関して説明したいと思います。

今回の取り組みに至った経緯

まずは、なぜOpenStackをKubernetes上で稼働させようとしているのかについて簡単にお話します。
ヤフーでは、2012年からOpenStackを利用してプライベートクラウド環境を構築・運用しています。
現在、社内で稼働しているOpenStackのクラスタ数は30以上、コントローラノードのマシンだけでも仮想および物理を合わせて1500台以上の規模になっております。
これだけの台数のコントローラノードを管理・運用することはOpenStackの運用のチームにとって、大きな負荷となっております。
そこで私たちは、この問題を解決すべくOpenStackのコントローラノードをコンテナ化してKubernetes上にデプロイしていくことで、コントローラノードの管理の一部をKubernetesによって行おうと考えました。

Kubernetes上へOpenStackのコントローラノードをデプロイしてみる

次のようなyamlファイルを記述し、実際にOpenStackのコントローラをKubernetes上で稼働させてみました。
今回動かしたコンポーネントは、keystone glance nova neutron horizon(mysql rabbitmq)です。
あくまで検証中であるため、一部コントローラ系のコンポーネントやコンピュートノードに関しては、今回は対応しておりません。具体的にどのような構成を取っていくのかを含めて、今後の課題となっています。

ここでは、次の観点から今回どのようにKubernetes上でOpenStackを稼動させたかについて説明します。

  • Podの管理方法
  • 各コンポーネントへのアクセス方法
  • データを保持する必要のあるコンポーネントへの対応方法
  • configの適用方法
  • コンポーネントの初期化処理の方法

まずは、各コンポーネントが稼動するPodをどのように起動しているかから説明いたします。
Podの管理はDeploymentを用いています。
Replication Controllerではなく、Deploymentを用いることでロールバックなどの機能が利用できます。詳細に関しては、次のdocumentを参考にしてみてください。

Deploymentについて
http://kubernetes.io/docs/user-guide/deployments/

下記がglanceのPodを起動するyamlファイルです。glance-registryはglance-apiから参照できれば良いので、同一Podの中にまとめて起動しています。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: glance
spec:
  replicas: 1
  template:
    metadata:
      name: glance
      labels:
        app: glance
    spec:
      containers:
        - name: glance-api
          image: artifactory:6555/openstack/glance
          command: ["/bin/glance-api"]
          args: ["--config-file=/etc/glance/glance-api.conf", "--log-file=/var/log/glance/glance-api.log"]
          ports:
            - containerPort: 9292
          volumeMounts:
            - name: configs-glance
              mountPath: /etc/glance
        - name: glance-registry
          image: artifactory:6555/openstack/glance
          command: ["/bin/glance-registry"]
          args: ["--config-file=/etc/glance/glance-registry.conf", "--log-file=/var/log/glance/glance-registry.log"]
          ports:
            - containerPort: 9191
          volumeMounts:
            - name: configs-glance
              mountPath: /etc/glance
            - name: glance-image
              mountPath: /var/lib/glance/images
      volumes:
        - name: configs-glance
          configMap:
            name: glance-confs
        - name: glance-image
          persistentVolumeClaim:
            claimName: glance-pvc

次に各コンポーネント間の連携方法です。Kubernetesでは通常、Pod間は直接やり取りをせず、Serviceというものを介して通信を行います。通常、ServiceにはIPアドレスのみが付与されますが、クラスタDNSを稼動させておくことで、作成したServiceの名前を用いたアクセスも可能になります。

Serviceについて
http://kubernetes.io/docs/user-guide/services/

下記は、glanceのServiceの例です。glance-apiとglance-registryで利用するポートを明示的に指定しています。selectorの項目で指定しているものと同名のlabelsをpodのmetadataに持たせることで、そのPodへ通信をproxyすることができます。

apiVersion: v1
kind: Service
metadata:
  name: glance
  labels:
    app: glance
spec:
  selector:
    app: glance
  ports:
    - port: 9292
      name: glance-api
    - port: 9191
      name: glance-registry

これによって次のようにnova.confの中でglanceの位置を指定することができます。

[glance]
api_servers=http://glance:9292

冗長性に関してもServiceを利用することで実現できます。同じmetadataを持ったPodを複数起動することで、Podへの通信が負荷分散されます。起動Pod数に関してはDeploymentの中で指定するreplicaの値を変更するだけで容易にPodの数を変更できます。
Serviceを利用することでPodが持つIPアドレスではなく、labelベースでproxy先を指定できるので、ユーザが容易に定義することが可能です。
また、これによって従来は負荷分散するために別途構築していたnginxやHAProxyといったものを用意する必要がなくなりました。

このServiceが持つIPアドレスはKubernetesの内部でのみ利用可能なもののため、外部からのアクセスをService経由で受けるためには、IngressやLBaaSの利用が必要になります。こちらの内容については次回連載で説明予定です。

glanceやmysqlのように、データを永続的に保持したいものに関しては、Persistent Volumesを利用しています。
今回のKubernetesは別のOpenStackで払いだされたVMを利用して構築しているため、Persistent VolumesのバックエンドとしてCinderを利用しています。
詳細は、前回の連載を参照してください。
たとえ何かがきっかけでPodが死んでしまったとしても、別のnodeで起動してきたPodに対して自動でvolumeをマウントし直します。
アプリケーションのライフサイクルと、大事なデータが入ったvolumeのライフサイクルが別で管理できるようになります。

下記は、イメージの実体を保持させるためのpvcのyamlです。
Deploymentの部分で記述したyamlの中で、このglance-pvcを指定してマウントしています。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: glance-pvc
  annotations:
    volume.alpha.kubernetes.io/storage-class: cinder
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Gi

各コンポーネントの設定ファイルですが、現在はKubernetesのconfigmapという機能を利用して、Podの起動時にマウントしています。
configmapはKubernetesのリソースとして管理されるため、各nodeすべてに設定ファイルを配布してそれをPodからマウントするといった処理は不要になります。

configmapについて
http://kubernetes.io/docs/user-guide/configmap/

db_syncの実行やendpoint・tenantやuserの作成といった初期構築のみに必要となる作業も、それを行うだけのシェルを実行するPodを作成することで対応できます。
次のようにrunでdb_syncコマンドを指定することで実現しています。
Dockerイメージはapiなどが稼働するpodと同じものを利用して、Kubernetesのrunを変えることで対応しています。
Dockerfileの中で実行するバイナリを指定してしまうと、それごとにDockerイメージを作成する必要がありますが、runを利用することで、同一のイメージを使い回すことができます。

apiVersion: v1
kind: Pod
metadata:
  name: glance-dbsync
spec:
  containers:
    - name: glance-dbsync
      image: artifactory:6555/openstack/glance
      command: ["/bin/glance-manage"]
      args: ["--config-file=/etc/glance/glance-api.conf", "--config-file=/etc/glance/glance-registry.conf", "db_sync"]
      volumeMounts:
        - name: config-glance
          mountPath: /etc/glance
  volumes:
    - name: config-glance
      configMap:
        name: glance-confs

各コンポーネントの初期化処理部分に関しては、OpenStack Kollaが参考になるかと思われます。
http://docs.openstack.org/developer/kolla/

各yamlファイルを見るとわかりますが、OpenStackのアプリケーションがインフラに依存していないのがわかります(IPアドレスの指定などが存在していない)。
Kubernetesのnodeがどういった環境で動作しているかといったことを全く意識しないため、従来の構成管理ツールで発生していた、インフラ記述コードの複雑化を避けることができます。

OpenStackがKubernetesで稼働させてみると

まだまだ検証段階ですが、構築・運用をする上で変わりそうな点を下記の表にまとめてみました。

マシン上でプロセスとして稼働させていたとき Kubernetes上にdeployしたことで
構成管理 chefやAnsibleで行うが、
複雑化していた
Kubernetesのyamlになりシンプルになった
障害対応 問題の切り分けやログの確認を行い必要に応じて対応 Podを使い捨て
nodeがおかしそうならnodeごと捨てることも可能オートヒール機能
冗長化と負荷分散 HAProxyやnginxを別途準備する必要 KubernetesのServiceをそのまま利用
コード変更の適用 パッケージを作りなおして適用、開発環境で確認 DockerImageを作り、
Kubernetesのコマンドで
rolling update
スケール VMを新規に起動しセットアップ、ロードバランサの設定を変更 yamlを変更してコマンド一つで適用

これらのメリットはOpenStackに限った話ではないことがわかるかと思います。
従来の方法(物理マシンや仮想マシン上でOpenStackのプロセスを稼働させる場合)でも同様の運用まで落としこむことが可能ですが、Kubernetesではデフォルトでそれらの機能を提供しているため、他のツールの学習コストや作りこみの工数が削減できます。

まとめ

本エントリーでは、Kubernetes上でOpenStackを稼動させる取り組みについて紹介しました。
Kubernetesは、microserviceを稼動させるプラットフォームとして非常に優れています。サービスディスカバリや負荷分散を備え、Podに問題が発生した場合は自己修復機能によって要求されるレプリカ数を維持します。
これによって、人の手で行ってきた障害時の対応の一部をKubernetesによって自動で行えるようになり、空いた時間で他のコンポーネントの検証やCI/CDパイプラインの構築に取り組むことができるようになります。

また、OpenStack自体はmicroserviceアーキテクチャであり、自然にKubernetes上へデプロイすることが可能です。
本エントリーでは部分的に説明をさせていただきましたが、Serviceを利用した各コンポーネント間の連携やconfigmapの利用など、Kubernetesが提供している機能を素直に利用することができます。

Austinで開催されたOpenStackSummitではCoreOSがOpenStackをKubernetes上で稼働させるデモを行い話題となったり、OpenStackをDockerコンテナとしてデプロイするKollaが、Kubernetes上でOpenStackを稼働させようとするなど、OpenStackの稼動環境としてKubernetesは注目を集めています。

OpenStackが稼動するプラットフォームとしてのKubernetes、Kubernetesを稼動させるプラットフォームとしてのOpenStack、どちらの面も今後の展開が非常に楽しみです。

こちらの記事のご感想を聞かせください。

  • 学びがある
  • わかりやすい
  • 新しい視点

ご感想ありがとうございました

このページの先頭へ