diff --git a/docs/samples/deployments/k8s/charts/presidio/.helmignore b/docs/samples/deployments/k8s/charts/presidio/.helmignore index f0c1319444..1c635d3537 100644 --- a/docs/samples/deployments/k8s/charts/presidio/.helmignore +++ b/docs/samples/deployments/k8s/charts/presidio/.helmignore @@ -14,8 +14,12 @@ *.swp *.bak *.tmp +*.orig *~ # Various IDEs .project .idea/ *.tmproj +.vscode/ +# CI/test artifacts +ci/ diff --git a/docs/samples/deployments/k8s/charts/presidio/Chart.yaml b/docs/samples/deployments/k8s/charts/presidio/Chart.yaml index 74162a7d9a..90733dbec4 100644 --- a/docs/samples/deployments/k8s/charts/presidio/Chart.yaml +++ b/docs/samples/deployments/k8s/charts/presidio/Chart.yaml @@ -1,5 +1,20 @@ apiVersion: v2 -description: A context aware, born to the cloud, customizable data loss prevention service name: presidio -version: 2.0 -appVersion: latest \ No newline at end of file +description: Presidio services (analyzer + anonymizer, optional image-redactor) — context-aware PII detection, anonymization, and image redaction +type: application +home: https://github.com/microsoft/presidio +sources: + - https://github.com/microsoft/presidio +keywords: + - presidio + - pii + - anonymization + - data-loss-prevention + - privacy +maintainers: + - name: Presidio Maintainers + url: https://github.com/microsoft/presidio +# Chart version — bump on any chart change (SemVer). +version: 0.2.0 +# Tracks the Presidio app release this chart targets. Quoted because "latest" is not SemVer. +appVersion: "latest" diff --git a/docs/samples/deployments/k8s/charts/presidio/README.md b/docs/samples/deployments/k8s/charts/presidio/README.md new file mode 100644 index 0000000000..9d2ab8da77 --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/README.md @@ -0,0 +1,134 @@ +# Presidio Helm chart + +Deploys the [Presidio](https://github.com/microsoft/presidio) stack to Kubernetes — the +`presidio-analyzer` (PII detection) and `presidio-anonymizer` (PII anonymization) text +services, plus an optional `presidio-image-redactor` (PII redaction in images). It mirrors the +`docker-compose-text.yml` / `docker-compose-image.yml` topology and is scaffolded from +`helm create` following community best practices. + +> Scope: the analyzer and anonymizer are enabled by default. The image-redactor is **disabled +> by default** (`imageRedactor.enabled=true` to opt in) — it bundles spaCy NLP models and +> Tesseract OCR, so it is heavy and slow to start. The transformers NLP engine and Ollama +> remain out of scope for this chart. + +## Prerequisites + +- Kubernetes 1.23+ +- Helm 3.8+ +- An Ingress controller (only if `ingress.enabled=true`); the default path rules assume + [ingress-nginx](https://kubernetes.github.io/ingress-nginx/) + +## Install + +```bash +# From this chart directory: docs/samples/deployments/k8s/charts/presidio +helm install presidio . --namespace presidio --create-namespace + +# Override the image tag (defaults to the chart appVersion) +helm install presidio . --namespace presidio --create-namespace --set image.tag=latest + +# Custom values +helm install presidio . --namespace presidio --create-namespace -f my-values.yaml +``` + +Check rollout (the analyzer loads NLP models on first start and can take a few minutes): + +```bash +kubectl rollout status deployment/presidio-presidio-analyzer -n presidio +helm test presidio -n presidio +``` + +## Uninstall + +```bash +helm uninstall presidio +``` + +## What gets deployed + +| Resource | analyzer | anonymizer | image-redactor | +| --- | --- | --- | --- | +| Enabled by default | ✅ | ✅ | ❌ (`imageRedactor.enabled`) | +| Deployment | ✅ | ✅ | ✅ | +| Service (ClusterIP) | ✅ | ✅ | ✅ | +| HorizontalPodAutoscaler | optional (`analyzer.autoscaling.enabled`) | optional (`anonymizer.autoscaling.enabled`) | optional (`imageRedactor.autoscaling.enabled`) | +| ServiceAccount (shared) | ✅ (`serviceAccount.create`) | | | +| Ingress (shared, routes all enabled) | optional (`ingress.enabled`) | | | + +Pods run as the non-root UID `1001` baked into the Presidio images, with `ALL` capabilities +dropped, `allowPrivilegeEscalation: false`, and the `RuntimeDefault` seccomp profile. + +Enable the image-redactor with: + +```bash +helm install presidio . --namespace presidio --create-namespace --set imageRedactor.enabled=true +``` + +## Configuration + +### Global + +| Key | Default | Description | +| --- | --- | --- | +| `image.registry` | `mcr.microsoft.com` | Registry for both images. Set `""` for bare repos. | +| `image.pullPolicy` | `IfNotPresent` | Default pull policy. | +| `image.tag` | `""` | Default tag; empty falls back to chart `appVersion`. | +| `imagePullSecrets` | `[]` | Private registry pull secrets. | +| `serviceAccount.create` | `true` | Create a shared ServiceAccount. | +| `serviceAccount.automount` | `false` | Mount the SA token (Presidio does not call the K8s API). | +| `podSecurityContext` | non-root 1001 | Pod-level security context for all components. | +| `securityContext` | drop ALL caps | Container-level security context for all components. | +| `nodeSelector` / `tolerations` / `affinity` | empty | Default scheduling; per-component overrides supported. | +| `ingress.enabled` | `false` | Enable the shared Ingress. | +| `ingress.className` | `nginx` | IngressClass. | +| `ingress.hosts` | `presidio.local` | Host/path rules; each path maps to a `service` alias (`analyzer`, `anonymizer`, `image-redactor`). | + +### Per component (`analyzer.*`, `anonymizer.*`, `imageRedactor.*`) + +| Key | analyzer default | anonymizer default | imageRedactor default | Description | +| --- | --- | --- | --- | --- | +| `enabled` | `true` | `true` | `false` | Toggle the component. | +| `image.repository` | `presidio-analyzer` | `presidio-anonymizer` | `presidio-image-redactor` | Image repo (joined with `image.registry`). | +| `replicaCount` | `1` | `1` | `1` | Replicas (ignored when autoscaling is on). | +| `containerPort` | `3000` | `3000` | `3000` | Listen port; exported as the `PORT` env var. | +| `workers` | `1` | `1` | `1` | gunicorn workers; exported as the `WORKERS` env var. | +| `extraEnv` | `[]` | `[]` | `[]` | Extra environment variables. | +| `service.type` / `service.port` | `ClusterIP` / `8080` | `ClusterIP` / `8080` | `ClusterIP` / `8080` | Service exposure. | +| `resources` | 1.5–3Gi / 1.5–2 CPU | 128–512Mi / 0.125–0.5 CPU | 1.5–3Gi / 1.5–2 CPU | Requests/limits. | +| `startupProbe` / `livenessProbe` / `readinessProbe` | `/health` | `/health` | `/health` | HTTP probes. | +| `autoscaling.enabled` | `false` | `false` | `false` | Enable the HPA. | + +> The image-redactor exposes `POST /redact` (multipart image upload) in addition to `/health`. +> Note the [`docker-compose-image.yml`](../../../../../../docker-compose-image.yml) reference sets +> `PORT=5001`, but this chart standardizes every component on container port `3000` (the image's +> Dockerfile default). + +## Scaling caveat + +Balancing traffic across **more than one replica** of a service requires a service mesh +(e.g. Linkerd or Istio) because the clients hold persistent connections — see +[microsoft/presidio#304](https://github.com/microsoft/presidio/issues/304). Plain +`replicaCount > 1` or HPA without a mesh may not distribute load evenly. + +## Example: enable Ingress + +```yaml +ingress: + enabled: true + className: nginx + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 + hosts: + - host: presidio.example.com + paths: + - path: /analyzer(/|$)(.*) + pathType: ImplementationSpecific + service: analyzer + - path: /anonymizer(/|$)(.*) + pathType: ImplementationSpecific + service: anonymizer + # Only routed when imageRedactor.enabled is true. + - path: /image-redactor(/|$)(.*) + pathType: ImplementationSpecific + service: image-redactor +``` diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/NOTES.txt b/docs/samples/deployments/k8s/charts/presidio/templates/NOTES.txt index 8465db4cbb..53f6a65f30 100644 --- a/docs/samples/deployments/k8s/charts/presidio/templates/NOTES.txt +++ b/docs/samples/deployments/k8s/charts/presidio/templates/NOTES.txt @@ -1,9 +1,42 @@ -presidio is now installed! +Presidio has been deployed. 🎉 -To find out about your newly configured system, run: +Release: {{ .Release.Name }} +Namespace: {{ .Release.Namespace }} - $ helm status {{ .Release.Name }} +Services deployed: +{{- if .Values.analyzer.enabled }} + - presidio-analyzer → {{ include "presidio.componentFullname" (dict "root" $ "name" "analyzer") }}:{{ .Values.analyzer.service.port }} +{{- end }} +{{- if .Values.anonymizer.enabled }} + - presidio-anonymizer → {{ include "presidio.componentFullname" (dict "root" $ "name" "anonymizer") }}:{{ .Values.anonymizer.service.port }} +{{- end }} +{{- if .Values.imageRedactor.enabled }} + - presidio-image-redactor → {{ include "presidio.componentFullname" (dict "root" $ "name" "image-redactor") }}:{{ .Values.imageRedactor.service.port }} +{{- end }} -To test your new Presidio service, forward the ports: the Analyzer publishes a service at port 8080. To use the Analyzer, you first need to forward this port to your local IP address: +NOTE: the analyzer{{ if .Values.imageRedactor.enabled }} and image-redactor{{ end }} load NLP models on first start and can +take a few minutes to become Ready. Watch rollout with: - $ kubectl port-forward -n presidio $(kubectl get pod -n presidio -l app=demo-presidio-analyzer -o jsonpath='{.items[0].metadata.name}') 8080:8080 \ No newline at end of file + kubectl --namespace {{ .Release.Namespace }} rollout status deployment/{{ include "presidio.componentFullname" (dict "root" $ "name" "analyzer") }} + +{{- if .Values.ingress.enabled }} + +Reach the services via the configured Ingress: +{{- range $host := .Values.ingress.hosts }} + {{- range $host.paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} → {{ .service }} + {{- end }} +{{- end }} +{{- else }} + +No Ingress is enabled. Test a service from your machine with port-forward, e.g. the anonymizer: + + kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "presidio.componentFullname" (dict "root" $ "name" "anonymizer") }} {{ .Values.anonymizer.service.port }}:{{ .Values.anonymizer.service.port }} + curl http://localhost:{{ .Values.anonymizer.service.port }}/health + +Then anonymize a sample: + + curl -X POST http://localhost:{{ .Values.anonymizer.service.port }}/anonymize \ + -H "Content-Type: application/json" \ + -d '{"text":"My name is Bond.","anonymizers":{"DEFAULT":{"type":"replace","new_value":"ANONYMIZED"}},"analyzer_results":[{"start":11,"end":15,"score":0.8,"entity_type":"PERSON"}]}' +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/_helpers.tpl b/docs/samples/deployments/k8s/charts/presidio/templates/_helpers.tpl index 935b0ad14a..16b18e486c 100644 --- a/docs/samples/deployments/k8s/charts/presidio/templates/_helpers.tpl +++ b/docs/samples/deployments/k8s/charts/presidio/templates/_helpers.tpl @@ -1,40 +1,263 @@ -{{/* vim: set filetype=mustache */}} {{/* Expand the name of the chart. */}} {{- define "presidio.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. */}} {{- define "presidio.fullname" -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} -{{- define "presidio.analyzer.fullname" -}} -{{ include "presidio.fullname" . | printf "%s-analyzer" }} -{{- end -}} -{{- define "presidio.anonymizer.fullname" -}} -{{ include "presidio.fullname" . | printf "%s-anonymizer" }} -{{- end -}} -{{- define "presidio.anonymizerimage.fullname" -}} -{{ include "presidio.fullname" . | printf "%s-image-redactor" }} -{{- end -}} +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "presidio.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} -{{- define "presidio.analyzer.address" -}} -{{template "presidio.analyzer.fullname" .}}:{{.Values.analyzer.service.externalPort}} -{{- end -}} +{{/* +Fully qualified name for a single component (e.g. "-presidio-analyzer"). +Call with a dict: (dict "root" $ "name" "analyzer"). +*/}} +{{- define "presidio.componentFullname" -}} +{{- printf "%s-%s" (include "presidio.fullname" .root) .name | trunc 63 | trimSuffix "-" }} +{{- end }} -{{- define "presidio.anonymizer.address" -}} -{{template "presidio.anonymizer.fullname" .}}:{{.Values.anonymizer.service.externalPort}} -{{- end -}} +{{/* +Common labels. +Call with a dict: (dict "root" $ "name" "analyzer"). +*/}} +{{- define "presidio.labels" -}} +helm.sh/chart: {{ include "presidio.chart" .root }} +{{ include "presidio.selectorLabels" . }} +{{- if .root.Chart.AppVersion }} +app.kubernetes.io/version: {{ .root.Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +app.kubernetes.io/part-of: presidio +{{- end }} -{{- define "presidio.anonymizerimage.address" -}} -{{template "presidio.anonymizerimage.fullname" .}}:{{.Values.anonymizerimage.service.externalPort}} +{{/* +Selector labels. The component label distinguishes the per-service workloads. +Call with a dict: (dict "root" $ "name" "analyzer"). +*/}} +{{- define "presidio.selectorLabels" -}} +app.kubernetes.io/name: {{ include "presidio.name" .root }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +app.kubernetes.io/component: {{ .name }} +{{- end }} + +{{/* +Create the name of the service account to use. +*/}} +{{- define "presidio.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "presidio.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Resolve the container image reference for a component. +Call with a dict: (dict "root" $ "component" .Values.analyzer). +registry and pullPolicy fall back to global image.* values; tag falls back to chart appVersion. +*/}} +{{- define "presidio.image" -}} +{{- $registry := .component.image.registry | default .root.Values.image.registry -}} +{{- $tag := .component.image.tag | default .root.Values.image.tag | default .root.Chart.AppVersion -}} +{{- if $registry -}} +{{- printf "%s/%s:%s" $registry .component.image.repository $tag -}} +{{- else -}} +{{- printf "%s:%s" .component.image.repository $tag -}} {{- end -}} +{{- end }} + +{{/* +Deployment template shared by all components. +Call with a dict: (dict "root" $ "name" "analyzer" "component" .Values.analyzer). +*/}} +{{- define "presidio.deployment" -}} +{{- $root := .root -}} +{{- $name := .name -}} +{{- $c := .component -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "presidio.componentFullname" (dict "root" $root "name" $name) }} + labels: + {{- include "presidio.labels" (dict "root" $root "name" $name) | nindent 4 }} +spec: + {{- if not $c.autoscaling.enabled }} + replicas: {{ $c.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "presidio.selectorLabels" (dict "root" $root "name" $name) | nindent 6 }} + template: + metadata: + annotations: + {{- with $root.Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $c.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "presidio.labels" (dict "root" $root "name" $name) | nindent 8 }} + {{- with $root.Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with $root.Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "presidio.serviceAccountName" $root }} + {{- with $root.Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ $name }} + {{- with $root.Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: {{ include "presidio.image" (dict "root" $root "component" $c) | quote }} + imagePullPolicy: {{ $c.image.pullPolicy | default $root.Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ $c.containerPort }} + protocol: TCP + env: + - name: PORT + value: {{ $c.containerPort | quote }} + - name: WORKERS + value: {{ $c.workers | quote }} + {{- with $c.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $c.startupProbe }} + startupProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $c.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $c.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $c.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $c.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $c.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with ($c.nodeSelector | default $root.Values.nodeSelector) }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with ($c.affinity | default $root.Values.affinity) }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with ($c.tolerations | default $root.Values.tolerations) }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} -{{- define "presidio.rbac.version" }}rbac.authorization.k8s.io/v1{{ end -}} \ No newline at end of file +{{/* +Service template shared by all components. +Call with a dict: (dict "root" $ "name" "analyzer" "component" .Values.analyzer). +*/}} +{{- define "presidio.service" -}} +{{- $root := .root -}} +{{- $name := .name -}} +{{- $c := .component -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "presidio.componentFullname" (dict "root" $root "name" $name) }} + labels: + {{- include "presidio.labels" (dict "root" $root "name" $name) | nindent 4 }} + {{- with $c.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ $c.service.type }} + ports: + - port: {{ $c.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "presidio.selectorLabels" (dict "root" $root "name" $name) | nindent 4 }} +{{- end }} + +{{/* +HorizontalPodAutoscaler template shared by all components. +Call with a dict: (dict "root" $ "name" "analyzer" "component" .Values.analyzer). +*/}} +{{- define "presidio.hpa" -}} +{{- $root := .root -}} +{{- $name := .name -}} +{{- $c := .component -}} +{{- if $c.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "presidio.componentFullname" (dict "root" $root "name" $name) }} + labels: + {{- include "presidio.labels" (dict "root" $root "name" $name) | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "presidio.componentFullname" (dict "root" $root "name" $name) }} + minReplicas: {{ $c.autoscaling.minReplicas }} + maxReplicas: {{ $c.autoscaling.maxReplicas }} + metrics: + {{- if $c.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ $c.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if $c.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ $c.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-deployment.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-deployment.yaml index 1e693d8c4b..0737b8b649 100644 --- a/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-deployment.yaml +++ b/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-deployment.yaml @@ -1,38 +1,3 @@ -{{ $fullname := include "presidio.analyzer.fullname" . }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ $fullname }} - labels: - app: {{ $fullname }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - replicas: {{ .Values.analyzer.replicas }} - selector: - matchLabels: - app: {{ $fullname }} - template: - metadata: - labels: - app: {{ $fullname }} - spec: - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.registry }}/{{ .Values.analyzer.name }}:{{ default .Chart.AppVersion .Values.tag }}" - imagePullPolicy: {{ default "IfNotPresent" .Values.analyzer.imagePullPolicy }} - ports: - - containerPort: {{ .Values.analyzer.service.internalPort }} - resources: - requests: - memory: {{ .Values.analyzer.container.resources.requests.memory }} - cpu: {{ .Values.analyzer.container.resources.requests.cpu }} - limits: - memory: {{ .Values.analyzer.container.resources.limits.memory }} - cpu: {{ .Values.analyzer.container.resources.limits.cpu }} - env: - - name: PORT - value: {{ .Values.analyzer.service.internalPort | quote }} - {{ if .Values.privateRegistry }}imagePullSecrets: - - name: {{.Values.privateRegistry}}{{ end }} \ No newline at end of file +{{- if .Values.analyzer.enabled }} +{{- include "presidio.deployment" (dict "root" $ "name" "analyzer" "component" .Values.analyzer) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-hpa.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-hpa.yaml new file mode 100644 index 0000000000..cf67e24e25 --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-hpa.yaml @@ -0,0 +1,3 @@ +{{- if .Values.analyzer.enabled }} +{{- include "presidio.hpa" (dict "root" $ "name" "analyzer" "component" .Values.analyzer) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-service.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-service.yaml index 5a2f6935ef..5bcfaa63df 100644 --- a/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-service.yaml +++ b/docs/samples/deployments/k8s/charts/presidio/templates/analyzer-service.yaml @@ -1,20 +1,3 @@ -{{ $fullname := include "presidio.analyzer.fullname" . }} -apiVersion: v1 -kind: Service -metadata: - name: {{ $fullname }} - labels: - app: {{ $fullname }} - service: {{ $fullname }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - type: {{ .Values.analyzer.service.type }} - ports: - - port: {{ .Values.analyzer.service.externalPort }} - targetPort: {{ .Values.analyzer.service.internalPort }} - protocol: TCP - name: {{ .Values.analyzer.service.name }} - selector: - app: {{ $fullname }} \ No newline at end of file +{{- if .Values.analyzer.enabled }} +{{- include "presidio.service" (dict "root" $ "name" "analyzer" "component" .Values.analyzer) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-deployment.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-deployment.yaml index f211ca0ce1..f9b993042e 100644 --- a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-deployment.yaml +++ b/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-deployment.yaml @@ -1,38 +1,3 @@ -{{ $fullname := include "presidio.anonymizer.fullname" . }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ $fullname }} - labels: - app: {{ $fullname }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - replicas: {{ .Values.anonymizer.replicas }} - selector: - matchLabels: - app: {{ $fullname }} - template: - metadata: - labels: - app: {{ $fullname }} - spec: - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.registry }}/{{ .Values.anonymizer.name }}:{{ default .Chart.AppVersion .Values.tag }}" - imagePullPolicy: {{ default "IfNotPresent" .Values.anonymizer.imagePullPolicy }} - ports: - - containerPort: {{ .Values.anonymizer.service.internalPort }} - resources: - requests: - memory: {{ .Values.anonymizer.container.resources.requests.memory }} - cpu: {{ .Values.anonymizer.container.resources.requests.cpu }} - limits: - memory: {{ .Values.anonymizer.container.resources.limits.memory }} - cpu: {{ .Values.anonymizer.container.resources.limits.cpu }} - env: - - name: PORT - value: {{ .Values.anonymizer.service.internalPort | quote }} - {{ if .Values.privateRegistry }}imagePullSecrets: - - name: {{.Values.privateRegistry}}{{ end }} \ No newline at end of file +{{- if .Values.anonymizer.enabled }} +{{- include "presidio.deployment" (dict "root" $ "name" "anonymizer" "component" .Values.anonymizer) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-hpa.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-hpa.yaml new file mode 100644 index 0000000000..133314dca3 --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-hpa.yaml @@ -0,0 +1,3 @@ +{{- if .Values.anonymizer.enabled }} +{{- include "presidio.hpa" (dict "root" $ "name" "anonymizer" "component" .Values.anonymizer) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-image-deployment.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-image-deployment.yaml deleted file mode 100644 index f4f3307b97..0000000000 --- a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-image-deployment.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{ $fullname := include "presidio.anonymizerimage.fullname" . }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ $fullname }} - labels: - app: {{ $fullname }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - replicas: {{ .Values.anonymizerimage.replicas }} - selector: - matchLabels: - app: {{ $fullname }} - template: - metadata: - labels: - app: {{ $fullname }} - spec: - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.registry }}/{{ .Values.anonymizerimage.name }}:{{ default .Chart.AppVersion .Values.tag }}" - imagePullPolicy: {{ default "IfNotPresent" .Values.anonymizerimage.imagePullPolicy }} - ports: - - containerPort: {{ .Values.anonymizerimage.service.internalPort }} - resources: - requests: - memory: {{ .Values.anonymizerimage.container.resources.requests.memory }} - cpu: {{ .Values.anonymizerimage.container.resources.requests.cpu }} - limits: - memory: {{ .Values.anonymizerimage.container.resources.limits.memory }} - cpu: {{ .Values.anonymizerimage.container.resources.limits.cpu }} - env: - - name: PORT - value: {{ .Values.anonymizerimage.service.internalPort | quote }} - {{ if .Values.privateRegistry }}imagePullSecrets: - - name: {{.Values.privateRegistry}}{{ end }} \ No newline at end of file diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-image-service.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-image-service.yaml deleted file mode 100644 index e9b045cf2f..0000000000 --- a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-image-service.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{ $fullname := include "presidio.anonymizerimage.fullname" . }} -apiVersion: v1 -kind: Service -metadata: - name: {{ $fullname }} - labels: - app: {{ $fullname }} - service: {{ $fullname }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - type: {{ .Values.anonymizerimage.service.type }} - ports: - - port: {{ .Values.anonymizerimage.service.externalPort }} - targetPort: {{ .Values.anonymizerimage.service.internalPort }} - protocol: TCP - name: {{ .Values.anonymizerimage.service.name }} - selector: - app: {{ $fullname }} \ No newline at end of file diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-service.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-service.yaml index 1cd4668203..10bd023366 100644 --- a/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-service.yaml +++ b/docs/samples/deployments/k8s/charts/presidio/templates/anonymizer-service.yaml @@ -1,20 +1,3 @@ -{{ $fullname := include "presidio.anonymizer.fullname" . }} -apiVersion: v1 -kind: Service -metadata: - name: {{ $fullname }} - labels: - app: {{ $fullname }} - service: {{ $fullname }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - type: {{ .Values.anonymizer.service.type }} - ports: - - port: {{ .Values.anonymizer.service.externalPort }} - targetPort: {{ .Values.anonymizer.service.internalPort }} - protocol: TCP - name: {{ .Values.anonymizer.service.name }} - selector: - app: {{ $fullname }} \ No newline at end of file +{{- if .Values.anonymizer.enabled }} +{{- include "presidio.service" (dict "root" $ "name" "anonymizer" "component" .Values.anonymizer) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-deployment.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-deployment.yaml new file mode 100644 index 0000000000..695bace388 --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-deployment.yaml @@ -0,0 +1,3 @@ +{{- if .Values.imageRedactor.enabled }} +{{- include "presidio.deployment" (dict "root" $ "name" "image-redactor" "component" .Values.imageRedactor) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-hpa.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-hpa.yaml new file mode 100644 index 0000000000..9c92a92e89 --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-hpa.yaml @@ -0,0 +1,3 @@ +{{- if .Values.imageRedactor.enabled }} +{{- include "presidio.hpa" (dict "root" $ "name" "image-redactor" "component" .Values.imageRedactor) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-service.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-service.yaml new file mode 100644 index 0000000000..193ac78169 --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/templates/image-redactor-service.yaml @@ -0,0 +1,3 @@ +{{- if .Values.imageRedactor.enabled }} +{{- include "presidio.service" (dict "root" $ "name" "image-redactor" "component" .Values.imageRedactor) }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/ingress.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/ingress.yaml index 32976d7140..4dff3a088b 100644 --- a/docs/samples/deployments/k8s/charts/presidio/templates/ingress.yaml +++ b/docs/samples/deployments/k8s/charts/presidio/templates/ingress.yaml @@ -1,36 +1,52 @@ -{{- if and (.Values.ingress.enabled) (eq .Values.ingress.class "nginx") -}} -{{- $analyzerfullname := include "presidio.analyzer.fullname" . -}} -{{- $anonymizerfullname := include "presidio.anonymizer.fullname" . -}} -{{- $anonymizerimagefullname := include "presidio.anonymizerimage.fullname" . -}} -apiVersion: networking.k8s.io/v1beta1 +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: presidio-ingress + name: {{ include "presidio.fullname" . }} labels: - app: presidio-ingress - chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} + {{- include "presidio.labels" (dict "root" $ "name" "shared") | nindent 4 }} + {{- with .Values.ingress.annotations }} annotations: - kubernetes.io/ingress.class: {{ .Values.ingress.class }} - nginx.ingress.kubernetes.io/rewrite-target: "/$2" + {{- toYaml . | nindent 4 }} + {{- end }} spec: + {{- with .Values.ingress.className }} + ingressClassName: {{ . }} + {{- end }} + {{- with .Values.ingress.tls }} + tls: + {{- range . }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} rules: - - http: - paths: - - backend: - serviceName: {{ $analyzerfullname }} - servicePort: {{ .Values.analyzer.service.externalPort }} - path: /{{ $analyzerfullname }}(/|$)(.*) - pathType: Prefix - - backend: - serviceName: {{ $anonymizerfullname }} - servicePort: {{ .Values.anonymizer.service.externalPort }} - path: /{{ $anonymizerfullname }}(/|$)(.*) - pathType: Prefix - - backend: - serviceName: {{ $anonymizerimagefullname }} - servicePort: {{ .Values.anonymizerimage.service.externalPort }} - path: /{{ $anonymizerimagefullname }}(/|$)(.*) - pathType: Prefix -{{- end -}} + {{- /* Map each ingress service alias (also the resource name) to its values block. */}} + {{- $components := dict "analyzer" $.Values.analyzer "anonymizer" $.Values.anonymizer "image-redactor" $.Values.imageRedactor }} + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + {{- $svc := .service }} + {{- if not (hasKey $components $svc) }} + {{- fail (printf "ingress path references unknown service %q (expected \"analyzer\", \"anonymizer\" or \"image-redactor\")" $svc) }} + {{- end }} + {{- $component := index $components $svc }} + {{- if $component.enabled }} + - path: {{ .path }} + {{- with .pathType }} + pathType: {{ . }} + {{- end }} + backend: + service: + name: {{ include "presidio.componentFullname" (dict "root" $ "name" $svc) }} + port: + number: {{ $component.service.port }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/serviceaccount.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/serviceaccount.yaml new file mode 100644 index 0000000000..1d25bd5f8d --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "presidio.serviceAccountName" . }} + labels: + {{- include "presidio.labels" (dict "root" $ "name" "shared") | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/templates/tests/test-connection.yaml b/docs/samples/deployments/k8s/charts/presidio/templates/tests/test-connection.yaml new file mode 100644 index 0000000000..fdd965d76e --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/templates/tests/test-connection.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "presidio.fullname" . }}-test-health" + labels: + {{- include "presidio.labels" (dict "root" $ "name" "test") | nindent 4 }} + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + restartPolicy: Never + containers: + - name: health + image: curlimages/curl:8.11.1 + command: ["/bin/sh", "-c"] + args: + - | + set -eu + {{- if .Values.analyzer.enabled }} + echo "Checking analyzer /health ..." + curl -fsS "http://{{ include "presidio.componentFullname" (dict "root" $ "name" "analyzer") }}:{{ .Values.analyzer.service.port }}/health" + {{- end }} + {{- if .Values.anonymizer.enabled }} + echo "Checking anonymizer /health ..." + curl -fsS "http://{{ include "presidio.componentFullname" (dict "root" $ "name" "anonymizer") }}:{{ .Values.anonymizer.service.port }}/health" + {{- end }} + {{- if .Values.imageRedactor.enabled }} + echo "Checking image-redactor /health ..." + curl -fsS "http://{{ include "presidio.componentFullname" (dict "root" $ "name" "image-redactor") }}:{{ .Values.imageRedactor.service.port }}/health" + {{- end }} diff --git a/docs/samples/deployments/k8s/charts/presidio/values.schema.json b/docs/samples/deployments/k8s/charts/presidio/values.schema.json new file mode 100644 index 0000000000..e45ef237a7 --- /dev/null +++ b/docs/samples/deployments/k8s/charts/presidio/values.schema.json @@ -0,0 +1,116 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "title": "Presidio chart values", + "type": "object", + "properties": { + "nameOverride": { "type": "string" }, + "fullnameOverride": { "type": "string" }, + "image": { + "type": "object", + "properties": { + "registry": { "type": "string" }, + "pullPolicy": { "type": "string", "enum": ["Always", "IfNotPresent", "Never"] }, + "tag": { "type": "string" } + } + }, + "imagePullSecrets": { "type": "array" }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { "type": "boolean" }, + "automount": { "type": "boolean" }, + "annotations": { "type": "object" }, + "name": { "type": "string" } + } + }, + "podAnnotations": { "type": "object" }, + "podLabels": { "type": "object" }, + "podSecurityContext": { "type": "object" }, + "securityContext": { "type": "object" }, + "nodeSelector": { "type": "object" }, + "tolerations": { "type": "array" }, + "affinity": { "type": "object" }, + "ingress": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "className": { "type": "string" }, + "annotations": { "type": "object" }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { "type": "string" }, + "paths": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { "type": "string" }, + "pathType": { "type": "string" }, + "service": { "type": "string", "enum": ["analyzer", "anonymizer", "image-redactor"] } + }, + "required": ["path", "service"] + } + } + } + } + }, + "tls": { "type": "array" } + }, + "required": ["enabled"] + }, + "analyzer": { "$ref": "#/definitions/component" }, + "anonymizer": { "$ref": "#/definitions/component" }, + "imageRedactor": { "$ref": "#/definitions/component" } + }, + "required": ["analyzer", "anonymizer", "imageRedactor"], + "definitions": { + "component": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "image": { + "type": "object", + "properties": { + "repository": { "type": "string" }, + "registry": { "type": "string" }, + "tag": { "type": "string" }, + "pullPolicy": { "type": "string" } + }, + "required": ["repository"] + }, + "replicaCount": { "type": "integer", "minimum": 0 }, + "containerPort": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "workers": { "type": "integer", "minimum": 1 }, + "extraEnv": { "type": "array" }, + "service": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"] }, + "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "annotations": { "type": "object" } + }, + "required": ["type", "port"] + }, + "resources": { "type": "object" }, + "startupProbe": { "type": ["object", "null"] }, + "livenessProbe": { "type": ["object", "null"] }, + "readinessProbe": { "type": ["object", "null"] }, + "autoscaling": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "minReplicas": { "type": "integer", "minimum": 1 }, + "maxReplicas": { "type": "integer", "minimum": 1 }, + "targetCPUUtilizationPercentage": { "type": ["integer", "null"] }, + "targetMemoryUtilizationPercentage": { "type": ["integer", "null"] } + }, + "required": ["enabled"] + } + }, + "required": ["enabled", "image", "containerPort", "service"] + } + } +} diff --git a/docs/samples/deployments/k8s/charts/presidio/values.yaml b/docs/samples/deployments/k8s/charts/presidio/values.yaml index cecdbfe8ca..4643ca35b8 100644 --- a/docs/samples/deployments/k8s/charts/presidio/values.yaml +++ b/docs/samples/deployments/k8s/charts/presidio/values.yaml @@ -1,68 +1,278 @@ -registry: mcr.microsoft.com - -# Image pull secret -# privateRegistry: acr-auth -tag: latest +# Default values for the Presidio (text) chart. +# Deploys the analyzer + anonymizer services from docker-compose-text.yml. +# This is a YAML-formatted file. Declare variables to be passed into the templates. -# supported types are nginx (wip: traefik and istio) -ingress: - enabled: true - class: nginx +# -- Override the chart name (used in resource names/labels). +nameOverride: "" +# -- Override the fully qualified release name. +fullnameOverride: "" + +# Global image defaults. Per-component blocks may override repository/tag/pullPolicy. +image: + # -- Container registry hosting the Presidio images. Set to "" to use bare repositories. + registry: mcr.microsoft.com + # -- Default image pull policy. + pullPolicy: IfNotPresent + # -- Default image tag. Empty string falls back to the chart appVersion. + tag: "" + +# -- Secrets for pulling images from a private registry. +# e.g. [ { name: my-registry-secret } ] +imagePullSecrets: [] + +# ServiceAccount shared by all Presidio pods. +serviceAccount: + # -- Create a dedicated ServiceAccount. + create: true + # -- Automatically mount the ServiceAccount API token (Presidio does not call the K8s API). + automount: false + # -- Annotations to add to the ServiceAccount. + annotations: {} + # -- Name to use. Generated from the fullname template when empty and create is true. + name: "" + +# -- Extra annotations applied to every pod. +podAnnotations: {} +# -- Extra labels applied to every pod. +podLabels: {} -# For any of the services below, a service mesh like Linkerd or Istio -# is needed to properly balance traffic to more than 1 replica. -# https://github.com/microsoft/presidio/issues/304 +# Pod-level security context (applied to every component). Matches the non-root UID 1001 +# baked into the Presidio images. +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 + seccompProfile: + type: RuntimeDefault +# Container-level security context (applied to every component). +# readOnlyRootFilesystem stays false: gunicorn workers may write temp files. +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: + - ALL + +# Default scheduling constraints (a component block may override any of these). +nodeSelector: {} +tolerations: [] +affinity: {} + +# Single Ingress fronting both services. Disabled by default. +# NOTE: the path rewrites below assume the ingress-nginx controller. +ingress: + enabled: false + className: nginx + annotations: {} + # nginx.ingress.kubernetes.io/rewrite-target: /$2 + hosts: + - host: presidio.local + paths: + - path: /analyzer(/|$)(.*) + pathType: ImplementationSpecific + # service references a component alias below (analyzer | anonymizer | image-redactor). + service: analyzer + - path: /anonymizer(/|$)(.*) + pathType: ImplementationSpecific + service: anonymizer + # Only routed when imageRedactor.enabled is true. + - path: /image-redactor(/|$)(.*) + pathType: ImplementationSpecific + service: image-redactor + tls: [] + # - secretName: presidio-tls + # hosts: + # - presidio.local + +# -------------------------------------------------------------------------------------- +# presidio-analyzer: PII detection (heavy — bundles spaCy NLP models, slow to start). +# -------------------------------------------------------------------------------------- analyzer: - name: presidio-analyzer - replicas: 1 - imagePullPolicy: Always - container: - resources: - requests: - memory: "1500Mi" - cpu: "1500m" - limits: - memory: "3000Mi" - cpu: "2000m" + enabled: true + image: + repository: presidio-analyzer + # registry/tag/pullPolicy inherit from the global image.* block unless set here. + registry: "" + tag: "" + pullPolicy: "" + # NOTE: balancing across >1 replica needs a service mesh (e.g. Linkerd/Istio). + # See https://github.com/microsoft/presidio/issues/304 + replicaCount: 1 + # Container port; also exported as the PORT env var consumed by gunicorn. + containerPort: 3000 + # Number of gunicorn workers (exported as the WORKERS env var). + workers: 1 + # Additional environment variables (e.g. OLLAMA_HOST). + extraEnv: [] service: type: ClusterIP - externalPort: 80 - internalPort: 8080 - name: http + port: 8080 + annotations: {} + resources: + requests: + memory: "1500Mi" + cpu: "1500m" + limits: + memory: "3000Mi" + cpu: "2000m" + # Generous startupProbe: NLP model loading can take minutes on first boot. + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 15 + timeoutSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + # Optional per-component overrides (fall back to the global defaults when empty). + podAnnotations: {} + nodeSelector: {} + tolerations: [] + affinity: {} + volumes: [] + volumeMounts: [] +# -------------------------------------------------------------------------------------- +# presidio-anonymizer: PII anonymization (lightweight, fast to start). +# -------------------------------------------------------------------------------------- anonymizer: - name: presidio-anonymizer - replicas: 1 - imagePullPolicy: Always - container: - resources: - requests: - memory: "128Mi" - cpu: "125m" - limits: - memory: "512Mi" - cpu: "500m" + enabled: true + image: + repository: presidio-anonymizer + registry: "" + tag: "" + pullPolicy: "" + replicaCount: 1 + containerPort: 3000 + workers: 1 + extraEnv: [] service: type: ClusterIP - externalPort: 80 - internalPort: 8080 - name: http + port: 8080 + annotations: {} + resources: + requests: + memory: "128Mi" + cpu: "125m" + limits: + memory: "512Mi" + cpu: "500m" + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 12 + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 15 + timeoutSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + podAnnotations: {} + nodeSelector: {} + tolerations: [] + affinity: {} + volumes: [] + volumeMounts: [] -anonymizerimage: - name: presidio-image-redactor - replicas: 1 - imagePullPolicy: Always - container: - resources: - requests: - memory: "1500Mi" - cpu: "1500m" - limits: - memory: "3000Mi" - cpu: "2000m" +# -------------------------------------------------------------------------------------- +# presidio-image-redactor: PII redaction in images (heavy — bundles spaCy NLP models and +# Tesseract OCR, slow to start). Disabled by default; this chart's primary scope is text. +# -------------------------------------------------------------------------------------- +imageRedactor: + enabled: false + image: + repository: presidio-image-redactor + # registry/tag/pullPolicy inherit from the global image.* block unless set here. + registry: "" + tag: "" + pullPolicy: "" + # NOTE: balancing across >1 replica needs a service mesh (e.g. Linkerd/Istio). + # See https://github.com/microsoft/presidio/issues/304 + replicaCount: 1 + # Container port; also exported as the PORT env var consumed by gunicorn. + containerPort: 3000 + # Number of gunicorn workers (exported as the WORKERS env var). + workers: 1 + # Additional environment variables. + extraEnv: [] service: type: ClusterIP - externalPort: 80 - internalPort: 8080 - name: http + port: 8080 + annotations: {} + # Sized like the analyzer: loads spaCy en_core_web_lg plus Tesseract OCR. + resources: + requests: + memory: "1500Mi" + cpu: "1500m" + limits: + memory: "3000Mi" + cpu: "2000m" + # Generous startupProbe: NLP model loading can take minutes on first boot. + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + failureThreshold: 30 + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 15 + timeoutSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + podAnnotations: {} + nodeSelector: {} + tolerations: [] + affinity: {} + volumes: [] + volumeMounts: [] diff --git a/docs/samples/deployments/k8s/deployment/deploy-presidio.sh b/docs/samples/deployments/k8s/deployment/deploy-presidio.sh index c6ceb9481a..da420f2626 100755 --- a/docs/samples/deployments/k8s/deployment/deploy-presidio.sh +++ b/docs/samples/deployments/k8s/deployment/deploy-presidio.sh @@ -2,4 +2,4 @@ REGISTRY=${1:-mcr.microsoft.com} TAG=${2:-latest} RELEASE=${3:-demo} -helm install $RELEASE --set registry=$REGISTRY,tag=$TAG ../charts/presidio --namespace presidio \ No newline at end of file +helm install $RELEASE --set image.registry=$REGISTRY,image.tag=$TAG ../charts/presidio --namespace presidio --create-namespace \ No newline at end of file diff --git a/docs/samples/deployments/k8s/index.md b/docs/samples/deployments/k8s/index.md index 5fd86e7066..87ecaff354 100644 --- a/docs/samples/deployments/k8s/index.md +++ b/docs/samples/deployments/k8s/index.md @@ -31,7 +31,8 @@ You can install Presidio locally using [KIND](https://github.com/kubernetes-sigs 5. Port forwarding of HTTP requests to the API micro-service will be done automatically. In order to run manual: ```sh - kubectl port-forward 8080:8080 -n presidio + # Forward the analyzer service (release name is "demo" when using run-with-kind.sh) + kubectl port-forward svc/demo-presidio-analyzer 8080:8080 -n presidio ``` ## Presidio As a Service with Kubernetes @@ -60,8 +61,8 @@ You can install Presidio locally using [KIND](https://github.com/kubernetes-sigs 2. Optional - Ingress controller for presidio API, e.g., [NGINX](https://docs.microsoft.com/en-us/azure/aks/ingress-tls). > **NOTE**: - > Presidio is deployed with an ingress controller by default, and uses `nginx` as `ingress.class`. To change - > this behavior, deploy the helm chart with `ingress.enabled=false`. + > The chart does not create an Ingress by default. To expose the services through an + > NGINX Ingress, deploy with `ingress.enabled=true` (the default `ingress.className` is `nginx`). 3. Deploy from `/docs/samples/deployments/k8s/charts/presidio` @@ -76,9 +77,9 @@ You can install Presidio locally using [KIND](https://github.com/kubernetes-sigs NAME= # Use Helm to install all required components - helm install $NAME . --set tag=$PRESIDIO_LABEL --namespace $NAMESPACE + helm install $NAME . --set image.tag=$TAG --namespace $NAMESPACE # If you have your own images in a separate ACR, run DOCKER_REGISTRY= - helm install $NAME . --set registry=$DOCKER_REGISTRY,tag=$PRESIDIO_LABEL . --namespace $NAMESPACE + helm install $NAME . --set image.registry=$DOCKER_REGISTRY,image.tag=$TAG --namespace $NAMESPACE ```