3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Multi Tenant OperatorでOpenShiftのマルチテナンシーを簡単に!

Last updated at Posted at 2024-09-19

はじめに

Kubernete/OpenShiftにおける「マルチテナンシー」の実現においては、いくつかの考え方が存在します。ここで言うマルチテナンシーとは、お互いに疎であるアプリケーションの集合体や、それらによって成立するシステムをなんらか物理的・論理的に隔離しつつ、クラスタ内でコンピューティングリソースを共有させることを指します。

特に、単一クラスタにおける最もわかりやすいマルチテナンシーの考え方としてはNameSpaceによる分離です。

image.png

ただし、多くのアプリケーションは複数の専用環境を求めます。具体的には開発環境(Dev)、テスト環境(test)、準本番環境(Stg)、そして商用環境(Prod)等です。各開発フェーズや用途に応じて複数のNameSpaceが必要だと思います。

また、MachinePoolを占有させることも考えられます。

image.png

MachinePoolWorker Nodeをラベンリングしてまとめた存在です。AffinityルールやTaint/Tolerationの設定によって、特定のPodしか特定のラベンリングがされたWorker Nodeにスケジュールされないようにするといったことが可能です。

KubernetesにおけるPodスケジューリング戦略まとめは以下がとてもわかりやすいです。
https://cstoku.dev/posts/2018/k8sdojo-18/

最後は、クラスタ自体を分割し、特定のアプリケーション群やシステム、プロジェクト、環境毎に異なるクラスタを割り当てます。OpenShiftについては「Hosted Control Plane」の登場によって、管理クラスタ上で子クラスタ(Hosted Clusterという)達のControl Plane(Master Nodeで動いているコンポーネント達、略してCP)をホストし、更にAdvanced Cluster Managementを活用してマルチクラスタの作成や管理が非常に容易になりました。

image.png

とは言え、やはりコンピューティングリソースの有効活用やインフラ集約率の向上を考える上で、すべてのシステムやプロジェクト毎に毎回Hosted Clusterを払い出す事は現実的ではないでしょう。単一クラスタの中でNameSpaceを切り、適切なユーザ権限管理やリソース管理(ResouceQuotaの設計)を実施した上で、テナント毎のリソース消費量とコストの可視化・最適化を目指したいところです。

ところが、この「適切なユーザ権限管理やリソース管理」やら「テナント毎のリソース消費量とコストの可視化・最適化」というのは言うは易く行うは難しです。じゃあ具体的にどうやって複数のNameSpaceと複数のユーザを適切な権限で紐づけ、それらを横断してResouceQuotaの設計をするのか?

そこで「Multi Tenant Operator(略してMTO)」が何等か一助になってくれるのではないか?と思い試してみました。

Multi Tenant Operator(MTO)とは

MTOは「Stakater」という企業が提供するKubernetes Operatorです。

image.png

同社のホームページを見ると、Kubernetesインフラの構築運用支援やPlatform Engineeringサービスを提供しているっぽいですね。

image.png

image.png

スウェーデンのストックホルム、David Bagares通りの一角に本拠地を構えているそうです。

さて、このMTOは単一クラスタにおけるマルチテナンシーを実現するためのCRD(Custom Resources Definitions)を提供し、クラスタに対して「テナント」という抽象化レイヤを提供してくれます。このテナントの中でOpenShiftユーザに対する権限の定義を行い、用意するべき環境を定義すると、いい感じにNameSpaceを作成しつつユーザ権限とのマッピング(RBACのバインディング)をしてくれます。

Red Hatの認定Operatorです

Red Hatのブログにもある通り、こちらのOperatorはRed Hatに正式に認定された Certified Operatorです。

無料でも使えますが...

2テナントまでしか作ることができません(泣)。3つめのテナントを作成しようとするとこんなエラーがでます。

Danger alert:エラーが発生しました
admission webhook "vtenant.kb.io" denied the request: free tier limit reached, max allowed tenants: 2. to upgrade, please visit https://www.stakater.com/mto

本格的に利用したい場合は課金してください。料金プランは公式サイトを御覧ください。1クラスタあたり795$/月だそうです。Enterpriseサポートはもちろん付いています。

image.png

MTOでテナントを作ろう

まずはものは試しということで、やってみます。

OpenShiftクラスタの準備

その前にOpenShiftクラスタを用意してください。今回は東京リージョンに2Worker NodeのROSA HCP(Red Hat OpenShift Service on AWS with Hosted Control Plane)を作成しました。また、Cluster-admin権限を有するアカウントを使って以下のユーザを作成しました。RBACはまだ何も設定していません。

  • user0@example.com
  • user1@example.com
  • user2@example.com
  • user3@example.com
  • user4@example.com

また、OpenShiftのコンソール画面「ユーザー管理」→「Group」からexmaple-groupというGroupeを作成し、user3@example.comuser4@example.comをぶち込んでおきます。以下のYAMLをApplyするのでもOKです。

apiVersion: user.openshift.io/v1
kind: Group
metadata:
  name: example-group
users:
  - user3@example.com
  - user4@example.com

さらに、Cluster-roleの「self-provisioner」削除してしまいます。この権限は、OAuth認証したユーザがProjectを自由に作成し、自身が作成したProjectに対するAdmin権限を持つ事ができるものです。

Cluser-admin権限を持つアカウントの「管理者向け表示」にて「ユーザー管理」→「Roles」と進んで、当該Cluster-roleを削除しておいてください。

image.png

そもそもOpenShift自体を一通り触って理解を深めたいよ、しかも無料で!
そんな都合の良い話が、、、あるんです。無料で作成できるRed Hatアカウントがあれば、ROSA HCPのハンズオンが8時間×3回まで試す事ができます。しかも日本語で用意された「歩き方ガイド」も用意されていますので、お気軽にお試しください。
https://rheb.hatenablog.com/entry/202312-rosa-hcp-trial

MTOをインストールする

OpenShiftのOperatorHubで「multi tenant operator」と検索すると出てきます。全てデフォルト設定のままインストールしてください。MTOのコンポーネント群は自動的に作られるNameSpace(multi-tenant-operator)にDeployされます。

image.png

インストールが完了すると複数のカスタムリソース(CR)が作成できるようになります。

image.png

QuotaとTenantを作ってみる

先に今から作る環境の雰囲気を以下の絵で説明します。

image.png

なんだか見慣れないOpenShift環境のポンチ絵です。通常はOpenShiftクラスタの上にいくつかのNameSpaceが切られているだけですが、今回はQuotaTenantと呼ばれる抽象化レイヤが存在しています。

さらに、これから作成するTenantにおいては以下の要件を実現させます。

  • exampleという名前のTenantを作成し、example-quotaという名前のQuotaを参照・適用する
  • 以下のNameSpaceをexampleテナント配下に置いて管理する
    • example-dev
    • example-test
  • exampleテナントに参加するユーザに対してそれぞれ以下の権限を付与する
    • Viewer: exampleテナント内のリソースの閲覧権限のみを付与される
      • user0@exmple.com
    • Editor: exampleテナント内に閉じてリソースの編集権限を付与される
      • user1@exmple.com
    • Owner: exampleテナントの完全な権限を付与される
      • user2@exmple.com
      • Group: exmaple-group
  • exampleテナントに参加する各ユーザは個別のSnadbox環境(NameSpace)を与えられ、Sandbox環境は各ユーザからは見えないようになる
    • ただしViewer権限しか無いuser0@example.comにはSandboxは不要

Quotaは、Tenantから必須で参照されるべきリソースであり、文字通り「Tenantが利用できるコンピューティングリソースの上限」を設定する事ができます。コンピューティングリソースと一言で言っても、かなり細かく設定できます。例を以下に示します。

apiVersion: tenantoperator.stakater.com/v1beta1
kind: Quota
metadata:
  name: example-quota
spec:
  limitrange: #1Pod毎のリソース上限・下限を設定できます
    limits:
      - max:
          cpu: '1'
          memory: 1Gi
        min:
          cpu: 100m
          memory: 100Mi
        type: Pod
  resourcequota: #テナント全体で利用できるリソース上限を設定できます
    hard:
      configmaps: '50'
      requests.cpu: '2'
      requests.memory: 10Gi
      requests.storage: 20Gi
      gp2-csi/requests.storage: '10Gi'
      gp3-csi/requests.storage: '10Gi'
      #<storageClassName>/requests.storage: '上限容量'と書くと、StorageClass毎に上限を個別設定できます
      secrets: '50'
      services: '50'
      services.loadbalancers: '10'

Tenantとして要求できるCPUやメモリ上限はもちろん、ストレージ容量についてもStorageClass単位での個別設定が可能で、かなりきめ細かく設定できます。

なお、ConfigMap数やSecret数、Service数まで設定できます。しかしこれらの設定がイマイチどう意味があるのか自分にはわかっていないので、わかる方は教えてください。NameSpaceにApplyできるConfigMap数を制限してどういう意味があるのか...🤔

また、Pod毎のリソース上限・下限も設定できます。上記のYAMLをApplyしてQuotaリソースをDeployします。

次にTenantリソースを作成します。

apiVersion: tenantoperator.stakater.com/v1beta3
kind: Tenant
metadata: #テナント名です。
  name: example
spec:
  accessControl:
    viewers: #テナント内のリソース閲覧権限のみを与えるアカウントを指定できます
      users:
        - user0@example.com
    editors: #テナント内のリソース操作権限を与えるアカウントを指定できます
      users:
        - user1@example.com
        #編集権限を付与したいアカウント名を入れてください
    owners: #テナント自体及び内部のリソースに対する操作権限を与えるアカウントを指定できます
      users:
        - user2@example.com
      groups: #以下Groupにはuser3, user4が所属しています
        - example-group
  namespaces:
    onDeletePurgeNamespaces: true
    sandboxes: #各ユーザ毎のSandbox環境(NameSpace)を作成し、それぞれのSnadboxが他ユーザからは見えない様にします
      enabled: true
      private: true
    withTenantPrefix: #テナント名-プレフィックスとなるNameSpaceを自動で作成します
      - dev
      - test
  quota: example-quota
  #テナントに適用するQuotaを指定します

このYAMLファイルもApplyしてTenantを作成します。Tenantが作成されると、以下のNameSpaceが作成されます。

image.png

NameSpacemetadata.labelsにはstakater.com/が付与されているはずです。

各ユーザからのTenantに対する操作の挙動を見てみる

まず初めにEditor権限を持つuser1@example.comでOpenShiftに再ログインしてください。「管理者向け表示」では、以下の通りNameSpaceが確認できます。

  • example-dev
  • example-test
  • example-user1-example-sandbox

Editor権限を持っているTenant配下のNameSpaceが見えています。また自身のSandbox環境も見えていますが、他ユーザのSandbox環境は見えてません。各NameSpace名の右端の縦三点リーダ︙をクリックしてみてください。NameSpaceに対するadmin権限が無いため「削除」がグレーアウトしており選択できません。

image.png

次に自身のSandbox内で適当なConfigMapを作成してみます。

apiVersion: v1
kind: ConfigMap
metadata:
  name: user1-configmap
  namespace: example-user1-example-sandbox
data:
  USER1_KEY: hogehoge

何の問題もなく作成できました。

image.png

次にexample-dev内に適当なアプリケーションをDepolyしてみます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
  namespace: example-dev
  labels:
    app: httpd
spec:
  replicas: 3
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: registry.redhat.io/rhel8/httpd-24
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 300m
            memory: 1Gi
          requests:
            cpu: 200m
            memory: 500Mi        
---
apiVersion: v1
kind: Service
metadata:
  name: httpd-service
  namespace: example-dev
spec:
  ports:
  - port: 8080 
  selector: #紐づけるアプリケーションのLabelsを指定します
    app: httpd
  clusterIP: None
---
kind: Route
apiVersion: route.openshift.io/v1
metadata:
  name: httpd-route
  namespace: example-dev
spec:
  to:
    kind: Service
    name: httpd-service
  port:
    targetPort: 8080

こちらはApache Webサーバコンテナを3つのPodで起動するDeploymentです。1PodあたりCPU: 100ミリコア、メモリを500メガバイト要求し、それぞれの上限を200ミリコア・1ギガバイトとしました。

image.png

リソースを作成することができました。Apache Webサーバも問題なく起動している模様です。

image.png

「開発者向け表示」のトポロジー画面でPod数をどんどん増やしてみます。すると当該環境においては10Podまでしか増やすことができなくなりました。

image.png

コンソール画面にも「Quotaを超えました」といった趣旨のエラーメッセージがでています。

今、exapleテナントにおけるコンピューティングリソース上限は

  • CPU上限: 2コア
  • メモリ上限: 10ギガバイト
    でした。そしてApache Webサーバコンテナの1Pod辺りのコンピューティングリソース利用については
  • CPU:
    • 要求: 200ミリコア
    • 上限: 300ミリコア
  • メモリ:
    • 要求: 500メガバイト
    • 上限: 1ギガバイト
      でした。今10Podまでレプリケーションしていますのでメモリについてはまだ余裕があるものの、CPUについては200ミリコア×10Pod≒2コアに達してしまい、これ以上リソース要求できない状態です。

では、NameSpace: example-testに対して、同じApache WebサーバのYAMLファイルをApplyしてみます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
  namespace: example-test
  labels:
    app: httpd
spec:
  replicas: 3
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: registry.redhat.io/rhel8/httpd-24
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 300m
            memory: 1Gi
          requests:
            cpu: 200m
            memory: 500Mi        
---
apiVersion: v1
kind: Service
metadata:
  name: httpd-service
  namespace: example-test
spec:
  ports:
  - port: 8080 
  selector: #紐づけるアプリケーションのLabelsを指定します
    app: httpd
  clusterIP: None
---
kind: Route
apiVersion: route.openshift.io/v1
metadata:
  name: httpd-route
  namespace: example-test
spec:
  to:
    kind: Service
    name: httpd-service
  port:
    targetPort: 8080

いつまで経ってもNameSpace: example-testの方ではPodが起動してきません。

image.png

NameSpace: example-devにDeployしたPodの数を7まで減らします。

image.png

お、NameSpace: example-testの方のPodが3つ起動しました。ではNameSpace: example-testの方のPodを3から4に増やすことを試みます。

増えません。

image.png

では、NameSpace: example-devにDeployしたPodの数を7から6に減らします。

image.png

NameSpacePod数合計は常に10が上限です。つまり同じexampleテナント配下の2つのNameSpace間でQuotaがシェアされていることがわかりました。

なお、現在のexampleテナント全体で利用されているリソース状況については、OpenShiftのコンソール画面でも確認することができます。「管理者向け表示」にて「管理」→「ResourceQuota」と進むと、「AppliedClusterResourceQuotas」というCRを発見できます。このインスタンス名「example」こそが、実はMTOで設定したTenantQuotaそのものなんですね。

image.png

では、NameSpace: example-devのApache Webサーバコンテナを3つまで減らし、NameSpace: example-testの方はDeploymentを削除し元に戻しておきます。

次に、NameSpaceを作成しようと試みます。以下のYAMLをApplyしてみます。

apiVersion: v1
kind: Namespace
metadata:
  name: sample

image.png

駄目でした。Editor権限のユーザでは、勝手にNameSpaceを追加することはできません。あくまで用意されたTenant及びその配下のNameSpaceの範疇でリソースの編集権限を与えられているに過ぎず、Tenantそのものを拡張・縮小したりすることはできないようです。

次にuser0@example.comで再ログインしてください。

image.png

Viewer権限しか無いuser0@example.comにはSandbox環境は与えられません。
(´・ω・) カワイソス

先ほどexample-devにDeployしたApache Webサーバを確認する事は可能です。

image.png

ただし、編集したり削除したりすることができません。

image.png

編集メニューはすべてグレーアウトし、Pod数変更などもできません。ConfigMapを作成しようとしても、GUIに「作成」ボタンすら見えません。

image.png

YAMLファイルをApplyしようとしても駄目です

apiVersion: v1
kind: ConfigMap
metadata:
  name: user1-configmap
  namespace: example-user1-example-sandbox
data:
  USER1_KEY: hogehoge

image.png

Tenant配下のNameSpaceに対する操作権限は本当に見るだけ(Viewer)の様です。

最後に、Owner権限を持つユーザuser2@example.comで再ログインします。
NameSpaceを作成しようと試みますが、

apiVersion: v1
kind: Namespace
metadata:
  name: sample

やはり駄目です。

image.png

しかし、作成しようとするNameSpaceに以下の様なラベルを付与してください。

apiVersion: v1
kind: Namespace
metadata:
  name: sample
  labels:
    stakater.com/tenant: example

あら不思議!sampleという新しいNameSpaceを作成することができました。

image.png

再度user1@example.comでログインし直して同じ様にやってみても駄目です。

image.png

つまり、Owner権限を持つユーザであれば、NameSpaceリソースのmetadata.labels.stakater.com/tenant: <テナント名>を付与することで、テナント配下にNameSpaceを追加することができます。しかしそれ以外の権限では追加できません。これがOwnerEditorの権限の差でした。

ArgoCDのマルチテナンシーを確認

では、ArgoCD(OpenShift GitOps)とMTOの連携機能を見てみます。

IntegrationConfigを変更

その前にMTOのカスタムリソースIntegrationConfigを弄る必要があります。Cluster-admin権限を持つアカウントの「管理者向け表示」で「Operator」→「インストール済みのOperator」と進み、MTOを選択、IntegrationConfigタブでインスタンス名:tenant-operator-configを確認、クリックしてください。

image.png

YAMLのspec.integrationsより下を以下の通り書き換えます。

spec:
(中略)
  integrations:
    argocd:
        namespace: openshift-gitops
        #OpenShift GitopsのArgoCDインスタンスが存在するNameSpace(インストール後のデフォルト値)を入れてあります
  metadata:
    groups: {}
    namespaces:
      labels:
        argocd.argoproj.io/managed-by: openshift-gitops
        #Tenantに所属するNameSpaceに対し、OpenShift Gitops配下とするためのLabelを付与します
    sandboxes: {}

OpenShift GitOps Operatorインストール後に自動作成されるArgoCDインスタンスの所在するNameSpaceを指定し、さらに各Tenant配下のNameSpaceに自動的にラベルargocd.argoproj.io/managed-by: openshift-gitopsを付与する設定をします。これにより各Tenant配下のNameSpaceはArgoCDから管理できようになります。ラベリング対象はNameSpace以外にもユーザGroupやSandobox環境に対しても実施できるようです。

「保存」をクリックして設定を反映します。

exampleテナント配下のNameSpaceに自動的にラベルが付与されています。

image.png

Extensionsの作成

更にExtensionsというカスタムリソースを作成します。こちらはTenantに紐づくArgoCDのプロジェクト(AppProject)を自動的に作成してくれる便利なCRです。

apiVersion: tenantoperator.stakater.com/v1alpha1
kind: Extensions
metadata:
  name: example-extensions
spec:
  tenantName: example
  argoCD:
    onDeletePurgeAppProject: true
    appProject:
      sourceRepos: #Tenantで利用する任意のGitリポジトリのURLを登録できます
        - "https://gitlab.com/masaki-oomura/multi-tenant-operator-demo.git"
        #とりあえず今回は今回のDemo用リポジトリを入れました
      clusterResourceWhitelist: #GitOpsによるApplyを許可するリソース種別を明示的に指定できます
        - group: ""
          kind: ""
      namespaceResourceBlacklist: #GitOpsによるApplyを禁止するリソース種別を明示的に指定できます
        - group: ""
          kind: "Secret"
          #今回はSecretを明示的に禁止にしてみました。

こちらのManifest.yamlをCluster-admin権限を持つアカウントからApplyしておきます。ここまでの流れで本クラスタに適用されたCRは以下の通りかと思います。

image.png

OpenShift GitOps Operatorをインストール

さて、このままCluster-admin権限を持つアカウントにて、OpenShift GitOps Operatorもインストールしておきます。OperatorHubにて検索して、デフォルト設定でインストールします。

image.png

お〜!exampleというAppProjectが自動的に作成されていますね〜

image.png

テンション上がってきました。では、Editor権限をもつuser1@example.comにてOpenShift GitOpsにログインしてみます。

image.png

タイル右上のリンクからOpenShift GitOpsのコンソール画面に遷移し、「LOG IN VIA OPENSHIFT」の方から、user1@example.comのID/PWにてシングルサインオンします。

image.png

「Setteings」→「Project」と進むと、user1@example.comAppProject: exampleに参加していることがわかります。

image.png

では何かアプリケーションをDeployしてみましょう。Postgresqlコンテナを試しにDeployします。ArgoCDアプリケーションのYAMLは以下の通りです。

本記事で利用するManifestはすべて以下のGitlabリポジトリにおいているのでご参考にしてください。
https://gitlab.com/masaki-oomura/multi-tenant-operator-demo.git

application-fail1.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: postgresql-sample-fail1
spec:
  destination:
    name: ''
    namespace: example-dev
    server: 'https://kubernetes.default.svc'
  source:
    path: Postgresql/Manifests-fail1
    repoURL: 'https://gitlab.com/masaki-oomura/multi-tenant-operator-demo.git'
    targetRevision: main
  sources: []
  project: example
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions: []

このArgoCDアプリケーションがApplyするManifestファイルなのですが、~/Postgresql/Manifests-fail1以下には2つのManifestファイルを入れておきました。

manifest.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgresql-pvc
spec:
  accessModes:
    - ReadWriteOnce #アクセスモードはStorageClassによって何が選べるか変わります。AWS EBSは仕様上RWOしか選べません
  storageClassName: gp3-csi
  resources:
    requests:
      storage: 11Gi #要求するStorage容量です。適切な容量を選択しましょう
  volumeMode: Filesystem
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql
spec:
  selector:
    matchLabels:
      app: postgresql
  strategy:
    type: Recreate
  template:
    metadata:
      labels: #Serviceからディスカバリーしてもらう為のKey:Valueをセットします
        app: postgresql
    spec:
      containers:
      - image: registry.redhat.io/rhel8/postgresql-15 #RedHatが公開している安心安全なpostgresqlコンテナイメージです。
        name: postgresql
        envFrom: #別ファイルから環境変数を読み込む様に指定します
        - secretRef: #Secretを参照するように指定します
            name: postgresql-secret #対象のSecretファイル名
        ports:
        - containerPort: 5432 #postgresqlのポート番号です
          name: postgresql
        volumeMounts:
        - name: postgresql-pv #作成されたPVの名称です
          mountPath: /var/lib/pgsql/data #永続ボリュームをマウントするべきパスです。これはLinux上で動くpostgresqlの仕様です。
      volumes:
      - name: postgresql-pv #PVCから作成するPVの名称を指定します
        persistentVolumeClaim:
          claimName: postgresql-pvc #利用するPVCの名称を指定します
secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: postgresql-secret
type: Opaque 
stringData: #ユーザ名とパスワード、初期作成するDB名を指定できます
  POSTGRESQL_USER: user
  POSTGRESQL_PASSWORD: password
  POSTGRESQL_DATABASE: mydatabase
# 通常のApplication開発において、Secretを公開Repositoryに置くのはNGです。あくまでデモ用途です。

Deployamentで設定する環境変数(Postgresqlのユーザ情報や初期作成するDB名)はSecretを参照するようにしました。ちなみに、YAMLにもコメントで記載している通り、通常のApplication開発において、Secretを公開Repositoryに置くのはNGです。stringDataを使わずDataを使っても、それはBASE64デコードすれば誰でも平文に戻せるので、基本的にSecretをその他のManifestといっしょにGitリポジトリで管理するのはおすすめしません。

Hashicorp Vaultを使おう!

では、ArgoCDのコンソール画面にて「CREATE APPLICATION」をクリックし、application.yamlをApplyして「CREATE」をクリックします。

アプリケーションがDeployできません。

image.png

まず、PVCを見てみるとstorageClass: gp3-csiに対して11ギガバイトの容量を要求しています。しかしexampleテナントにおいては10ギガバイトまでしか利用できませんので、Quota超過です。おっとうっかり。一度アプリケーションをDeleteしてください。

image.png

では、Manifestを修正して5ギガバイトまでに直しましょう。修正後のManifestが格納されたリポジトリパスを参照するArgoCDアプリケーションは以下です。

application-fail1.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: postgresql-sample-fail2
spec:
  destination:
    name: ''
    namespace: example-dev
    server: 'https://kubernetes.default.svc'
  source:
    path: Postgresql/Manifests-fail2
    repoURL: 'https://gitlab.com/masaki-oomura/multi-tenant-operator-demo.git'
    targetRevision: main
  sources: []
  project: example
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions: []

同様にArgoCDからApplyを試みます。

image.png

また駄目でした。そういえば、ExtensionsSecretの同期を禁止していました。うっかりうっかり。テヘペロ。SecretがApplyできないと環境変数を参照できないためDeploymentもApplyできません。先ほど同様、ArgoCDアプリケーションは削除しておいてください。

image.png

では、先にNameSpace: example-devSecretを手動でApplyしておき、GitリポジトリからはSecretは除外しましょう。

secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: postgresql-secret
type: Opaque 
stringData: #ユーザ名とパスワード、初期作成するDB名を指定できます
  POSTGRESQL_USER: user
  POSTGRESQL_PASSWORD: password
  POSTGRESQL_DATABASE: mydatabase
# 通常のApplication開発において、Secretを公開Repositoryに置くのはNGです。あくまでデモ用途です。

image.png

コンソール画面のGUIからYAMLをApplyしました。仏の顔も三度まで。Secretを除外したManifest一式を参照するArgoCDアプリケーションは以下です。以下のArgoCDアプリケーションをApplyします。

application-success.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: postgresql-sample-success
spec:
  destination:
    name: ''
    namespace: example-dev
    server: 'https://kubernetes.default.svc'
  source:
    path: Postgresql/Manifests-success
    repoURL: 'https://gitlab.com/masaki-oomura/multi-tenant-operator-demo.git'
    targetRevision: main
  sources: []
  project: example
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions: []

やったぜ。

image.png

OpenShiftのコンソールからもPostgresqlコンテナがDeployできたことを確認できました。

image.png

なおViewer権限しか持っていないuser0@example.comからは、当たり前ですがArgoCDにログインできてもアプリケーションのDeployはできません。

image.png

また、他のユーザが作成したアプリケーションを参照することはできますが、削除もできません。

image.png

このようにして、MTOを利用してArgoCDのマルチテナンシーまで実現できます。ArgoCDのAppProjectに対するRBACと、MTOで設定したTenant向けのRBACがいい感じに同期してくれます。

NameSpaceにリソース配布してみる

さて、最後にTenant配下のNameSpaceに対して一斉にリソースを配布してみます。想像できる活用事例としては、

  • コンテナイメージレジストリへのアクセス情報(Secret)を配布する
  • 新しいプロジェクトが立ち上がったので、対応するTenantを作成し、Platform Engineering的な文脈から、配下のNameSpaceにサンプルアプリケーションやパイプラインを配布してあげる

などが考えられます。Tenant配下のNameSpaceに一斉にリソースを配布できるのはかなり便利です。今回はDocker HubにあるプライベートレジストリからコンテナイメージをPullする為に必要な認証情報を配布したいとします。具体的には以下のSecretを配布したいと考えましょう。

配布したいSecret.yaml
kind: Secret
apiVersion: v1
metadata:
  name: docker-pull-secret
data:
  .dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsidXNlcm5hbWUiOiJteS1kb2NrZXItdXNlcm5hbWUiLCJwYXNzd29yZCI6Im15LWFjY2Vzcy10b2tlbiIsImVtYWlsIjoibXktZW1haWxAZXhhbXBsZS5jb20iLCJhdXRoIjoiYlhrdFpHOWphMlZ5TFhWelpYSnVZVzFsT20xNUxXRmpZMlZ6Y3kxMGIydGxiZz09In19fQ==
type: kubernetes.io/dockerconfigjson

プライベートレジストリに存在するコンテナイメージをpullしてくる為には認証情報が必要です。そのためには管理者からアクセストークンをDocker Hub上で発行してもらう必要があります。

スクリーンショット 2024-09-16 21.37.49.png

管理者のDocker Hubアカウントで https://app.docker.com/settings/personal-access-tokens/create にアクセスするとアクセストークンを発行できます。ここで発行したアクセストークンをメモっておきます。

上記Secret: docker-pull-secret.dockerconfigjson:以下の文字列は、以下の.jsonがBASE64エンコードされたものです。

.json
{"auths":{"docker.io":{"username":"my-docker-username","password":"my-access-token","auth":"bXktZG9ja2VyLXVzZXJuYW1lOm15LWFjY2Vzcy10b2tlbg==","email":"my-email@example.com"}}}

さらに、bXktZG9ja2VyLXVzZXJuYW1lOm15LWFjY2Vzcy10b2tlbg==my-docker-username:my-access-tokenをBASE64エンコードしたものです。え、なんか2回もBASE64エンコードしてめんどくさいなぁと思った方、ご安心を。こちらのSecretはOpenShiftのGUIで簡単に作成できます。

image.png

平文でvalueを入れれば同じSecretができます。なお、「パスワード」欄はアクセストークンを入れてください。このSecretを参照してDeployment等のリソースにてコンテナイメージをPullすることができます。例えば以下のように記載することで、プライベートレジストリに格納されたコンテナイメージを認証を経てPull、クラスタ上にPodとしてスケジュールすることができます。

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: <container-app-name>
        image: <your-docker-hub-username>/<your-image>:<tag>
      imagePullSecrets:
      - name: docker-pull-secret

さて、こんなSecretTenant配下のNameSpaceに配布したいです。

Templateを作成する

Tenant配下のNameSpaceに配布したいKubernetesリソースを定義するカスタムリソースTemplateを作成します。例えば今回は以下のようなTemplateを作成しました。

template.yaml
apiVersion: tenantoperator.stakater.com/v1alpha1
kind: Template
metadata:
  name: docker-secret
resources:
  manifests:
    - kind: Secret
      apiVersion: v1
      metadata:
        name: docker-pull-secret
      data:
        .dockercfg: eyJhdXRocyI6eyJkb2NrZXIuaW8iOnsidXNlcm5hbWUiOiJteS1kb2NrZXItdXNlcm5hbWUiLCJwYXNzd29yZCI6Im15LWFjY2Vzcy10b2tlbiIsImF1dGgiOiJiWGt0Wkc5amEyVnlMWFZ6WlhKdVlXMWxPbTE1TFdGalkyVnpjeTEwYjJ0bGJnPT0iLCJlbWFpbCI6Im15LWVtYWlsQGV4YW1wbGUuY29tIn19fQ==
      type: kubernetes.io/dockercfg

resources.manifests以下にSecretのYAMLをそのまま貼り付けるだけです。このtemplate.yamlをCluster-admin権限を持つアカウントにて、OpenShiftクラスタにApplyしておきます。

TemplateGroupInstanceを作成する

先ほど作成したTemplateを参照するTemplateGroupInstancesをApplyすることで、任意のTenant配下のNameSpaceに先ほど示したSecretを配布することができます。

templategroupinstance.yaml
apiVersion: tenantoperator.stakater.com/v1alpha1
kind: TemplateGroupInstance
metadata:
  name: docker-secret-group-instance
spec:
  template: docker-secret
  #作成済みのTemplate名を指定します
  selector:
    matchLabels: #ラベル(key: value)を指定すると、そのラベルが付いたNameSpaceにTemplateで定義したリソースが配布されます
      stakater.com/tenant: example
  sync: true
  #Templateの内容が変更されると自動的に反映される設定(True)

特定のラベルが付いたNameSpaceにリソースを配布する事ができます。今回はTenant配下のすべてのNameSpaceに同じSecretを配布したいとします。すべてのNameSpaceにはIntegrationConfigによってstakater.com/tenant: exampleが付与されていますので、これをしてします。例えばもし開発環境(dev)のみに配布したい場合は、NameSpace: example-devにだけ付与されているラベルstakater.com/kind: devを指定しても良いでしょう。

image.png

ではtemplategroupinstances.yamlもCluster-admin権限を持つアカウントにてApplyしておきます。これまでの作業をしてきて、今クラスタ内に作成されたカスタムリソースは以下になっているでしょう。

image.png

ではdocker-pull-secretは配布されたのでしょうか?

image.png

user2@example.comでログイン済みの状態で各NameSpaceのシークレット一覧を見ると、すべてのNameSpacedocker-pull-secretがきちんと配布されていました。

このようにして、TemplateTemplateGroupInstanceを使うと、簡単に指定したNameSpaceに同じリソースを一元的に配布できます。各Tenant内で共通的に利用する認証情報や、サンプルアプリのパイプラインやマニフェストを配布することで、Tenantに参加するユーザにより良い開発者体験を提供できることでしょう。

MTOコンソールでテナントのコストを可視化しよう

実はMTOにはコンソール画面を提供するコンポーネントがあります。先にどんな事がわかるのかを示します。

ログインすると、Dashboardに遷移します。

image.png

ここでは、ユーザが参加しているTenantの情報が一元的に確認できます。

image.png

こんな感じでTenantのリソース消費状況をチャートで見たりできます。Tenant配下のNameSpaceごとの利用状況も確認できます。

image.png

NameSpace名をクリックすると、NameSpace毎かつワークロード種別毎のより詳細な利用状況が確認できます。

image.png

MTOコンソールを有効化しよう

MTOのデフォルト設定ではコンソール機能が無効化されています。有効化はIntegrationConfigを編集することで可能です。Cluster-admin権限を持つアカウントの「管理者向け表示」にて、「Operator」→「インストール済みのOperator」と進み、MTOのカスタムリソース欄からIntegrationConfigを選択します。

image.png

「tenant-operator-config」のYAML編集画面にて以下のように変更してください

integrationconfig.yaml
(中略)
spec:
(中略)
  components:
    console: true
    showback: true
(中略)

image.png

「保存」をクリックすると、NameSpace: multi-tenant-operatorのトポロジー画面でMTOコンソール関連のコンポーネントが起動してきます。すべてのコンポーネントが起動してくるまでに2,3分かかるのでしばし待ちます。

image.png

左の塊がMTOコンソールを構成するコンポーネント達になります。MTOコンソール自体のURLは「tenant-operator-console」のRouteで提供されます。

image.png

クリックしてアクセスしてみます。

image.png

サインイン画面にはなりますが、ユーザを作成していないのでここから先には進めません。ちなみに、デフォルトユーザとして

  • Username: mto
  • Password: mto

でログインすることはできます。ただしデフォルトユーザは何のTenantにも参加していないので、何も見れません。ユーザ登録の仕組みや認証の仕組みは別途MTOコンソールコンポーネントたちの中で起動してきた「Keycloak」を使って整える必要があります。

Keycloakを設定変更してユーザ登録機能を開放する

MTOコンソールコンポーネント達の「tenant-operator-keycloak」のRouteからKey Cloakログイン画面に遷移します。Adminのアクセス情報は以下です

  • Username or email: admin
  • Password: admin

image.png

Keycloakは、オープンソースの認証基盤ソフトウェアです。OAuth 2.0、OpenID Connect、SAML 2.0 などの業界標準のプロトコルをサポートしており、既存のシステムや外部認証プロバイダー(Google、GitHub、LDAPなど)と簡単に統合できます。これにより、企業や開発チームが既存の認証基盤にKeycloakをシームレスに導入できる点が大きな強みです。認証機能を有する様々なソフトウェアに組み込まれています。

なお余談ですが、Red HatがEnterpriseサポートを提供している「Red Hat Build of Keycloak」はOpenShiftのサブスクリプションにエンタイトルメントされており、追加のサブスクリプション等を不要でOpenShiftの上で認証基盤が構築できます。OperatorHubから簡単にインストールして、Keycloakインスタンスを始めとしたカスタムリソースを簡単に作成・管理できます。OpenShiftを利用しているなら非常に幅広く使われている認証基盤ソフトウェアをEnterpriseサポート付きでタダで使えますので、何等かの認証機能を持たせたいシステムをOpenShift上で構築運用するなら、「Red Hat Build of Keycloak」は最有力候補のソリューションです。

ここではKeycloak自体の機能の詳細は割愛しますが、ざっくり「Realm」という論理空間がKeycloakインスタンスの中には存在しており、このRealm単位でフロントエンドアプリのユーザ管理や外部プロバイダとの連携を実施します。既にMTOコンソールのためのRealm名「mto」が作られており、mto RealmにはClient(フロントエンド)として「mto-console」が登録済みです。確認しておきましょう。

左上からmto Realmに変えてください。

image.png

Clientsに「mto-console」があります。

image.png

ただ、ここは特にいじりません。「Realm settings」の「Login」タブをクリックしてください。

image.png

デフォルト設定だとMTOコンソールのログイン画面にユーザ登録機能がありませんので、ユーザ登録できるように機能を有効化してください。以下のスクリーンショットの通りにトグルスイッチをONにします。

image.png

トグルをONにするとすぐにMTOコンソールに反映されます。MTOコンソール画面を更新してみます。

image.png

MTOコンソールのログイン認証をOpenShiftの認証機能にブローカーさせる

MTOコンソールのユーザ登録機能が有効化されました。ただ、できればOpenShiftコンソールのログインとSSOしたいので、IdP(Identity provider)としてOpenShift自体の認証機能を設定します。「Identity providers」をクリックし、「OpenShift v4」を選択します。

image.png

ここに必要な情報を色々いれるとOpenShiftの認証機能にブローカーしてくれるようになります。

image.png

これらの情報はOpenShiftの認証機能側から払い出される値になるので、それを今から登録します。OpenShiftのコンソール画面にログインしようとしたり、OpenShift GitOpsで「LOG IN VIA OPENSHIFT」をクリックすると、

image.png

この画面にいつも行くと思うのですが、これもOpenShift上で動いているアプリケーションです。こいつにクライアント側からリダイレクトしてもらう為には、OAuthClientというカスタムリソースを作成する必要があります。Cluster-admin権限を持つアカウントの「管理者向け表示」にて、「ホーム」→「検索」をクリックし、リソースで「OAuthClient」と検索すると、いくつかのクライアントが出てきます。ちなみに「console」はOpenShift自体のコンソール(これもひとつのクライアント)です。

image.png

「OAuthClientの作成」をクリックして新しいインスタンスを作成できます。あるいは以下のYAMLをApplyしてもOKです。

oauthclient.yaml
kind: OAuthClient
apiVersion: oauth.openshift.io/v1
metadata:
  name: mto-console
  #↑これがclient-idになります。
secret: mto-secret
#↑これがclinet-secretになります。何か任意の文字列を入れておけば良いですが、きちんと管理する必要はあります。
redirectURIs:
  - '<MTO Key cloak Redirect URI>'
#↑ MTOのKey cloakで指定されるRedirect URIをコピペします
grantMethod: auto

ユーザが設定するべき要素は3つです。まずmetadata.nameですが、これはClient-idになります。とりあえず今回はmto-consoleと入れます。次にsecretです。これはclient-secretに該当します。ここではmto-secretと入れておきます。通常、こうしたクレデンシャル情報は機密情報になるので、適切に管理しましょう。最後にredirectURIsですが、これはKeycloakから提供されるものです。正しく認証がされたら戻って来る(リダイレクトされる)URIです。

image.png

「Redirect URI」欄に出ている文字列をコピペしておいてください。必要な情報を入れて「作成」をクリックすれば、OauthClientが作成されます。

image.png

次に「Base URL」を取得します。これはOpenShiftのAPIサーバのアドレスです。KeycloakからOpenShiftの認証画面にリダイレクトされるのに必要です。この情報は簡単に入手できます。OpenShiftのコンソール画面で「ログインユーザ名」をクリックし、「ログインコマンドのコピー」をクリック、認証が完了すると「Display Token」というボタンが出るので、それをクリックします。

スクリーンショット 2024-09-17 17.43.03.png

こんなコマンドが出てくる画面に行きます。「Base URL」とは、https://apiから始まり.comで終わる部分です。:443は不要です。

image.png

自身のクラスタ名を入れて「Add」をクリックしてください。「Trust Email」は必要に応じてONにしてください。「Save」をクリックすると反映されます。

image.png

さて、これでMTOコンソールのサインイン手段にOpenShift自体の認証機能を使うことができるようになりました。再度MTOコンソールのサインイン画面を更新します。

image.png

サインイン手段として「OpenShift v4」が出てきましたので、こちらをクリックします。

image.png

無事OpenShiftの認証画面にリダイレクトされました。とりあえずuser2@example.comでログインしてみます。まだユーザが存在しなかったので、登録画面に遷移しました。

image.png

とりあえず必須情報を求められるのでなにか適当に入れて「Submit」をクリックします。

image.png

無事にログインできました。

image.png

一度ログアウトしてから再度ログインする場合は、OpenShift自体のログイン情報だけですぐにMTOコンソールにもログインできるようになっています。わざわざMTOコンソールだけの為にログイン情報を管理するのはめんどくさいので、これは便利ですね。

Show back機能を見てみる

MTOコンソールのShow backメニューでは、ログインしているユーザが参加しているTenantごとのリソース消費実績を遡及して確認できます。

image.png

これによりTenant単位でのリソース消費量を週や月、年単位で集計し、最適化の議論の材料に使ったり、費用按分のためのエビデンスとして利用する事が可能だと思われます。

こちらのShow backで可視化されるリソース消費量とコスト換算する際の係数はIntegrationConfigで変更できます。IntegrationConfigに以下の様に追記して、係数を設定できる模様です。

integrationconfig.yaml
(中略)
spec:
  components:
    customPricingModel:
      CPU: '0.031611'
      GPU: '0.95'
      RAM: '0.004237'
      internetNetworkEgress: '0.12'
      regionNetworkEgress: '0.01'
      spotCPU: '0.006655'
      spotRAM: '0.000892'
      storage: '0.00005479452'
      zoneNetworkEgress: '0.01'

ちなみにこちらの係数は「GCPのus-central1リージョン」のプライスに基づいているものです。ここは利用しているOpenShiftがDeployされている環境(本環境であればAWS TokyoリージョンでNodeとして利用しているEC2のインスタンスタイプ)に合わせて設定する必要があると思います。ちょっとそこまで細かく調べるのが面倒なので、ここでは正確な値をいれるのは割愛します。こんな感じで設定できるんだなというとこだけ抑えておいていただければ幸いです。

おわりに

以上、Red Hatの認定オペレータであるMulti Tenant Operatorの概況を見てみました。公式のドキュメントには他にも様々な拡張機能の情報が載っているので、興味があれば御覧ください。例えば、定期的にTenant内の特定のNameSpaceにDeployされているPodを0スケールしてくれる機能(ハイバネーション機能)なんかもあるそうですよ。
ただし、私の見た感じお世辞にもわかりやすいとは思えなかったので、ドキュメントが今後改善されるといいなぁ〜という感じでした。例えば、この記事で説明したKeycloak周りの説明はほとんどありません。またドキュメント通りにHashicorp Vaultとの連携も試してみたんですが、うまくいきませんでした。どなたかうまく行った方は教えて下さい><

おわり。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?