はじめに
Kubernete/OpenShiftにおける「マルチテナンシー」の実現においては、いくつかの考え方が存在します。ここで言うマルチテナンシーとは、お互いに疎であるアプリケーションの集合体や、それらによって成立するシステムをなんらか物理的・論理的に隔離しつつ、クラスタ内でコンピューティングリソースを共有させることを指します。
特に、単一クラスタにおける最もわかりやすいマルチテナンシーの考え方としてはNameSpace
による分離です。
ただし、多くのアプリケーションは複数の専用環境を求めます。具体的には開発環境(Dev)、テスト環境(test)、準本番環境(Stg)、そして商用環境(Prod)等です。各開発フェーズや用途に応じて複数のNameSpace
が必要だと思います。
また、MachinePool
を占有させることも考えられます。
MachinePool
はWorker 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を活用してマルチクラスタの作成や管理が非常に容易になりました。
とは言え、やはりコンピューティングリソースの有効活用やインフラ集約率の向上を考える上で、すべてのシステムやプロジェクト毎に毎回Hosted Clusterを払い出す事は現実的ではないでしょう。単一クラスタの中でNameSpace
を切り、適切なユーザ権限管理やリソース管理(ResouceQuota
の設計)を実施した上で、テナント毎のリソース消費量とコストの可視化・最適化を目指したいところです。
ところが、この「適切なユーザ権限管理やリソース管理」やら「テナント毎のリソース消費量とコストの可視化・最適化」というのは言うは易く行うは難しです。じゃあ具体的にどうやって複数のNameSpace
と複数のユーザを適切な権限で紐づけ、それらを横断してResouceQuota
の設計をするのか?
そこで「Multi Tenant Operator(略してMTO)」が何等か一助になってくれるのではないか?と思い試してみました。
Multi Tenant Operator(MTO)とは
MTOは「Stakater」という企業が提供するKubernetes Operatorです。
同社のホームページを見ると、Kubernetesインフラの構築運用支援やPlatform Engineeringサービスを提供しているっぽいですね。
スウェーデンのストックホルム、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サポートはもちろん付いています。
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.com
とuser4@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を削除しておいてください。
そもそも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されます。
インストールが完了すると複数のカスタムリソース(CR)が作成できるようになります。
QuotaとTenantを作ってみる
先に今から作る環境の雰囲気を以下の絵で説明します。
なんだか見慣れないOpenShift環境のポンチ絵です。通常はOpenShiftクラスタの上にいくつかのNameSpace
が切られているだけですが、今回はQuota
とTenant
と呼ばれる抽象化レイヤが存在しています。
さらに、これから作成する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
-
Viewer: exampleテナント内のリソースの閲覧権限のみを付与される
- exampleテナントに参加する各ユーザは個別のSnadbox環境(
NameSpace
)を与えられ、Sandbox環境は各ユーザからは見えないようになる- ただしViewer権限しか無い
user0@example.com
にはSandboxは不要
- ただしViewer権限しか無い
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
が作成されます。
各NameSpace
のmetadata.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権限が無いため「削除」がグレーアウトしており選択できません。
次に自身のSandbox内で適当なConfigMap
を作成してみます。
apiVersion: v1
kind: ConfigMap
metadata:
name: user1-configmap
namespace: example-user1-example-sandbox
data:
USER1_KEY: hogehoge
何の問題もなく作成できました。
次に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ギガバイトとしました。
リソースを作成することができました。Apache Webサーバも問題なく起動している模様です。
「開発者向け表示」のトポロジー画面でPod
数をどんどん増やしてみます。すると当該環境においては10Pod
までしか増やすことができなくなりました。
コンソール画面にも「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
が起動してきません。
NameSpace: example-dev
にDeployしたPodの数を7まで減らします。
お、NameSpace: example-test
の方のPod
が3つ起動しました。ではNameSpace: example-test
の方のPod
を3から4に増やすことを試みます。
増えません。
では、NameSpace: example-dev
にDeployしたPodの数を7から6に減らします。
両NameSpace
のPod
数合計は常に10が上限です。つまり同じexampleテナント配下の2つのNameSpace
間でQuota
がシェアされていることがわかりました。
なお、現在のexampleテナント全体で利用されているリソース状況については、OpenShiftのコンソール画面でも確認することができます。「管理者向け表示」にて「管理」→「ResourceQuota」と進むと、「AppliedClusterResourceQuotas」というCRを発見できます。このインスタンス名「example」こそが、実はMTOで設定したTenant
のQuota
そのものなんですね。
では、NameSpace: example-dev
のApache Webサーバコンテナを3つまで減らし、NameSpace: example-test
の方はDeploymentを削除し元に戻しておきます。
次に、NameSpace
を作成しようと試みます。以下のYAMLをApplyしてみます。
apiVersion: v1
kind: Namespace
metadata:
name: sample
駄目でした。Editor権限のユーザでは、勝手にNameSpace
を追加することはできません。あくまで用意されたTenant
及びその配下のNameSpace
の範疇でリソースの編集権限を与えられているに過ぎず、Tenant
そのものを拡張・縮小したりすることはできないようです。
次にuser0@example.com
で再ログインしてください。
Viewer権限しか無いuser0@example.com
にはSandbox環境は与えられません。
(´・ω・) カワイソス
先ほどexample-dev
にDeployしたApache Webサーバを確認する事は可能です。
ただし、編集したり削除したりすることができません。
編集メニューはすべてグレーアウトし、Pod
数変更などもできません。ConfigMap
を作成しようとしても、GUIに「作成」ボタンすら見えません。
YAMLファイルをApplyしようとしても駄目です
apiVersion: v1
kind: ConfigMap
metadata:
name: user1-configmap
namespace: example-user1-example-sandbox
data:
USER1_KEY: hogehoge
Tenant配下のNameSpace
に対する操作権限は本当に見るだけ(Viewer)の様です。
最後に、Owner権限を持つユーザuser2@example.com
で再ログインします。
NameSpace
を作成しようと試みますが、
apiVersion: v1
kind: Namespace
metadata:
name: sample
やはり駄目です。
しかし、作成しようとするNameSpace
に以下の様なラベルを付与してください。
apiVersion: v1
kind: Namespace
metadata:
name: sample
labels:
stakater.com/tenant: example
あら不思議!sample
という新しいNameSpace
を作成することができました。
再度user1@example.com
でログインし直して同じ様にやってみても駄目です。
つまり、Owner権限を持つユーザであれば、NameSpace
リソースのmetadata.labels.stakater.com/tenant: <テナント名>
を付与することで、テナント配下にNameSpace
を追加することができます。しかしそれ以外の権限では追加できません。これがOwnerとEditorの権限の差でした。
ArgoCDのマルチテナンシーを確認
では、ArgoCD(OpenShift GitOps)とMTOの連携機能を見てみます。
IntegrationConfig
を変更
その前にMTOのカスタムリソースIntegrationConfig
を弄る必要があります。Cluster-admin権限を持つアカウントの「管理者向け表示」で「Operator」→「インストール済みのOperator」と進み、MTOを選択、IntegrationConfig
タブでインスタンス名:tenant-operator-configを確認、クリックしてください。
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
に自動的にラベルが付与されています。
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は以下の通りかと思います。
OpenShift GitOps Operatorをインストール
さて、このままCluster-admin権限を持つアカウントにて、OpenShift GitOps Operatorもインストールしておきます。OperatorHubにて検索して、デフォルト設定でインストールします。
お〜!exampleというAppProject
が自動的に作成されていますね〜
テンション上がってきました。では、Editor権限をもつuser1@example.com
にてOpenShift GitOpsにログインしてみます。
タイル右上のリンクからOpenShift GitOpsのコンソール画面に遷移し、「LOG IN VIA OPENSHIFT」の方から、user1@example.com
のID/PWにてシングルサインオンします。
「Setteings」→「Project」と進むと、user1@example.com
がAppProject: example
に参加していることがわかります。
では何かアプリケーションをDeployしてみましょう。Postgresqlコンテナを試しにDeployします。ArgoCDアプリケーションのYAMLは以下の通りです。
本記事で利用するManifestはすべて以下のGitlabリポジトリにおいているのでご参考にしてください。
https://gitlab.com/masaki-oomura/multi-tenant-operator-demo.git
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ファイルを入れておきました。
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の名称を指定します
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できません。
まず、PVCを見てみるとstorageClass: gp3-csi
に対して11ギガバイトの容量を要求しています。しかしexampleテナントにおいては10ギガバイトまでしか利用できませんので、Quota
超過です。おっとうっかり。一度アプリケーションをDeleteしてください。
では、Manifestを修正して5ギガバイトまでに直しましょう。修正後のManifestが格納されたリポジトリパスを参照するArgoCDアプリケーションは以下です。
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を試みます。
また駄目でした。そういえば、Extensions
でSecret
の同期を禁止していました。うっかりうっかり。テヘペロ。Secret
がApplyできないと環境変数を参照できないためDeployment
もApplyできません。先ほど同様、ArgoCDアプリケーションは削除しておいてください。
では、先にNameSpace: example-dev
にSecret
を手動でApplyしておき、GitリポジトリからはSecret
は除外しましょう。
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です。あくまでデモ用途です。
コンソール画面のGUIからYAMLをApplyしました。仏の顔も三度まで。Secret
を除外したManifest一式を参照するArgoCDアプリケーションは以下です。以下のArgoCDアプリケーションをApplyします。
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: []
やったぜ。
OpenShiftのコンソールからもPostgresqlコンテナがDeployできたことを確認できました。
なおViewer権限しか持っていないuser0@example.com
からは、当たり前ですがArgoCDにログインできてもアプリケーションのDeployはできません。
また、他のユーザが作成したアプリケーションを参照することはできますが、削除もできません。
このようにして、MTOを利用してArgoCDのマルチテナンシーまで実現できます。ArgoCDのAppProject
に対するRBACと、MTOで設定したTenant
向けのRBACがいい感じに同期してくれます。
NameSpaceにリソース配布してみる
さて、最後にTenant
配下のNameSpace
に対して一斉にリソースを配布してみます。想像できる活用事例としては、
- コンテナイメージレジストリへのアクセス情報(
Secret
)を配布する - 新しいプロジェクトが立ち上がったので、対応する
Tenant
を作成し、Platform Engineering的な文脈から、配下のNameSpace
にサンプルアプリケーションやパイプラインを配布してあげる
などが考えられます。Tenant
配下のNameSpace
に一斉にリソースを配布できるのはかなり便利です。今回はDocker HubにあるプライベートレジストリからコンテナイメージをPullする為に必要な認証情報を配布したいとします。具体的には以下のSecret
を配布したいと考えましょう。
kind: Secret
apiVersion: v1
metadata:
name: docker-pull-secret
data:
.dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsidXNlcm5hbWUiOiJteS1kb2NrZXItdXNlcm5hbWUiLCJwYXNzd29yZCI6Im15LWFjY2Vzcy10b2tlbiIsImVtYWlsIjoibXktZW1haWxAZXhhbXBsZS5jb20iLCJhdXRoIjoiYlhrdFpHOWphMlZ5TFhWelpYSnVZVzFsT20xNUxXRmpZMlZ6Y3kxMGIydGxiZz09In19fQ==
type: kubernetes.io/dockerconfigjson
プライベートレジストリに存在するコンテナイメージをpullしてくる為には認証情報が必要です。そのためには管理者からアクセストークンをDocker Hub上で発行してもらう必要があります。
管理者のDocker Hubアカウントで https://app.docker.com/settings/personal-access-tokens/create にアクセスするとアクセストークンを発行できます。ここで発行したアクセストークンをメモっておきます。
上記Secret: docker-pull-secret
の.dockerconfigjson:
以下の文字列は、以下の.jsonがBASE64エンコードされたものです。
{"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で簡単に作成できます。
平文でvalue
を入れれば同じSecret
ができます。なお、「パスワード」欄はアクセストークンを入れてください。このSecret
を参照してDeployment
等のリソースにてコンテナイメージをPullすることができます。例えば以下のように記載することで、プライベートレジストリに格納されたコンテナイメージを認証を経てPull、クラスタ上にPod
としてスケジュールすることができます。
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
さて、こんなSecret
をTenant
配下のNameSpace
に配布したいです。
Template
を作成する
Tenant
配下のNameSpace
に配布したいKubernetesリソースを定義するカスタムリソースTemplate
を作成します。例えば今回は以下のようなTemplate
を作成しました。
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
を配布することができます。
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
を指定しても良いでしょう。
ではtemplategroupinstances.yaml
もCluster-admin権限を持つアカウントにてApplyしておきます。これまでの作業をしてきて、今クラスタ内に作成されたカスタムリソースは以下になっているでしょう。
ではdocker-pull-secret
は配布されたのでしょうか?
user2@example.com
でログイン済みの状態で各NameSpace
のシークレット一覧を見ると、すべてのNameSpace
にdocker-pull-secret
がきちんと配布されていました。
このようにして、Template
とTemplateGroupInstance
を使うと、簡単に指定したNameSpace
に同じリソースを一元的に配布できます。各Tenant
内で共通的に利用する認証情報や、サンプルアプリのパイプラインやマニフェストを配布することで、Tenant
に参加するユーザにより良い開発者体験を提供できることでしょう。
MTOコンソールでテナントのコストを可視化しよう
実はMTOにはコンソール画面を提供するコンポーネントがあります。先にどんな事がわかるのかを示します。
ログインすると、Dashboardに遷移します。
ここでは、ユーザが参加しているTenant
の情報が一元的に確認できます。
こんな感じでTenant
のリソース消費状況をチャートで見たりできます。Tenant
配下のNameSpace
ごとの利用状況も確認できます。
NameSpace
名をクリックすると、NameSpace
毎かつワークロード種別毎のより詳細な利用状況が確認できます。
MTOコンソールを有効化しよう
MTOのデフォルト設定ではコンソール機能が無効化されています。有効化はIntegrationConfig
を編集することで可能です。Cluster-admin権限を持つアカウントの「管理者向け表示」にて、「Operator」→「インストール済みのOperator」と進み、MTOのカスタムリソース欄からIntegrationConfig
を選択します。
「tenant-operator-config」のYAML編集画面にて以下のように変更してください
(中略)
spec:
(中略)
components:
console: true
showback: true
(中略)
「保存」をクリックすると、NameSpace: multi-tenant-operator
のトポロジー画面でMTOコンソール関連のコンポーネントが起動してきます。すべてのコンポーネントが起動してくるまでに2,3分かかるのでしばし待ちます。
左の塊がMTOコンソールを構成するコンポーネント達になります。MTOコンソール自体のURLは「tenant-operator-console」のRoute
で提供されます。
クリックしてアクセスしてみます。
サインイン画面にはなりますが、ユーザを作成していないのでここから先には進めません。ちなみに、デフォルトユーザとして
- Username:
mto
- Password:
mto
でログインすることはできます。ただしデフォルトユーザは何のTenant
にも参加していないので、何も見れません。ユーザ登録の仕組みや認証の仕組みは別途MTOコンソールコンポーネントたちの中で起動してきた「Keycloak」を使って整える必要があります。
Keycloakを設定変更してユーザ登録機能を開放する
MTOコンソールコンポーネント達の「tenant-operator-keycloak」のRoute
からKey Cloakログイン画面に遷移します。Adminのアクセス情報は以下です
- Username or email: admin
- Password: admin
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に変えてください。
Clientsに「mto-console」があります。
ただ、ここは特にいじりません。「Realm settings」の「Login」タブをクリックしてください。
デフォルト設定だとMTOコンソールのログイン画面にユーザ登録機能がありませんので、ユーザ登録できるように機能を有効化してください。以下のスクリーンショットの通りにトグルスイッチをONにします。
トグルをONにするとすぐにMTOコンソールに反映されます。MTOコンソール画面を更新してみます。
MTOコンソールのログイン認証をOpenShiftの認証機能にブローカーさせる
MTOコンソールのユーザ登録機能が有効化されました。ただ、できればOpenShiftコンソールのログインとSSOしたいので、IdP(Identity provider)としてOpenShift自体の認証機能を設定します。「Identity providers」をクリックし、「OpenShift v4」を選択します。
ここに必要な情報を色々いれるとOpenShiftの認証機能にブローカーしてくれるようになります。
これらの情報はOpenShiftの認証機能側から払い出される値になるので、それを今から登録します。OpenShiftのコンソール画面にログインしようとしたり、OpenShift GitOpsで「LOG IN VIA OPENSHIFT」をクリックすると、
この画面にいつも行くと思うのですが、これもOpenShift上で動いているアプリケーションです。こいつにクライアント側からリダイレクトしてもらう為には、OAuthClient
というカスタムリソースを作成する必要があります。Cluster-admin権限を持つアカウントの「管理者向け表示」にて、「ホーム」→「検索」をクリックし、リソースで「OAuthClient」と検索すると、いくつかのクライアントが出てきます。ちなみに「console」はOpenShift自体のコンソール(これもひとつのクライアント)です。
「OAuthClientの作成」をクリックして新しいインスタンスを作成できます。あるいは以下のYAMLをApplyしてもOKです。
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です。
「Redirect URI」欄に出ている文字列をコピペしておいてください。必要な情報を入れて「作成」をクリックすれば、OauthClient
が作成されます。
次に「Base URL」を取得します。これはOpenShiftのAPIサーバのアドレスです。KeycloakからOpenShiftの認証画面にリダイレクトされるのに必要です。この情報は簡単に入手できます。OpenShiftのコンソール画面で「ログインユーザ名」をクリックし、「ログインコマンドのコピー」をクリック、認証が完了すると「Display Token」というボタンが出るので、それをクリックします。
こんなコマンドが出てくる画面に行きます。「Base URL」とは、https://api
から始まり.com
で終わる部分です。:443
は不要です。
自身のクラスタ名を入れて「Add」をクリックしてください。「Trust Email」は必要に応じてONにしてください。「Save」をクリックすると反映されます。
さて、これでMTOコンソールのサインイン手段にOpenShift自体の認証機能を使うことができるようになりました。再度MTOコンソールのサインイン画面を更新します。
サインイン手段として「OpenShift v4」が出てきましたので、こちらをクリックします。
無事OpenShiftの認証画面にリダイレクトされました。とりあえずuser2@example.com
でログインしてみます。まだユーザが存在しなかったので、登録画面に遷移しました。
とりあえず必須情報を求められるのでなにか適当に入れて「Submit」をクリックします。
無事にログインできました。
一度ログアウトしてから再度ログインする場合は、OpenShift自体のログイン情報だけですぐにMTOコンソールにもログインできるようになっています。わざわざMTOコンソールだけの為にログイン情報を管理するのはめんどくさいので、これは便利ですね。
Show back機能を見てみる
MTOコンソールのShow backメニューでは、ログインしているユーザが参加しているTenant
ごとのリソース消費実績を遡及して確認できます。
これによりTenant
単位でのリソース消費量を週や月、年単位で集計し、最適化の議論の材料に使ったり、費用按分のためのエビデンスとして利用する事が可能だと思われます。
こちらのShow backで可視化されるリソース消費量とコスト換算する際の係数はIntegrationConfig
で変更できます。IntegrationConfig
に以下の様に追記して、係数を設定できる模様です。
(中略)
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との連携も試してみたんですが、うまくいきませんでした。どなたかうまく行った方は教えて下さい><
おわり。