NamespaceをプロビジョニングするOperatorを作ってみる(NetworkPolicy編)

NamespaceをプロビジョニングするOperatorがあったら、マルチテナントでクラスター運用するのが楽になるかなぁと思って作成してみています。本日はNetworkPolicyのプロビジョニング機能を作成します。

  • はじめに
  •  Operatorを作成する
    • CRDを作成する
    • Controllerを追加する
  • Operatorをビルドする
  • Operatorをデプロイする
  • Operatorを確認する

はじめに


以下の記事からNamespaceをプロビジョニングするOperatorを作成しています。以下の記事では、リクエストサイズに合わせてResourceQuotaを作成する機能を作成しました。

NamespaceをプロビジョニングするOperatorを作ってみる(サイズ編)

今回は、上記で作成したOperatorにNetworkPolicyを作成する機能を追加します。アプリケーション毎にNetworkPolicy要件は変わりますが、ログ管理やメトリクス管理などの管理系Podからの通信は許可することが多いです。従って、以下の通信要件を持ったNetworkPolicyをプロビジョニングするOperatorを作成します。

  • Ingress・・・カスタムリソースのmanagedの値に応じて、通信要件を変更する。
    • managed: true・・・ラベルが「app=infra」のNamespace内にあるPod(管理系Pod)からの通信を許可する。それ以外は拒否。
    • managed: false・・・全て許可。
  • Egress・・・全て許可。

Operatorを作成する


CRDを作成する


CRで設定できる項目を定義します。pkg/apis/nspro/v1alpha1/nspro_types.goのNsproSpecにManaged bool `json:”managed”`を加えて、運用管理対象か指定できるようにします。

~~省略~~
type NsproSpec struct {
    // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
    // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
    // Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html
    NsSize string `json:"nssize"`
    Managed bool `json:"managed"`
}
~~省略~~

変更を加えたら、以下のコマンドで必要なコードを再生成します。

$ operator-sdk generate k8s
INFO[0006] Running deepcopy code-generation for Custom Resource group versions: [nspro:[v1alpha1], ]
INFO[0011] Code-generation complete.

Controllerを追加する


ControllerのReconcile関数にはnewNetworkPolicyForNSを呼び出して、リソースを作成する処理を記述します。

func (r *ReconcileNspro) Reconcile(request reconcile.Request) (reconcile.Result, error) {
    ~~省略~~
    np := r.newNetworkPolicyForNS(instance)
    err = r.client.Create(context.TODO(), np)
    ~~省略~~
}

newNetworkPolicyforNSは以下の通りです。作成方法は他のリソースと同じですが、NetworkPolicyのAPIはvender/k8s.io/api/networking/v1/type.goに記述されています。

func (r *ReconcileNspro) newNetworkPolicyForNS(cr *nsprov1alpha1.Nspro) *networkingv1.NetworkPolicy {
  nsname  := cr.Name
  namespace_labels := map[string]string{}
  if ( cr.Spec.Managed ) {
    namespace_labels["app"] = "infra"
  }
  np := &networkingv1.NetworkPolicy{
    ObjectMeta: metav1.ObjectMeta{
      Name:      nsname + "-np",
      Namespace: nsname,
    },
    Spec: networkingv1.NetworkPolicySpec{
      Ingress: []networkingv1.NetworkPolicyIngressRule{
        {
          From: []networkingv1.NetworkPolicyPeer{
            {
              NamespaceSelector: &metav1.LabelSelector{
                MatchLabels: namespace_labels,
              },
            },
          },
        },
      },
    },
  }
  controllerutil.SetControllerReference(cr, np, r.scheme)
  return np
}

Operatorをビルドする


作成したOperatorのコンテナイメージをビルドします。

$ operator-sdk build 192.168.64.2:32000/nspro-operator:v1

以下のコマンドでレジストリに登録します。今回はMicroK8s上のコンテナレジストリ上に登録するので、[WorkerNodeのIPアドレス]:[NodePort]/[イメージ名]:[タグ名]のように登録しておきます。

$ docker push 192.168.64.2:32000/nspro-operator:v1

Operatorをデプロイする


Operatorをデプロイします。Operatorの前にCRDを作成しないとapiVersionが利用できないので、CRDから作成します。

$ kubectl create -f deploy/crds/nspro_v1alpha1_nspro_crd.yaml 
customresourcedefinition.apiextensions.k8s.io/nspros.nspro.example.com created

続いてdeploy/配下にあるyamlを作成します。operator.yamlだけイメージ名を変更する必要がありました。

$ kubectl delete -f deploy/operator.yaml 
deployment.apps "nspro-operator" deleted
$ kubectl create -f deploy/operator.yaml -n nspro-operator
deployment.apps/nspro-operator created
$ kubectl create -f deploy/service_account.yaml -n nspro-operator
serviceaccount/nspro-operator created
$ kubectl create -f deploy/role.yaml -n nspro-operator
role.rbac.authorization.k8s.io/nspro-operator created
$ kubectl create -f deploy/role_binding.yaml -n nspro-operator
rolebinding.rbac.authorization.k8s.io/nspro-operator created

Operatorを確認する


デプロイしたOperatorが正常に動くか確認します。

largeサイズの運用管理対象とするNamespaceを作成するカスタムリソースを作成します。以下のyamlを$ kubectl create -f [ファイル名] -n [Operatorのnamespace]で作成します。

apiVersion: nspro.example.com/v1alpha1
kind: Nspro
metadata:
  name: np
spec:
  nssize: large
  managed: true

カスタムリソース作成後、Namespaceが確認できました。

$ kubectl get ns np --show-labels
NAME   STATUS   AGE   LABELS
np     Active   7s    managed=true,size=large

想定しているNetworkPolicyが作成されていることも確認できました。

$ kubectl describe networkpolicy np-np -n np
Name:         np-np
Namespace:    np
Created on:   2020-06-28 15:26:29 +0900 JST
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     <none> (Allowing the specific traffic to all pods in this namespace)
  Allowing ingress traffic:
    To Port: <any> (traffic allowed to all ports)
    From:
      NamespaceSelector: app=infra
  Allowing egress traffic:
    <none> (Selected pods are isolated for egress connectivity)
  Policy Types: Ingress

以上です。実際の挙動確認しようと思いましたが、network-pluginが入ってないと機能しないので断念しました。