diff --git a/docs/azure.md b/docs/azure.md new file mode 100644 index 00000000..a83cf680 --- /dev/null +++ b/docs/azure.md @@ -0,0 +1,318 @@ +# Microsoft Azure Setup + +## Using Azure Managed PostgreSQL + +With the unified PostgreSQL configuration, connecting to an Azure managed PostgreSQL instance has become more straightforward. Here's how to set it up: + +1. **Create an Azure PostgreSQL server**: Create a PostgreSQL server using the Azure portal or the Azure CLI. + + ```bash + # Example of creating an Azure PostgreSQL flexible server + az postgres flexible-server create \ + --resource-group myResourceGroup \ + --name mypostgresserver \ + --location westus \ + --admin-user myusername \ + --admin-password mypassword \ + --sku-name Standard_B1ms + ``` + +2. **Create a PostgreSQL database**: After creating the server, create a database for your EOAPI deployment. + + ```bash + # Create a database on the Azure PostgreSQL server + az postgres flexible-server db create \ + --resource-group myResourceGroup \ + --server-name mypostgresserver \ + --database-name eoapi + ``` + +3. **Configure firewall rules**: Ensure that the PostgreSQL server allows connections from your Kubernetes cluster's IP address. + + ```bash + # Allow connections from your AKS cluster's outbound IP + az postgres flexible-server firewall-rule create \ + --resource-group myResourceGroup \ + --server-name mypostgresserver \ + --name AllowAKS \ + --start-ip-address \ + --end-ip-address + ``` + +4. **Store PostgreSQL credentials in Azure Key Vault**: Create secrets in your Azure Key Vault to store the database connection information. + + ```bash + # Create Key Vault secrets for PostgreSQL connection + az keyvault secret set --vault-name your-keyvault-name --name db-host --value "mypostgresserver.postgres.database.azure.com" + az keyvault secret set --vault-name your-keyvault-name --name db-port --value "5432" + az keyvault secret set --vault-name your-keyvault-name --name db-name --value "eoapi" + az keyvault secret set --vault-name your-keyvault-name --name db-username --value "myusername@mypostgresserver" + az keyvault secret set --vault-name your-keyvault-name --name db-password --value "mypassword" + ``` + +## Azure Configuration for eoapi-k8s + +When deploying on Azure, you'll need to configure several settings in your values.yaml file. Below are the configurations needed for proper integration with Azure services. + +### Common Azure Configuration + +First, configure the service account with Azure Workload Identity: + +```yaml +# Service Account Configuration +serviceAccount: + create: true + annotations: + azure.workload.identity/client-id: "your-client-id" + azure.workload.identity/tenant-id: "your-tenant-id" +``` + +### Unified PostgreSQL Configuration + +Use the unified PostgreSQL configuration with the `external-secret` type to connect to your Azure managed PostgreSQL: + +```yaml +# Configure PostgreSQL connection to use Azure managed PostgreSQL with secrets from Key Vault +postgresql: + # Use external-secret type to get credentials from a pre-existing secret + type: "external-secret" + + # Basic connection information + external: + host: "mypostgresserver.postgres.database.azure.com" # Can be overridden by secret values + port: "5432" # Can be overridden by secret values + database: "eoapi" # Can be overridden by secret values + + # Reference to a secret that will be created by Azure Key Vault integration + existingSecret: + name: "azure-pg-credentials" + keys: + username: "username" # Secret key for the username + password: "password" # Secret key for the password + host: "host" # Secret key for the host (optional) + port: "port" # Secret key for the port (optional) + database: "database" # Secret key for the database name (optional) +``` + +With this configuration, you're telling the PostgreSQL components to use an external PostgreSQL database and to get its connection details from a Kubernetes secret named `azure-pg-credentials`. This secret will be created using Azure Key Vault integration as described below. + +### Disable internal PostgreSQL cluster + +When using Azure managed PostgreSQL, you should disable the internal PostgreSQL cluster: + +```yaml +postgrescluster: + enabled: false +``` + +### Azure Key Vault Integration + +To allow your Kubernetes pods to access PostgreSQL credentials stored in Azure Key Vault, create a SecretProviderClass: + +```yaml +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: azure-pg-secret-provider +spec: + provider: azure + parameters: + usePodIdentity: "false" + clientID: "your-client-id" + keyvaultName: "your-keyvault-name" + tenantId: "your-tenant-id" + objects: | + array: + - | + objectName: db-host + objectType: secret + objectAlias: host + - | + objectName: db-port + objectType: secret + objectAlias: port + - | + objectName: db-name + objectType: secret + objectAlias: database + - | + objectName: db-username + objectType: secret + objectAlias: username + - | + objectName: db-password + objectType: secret + objectAlias: password + secretObjects: + - secretName: azure-pg-credentials + type: Opaque + data: + - objectName: host + key: host + - objectName: port + key: port + - objectName: database + key: database + - objectName: username + key: username + - objectName: password + key: password +``` + +### Service Configuration + +For services that need to mount the Key Vault secrets, add the following configuration to each service (pgstacBootstrap, raster, stac, vector, multidim): + +```yaml +# Define a common volume configuration for all services +commonVolumeConfig: &commonVolumeConfig + labels: + azure.workload.identity/use: "true" + extraVolumeMounts: + - name: azure-keyvault-secrets + mountPath: /mnt/secrets-store + readOnly: true + extraVolumes: + - name: azure-keyvault-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: azure-pg-secret-provider + +# Apply the common volume configuration to each service +pgstacBootstrap: + enabled: true + settings: + <<: *commonVolumeConfig + +raster: + enabled: true + settings: + <<: *commonVolumeConfig + +stac: + enabled: true + settings: + <<: *commonVolumeConfig + +vector: + enabled: true + settings: + <<: *commonVolumeConfig + +multidim: + enabled: false # set to true if needed + settings: + <<: *commonVolumeConfig +``` + +## Azure Managed Identity Setup + +To use Azure Managed Identity with your Kubernetes cluster: + +1. **Enable Workload Identity on your AKS cluster**: + ```bash + az aks update -g -n --enable-workload-identity + ``` + +2. **Create a Managed Identity**: + ```bash + az identity create -g -n eoapi-identity + ``` + +3. **Configure Key Vault access**: + ```bash + # Get the client ID of the managed identity + CLIENT_ID=$(az identity show -g -n eoapi-identity --query clientId -o tsv) + + # Grant access to Key Vault + az keyvault set-policy -n --secret-permissions get list --spn $CLIENT_ID + ``` + +4. **Create a federated identity credential** to connect the Kubernetes service account to the Azure managed identity: + ```bash + az identity federated-credential create \ + --name eoapi-federated-credential \ + --identity-name eoapi-identity \ + --resource-group \ + --issuer \ + --subject system:serviceaccount::eoapi-sa + ``` + +## Complete Example + +Here's a complete example configuration for connecting EOAPI to an Azure managed PostgreSQL database: + +```yaml +# Service Account Configuration with Azure Workload Identity +serviceAccount: + create: true + annotations: + azure.workload.identity/client-id: "12345678-1234-1234-1234-123456789012" + azure.workload.identity/tenant-id: "87654321-4321-4321-4321-210987654321" + +# Unified PostgreSQL Configuration - using external-secret type +postgresql: + type: "external-secret" + external: + host: "mypostgresserver.postgres.database.azure.com" + port: "5432" + database: "eoapi" + existingSecret: + name: "azure-pg-credentials" + keys: + username: "username" + password: "password" + host: "host" + port: "port" + database: "database" + +# Disable internal PostgreSQL cluster +postgrescluster: + enabled: false + +# Define common volume configuration with Azure Key Vault integration +commonVolumeConfig: &commonVolumeConfig + labels: + azure.workload.identity/use: "true" + extraVolumeMounts: + - name: azure-keyvault-secrets + mountPath: /mnt/secrets-store + readOnly: true + extraVolumes: + - name: azure-keyvault-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: azure-pg-secret-provider + +# Apply the common volume configuration to each service +pgstacBootstrap: + enabled: true + settings: + <<: *commonVolumeConfig + +stac: + enabled: true + settings: + <<: *commonVolumeConfig + +raster: + enabled: true + settings: + <<: *commonVolumeConfig + +vector: + enabled: true + settings: + <<: *commonVolumeConfig + +multidim: + enabled: false + settings: + <<: *commonVolumeConfig +``` + +Make sure to create the SecretProviderClass as shown in the "Azure Key Vault Integration" section above before deploying EOAPI with this configuration. diff --git a/helm-chart/eoapi/templates/service-account.yaml b/helm-chart/eoapi/templates/service-account.yaml new file mode 100644 index 00000000..e2cc52a1 --- /dev/null +++ b/helm-chart/eoapi/templates/service-account.yaml @@ -0,0 +1,18 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "eoapi.serviceAccountName" . }} + labels: + app: eoapi-{{ .Release.Name }} + {{- range $key, $value := .Values.serviceAccount.labels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- if .Values.serviceAccount.annotations }} + annotations: + {{- range $key, $value := .Values.serviceAccount.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} +automountServiceAccountToken: {{ default true .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm-chart/eoapi/templates/services/deployment.yaml b/helm-chart/eoapi/templates/services/deployment.yaml index afe02913..4d92fae4 100644 --- a/helm-chart/eoapi/templates/services/deployment.yaml +++ b/helm-chart/eoapi/templates/services/deployment.yaml @@ -30,8 +30,10 @@ spec: metadata: labels: app: {{ $serviceName }}-{{ $.Release.Name }} + {{- with index $v "settings" "labels" }} + {{- toYaml . | nindent 8 }} + {{- end }} spec: - serviceAccountName: eoapi-sa-{{ $.Release.Name }} containers: - image: {{ index $v "image" "name" }}:{{ index $v "image" "tag" }} name: {{ $serviceName }} @@ -88,6 +90,15 @@ spec: name: {{ $secret }} {{- end }} {{- end }} + {{- with index $v "settings" "extraVolumeMounts" }} + volumeMounts: + {{- toYaml . | nindent 10 }} + {{- end }} + volumes: + {{- with index $v "settings" "extraVolumes" }} + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "eoapi.serviceAccountName" $ }} {{- with index $v "settings" "affinity" }} affinity: {{- toYaml . | nindent 8 }} diff --git a/helm-chart/eoapi/templates/services/rbac.yaml b/helm-chart/eoapi/templates/services/rbac.yaml index 7b4e5f9e..595c44ae 100644 --- a/helm-chart/eoapi/templates/services/rbac.yaml +++ b/helm-chart/eoapi/templates/services/rbac.yaml @@ -1,12 +1,4 @@ {{- if .Values.apiServices }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: eoapi-sa-{{ $.Release.Name }} - labels: - app: eoapi-{{ $.Release.Name }} ---- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -26,7 +18,7 @@ metadata: app: eoapi-{{ $.Release.Name }} subjects: - kind: ServiceAccount - name: eoapi-sa-{{ $.Release.Name }} + name: {{ include "eoapi.serviceAccountName" . }} namespace: {{ $.Release.Namespace }} roleRef: kind: Role diff --git a/helm-chart/eoapi/test-k3s-unittest-values.yaml b/helm-chart/eoapi/test-k3s-unittest-values.yaml index 6f547e3e..f22194a5 100644 --- a/helm-chart/eoapi/test-k3s-unittest-values.yaml +++ b/helm-chart/eoapi/test-k3s-unittest-values.yaml @@ -3,6 +3,8 @@ testing: true ingress: enabled: true className: "traefik" +postgrescluster: + enabled: true pgstacBootstrap: enabled: true settings: diff --git a/helm-chart/eoapi/values.yaml b/helm-chart/eoapi/values.yaml index d2ebc2d4..90632a97 100644 --- a/helm-chart/eoapi/values.yaml +++ b/helm-chart/eoapi/values.yaml @@ -21,6 +21,16 @@ gitSha: "gitshaABC123" # only used in CI for running parallel helm installs testing: false +####################### +# SERVICE ACCOUNT +####################### +serviceAccount: + create: true + name: "" + automount: true + annotations: {} + labels: {} + ###################### # SERVICE & INGRESS @@ -208,6 +218,8 @@ raster: - "--host=$(HOST)" - "--port=$(PORT)" settings: + # Additional labels to add to the pod + labels: {} # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: limits: @@ -216,6 +228,12 @@ raster: requests: cpu: "256m" memory: "3072Mi" + # Additional environment variables from references like ConfigMap or Secret + extraEnvFrom: [] + # Additional volume mounts + extraVolumeMounts: [] + # Additional volumes + extraVolumes: [] envVars: ############## # titiler @@ -236,6 +254,8 @@ raster: PORT: "8080" # https://www.uvicorn.org/settings/#production WEB_CONCURRENCY: "5" + # Additional environment variables + extraEnvVars: {} multidim: enabled: false # disabled by default @@ -269,6 +289,8 @@ multidim: - "--host=$(HOST)" - "--port=$(PORT)" settings: + # Additional labels to add to the pod + labels: {} # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: limits: @@ -277,6 +299,12 @@ multidim: requests: cpu: "256m" memory: "3072Mi" + # Additional environment variables from references like ConfigMap or Secret + extraEnvFrom: [] + # Additional volume mounts + extraVolumeMounts: [] + # Additional volumes + extraVolumes: [] envVars: ############## # titiler @@ -297,6 +325,8 @@ multidim: PORT: "8080" # https://www.uvicorn.org/settings/#production WEB_CONCURRENCY: "5" + # Additional environment variables + extraEnvVars: {} stac: enabled: true @@ -330,6 +360,8 @@ stac: - "--host=$(HOST)" - "--port=$(PORT)" settings: + # Additional labels to add to the pod + labels: {} # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: limits: @@ -338,6 +370,12 @@ stac: requests: cpu: "256m" memory: "1024Mi" + # Additional environment variables from references like ConfigMap or Secret + extraEnvFrom: [] + # Additional volume mounts + extraVolumeMounts: [] + # Additional volumes + extraVolumes: [] envVars: ############## # uvicorn @@ -379,6 +417,8 @@ vector: - "--host=$(HOST)" - "--port=$(PORT)" settings: + # Additional labels to add to the pod + labels: {} # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ resources: limits: @@ -387,6 +427,12 @@ vector: requests: cpu: "256m" memory: "256Mi" + # Additional environment variables from references like ConfigMap or Secret + extraEnvFrom: [] + # Additional volume mounts + extraVolumeMounts: [] + # Additional volumes + extraVolumes: [] envVars: ############## # tipg @@ -400,6 +446,8 @@ vector: PORT: "8080" # https://www.uvicorn.org/settings/#production WEB_CONCURRENCY: "5" + # Additional environment variables + extraEnvVars: {} docServer: enabled: true