Project Sveltosチートシート

内容は随時更新していきます。

## ClusterProfile

### ClusterProfileの概要

ClusterProfileは、Sveltosシステムにおいて複数のKubernetesクラスターにアドオンをデプロイするための主要なカスタムリソースです。ClusterProfileを使用することで、管理クラスターから複数のワークロードクラスターに対して一貫した設定を適用できます。

### ClusterProfileの基本概念

ClusterProfileはクラスタースコープのリソースで、以下の主要な機能を提供します：

1. クラスター選択: ラベルセレクターを使用して、設定を適用する対象クラスターを選択します
2. アドオン定義: 選択したクラスターにデプロイするリソース（Helmチャート、Kubernetesリソース、Kustomizeリソース）を定義します
3. 同期モード: リソースの適用方法（一度のみ、継続的、ドリフト検出付き継続的、ドライラン）を指定します

### ClusterProfileの実装例1

以下は、本番環境（env: fvラベルを持つ）のすべてのクラスターにKyvernoをデプロイする例です：

```yaml
# ClusterProfile定義
apiVersion: config.projectsveltos.io/v1beta1
kind: ClusterProfile
metadata:
  name: deploy-kyverno
spec:
  clusterSelector:
    matchLabels:
      env: fv
  syncMode: Continuous
  helmCharts:
  - repositoryURL:    https://kyverno.github.io/kyverno/
    repositoryName:   kyverno
    chartName:        kyverno/kyverno
    chartVersion:     v3.3.3
    releaseName:      kyverno-latest
    releaseNamespace: kyverno
    helmChartAction:  Install
  policyRefs:
  - name: disallow-latest
    namespace: default
    kind: ConfigMap
  patches:
  - target:
      group: apps
      version: v1
      kind: Deployment
      name: ".*"
    patch: |
      - op: add
        path: /metadata/labels/environment
        value: production
```

引用: [Post Renderer Patches - Projectsveltos Documentation](https://projectsveltos.github.io/sveltos/features/post-renderer-patches/)


### ClusterProfileの実装例2

以下は、 `karpenter: enabled` ラベルを持つすべてのクラスターにKarpenterのdefault NodePoolとEC2NodeClassをデプロイする例です：

```yaml
apiVersion: config.projectsveltos.io/v1beta1
kind: ClusterProfile
metadata:
  name: karpenter-deployment
spec:
  clusterSelector:
    matchLabels:
      karpenter: enabled
  syncMode: ContinuousWithDriftDetection
  policyRefs:
  - name: karpenter-resources
    namespace: "{{ .Cluster.metadata.namespace }}"
    kind: ConfigMap
  templateResourceRefs:
  - resource:
      apiVersion: v1
      kind: ConfigMap
      name: cluster-config
      namespace: "{{ .Cluster.metadata.namespace }}"
    identifier: ClusterConfig
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: karpenter-resources
  namespace: default
  annotations:
    policy.projectsveltos.io/template: "ok"
data:
  nodepool.yaml: |
    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: default
    spec:
      template:
        spec:
          nodeClassRef:
            group: karpenter.k8s.aws
            kind: EC2NodeClass
            name: default
  ec2nodeclass.yaml: |
    apiVersion: karpenter.k8s.aws/v1
    kind: EC2NodeClass
    metadata:
      name: default
    spec:
      kubelet:
        podsPerCore: 2
        maxPods: {{ if eq .ClusterConfig.data.environment "production" }}20{{ else }}10{{ end }}
        systemReserved:
            cpu: 100m
            memory: 100Mi
            ephemeral-storage: 1Gi
        kubeReserved:
            cpu: 200m
            memory: 100Mi
            ephemeral-storage: 3Gi
        evictionHard:
            memory.available: 5%
            nodefs.available: 10%
            nodefs.inodesFree: 10%
        evictionSoft:
            memory.available: 500Mi
            nodefs.available: 15%
            nodefs.inodesFree: 15%
        evictionSoftGracePeriod:
            memory.available: 1m
            nodefs.available: 1m30s
            nodefs.inodesFree: 2m
        evictionMaxPodGracePeriod: 60
        imageGCHighThresholdPercent: 85
        imageGCLowThresholdPercent: 80
        cpuCFSQuota: true
        clusterDNS: ["10.0.1.100"]
      amiFamily: AL2
      subnetSelectorTerms:
        - tags:
            karpenter.sh/discovery: "{{ .Cluster.metadata.name }}"
            environment: {{ .ClusterConfig.data.environment }}
      securityGroupSelectorTerms:
        - tags:
            karpenter.sh/discovery: "{{ .Cluster.metadata.name }}"
            environment: {{ .ClusterConfig.data.environment }}
      role: "KarpenterNodeRole-{{ .Cluster.metadata.name }}"
      instanceProfile: "KarpenterNodeInstanceProfile-{{ .Cluster.metadata.name }}"
      amiSelectorTerms:
        - tags:
            karpenter.sh/discovery: "{{ .Cluster.metadata.name }}"
            environment: {{ .ClusterConfig.data.environment }}
      tags:
        team: {{ .ClusterConfig.data.team }}
        app: {{ .ClusterConfig.data.application }}
      metadataOptions:
        httpEndpoint: enabled
        httpProtocolIPv6: disabled
        httpPutResponseHopLimit: 1
        httpTokens: required
      blockDeviceMappings:
        - deviceName: /dev/xvda
          ebs:
            volumeSize: {{ if eq .ClusterConfig.data.environment "production" }}100Gi{{ else }}50Gi{{ end }}
            volumeType: gp3
            iops: {{ if eq .ClusterConfig.data.environment "production" }}10000{{ else }}3000{{ end }}
            encrypted: true
            deleteOnTermination: true
```

templateResourceRefsで参照されているClusterConfigの例は以下の通りです。

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-config
  namespace: default
data:
  environment: "production"
  team: "platform-team"
  application: "karpenter-autoscaler"
  region: "us-west-2"
  clusterType: "eks"
  nodeInstanceType: "m5.xlarge"
  minNodes: "3"
  maxNodes: "10"
  diskSize: "100"
  kubernetesVersion: "1.28"
  enableSpotInstances: "true"
  enableGPUNodes: "false"
```

参考1: [NodeClasses | Karpenter](https://karpenter.sh/docs/concepts/nodeclasses/)
参考2: [Templates - Projectsveltos Documentation](https://projectsveltos.github.io/sveltos/template/intro_template/)

### Kustomizeを使用した例

Kustomizeリソースを使用する場合の例：

```yaml
apiVersion: config.projectsveltos.io/v1beta1
kind: ClusterProfile
metadata:
  name: hello-world
spec:
  clusterSelector:
    matchLabels:
      env: fv
  syncMode: Continuous
  kustomizationRefs:
  - namespace: flux2
    name: flux2
    kind: GitRepository
    path: ./helloWorld/
    targetNamespace: eng
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: flux2
  namespace: flux2
spec:
  interval: 1m0s
  ref:
    branch: main
  timeout: 60s
  url: ssh://git@github.com/gianlucam76/kustomize
```

引用: [Sveltos - Kubernetes Add-on Controller | Manage Kubernetes Add-ons with Ease - Projectsveltos Documentation](https://projectsveltos.github.io/sveltos/addons/kustomize/#example-introduction-to-kustomize-and-sveltos)

### 同期モードの種類

ClusterProfileは以下の同期モードをサポートしています：

1. OneTime: クラスター初期設定時に一度だけリソースをデプロイします。CNIプラグインなどの重要なインフラコンポーネントの初期デプロイに適しています。
2. Continuous: ClusterProfileの変更を継続的に監視し、マッチするクラスターに自動的に適用します。複数クラスターの一貫性を維持するのに役立ちます。
3. ContinuousWithDriftDetection: 継続的な同期に加えて、管理クラスターで定義された望ましい状態と実際のクラスター状態の間のドリフト（構成の乖離）を検出し、自動的に修正します。
4. DryRun: 実際の変更を行わずにデプロイアクションをシミュレートします。テストや検証に役立ちます。
構成ドリフト検出
ContinuousWithDriftDetectionモードを使用すると、Sveltosは管理クラスターで定義された望ましい状態と実際のクラスター状態の間のドリフトを自動的に検出し、修正できます。 README.md:130-137

### Subresources

`projectsveltos.io/subresources` annotationを付けるとstatus等のsubresourcesも合わせて更新する事ができます.

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  annotations:
    projectsveltos.io/subresources: status
  name: load-balancer-service
  namespace: default
data:
  service.yaml: |
    apiVersion: v1
    kind: Service
    metadata:
      name: sveltos-subresource
      namespace: default
    spec:
      selector:
        app: foo
      ports:
      - name: my-port
        port: 443
        protocol: TCP
        targetPort: 1032
      type: LoadBalancer
    status:
      loadBalancer:
        ingress:
        - ip: 1.1.1.1
```

参考: [Sveltos - Kubernetes Add-on Controller | Manage Kubernetes Add-ons with Ease - Projectsveltos Documentation](https://projectsveltos.github.io/sveltos/addons/raw_yaml/#subresources)

### 既存リソースの編集

以下のリソースがあるとします.

```bash
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
  labels:
    apps: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 1
EOF
```

以下のClusterProfileでそのリソースを編集することが可能です。

```yaml
apiVersion: config.projectsveltos.io/v1beta1
kind: ClusterProfile
metadata:
  name: deploy-resources
spec:
  clusterSelector:
    matchLabels:
      env: production
  templateResourceRefs:
  - resource:
      apiVersion: apps/v1
      kind: Deployment
      name: nginx-deployment
      namespace: default
    identifier: NginxDeployment
  policyRefs:
  - kind: ConfigMap
    name: info
    namespace: default
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: info
  namespace: default
  annotations:
    projectsveltos.io/template: "true"  # add annotation to indicate Sveltos content is a template
data:
  secret.yaml: |
    {{ $depl := (getResource "NginxDeployment") }}
    {{ $depl := (chainSetField $depl "spec.replicas" (int64 1) ) }}
    {{ $depl := (chainSetField $depl "metadata.namespace" .Cluster.metadata.namespace ) }}
    {{ $depl := (chainSetField $depl "spec.template.spec.serviceAccountName" "default" ) }}
    {{ $depl := (chainSetField $depl "spec.paused" true ) }}
    {{ toYaml $depl }}
```

参考: [Resource Manipulating Functions - Examples - Projectsveltos Documentation](https://projectsveltos.github.io/sveltos/template/examples/#setfieldremovefield-example)

### ClusterProfileの動作の仕組み

ClusterProfileが作成または更新されると、以下のプロセスが実行されます：

1. ClusterProfileReconcilerがClusterProfileを監視し、マッチするクラスターを選択します
2. 各マッチするクラスターに対してClusterSummaryリソースを作成します
3. デプロイ状態がClusterSummaryのステータスに反映されます

## Event Manager

### EventSource/EventTriggerの概要

ProjectsveltosのEventTriggerは、クラスターで発生したイベントに基づいてリソースを自動的に作成するための機能です。発生したイベントを表現するリソースはEventSourceです。以下に、シンプルなEventSource/EventTriggerの実装例を示します。

### EventSource/EventTriggerの実装例1

以下はサービスがあったらNetworkPolicyを自動で作成する例です

```yaml
---
apiVersion: lib.projectsveltos.io/v1beta1
kind: EventSource
metadata:
  name: sveltos-service
spec:
  collectResources: true
  resourceSelectors:
  - group: ""
    version: "v1"
    kind: "Service"
    labelFilters:
    - key: sveltos
      operation: Equal
      value: fv
---
apiVersion: lib.projectsveltos.io/v1beta1
kind: EventTrigger
metadata:
  name: service-network-policy
spec:
  sourceClusterSelector:
    matchLabels:
      env: fv
  eventSourceName: sveltos-service
  oneForEvent: true
  policyRefs:
  - name: network-policy
    namespace: default
    kind: ConfigMap
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: network-policy
  namespace: default
  annotations:
    projectsveltos.io/instantiate: ok # this annotation is what tells Sveltos to instantiate this ConfigMap
data:
  networkpolicy.yaml: |
    kind: NetworkPolicy
    apiVersion: networking.k8s.io/v1
    metadata:
      name: front-{{ .Resource.metadata.name }}
      namespace: {{ .Resource.metadata.namespace }}
    spec:
      podSelector:
        matchLabels:
          {{ range $key, $value := .Resource.spec.selector }}
          {{ $key }}: {{ $value }}
          {{ end }}
      ingress:
        - from:
          - podSelector:
              matchLabels:
                app: internal
          ports:
            {{ range $port := .Resource.spec.ports }}
            - port: {{ $port.port }}
            {{ end }}
```

### EventSource/EventTriggerの実装例2

以下は先ほどと同じサービスがあったらNetworkPolicyを自動で作成する例です。
ただし、ラベル `sveltos: fv` がついているServiceのみを対象としています。


```yaml
---
apiVersion: lib.projectsveltos.io/v1beta1
kind: EventSource
metadata:
  name: sveltos-service
spec:
  collectResources: true
  resourceSelectors:
  - group: ""
    version: "v1"
    kind: "Service"
    evaluate: |
      function evaluate()
        hs = {}
        hs.matching = false
        hs.message = ""
        if obj.metadata.labels ~= nil then
          for key, value in pairs(obj.metadata.labels) do
            if key == "sveltos" then
              if value == "fv" then
                hs.matching = true
              end
            end
          end
        end
        return hs
      end
---
apiVersion: lib.projectsveltos.io/v1beta1
kind: EventTrigger
metadata:
  name: service-network-policy
spec:
  sourceClusterSelector:
    matchLabels:
      env: fv
  eventSourceName: sveltos-service
  oneForEvent: true
  policyRefs:
  - name: network-policy
    namespace: default
    kind: ConfigMap
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: network-policy
  namespace: default
  annotations:
    projectsveltos.io/instantiate: ok # this annotation is what tells Sveltos to instantiate this ConfigMap
data:
  networkpolicy.yaml: |
    kind: NetworkPolicy
    apiVersion: networking.k8s.io/v1
    metadata:
      name: front-{{ .Resource.metadata.name }}
      namespace: {{ .Resource.metadata.namespace }}
    spec:
      podSelector:
        matchLabels:
          {{ range $key, $value := .Resource.spec.selector }}
          {{ $key }}: {{ $value }}
          {{ end }}
      ingress:
        - from:
          - podSelector:
              matchLabels:
                app: internal
          ports:
            {{ range $port := .Resource.spec.ports }}
            - port: {{ $port.port }}
            {{ end }}
```

### EventSourceの実装例3

異なる種類のリソースをまとめて調べる例です。
以下は 対応する Deployment が存在しない HPA を検出する EventSource です。

```yaml
apiVersion: lib.projectsveltos.io/v1beta1
kind: EventSource
metadata:
  name: sveltos-service
spec:
  collectResources: true
  resourceSelectors:
  - group: "apps"
    version: "v1"
    kind: "Deployment"
  - kind: HorizontalPodAutoscaler
    group: "autoscaling"
    version: v2
  aggregatedSelection: |
      function getKey(namespace, name)
        return namespace .. ":" .. name
      end

      function evaluate()
        local hs = {}
        hs.message = ""

        local deployments = {}
        local autoscalers = {}
        local deploymentsWithNoAutoscaler = {}

        for _, resource in ipairs(resources) do
          local kind = resource.kind
          if kind == "Deployment" then
            key = getKey(resource.metadata.namespace, resource.metadata.name)
            deployments[key] = true
          elseif kind == "HorizontalPodAutoscaler" then
            table.insert(autoscalers, resource)
          end
        end

        -- Check for each horizontalPodAutoscaler if there is a matching Deployment
        for _,hpa in ipairs(autoscalers) do
            key = getKey(hpa.metadata.namespace, hpa.spec.scaleTargetRef.name)
            if hpa.spec.scaleTargetRef.kind == "Deployment" then
              if not deployments[key] then
                table.insert(unusedAutoscalers, hpa)
              end
            end
        end

        if #unusedAutoscalers > 0 then
          hs.resources = unusedAutoscalers
        end
        return hs
      end
```

## Classifier

### Classifierの概要

Classifierは、Kubernetesクラスターの状態に基づいてクラスターを分類し、ラベルを付与するコンポーネントです。このラベル付けにより、クラスターは特定のClusterProfileにマッチするようになり、それに応じてアドオンやアプリケーションが自動的にデプロイされます。

### Classifierの主な機能

Classifierは以下の基準に基づいてクラスターを分類できます：

1. Kubernetesバージョン
2. クラスター内にデプロイされているリソース
3. Luaスクリプトによる評価

### Classifierの動作の仕組み

1. クラスターの状態の評価: Classifierはクラスターの状態（Kubernetesバージョンやデプロイされたリソースなど）を評価します
2. マッチング: 定義された条件にクラスターがマッチする場合、ClassifierReportが生成されます
3. ラベル付け: マッチしたクラスターに対して、Classifierで定義されたラベルが適用されます
4. ClusterProfileの適用: ラベル付けされたクラスターは、そのラベルに基づいて特定のClusterProfileにマッチするようになります
5. アドオンとアプリケーションのデプロイ: マッチしたClusterProfileに基づいて、アドオンやアプリケーションがクラスターにデプロイされます classifier_controller.go:445-463 classifier_controller.go:538-563

### Classifierの実装例1

Kubernetesバージョンに基づくクラスターの分類.
例えば、Kubernetesバージョンが1.24.xのクラスターを分類し、特定のラベルを付与するClassifierを作成できます：

```yaml
apiVersion: lib.projectsveltos.io/v1beta1
kind: Classifier
metadata:
  name: kubernetes-v1.24
spec:
  classifierLabels:
  - key: k8s-version
    value: v1.24
  kubernetesVersionConstraints:
  - comparison: GreaterThanOrEqualTo
    version: 1.24.0
  - comparison: LessThan
    version: 1.25.0
```

### Kubernetesバージョンに基づくアドオンの自動アップグレード

Classifierを使用して、クラスターのKubernetesバージョンが更新されたときに自動的にアドオンをアップグレードすることができます。例えば：

1. Kubernetes v1.24.xのクラスターには「gatekeeper: v3-9」というラベルを付与
2. Kubernetes v1.25.x以上のクラスターには「gatekeeper: v3-10」というラベルを付与

これにより、クラスターがKubernetes v1.24.xからv1.25.xにアップグレードされると、Gatekeeperも自動的に3.9.0から3.10.0にアップグレードされます。

### Classifierの制限事項

#### マネージドクラスターとマネジメントクラスターの関係

質問にあった「マネジメントクラスター上のリソースに基づいてマネージドクラスターにラベルを付与できるか？」という点については、Classifierはマネージドクラスター自体の状態に基づいてのみラベルを付与します。マネジメントクラスター上のリソース（例：KeptnMetric）に基づいてマネージドクラスターにラベルを付与することはできません。

このようなユースケースには、Sveltosのイベントフレームワークを使用することが推奨されています。

#### Classifierの技術的な実装

Classifierは以下のコンポーネントで構成されています：

1. ClassifierReconciler: Classifier CRの調整を担当するコントローラー
2. ClassifierReport: クラスターがClassifierの条件にマッチするかどうかを報告するCR
3. KeyManager: ラベルの管理と競合解決を担当するコンポーネント

#### デプロイメントモード

Classifierは以下の2つのモードで動作します：

1. AgentInMgmtCluster (Agentless): マネジメントクラスターでエージェントが実行され、マネージドクラスターを監視
2. Agent in Managed Clusters: 各マネージドクラスターでエージェントが実行され、自身の状態を報告

#### レポートモード

ClassifierReportの収集方法も2つのモードがあります：

1. CollectFromManagementCluster: マネジメントクラスターが定期的にマネージドクラスターからレポートを収集
2. AgentSendReportsNoGateway: マネージドクラスターのエージェントがレポートを直接マネジメントクラスターに送信

### まとめ

Sveltos Classifierは、クラスターの状態に基づいて動的にラベルを付与し、それによってClusterProfileとのマッチングを可能にする強力なツールです。特にKubernetesバージョンのアップグレードに伴うアドオンの自動アップグレードなど、クラスター管理の自動化に役立ちます。

ただし、マネジメントクラスター上のリソースに基づいてマネージドクラスターにラベルを付与することはできないという制限があります。このようなユースケースには、Sveltosのイベントフレームワークを使用することが推奨されています。

## Q&A

### Q1: Sveltos Agentのdeploymentを手動で削除した場合、自動的に回復しますか？

A1: いいえ、Sveltos Agentのdeploymentを手動で削除しても自動的には回復しません。

### Q2: Sveltos Agentを回復するにはどうすればよいですか？

Sveltos AgentはClassifier Managerが管理しているため、クラスタに対応するClassifierリソースを再作成してClassifier Managerを再起動すればSveltos Agentが再生成されます。