CRDでAPIを広げる
目標
CustomResourceDefinitionを自分で書いてKubernetesのAPIを拡張し、スキーマ検証が実際にリクエストを拒否することを確認し、新しいリソースに対する権限をAggregated ClusterRoleで付与します。
なぜ重要なのか
CKAの出題範囲にCRDが入っているのは、Operatorを作れという意味ではありません。Operatorが導入されたクラスターを運用できるかを問うているのです。現場のクラスターには、すでに数十個のCRDが導入されています。
ここで理解すべき構造があります。CRDを作るとapiserverに新しいエンドポイントができ、そのエンドポイントは、保存・検証・watchを提供します。しかし、それだけです。実際に何かを行うのは、そのCRをwatchするコントローラーであり、それは別のソフトウェアです。CRを作ったのに何も起きないなら、たいていはコントローラーがないか、死んでいるのです。
スキーマも同じ文脈です。apiextensions.k8s.io/v1では、schemaは任意ではなく必須です。スキーマがそのAPIの契約であり、不正な値を、コントローラーではなくapiserverが先に防いでくれます。
ステップ
- CRD
widgets.labhub.ioを作成してください。grouplabhub.io、scopeNamespaced、kindWidget、pluralwidgets、singularwidget、shortNamesにwg、バージョンはv1alpha11つで、servedとstorageはどちらもtrueです。 - ネームスペース
cka-crdを作成し、その中にWidgetdemoを作成してください。spec.replicasは3、spec.tierはsmallです。 - CRDのv1alpha1のスキーマを修正して、検証ルールを入れてください。
spec.replicasはtypeintegerで、minimum 1、maximum 10です。spec.tierはtypestringで、enum[small, large]です。specオブジェクトのrequiredは[replicas, tier]です。 spec.replicasが99のWidgettoo-bigを作成してみて、拒否されたエラー出力を/root/cka-crd/reject.txtに保存してください。too-bigは作成されていてはいけません。- v1alpha1にadditionalPrinterColumnsを2つ追加してください。名前
REPLICAS(typeinteger、jsonPath.spec.replicas)、名前TIER(typestring、jsonPath.spec.tier)です。 - CRD
clusterwidgets.labhub.ioを作成してください。scopeCluster、kindClusterWidget、pluralclusterwidgets、grouplabhub.io、バージョンv1alpha1です。そしてClusterWidgetglobalを作成してください。 - ClusterRole
cka-widget-viewerを作成してください。ラベルはrbac.labhub.io/aggregate-to-widget=true、ルールはapiGroupslabhub.ioのwidgetsに対するget、list、watchです。そしてClusterRolecka-widget-aggregateを作成し、aggregationRuleがそのラベルをセレクターとして選ぶようにしてください。
参考
- スキーマの最小の形は、
openAPIV3Schema: {type: object, properties: {spec: {type: object, x-kubernetes-preserve-unknown-fields: true}}}です。ステップ3で、このspecを具体化することになります。 kubectl get crd widgets.labhub.io -o yamlで現在のスキーマを取り出して修正し、再度適用する方法が速いです。- よくあるミス1: CRD名を
widget.labhub.ioのように単数で書いてしまうことです。必ずpluralとgroupをつなげる必要があります。 - よくあるミス2: aggregationRuleを使いながら、rulesも一緒に書いてしまうことです。コントローラーがrulesを上書きするので、手で書いたルールは消えます。
CustomResourceDefinitionを作成する
CRDwidgets.labhub.ioを作成してください。grouplabhub.io、scopeNamespaced、kindWidget、pluralwidgets、singularwidget、shortNamesにwg、バージョンはv1alpha11つで、servedとstorageはどちらもtrueです。
CRDのmetadata.nameは、必ず「複数形.グループ」の形式である必要があります。apiextensions.k8s.io/v1では、versions配列の各項目にschemaが必須です。
カスタムリソースを作成する
ネームスペースcka-crdを作成し、その中にWidgetdemoを作成してください。spec.replicasは3、spec.tierはsmallです。
CRのapiVersionは「グループ/バージョン」です。スキーマが緩いと任意のフィールドが入ってしまうので、まずspecの下に未知のフィールドを許可しておくと楽です。
スキーマに検証ルールを入れる
CRDのv1alpha1のスキーマを修正して、検証ルールを入れてください。spec.replicasはtypeintegerで、minimum 1、maximum 10です。spec.tierはtypestringで、enum[small, large]です。specオブジェクトのrequiredは[replicas, tier]です。
openAPIV3Schemaの中のproperties.spec.propertiesの下に、フィールドごとのtypeとminimum/maximum/enumを付けます。requiredは値ではなく、そのオブジェクトレベルの配列です。
検証が拒否する瞬間を確認する
spec.replicasが99のWidgettoo-bigを作成してみて、拒否されたエラー出力を/root/cka-crd/reject.txtに保存してください。too-bigは作成されていてはいけません。
エラーは標準出力ではなく、標準エラー出力に出ます。リダイレクトするときは、2>&1を忘れないでください。拒否されたリソースは、作成されていないのが正常です。
kubectl getの出力にカラムを追加する
v1alpha1にadditionalPrinterColumnsを2つ追加してください。名前REPLICAS(typeinteger、jsonPath.spec.replicas)、名前TIER(typestring、jsonPath.spec.tier)です。
additionalPrinterColumnsは、versions配列の各バージョンの中に入ります。name、type、jsonPathの3つのフィールドが必要で、jsonPathはドットで始まります。
クラスタースコープのCRDを作成する
CRDclusterwidgets.labhub.ioを作成してください。scopeCluster、kindClusterWidget、pluralclusterwidgets、grouplabhub.io、バージョンv1alpha1です。そしてClusterWidgetglobalを作成してください。
scopeは、CRDを作成したあとでは変更できません。クラスタースコープのリソースは、-nオプションを受け付けません。
Aggregated ClusterRoleで権限を広げる
ClusterRolecka-widget-viewerを作成してください。ラベルはrbac.labhub.io/aggregate-to-widget=true、ルールはapiGroupslabhub.ioのwidgetsに対するget、list、watchです。そしてClusterRolecka-widget-aggregateを作成し、aggregationRuleがそのラベルをセレクターとして選ぶようにしてください。
aggregationRuleのあるClusterRoleのrulesは、直接書きません。ラベルの付いたほかのClusterRoleを、コントローラーが見つけて統合してくれます。