diff --git a/getting-started/installation/kubernetes.md b/getting-started/installation/kubernetes.md index ccea7fb..d08080a 100644 --- a/getting-started/installation/kubernetes.md +++ b/getting-started/installation/kubernetes.md @@ -1,65 +1,172 @@ # Install on Kubernetes -The official Helm chart deploys Dagu to any Kubernetes 1.19+ cluster. +The official Helm chart installs Dagu in a plug-and-play standalone mode by default. One pod runs the Web UI, scheduler, and local workflow executor, so a regular `ReadWriteOnce` volume is enough for most installations. ## Prerequisites -- Kubernetes 1.19+ -- Helm 3.0+ -- **A `StorageClass` that supports `ReadWriteMany`** — Dagu's server components need shared storage so the Web UI, scheduler, and run history stay in sync across pods. Examples: NFS (`nfs-client-provisioner`), AWS EFS, CephFS, Azure Files Premium, GlusterFS. +- Kubernetes 1.19 or newer +- Helm 3 +- A default `StorageClass` that supports `ReadWriteOnce`, or the name of one to select + +An Ingress controller, DNS record, and TLS certificate are needed only when the UI should be reachable through Ingress. ## Install ```bash helm repo add dagu https://dagucloud.github.io/dagu helm repo update -helm install dagu dagu/dagu --set persistence.storageClass= +helm upgrade --install dagu dagu/dagu \ + --namespace dagu \ + --create-namespace \ + --wait ``` -If your cluster's default `StorageClass` already supports `ReadWriteMany`, you can drop the `--set persistence.storageClass=...` flag. +The default installation creates: + +- a release-scoped Kubernetes ServiceAccount +- one Dagu Deployment running the server, scheduler, and local executor +- a 10 Gi `ReadWriteOnce` PersistentVolumeClaim +- a `ClusterIP` Service for the UI and API + +The PVC is retained when the Helm release is uninstalled. + +If the cluster has no default StorageClass, save the selection in `dagu-values.yaml`: + +```yaml +persistence: + storageClass: standard +``` -## Render manifests without installing +Use that file for the installation and every later upgrade: ```bash -helm template dagu dagu/dagu --set persistence.storageClass= +helm upgrade --install dagu dagu/dagu \ + --namespace dagu \ + --create-namespace \ + --values dagu-values.yaml \ + --wait ``` -## Upgrade +## Open the UI + +For temporary access, forward the UI Service: ```bash -helm repo update -helm upgrade dagu dagu/dagu --set persistence.storageClass= +kubectl --namespace dagu port-forward service/dagu-ui 8080:8080 +``` + +Open and complete the administrator setup. Run `helm get notes dagu --namespace dagu` to see access commands for the installed release. + +## Configure Ingress + +Create `dagu-values.yaml`, or add these settings to the existing file while preserving values such as `persistence.storageClass`. Replace the ingress class, hostname, and TLS Secret with values for the cluster: + +```yaml +ingress: + enabled: true + className: your-ingress-class + host: dagu.example.com + tls: + enabled: true + secretName: dagu-tls + +config: + publicUrl: https://dagu.example.com +``` + +The TLS Secret must exist in the Dagu namespace. Leave `secretName` empty only when the ingress controller supplies a default certificate. + +Apply the values: + +```bash +helm upgrade --install dagu dagu/dagu \ + --namespace dagu \ + --create-namespace \ + --values dagu-values.yaml \ + --wait ``` -## From a source checkout +Point `dagu.example.com` at the ingress controller, then check the resource: ```bash -git clone https://github.com/dagucloud/dagu.git -cd dagu -helm install dagu ./charts/dagu --set persistence.storageClass= +kubectl --namespace dagu get ingress dagu-ui ``` -## Image and version +Open . The bundled UI and API use the same origin, so no CORS setting is needed. With OIDC, use the same URL for `auth.oidc.clientUrl` and register `https://dagu.example.com/oidc-callback` with the identity provider. + +Proxy-header authentication has a separate trust model and cannot use the chart-managed Ingress. Follow [Proxy Authentication](/server-admin/authentication/proxy) before enabling it. -- Chart version comes from `charts/dagu/Chart.yaml`. -- Container image comes from `values.yaml` — defaults to `ghcr.io/dagucloud/dagu:latest`. Pin a specific tag in production: +## Customize storage - ```bash - helm install dagu dagu/dagu \ - --set persistence.storageClass= \ - --set image.tag=vX.Y.Z - ``` +Set the size or StorageClass with a values file: -## Full configuration +```yaml +persistence: + size: 20Gi + storageClass: standard +``` + +To reuse a PVC managed outside the release: -All values are documented in [`charts/dagu/README.md`](https://github.com/dagucloud/dagu/blob/main/charts/dagu/README.md) and validated against `values.schema.json`. +```yaml +persistence: + existingClaim: dagu-data +``` + +The existing PVC must be in the release namespace. The chart does not create, resize, or delete it. + +## Use distributed mode + +Distributed mode runs the UI server, scheduler, coordinator, and workers in separate Deployments. The server-side components require shared `ReadWriteMany` storage: + +```yaml +deploymentMode: distributed + +persistence: + accessMode: ReadWriteMany + storageClass: nfs-client +``` + +Workers use ephemeral local storage and report results through the coordinator. See [Kubernetes deployment](/server-admin/deployment/kubernetes#distributed-mode) for worker pools and operational details. + +## Upgrade + +```bash +helm repo update +helm upgrade dagu dagu/dagu \ + --namespace dagu \ + --values dagu-values.yaml \ + --wait +``` + +Keep the release configuration in a values file and pass the same file on every upgrade. If the release uses only chart defaults, omit the `--values` line. + +## Install from a source checkout + +From the Dagu repository root: + +```bash +helm upgrade --install dagu ./charts/dagu \ + --namespace dagu \ + --create-namespace \ + --wait +``` + +## Image version + +The chart uses its `appVersion` as the image tag by default. Override the tag only when a different Dagu version is required: + +```yaml +image: + tag: "" +``` ## Verify ```bash -kubectl get pods -l app.kubernetes.io/name=dagu -kubectl port-forward svc/dagu 8080:8080 -# visit http://localhost:8080 +kubectl --namespace dagu get pods +kubectl --namespace dagu get pvc +helm test dagu --namespace dagu ``` -Next: [Quickstart](/getting-started/quickstart). +For all chart settings, troubleshooting, and distributed configuration, see [Kubernetes deployment](/server-admin/deployment/kubernetes). To remove the release, see [Uninstall](/getting-started/installation/uninstall#helm). diff --git a/getting-started/installation/uninstall.md b/getting-started/installation/uninstall.md index 8a81745..92ef783 100644 --- a/getting-started/installation/uninstall.md +++ b/getting-started/installation/uninstall.md @@ -87,6 +87,25 @@ docker volume rm dagu # destroys workflow history ## Helm ```bash -helm uninstall dagu -kubectl delete pvc -l app.kubernetes.io/name=dagu # destroys workflow history +RELEASE=dagu +NAMESPACE=dagu +helm uninstall "$RELEASE" --namespace "$NAMESPACE" ``` + +The official chart retains its managed PVC by default, so uninstalling Dagu does not remove workflows, run history, credentials, or other persisted state. + +List the PVC for the release: + +```bash +kubectl --namespace "$NAMESPACE" get pvc \ + --selector "app.kubernetes.io/instance=$RELEASE" +``` + +If the chart created the PVC, delete it only when the data is no longer needed. Set `PVC_NAME` to the name returned by the previous command: + +```bash +PVC_NAME=dagu-data +kubectl --namespace "$NAMESPACE" delete pvc "$PVC_NAME" +``` + +Set `RELEASE` and `NAMESPACE` to the values used during installation. A claim configured through `persistence.existingClaim` is managed outside Helm and is never deleted by the chart; do not run the manual deletion command for that claim. diff --git a/overview/architecture.md b/overview/architecture.md index ca7331e..64142ce 100644 --- a/overview/architecture.md +++ b/overview/architecture.md @@ -123,7 +123,7 @@ Dagu follows the XDG Base Directory specification for file organization: ## Distributed Execution Architecture -Dagu supports distributed execution through a coordinator-worker model. DAG definitions are transmitted to workers via gRPC, so workers only need shared storage for execution state and logs. +Dagu supports distributed execution through a coordinator-worker model. DAG definitions are transmitted to workers via gRPC. Shared-nothing workers also return status and logs over gRPC, while shared-filesystem workers write that data directly to common storage. ### Overview diff --git a/server-admin/authentication/proxy.md b/server-admin/authentication/proxy.md index 2e696ec..c26b580 100644 --- a/server-admin/authentication/proxy.md +++ b/server-admin/authentication/proxy.md @@ -279,7 +279,8 @@ auth: ``` The chart rejects proxy authentication when `ui.replicas` is not `1`, when -`auth.mode` is not `builtin`, or when `extraEnv` contains a +`auth.mode` is not `builtin`, when the UI Service is not a `ClusterIP`, when +the chart-managed Ingress is enabled, or when `extraEnv` contains a `DAGU_AUTH_PROXY_*` override. It uses the `Recreate` strategy for the UI Deployment, so UI upgrades briefly interrupt browser access. @@ -288,8 +289,9 @@ Start with the chart's then change its namespaces and selectors. Allow the component that forwards the application request to Dagu, not necessarily the authentication service. -Keep the UI Service as `ClusterIP`. Do not expose another `LoadBalancer`, -`NodePort`, Ingress, Gateway, or service-mesh route that bypasses authentication. +Keep `ui.service.type: ClusterIP` and `ingress.enabled: false`. Create the +authenticated Ingress, Gateway, or service-mesh route separately. Do not expose +another `LoadBalancer`, `NodePort`, or route that bypasses authentication. ## Verify the deployment diff --git a/server-admin/deployment/kubernetes.md b/server-admin/deployment/kubernetes.md index 9647829..5516ad2 100644 --- a/server-admin/deployment/kubernetes.md +++ b/server-admin/deployment/kubernetes.md @@ -1,471 +1,419 @@ # Kubernetes (Helm) -This page documents the Helm chart in `charts/dagu` as implemented in this repository. +The official Helm chart in `charts/dagu` deploys Dagu on Kubernetes 1.19 or newer. Its default configuration is a self-contained standalone installation designed to work with a regular cluster StorageClass. -## Chart Path +For the shortest installation path, see [Install on Kubernetes](/getting-started/installation/kubernetes). -```text -charts/dagu -``` +## Deployment modes -## Rendered Objects +| Mode | Components | Storage | Best for | +|---|---|---|---| +| `standalone` (default) | One `dagu start-all` pod runs the UI, scheduler, and local executor | `ReadWriteOnce` | Most installations and initial evaluation | +| `distributed` | Separate UI server, scheduler, coordinator, and worker Deployments | `ReadWriteMany` for server-side state | Horizontal workers and specialized worker pools | -For a release named ``, the chart renders: +Standalone mode intentionally keeps one UI replica and uses the `Recreate` update strategy. This prevents two pods from writing the same file-backed state while a `ReadWriteOnce` volume is attached. -- `ConfigMap/-config` -- `PersistentVolumeClaim/-data` -- `Deployment/-coordinator` -- `Deployment/-scheduler` -- `Deployment/-ui` -- `Deployment/-worker-` for each entry in `workerPools` -- `Service/-coordinator` (`ClusterIP`, port `50055`) -- `Service/-scheduler` (`ClusterIP`, port `8090`) -- `Service/-ui` (`ClusterIP`, port `ui.service.port`) +## Prerequisites -The chart does not render: +- Kubernetes 1.19 or newer +- Helm 3 +- A default `ReadWriteOnce` StorageClass, or the name of one to select +- For distributed mode, a StorageClass that supports `ReadWriteMany` +- For Ingress access, an installed ingress controller and a DNS record for the UI hostname -- `Ingress` -- `NetworkPolicy` -- `ServiceAccount`, `Role`, or `RoleBinding` -- `HorizontalPodAutoscaler` -- `PodDisruptionBudget` -- Services for worker Deployments +## Install standalone mode -`` is resolved by the helper in `charts/dagu/templates/_helpers.tpl`: +```bash +helm repo add dagu https://dagucloud.github.io/dagu +helm repo update +helm upgrade --install dagu dagu/dagu \ + --namespace dagu \ + --create-namespace \ + --wait +``` -- `fullnameOverride` if set -- otherwise let `` be `nameOverride` if set, or `dagu` if not -- if the release name already contains ``, use the release name unchanged -- otherwise use `-` +If the cluster has no default StorageClass, pass one explicitly: -`nameOverride` and `fullnameOverride` are supported by the templates even though they are not listed in `charts/dagu/values.yaml`. +```bash +helm upgrade --install dagu dagu/dagu \ + --namespace dagu \ + --create-namespace \ + --set-string persistence.storageClass=standard \ + --wait +``` -## Install +From a Dagu source checkout, replace `dagu/dagu` with `./charts/dagu`. -Official Helm repository URL: +## Rendered resources -```text -https://dagucloud.github.io/dagu -``` +For a release named `dagu`, standalone mode creates these primary resources: -Add the repository and install the chart: +- `ServiceAccount/dagu` +- `ConfigMap/dagu-config` +- `PersistentVolumeClaim/dagu-data`, unless `persistence.existingClaim` is set +- `Deployment/dagu-ui` +- `Service/dagu-ui` +- `Ingress/dagu-ui` when `ingress.enabled=true` -```bash -helm repo add dagu https://dagucloud.github.io/dagu -helm repo update -helm install dagu dagu/dagu --set persistence.storageClass= -``` +Distributed mode also creates: + +- `Deployment/dagu-scheduler` and `Service/dagu-scheduler` +- `Deployment/dagu-coordinator` and `Service/dagu-coordinator` +- one `Deployment/dagu-worker-` for every entry in `workerPools` -Render manifests without installing: +The chart uses stable selectors so upgrades do not replace workloads merely because chart metadata changes. `nameOverride` and `fullnameOverride` can change the resource-name prefix. Run `helm get manifest` or query resources by the release label when custom names are used: ```bash -helm template dagu dagu/dagu --set persistence.storageClass= +kubectl --namespace dagu get deployment,service,serviceaccount,configmap,pvc,ingress \ + --selector app.kubernetes.io/instance=dagu ``` -To upgrade an existing release: +## Access the UI + +### Port forwarding + +The default UI Service is a `ClusterIP` on port 8080: ```bash -helm repo update -helm upgrade dagu dagu/dagu --set persistence.storageClass= +kubectl --namespace dagu port-forward service/dagu-ui 8080:8080 ``` -From the repository root, the local chart path remains available: +Open . The first visit guides the administrator through builtin-auth setup. + +The release notes contain commands with the actual rendered resource names: ```bash -helm install dagu ./charts/dagu --set persistence.storageClass= +helm get notes dagu --namespace dagu ``` -Replace `` with a StorageClass in your cluster that supports `ReadWriteMany`. If your cluster default storage class already supports `ReadWriteMany`, you can omit the flag. +### Ingress -## Version Fields +Create a values file and replace the ingress class, hostname, and TLS Secret with values for the cluster: -`charts/dagu/Chart.yaml` defines the chart `version`, which is the version published in the Helm repository. +```yaml +ingress: + enabled: true + className: your-ingress-class + annotations: {} + host: dagu.example.com + tls: + enabled: true + secretName: dagu-tls -The deployed container image comes from `values.yaml -> image.repository` and `values.yaml -> image.tag`. With the current defaults, the chart deploys `ghcr.io/dagucloud/dagu:latest`. +config: + publicUrl: https://dagu.example.com +``` -## Pod Configuration +The TLS Secret must exist in the release namespace. Leave `ingress.tls.secretName` empty only when the ingress controller supplies a default certificate. Provider-specific settings can be added through `ingress.annotations`. -All Deployments created by the chart: +Apply the values: -- mount the shared PVC at `/data` -- mount the ConfigMap at `/etc/dagu` -- set `enableServiceLinks: false` -- set `DAGU_HOME=/data` +```bash +helm upgrade --install dagu dagu/dagu \ + --namespace dagu \ + --create-namespace \ + --values dagu-values.yaml \ + --wait +``` -Additional per-component environment variables: +Point the hostname at the ingress controller and inspect the assigned address: -- UI: - `DAGU_PORT=` -- Coordinator: - `DAGU_COORDINATOR_HOST=0.0.0.0` - `DAGU_COORDINATOR_ADVERTISE=-coordinator..svc.cluster.local` -- Worker: - `DAGU_WORKER_ID` from `metadata.name` +```bash +kubectl --namespace dagu get ingress dagu-ui +``` -The chart also supports a shared `extraEnv` value. Every entry in `extraEnv` is added to the `env:` list of the coordinator, scheduler, UI, and all worker Deployments. +Open . Because the bundled UI and API use the same origin, `config.corsAllowedOrigins` is not needed for this setup. -Container commands are fixed by the templates: +When OIDC is enabled, set `auth.oidc.clientUrl` to the same external URL and register its `/oidc-callback` path with the identity provider. -```text -dagu coordinator --config /etc/dagu/dagu.yaml -dagu scheduler --config /etc/dagu/dagu.yaml -dagu server --config /etc/dagu/dagu.yaml -dagu worker --config /etc/dagu/dagu.yaml [--worker.labels ...] -``` +::: warning Proxy authentication +Proxy-header authentication cannot use the chart-managed Ingress. The authenticating proxy must be the only path to the UI Service. Keep `ingress.enabled=false` and follow [Proxy Authentication](/server-admin/authentication/proxy). +::: -## Dagu Configuration Written By The Chart +### LoadBalancer or NodePort -The chart writes the following `dagu.yaml` to `-config`: +Clusters without an ingress controller can expose the Service directly: ```yaml -host: "0.0.0.0" -port: 8080 -public_url: "https://dagu.example.com" # only written when config.publicUrl is set -api_base_path: "/api/v1" -default_execution_mode: "distributed" - -coordinator: - host: "0.0.0.0" - port: 50055 - -scheduler: - port: 8090 - -paths: - data_dir: /data - dags_dir: /data/dags - log_dir: /data/logs - base_config: /data/base.yaml - suspend_flags_dir: /data/suspend - admin_logs_dir: /data/admin/logs - dag_runs_dir: /data/dag-runs - queue_dir: /data/queue - proc_dir: /data/proc - service_registry_dir: /data/services - api_keys_dir: /data/api-keys - webhooks_dir: /data/webhooks - users_dir: /data/users - -auth: - mode: "builtin" - builtin: - token: - secret: "" - ttl: "24h" - -peer: - insecure: true +ui: + service: + type: LoadBalancer + port: 80 + annotations: {} ``` -The `auth.mode`, `auth.builtin.token.secret`, and `auth.builtin.token.ttl` fields come from `values.yaml`. The other fields shown above are fixed by the chart templates. - -When `config.envPassthrough` is non-empty, the chart also writes top-level `env_passthrough` into `dagu.yaml`. +`ui.service.port` is the Kubernetes Service port. Dagu still listens on `ui.containerPort`, which defaults to 8080, so exposing Service port 80 does not require the application process to bind a privileged port. -When `config.envPassthroughPrefixes` is non-empty, the chart also writes top-level `env_passthrough_prefixes` into `dagu.yaml`. +`NodePort` is also supported. Kubernetes selects the node port because the chart does not set a fixed one. -The UI Deployment also sets `DAGU_PORT=`. That environment variable overrides `port` from `dagu.yaml` at runtime when `ui.service.port` is changed from the default. +## Persistence -The following paths are not set explicitly in the chart, but Dagu derives them from `paths.data_dir` and `paths.dags_dir` at runtime: +The default persistence settings are: ```yaml -paths: - remote_nodes_dir: /data/remote-nodes - workspaces_dir: /data/workspaces +persistence: + enabled: true + retain: true + existingClaim: "" + accessMode: ReadWriteOnce + size: 10Gi + storageClass: "" + annotations: {} ``` -That path layout is consistent with `DAGU_HOME=/data` and with the explicit `base_config: /data/base.yaml`. +An empty `storageClass` uses the cluster's default StorageClass. Dagu requires persistence, so `persistence.enabled` must remain `true`. + +### PVC retention + +The chart annotates its PVC so Helm retains it when the release is uninstalled. This preserves workflows, run history, credentials, and other Dagu state. + +Set `persistence.retain: false` only when uninstalling the release should also delete the chart-managed PVC. To remove a retained PVC explicitly: + +```bash +kubectl --namespace dagu delete pvc dagu-data +``` -The chart does not render DAG definition files. `paths.dags_dir` points to `/data/dags` on the shared PVC. Populate that directory through the UI/API or by writing files into the shared volume. +Confirm the release name and PVC contents before deleting it. -## Values Exposed By `values.yaml` +### Existing PVC -The chart currently defines these top-level values in `charts/dagu/values.yaml`: +To mount a PVC managed outside the release: ```yaml -image: - repository: ghcr.io/dagucloud/dagu - tag: latest - pullPolicy: IfNotPresent +persistence: + existingClaim: existing-dagu-data +``` -scheduler: - replicas: 1 - resources: ... +The claim must already exist in the release namespace. The chart does not create, modify, retain, or delete it. -coordinator: - replicas: 1 - resources: ... +In distributed mode, also declare `persistence.accessMode: ReadWriteMany`. The declaration must match the existing PVC; Helm cannot inspect the live claim while rendering. -workerPools: - general: - replicas: 2 - labels: {} - resources: ... - nodeSelector: {} - tolerations: [] - affinity: {} +## Distributed mode -ui: - replicas: 1 - service: - port: 8080 - resources: ... +Distributed mode is an explicit opt-in: -auth: - mode: "builtin" - builtin: - token: - secret: "" - ttl: "24h" +```yaml +deploymentMode: distributed persistence: - enabled: true accessMode: ReadWriteMany - size: 10Gi - storageClass: "" - skipValidation: false + storageClass: nfs-client + size: 20Gi -config: - publicUrl: "" - envPassthrough: [] - envPassthroughPrefixes: [] +worker: + maxActiveRuns: 100 -extraEnv: [] +workerPools: + general: + replicas: 2 + labels: {} + dataVolume: + sizeLimit: 2Gi ``` -`image.pullPolicy` is the actual key used by the chart. `pull_policy` is not used. +Install it with a values file: -Set `config.publicUrl` to the externally reachable Web UI URL when notification messages should include absolute DAG-run links. +```bash +helm upgrade --install dagu dagu/dagu \ + --namespace dagu \ + --create-namespace \ + --values distributed-values.yaml \ + --wait +``` -## Step Environment Passthrough +The UI server, scheduler, and coordinator share the RWX PVC. Workers use ephemeral pod storage and communicate with the coordinator through its `ClusterIP` Service; they do not mount the shared PVC. -The chart exposes Dagu's environment passthrough settings through `values.yaml -> config`: +Each `workerPools` entry creates an independent worker Deployment. Labels are Dagu worker-selection capabilities, while scheduling fields control Kubernetes placement: ```yaml -config: - envPassthrough: - - SSL_CERT_FILE - - HTTP_PROXY - - HTTPS_PROXY - - NO_PROXY - envPassthroughPrefixes: - - AWS_ +workerPools: + gpu: + replicas: 2 + labels: + gpu: "true" + dataVolume: + sizeLimit: 10Gi + resources: + requests: + cpu: "1" + memory: 2Gi + ephemeral-storage: 2Gi + limits: + cpu: "2" + memory: 4Gi + ephemeral-storage: 10Gi + nodeSelector: + accelerator: nvidia + tolerations: [] + affinity: {} ``` -Those values only change Dagu's filter. They do not create container environment variables. +Non-empty `nodeSelector`, `tolerations`, and `affinity` values on a worker pool override their global counterparts for that pool. -If the variable must come from the pod environment, inject it with `extraEnv`: +## Configuration and environment variables -```yaml -extraEnv: - - name: HTTP_PROXY - value: http://proxy.example.com:8080 - - name: HTTPS_PROXY - value: http://proxy.example.com:8080 - - name: NO_PROXY - value: 127.0.0.1,localhost,.svc,.cluster.local - - name: SSL_CERT_FILE - value: /etc/ssl/certs/custom-ca.pem -``` +The chart renders a minimal Dagu configuration into `dagu-config` and mounts it at `/etc/dagu/dagu.yaml`. Every Deployment receives a checksum annotation, so configuration changes trigger a rollout. -Combined example: +Common top-level settings include: ```yaml config: - envPassthrough: - - SSL_CERT_FILE - - HTTP_PROXY - - HTTPS_PROXY - - NO_PROXY + publicUrl: https://dagu.example.com + corsAllowedOrigins: [] + envPassthrough: [] + envPassthroughPrefixes: [] -extraEnv: - - name: HTTP_PROXY - value: http://proxy.example.com:8080 - - name: HTTPS_PROXY - value: http://proxy.example.com:8080 - - name: NO_PROXY - value: 127.0.0.1,localhost,.svc,.cluster.local - - name: SSL_CERT_FILE - value: /etc/ssl/certs/custom-ca.pem +extraEnv: [] ``` -With that configuration: +`extraEnv` adds variables to every Dagu Deployment. `config.envPassthrough` and `config.envPassthroughPrefixes` control which pod environment variables Dagu forwards into workflow processes; they do not create those variables. -- the container processes receive the variables from `extraEnv` -- Dagu forwards the listed variables into step execution -- variables not present in the pod environment are not created by `config.envPassthrough` +## Authentication and licensing -## Persistence Rules +Builtin authentication is enabled by default. The chart also supports Secret-backed basic authentication and chart-managed OIDC configuration: -The PVC template enforces two conditions: +- [Builtin authentication](/server-admin/authentication/builtin) +- [Basic authentication](/server-admin/authentication/basic) +- [OIDC authentication](/server-admin/authentication/oidc) +- [Proxy authentication](/server-admin/authentication/proxy) -1. `persistence.enabled` must be `true` -2. `persistence.accessMode` must be `ReadWriteMany` unless `persistence.skipValidation=true` +The chart keeps Dagu's runtime-compatible OIDC defaults: first-time identities are created automatically and unmatched validated users receive viewer access to all named workspaces. Set `auth.oidc.roleMapping.defaultWorkspaceAccess: none` when named workspaces must be isolated, and use `allowedDomains`, `whitelist`, or explicit mappings to limit who can sign in. -Examples: +OIDC and license activation values reference existing Kubernetes Secrets. Secret data is read when the UI pod starts, so restart the UI after changing a referenced Secret: ```bash -helm install dagu dagu/dagu \ - --set persistence.storageClass= +kubectl --namespace dagu rollout restart deployment/dagu-ui ``` -```bash -helm install dagu dagu/dagu \ - --set persistence.accessMode=ReadWriteOnce \ - --set persistence.skipValidation=true \ - --set workerPools.general.replicas=1 +## Images and private registries + +An empty `image.tag` uses the chart's `appVersion`, keeping the default image aligned with the chart release: + +```yaml +image: + repository: ghcr.io/dagucloud/dagu + tag: "" + pullPolicy: IfNotPresent ``` -`persistence.skipValidation=true` only disables the chart's RWX check. It does not change how Kubernetes storage works. +Set an explicit tag only when a different Dagu version is required. Private registry credentials can be applied to every Dagu pod: -If `persistence.storageClass` is the empty string, the rendered PVC omits `storageClassName` and Kubernetes uses the cluster default behavior. +```yaml +imagePullSecrets: + - name: registry-credentials +``` -## Worker Pools +## Service account and mounted files -Each entry in `workerPools` creates one Deployment: +The chart creates a release-scoped ServiceAccount and assigns it to every Dagu pod. Provider-specific annotations can connect it to supported workload identity systems such as EKS IRSA or GKE Workload Identity: -```text --worker- +```yaml +serviceAccount: + create: true + name: "" + annotations: + example.com/workload-identity: dagu ``` -Pool names are validated by the template and must match: +Use an existing ServiceAccount by disabling creation and setting its name: -```text -^[a-z][a-z0-9-]*$ +```yaml +serviceAccount: + create: false + name: dagu-runtime ``` -Examples: +With `create: false` and an empty name, Dagu uses the namespace's `default` ServiceAccount. The chart does not create RoleBindings or grant Kubernetes API access. Bind the permissions required by workflows or the [Kubernetes Secret provider](/writing-workflows/secrets/kubernetes-provider) to the selected account separately. -- valid: `general`, `gpu-workers`, `us-east-1` -- invalid: `GPU`, `1-workers`, `my_pool` +Additional volumes and mounts are applied to every Dagu container. This example exposes a custom CA bundle to Dagu and its workflow processes: -### Worker Labels - -`workerPools..labels` are Dagu worker capability labels. They are not copied into Kubernetes pod metadata. +```yaml +extraVolumes: + - name: ca-bundle + secret: + secretName: dagu-ca-bundle -The worker pod metadata always includes `dagu.sh/worker-pool: `, but the contents of `workerPools..labels` are only used to build the `--worker.labels` argument. +extraVolumeMounts: + - name: ca-bundle + mountPath: /etc/ssl/certs/dagu-ca-bundle.pem + subPath: ca-bundle.pem + readOnly: true -When `labels` is non-empty, the worker Deployment adds: +extraEnv: + - name: SSL_CERT_FILE + value: /etc/ssl/certs/dagu-ca-bundle.pem -```text ---worker.labels +config: + envPassthrough: + - SSL_CERT_FILE ``` -When `labels: {}` is used, the chart does not add a `--worker.labels` argument. +Use unique volume names that do not conflict with the chart-managed `data` and `config` volumes. Referenced Secrets, ConfigMaps, PVCs, and CSI resources must already be available in the release namespace where applicable. -Example values: +## Pod settings and resources + +These values apply to every Dagu pod: ```yaml -workerPools: - gpu: - replicas: 2 - labels: - gpu: "true" - cuda: "12.0" - resources: - requests: - memory: "1Gi" - cpu: "500m" - nvidia.com/gpu: "1" - limits: - memory: "4Gi" - cpu: "2" - nvidia.com/gpu: "1" - nodeSelector: - nvidia.com/gpu.present: "true" - tolerations: - - key: nvidia.com/gpu - operator: Exists - effect: NoSchedule +podAnnotations: {} +nodeSelector: {} +tolerations: [] +affinity: {} ``` -The rendered worker command includes a `--worker.labels` flag built from that map. The exact pair order should not be relied on. - -### Kubernetes Scheduling Fields +The default `podSecurityContext.fsGroup` is 1000 so mounted runtime files remain writable after the image entrypoint switches to the default Dagu user. -These pool fields are copied directly into the worker pod spec: +The standalone UI container has resource requests but no default limits because local workflow subprocesses run inside it. Set limits only after accounting for the workflows the pod will execute. -- `nodeSelector` -- `tolerations` -- `affinity` -- `resources` +## Upgrade -Example: +Keep release configuration in a values file for reproducible upgrades: -```yaml -workerPools: - gpu: - replicas: 1 - labels: - gpu: "true" - nodeSelector: - nvidia.com/gpu.present: "true" - tolerations: - - key: nvidia.com/gpu - operator: Exists - effect: NoSchedule - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: nvidia.com/gpu.product - operator: In - values: - - NVIDIA-A100-SXM4-40GB +```bash +helm repo update +helm upgrade dagu dagu/dagu \ + --namespace dagu \ + --values dagu-values.yaml \ + --wait ``` -## Minimal Values Example +Inspect the proposed manifests before applying them: -```yaml -image: - tag: latest +```bash +helm template dagu dagu/dagu \ + --namespace dagu \ + --values dagu-values.yaml +``` -auth: - mode: builtin - builtin: - token: - secret: "" - ttl: 24h +## Verify and troubleshoot -persistence: - enabled: true - accessMode: ReadWriteMany - size: 10Gi - storageClass: "" +Check the release and run the chart test: -workerPools: - general: - replicas: 2 - labels: {} - resources: - requests: - memory: 128Mi - cpu: 100m - limits: - memory: 256Mi - cpu: 200m - nodeSelector: {} - tolerations: [] - affinity: {} +```bash +helm status dagu --namespace dagu +kubectl --namespace dagu get pods,pvc,service,ingress +helm test dagu --namespace dagu ``` -Install with that file: +If a pod remains pending, inspect its events and the PVC: ```bash -helm install dagu dagu/dagu -f values.yaml +kubectl --namespace dagu describe pod +kubectl --namespace dagu get pvc +kubectl --namespace dagu describe pvc +kubectl get storageclass ``` -To force a different image tag: +If Ingress has no address, confirm that `ingress.className` selects an installed controller. If the address works but the hostname does not, check the DNS record and TLS Secret. -```yaml -image: - tag: 2.2.4 -``` +Distributed rendering fails unless `persistence.accessMode` is `ReadWriteMany`. A successful render does not guarantee that the selected StorageClass can provision RWX volumes; verify that capability with the storage provider. -## Access The UI +## Uninstall ```bash -kubectl port-forward svc/dagu-ui 8080:8080 +helm uninstall dagu --namespace dagu ``` -If you used a different release name or `fullnameOverride`, replace `dagu-ui` with the rendered UI service name. +The chart-managed PVC remains by default. See [Uninstall](/getting-started/installation/uninstall#helm) before deleting persistent data. + +The complete value reference is maintained with the chart in [`charts/dagu/README.md`](https://github.com/dagucloud/dagu/blob/main/charts/dagu/README.md). diff --git a/server-admin/distributed/networking.md b/server-admin/distributed/networking.md index 8baa2c1..9b11cce 100644 --- a/server-admin/distributed/networking.md +++ b/server-admin/distributed/networking.md @@ -88,15 +88,15 @@ The examples below use `dagu coordinator` when showing only the coordinator gRPC In Kubernetes, expose the coordinator gRPC port with a Service and use the Service DNS name as the coordinator address. -For the official Helm chart, the chart creates a coordinator `ClusterIP` Service on port `50055` and sets the coordinator advertise address to the Service DNS name: +In the official Helm chart's distributed mode, the chart creates a coordinator `ClusterIP` Service on port `50055`, sets the coordinator advertise address to that Service name, and passes the same address to every worker. For a release named `dagu`, the worker endpoint is: ```bash -..svc.cluster.local +dagu-coordinator:50055 ``` -The chart names the Service with Dagu's Helm `fullname` plus `-coordinator`; render the chart or run `kubectl get service` to see the exact name for a release. +The short Service name works because the chart deploys the coordinator and workers in the same namespace. Render the chart or run `kubectl get service` to find the exact name when the release uses a different name or override. -The Helm chart mounts shared storage for the server-side components and workers. Workers can discover coordinators through the shared Dagu service registry. For Helm installation details, see [Install on Kubernetes](/getting-started/installation/kubernetes). +The UI server, scheduler, and coordinator share an RWX volume. Workers use ephemeral pod storage and send status and logs through gRPC; they do not mount the shared PVC. For Helm installation details, see [Kubernetes deployment](/server-admin/deployment/kubernetes#distributed-mode). For hand-written manifests without shared service registry access, configure workers with the Service DNS name explicitly: diff --git a/server-admin/distributed/workers/shared-filesystem.md b/server-admin/distributed/workers/shared-filesystem.md index f1820bf..d8e6747 100644 --- a/server-admin/distributed/workers/shared-filesystem.md +++ b/server-admin/distributed/workers/shared-filesystem.md @@ -253,4 +253,4 @@ spec: claimName: dagu-shared ``` -For Helm-based Kubernetes deployment, see [Kubernetes (Helm)](/server-admin/deployment/kubernetes). +The official Helm chart uses shared-nothing workers with ephemeral local storage; only its UI server, scheduler, and coordinator share the RWX PVC. The manifests above are for a custom shared-filesystem worker deployment. For the supported chart topology, see [Kubernetes (Helm)](/server-admin/deployment/kubernetes#distributed-mode).