diff --git a/docs/configure-wave.md b/docs/configure-wave.md deleted file mode 100644 index c0daba8fa..000000000 --- a/docs/configure-wave.md +++ /dev/null @@ -1,312 +0,0 @@ ---- -title: Configure Wave ---- - -This page describes common operations to set up and configure Wave. - -:::info -See [Configuration reference](./configuration.md) for a full list configuration options for self-hosted Wave deployments. -::: - -## Email notifications - -Wave can be configured to send email notifications for various build related events. - -### SMTP - -#### Configuration - -Add `mail` to your Micronaut environments and configure the SMTP settings in your Wave configuration: - -**Environment configuration:** - -```yaml -# Add 'mail' to your existing environments -MICRONAUT_ENVIRONMENTS: "postgres,redis,lite,mail" -``` - -**SMTP configuration:** - -```yaml -mail: - from: "wave-notifications@your-domain.com" - smtp: - host: "smtp.your-provider.com" - port: "587" - user: "your-smtp-username" - password: "your-smtp-password" - auth: true - starttls: - enable: true - required: true - ssl: - protocols: "TLSv1.2" -``` - -#### Configuration Options - -| Setting | Description | Example Values | -| ------------------- | ------------------------------------ | ------------------------------------------- | -| `from` | Email address that appears as sender | `wave@company.com` | -| `host` | SMTP server hostname | `smtp.gmail.com`, `smtp.office365.com` | -| `port` | SMTP server port | `587` (STARTTLS), `465` (SSL), `25` (plain) | -| `user` | SMTP authentication username | Usually your email address | -| `password` | SMTP authentication password | App password or account password | -| `auth` | Enable SMTP authentication | `true` (recommended) | -| `starttls.enable` | Enable STARTTLS encryption | `true` (recommended) | -| `starttls.required` | Require STARTTLS encryption | `true` (recommended) | -| `ssl.protocols` | Supported SSL/TLS protocols | `TLSv1.2`, `TLSv1.3` | - -### SES - -For AWS environments, Wave supports direct integration with Amazon Simple Email Service (SES) using IAM authentication instead of SMTP credentials. - -#### Requirements - -- AWS SES must be configured in the same region as your Wave deployment -- Wave must have appropriate IAM permissions to send emails via SES -- The IAM role or user must have `ses:SendEmail` and `ses:SendRawEmail` permissions - -#### Configuration - -Add `aws-ses` to your Micronaut environments along with `mail`: - -**Environment configuration:** - -```yaml -# Add both 'mail' and 'aws-ses' to your existing environments -MICRONAUT_ENVIRONMENTS: "postgres,redis,lite,mail,aws-ses" -``` - -**SES configuration:** - -```yaml -mail: - from: "wave-notifications@your-domain.com" -``` - -#### IAM permissions - -Wave requires the following IAM permissions for SES integration: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": ["ses:SendEmail", "ses:SendRawEmail"], - "Resource": "*" - } - ] -} -``` - -#### SES setup requirements - -Before configuring Wave with SES: - -1. **Verify your sending domain** in the AWS SES console -2. **Move out of SES sandbox** if sending to unverified email addresses -3. **Configure appropriate sending limits** for your use case -4. **Ensure SES is available** in your Wave deployment region - -#### Regional considerations - -Wave will automatically use SES in the same AWS region where it's deployed. Ensure SES is: - -- Available and configured in your deployment region -- Has verified domains/addresses for your `from` email address -- Not in sandbox mode if sending to external recipients - -**Note:** No SMTP configuration is needed when using SES with IAM authentication - Wave will use the AWS SDK to send emails directly through the SES API. - -## Security scanning - -Wave can perform security scanning on container builds. This feature requires the build service to be enabled and additional scanning infrastructure. - -**Prerequisites:** - -- Wave build service must be enabled (`wave.build.enabled: true`) -- Scanning backend must be configured and accessible -- Appropriate compute resources for scanning workloads - -``` -wave: - build: - enabled: true - scan: - enabled: true -``` - -## ECR cache repository - -Wave supports using Amazon Elastic Container Registry (ECR) as a cache repository to store and reuse **build** layers, improving build performance and reducing bandwidth usage. - -**Prerequisites:** - -- AWS ECR repository configured in the same region as Wave -- Wave must have appropriate IAM permissions to push/pull from ECR -- ECR repository must be accessible from Wave build infrastructure - -#### Configuration - -Configure ECR cache repository in your Wave configuration: - -```yaml -wave: - build: - enabled: true - cache: "123456789012.dkr.ecr.us-east-1.amazonaws.com/wave-cache" -``` - -#### IAM permissions - -ECR cache IAM permissions are defined in [AWS credentials to access ECR](../docs/install/kubernetes.md#aws-credentials-to-access-ecr). Ensure your cache ARN is an allowed `Resource`. - -#### ECR repository setup - -Create and configure your ECR cache repository: - -1. **Create ECR repository:** - - ```bash - aws ecr create-repository --repository-name wave-cache --region us-east-1 - ``` - -2. **Configure lifecycle policy** to manage cache storage costs: - ```json - { - "rules": [ - { - "rulePriority": 1, - "selection": { - "tagStatus": "untagged", - "countType": "sinceImagePushed", - "countUnit": "days", - "countNumber": 7 - }, - "action": { - "type": "expire" - } - } - ] - } - ``` - -#### Benefits - -Using ECR as a cache repository provides: - -- **Faster builds** by reusing cached layers -- **Reduced bandwidth** usage for repeated builds -- **Cost optimization** through efficient layer storage -- **Regional performance** with ECR in the same region as Wave -- **Integrated security** with AWS IAM and ECR security features - -#### Configuration Options - -| Setting | Description | Example | -| -------------------------- | --------------------------------- | --------------------------------------------------------- | -| `wave.build.cache` | Cache repository URL or S3 path | `123456789012.dkr.ecr.us-east-1.amazonaws.com/wave-cache` | - -**Note:** ECR cache requires Wave build service to be enabled and is only available in AWS deployments with proper ECR access configured. - -## S3 cache authentication - -When using S3 as the BuildKit cache backend (by configuring `wave.build.cache` with an S3 bucket path), Wave relies on AWS native authentication mechanisms rather than static credentials in configuration files. - -For the related configuration options (`wave.build.cache`, `wave.build.cache-bucket-region`, `wave.build.cache-bucket-upload-parallelism`), see [Container build process](./configuration.md#container-build-process). - -### Kubernetes deployments - -S3 cache uses **IAM Roles for Service Accounts (IRSA)** for secure, credential-free authentication. - -Configure your Kubernetes ServiceAccount with an IAM role annotation: - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: wave-build-sa - namespace: wave-build - annotations: - eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/WaveBuildRole -``` - -The IAM role must have permissions to access the S3 cache bucket: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:PutObject", - "s3:GetObject", - "s3:DeleteObject", - "s3:ListBucket", - "s3:AbortMultipartUpload", - "s3:ListMultipartUploadParts", - "s3:ListBucketMultipartUploads" - ], - "Resource": [ - "arn:aws:s3:::my-bucket/wave/cache", - "arn:aws:s3:::my-bucket/wave/cache/*" - ] - } - ] -} -``` - -Update your Wave deployment to use the annotated ServiceAccount: - -```yaml -spec: - template: - spec: - serviceAccountName: wave-build-sa -``` - -### Docker deployments - -For Docker-based builds, use **EC2 Instance Profile** for automatic credential management. - -Attach an IAM role to the EC2 instance running Docker with the S3 permissions shown above. BuildKit automatically uses the instance metadata service to obtain temporary credentials. - -No additional configuration is required. The AWS SDK in BuildKit automatically discovers and uses the instance profile credentials. - -:::note -For development and testing purposes only, you can provide AWS credentials via environment variables: - -```bash -export AWS_ACCESS_KEY_ID=your_access_key -export AWS_SECRET_ACCESS_KEY=your_secret_key -export AWS_REGION=us-east-1 -``` - -**Warning:** This approach is not recommended for production environments as it requires managing static credentials. Always use EC2 Instance Profile for production Docker deployments. -::: - -### Configuration example - -```yaml -wave: - build: - cache: "s3://wave-cache-bucket/buildkit" - cache-bucket-region: "us-east-1" # Optional if AWS_REGION is set - cache-bucket-upload-parallelism: 8 # Optional, controls parallel S3 uploads -``` - -## Client IP address resolution - -Wave uses client IP addresses for rate limiting. By default, Wave uses socket addresses (secure). - -For AWS ALB deployments, enable the `alb` profile: - -```bash -export MICRONAUT_ENVIRONMENTS=alb -``` - -This trusts X-Forwarded-For headers from ALB for correct client IP resolution. diff --git a/docs/how-wave-works.md b/docs/how-wave-works.md index 7f6bde783..72976eba5 100644 --- a/docs/how-wave-works.md +++ b/docs/how-wave-works.md @@ -123,7 +123,7 @@ The lifetime of an ephemeral image that includes an access token in its URI depe - **Other requests.** Builds, mirrors, and requests not bound to a workflow keep a fixed lifetime of 36 hours (the `cache.duration`) from submission. -System administrators can revoke images before these limits. The [`wave.tokens.*`](./configuration.md#general) settings control this behavior. To disable it, set `wave.tokens.watcher.enabled: false`. All requests then use the fixed 36-hour lifetime. +System administrators can revoke images before these limits. The [`wave.tokens.*`](./install/reference.md#general) settings control this behavior. To disable it, set `wave.tokens.watcher.enabled: false`. All requests then use the fixed 36-hour lifetime. ### Context directory size limits diff --git a/docs/install/aws-build.md b/docs/install/aws-build.md new file mode 100644 index 000000000..5f6a10278 --- /dev/null +++ b/docs/install/aws-build.md @@ -0,0 +1,536 @@ +--- +title: Enable Wave builds +description: Add on-demand container builds, mirroring, and scanning to a Wave Lite deployment on Amazon EKS. +--- + +The full Wave configuration is a Wave Lite deployment on Amazon EKS with on-demand builds, freeze, mirroring, and security scanning enabled. Provision the AWS build infrastructure those features need, then turn them on by extending your existing Wave Lite deployment. + +Build, mirror, and scan are independent toggles. Enable any subset, for example mirror without build. Scan and freeze depend on the build pipeline. With `build.enabled: false`, both are unavailable. + +:::info[**Prerequisites**] + +You need the following: + +- A Wave Lite deployment running on an Amazon EKS cluster (see [Install Wave Lite on Kubernetes](kubernetes-lite.md)). +- Permission to create EFS, ECR, S3, IAM, and node-group resources in the cluster's AWS account. +- The AWS CLI and `kubectl`, authenticated against the account. + +::: + +:::tip +The Kubernetes manifests in this guide assemble into a single file. Save each YAML block into `wave-build.yaml` in the order shown, separated by `---`, then apply the file once at the end. The AWS CLI steps (ECR, S3, and IRSA) run on their own and are not part of this file. +::: + +## Infrastructure requirements + +Builds run as Kubernetes jobs on EKS and need infrastructure beyond the base Wave Lite deployment. + +In addition to the EKS cluster, managed database, and Redis that your Wave Lite deployment already uses, you need: + +| Component | Purpose | +| --- | --- | +| Amazon Elastic File System (EFS) and its CSI driver | ReadWriteMany build workspace shared across build pods. | +| Amazon Elastic Container Registry (ECR) repositories | One for built images, one for the BuildKit layer cache. | +| Amazon S3 bucket | Build logs, build lock files, and scan reports. | +| IAM OpenID Connect (OIDC) provider and IAM Roles for Service Accounts (IRSA) role | The AWS identity for the Wave service, used for S3 access and STS role assumption from the cluster. | +| Dedicated build node group | Isolates build workloads. Label its nodes `service=wave-build` (and `service=wave-build-arm64` for ARM) to match the node selector. | + +## Set the shell variables + +The AWS CLI commands in this guide use the following variables. Set them for your environment before you run anything else: + +```bash +export AWS_REGION=us-east-1 +export AWS_ACCOUNT= +export AWS_EKS_CLUSTER_NAME= +export WAVE_S3_BUCKET= +export WAVE_CONFIG_NAME=wave # reused as the IAM policy and role name +``` + +## Create the build namespace + +Build, scan, and mirror pods run in a dedicated namespace: + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: "wave-build" + labels: + app: wave-app +``` + +## Create the ECR repositories + +Create two repositories with a shared prefix, one for built images and one for the BuildKit layer cache: + +```bash +aws --region "$AWS_REGION" ecr create-repository --repository-name wave/build +aws --region "$AWS_REGION" ecr create-repository --repository-name wave/cache +``` + +ECR does not auto-create repositories. Both must exist before Wave first pushes to them. If you point builds or mirroring at a registry other than ECR, check its rules first. See [Registry pre-creation](reference.md#registry-pre-creation) for the per-registry matrix, and [Registry push and authentication failures](../troubleshoot.md#registry-push-and-authentication-failures) when a push fails partway through. + +## Create the S3 bucket + +Wave writes build logs, build lock files, and scan reports to S3: + +```bash +aws --region "$AWS_REGION" s3 mb "s3://$WAVE_S3_BUCKET" +``` + +For naming rules and bucket options, see [Creating a bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-overview.html). + +## Grant Wave access to AWS APIs with IRSA + +IRSA gives the Wave pod its AWS identity. Wave uses it directly for S3 (build logs and Conda lock files), and to sign the STS calls it makes when an IAM role ARN is configured as an ECR registry credential. Wave authenticates ECR pushes and pulls with the `wave.registries` credentials you configure in [Enable build features](#enable-build-features), which can be either: + +- An IAM access key pair. Attach the ECR statements of the policy in this section to that IAM user. +- The ARN of an IAM role whose trust policy allows the Wave role to call `sts:AssumeRole`. Attach the ECR statements to that role. + +IRSA requires an IAM OIDC provider for the cluster. For the mechanics of associating a role with a service account, see [Assign IAM roles to Kubernetes service accounts](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) in the AWS documentation. This section covers the Wave-specific part: which permissions to grant, and which service account to bind them to. + +Author the two policy documents that follow, then create and attach the role. The `describe-cluster` call prints the OIDC issuer URL that `seqera-wave-role.json` needs: + +```bash +aws --region "$AWS_REGION" eks describe-cluster \ + --name "$AWS_EKS_CLUSTER_NAME" \ + --query "cluster.identity.oidc.issuer" \ + --output text + +aws --region "$AWS_REGION" iam create-policy \ + --policy-name "$WAVE_CONFIG_NAME" \ + --policy-document file://seqera-wave-policy.json + +aws --region "$AWS_REGION" iam create-role \ + --role-name "$WAVE_CONFIG_NAME" \ + --assume-role-policy-document file://seqera-wave-role.json + +aws --region "$AWS_REGION" iam attach-role-policy \ + --role-name "$WAVE_CONFIG_NAME" \ + --policy-arn "arn:aws:iam::$AWS_ACCOUNT:policy/$WAVE_CONFIG_NAME" +``` + +`seqera-wave-policy.json` grants access to ECR (for built and cached images) and S3 (for build logs, lock files, and scan reports). Replace ``, ``, and `` with the values you exported earlier: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "ecr:GetAuthorizationToken", + "Resource": "*" + }, + { + "Sid": "CorePermissionsForBuildAndCache", + "Effect": "Allow", + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:CompleteLayerUpload", + "ecr:GetDownloadUrlForLayer", + "ecr:InitiateLayerUpload", + "ecr:PutImage", + "ecr:UploadLayerPart" + ], + "Resource": ["arn:aws:ecr:::repository/wave/*"] + }, + { + "Sid": "ExtraPermissionsForBuild", + "Effect": "Allow", + "Action": [ + "ecr:DescribeImageScanFindings", + "ecr:DescribeImages", + "ecr:DescribeRepositories", + "ecr:GetLifecyclePolicy", + "ecr:GetLifecyclePolicyPreview", + "ecr:GetRepositoryPolicy", + "ecr:ListImages", + "ecr:ListTagsForResource" + ], + "Resource": ["arn:aws:ecr:::repository/wave/*"] + }, + { + "Sid": "BuildLogsLocksAndScanReports", + "Effect": "Allow", + "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"], + "Resource": [ + "arn:aws:s3:::", + "arn:aws:s3:::/*" + ] + } + ] +} +``` + +`seqera-wave-role.json` is the trust policy that lets the `wave-sa` service account assume the role through the cluster's OIDC provider. Replace `` with the issuer host from the `describe-cluster` output (the URL without `https://`): + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam:::oidc-provider/" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + ":sub": "system:serviceaccount:wave:wave-sa", + ":aud": "sts.amazonaws.com" + } + } + } + ] +} +``` + +Create the Wave service account, annotated with the role ARN so the Wave pod assumes the role. Replace `` with the `$WAVE_CONFIG_NAME` value you exported earlier: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: wave-sa + namespace: wave + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam:::role/ +``` + +Only the Wave service pod uses this role, through the `wave-sa` service account. Build, scan, and mirror pods run as `wave-build-sa`, which needs no AWS identity. Wave writes registry credentials into the shared build workspace for them. If IRSA is unavailable, attach an EC2 instance profile carrying the same policy to the node group that runs the Wave service pod. + +Two rules matter here, on top of the [credential sources](kubernetes-lite.md#registry-credentials) Wave Lite already uses: + +- Operator-owned targets — the registry hosts of `wave.build.repo`, `wave.build.cache`, and `wave.build.public-repo` — always use the server-side `wave.registries.` credentials, never a user's workspace credentials. +- The cloud identity does not authenticate to registries by itself. For ECR, Wave exchanges the configured `wave.registries` credentials (an access key pair, or a role ARN it assumes via STS) for an ECR auth token. + +## Configure EFS storage + +Builds and scans share a ReadWriteMany workspace on EFS. Create an EFS file system in the same VPC as the cluster, install the [AWS EFS CSI driver](https://docs.aws.amazon.com/eks/latest/userguide/efs-csi.html), then define the storage resources. + +### Storage class + +Define a storage class backed by the EFS CSI driver: + +```yaml +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: efs-wave-sc +provisioner: efs.csi.aws.com +parameters: + provisioningMode: efs-ap + fileSystemId: "" + directoryPerms: "0755" +``` + +### Persistent volumes and claims + +The Wave service pod and the build pods each mount the workspace, and they run in different namespaces. Because persistent volume claims are namespaced, the claim must exist in both `wave` and `wave-build`. A claim only in `wave` leaves every build, scan, and mirror pod stuck in `Pending`. Create one volume and one claim per namespace, all pointing at the same EFS file system: + +```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: wave-build-pv +spec: + capacity: + storage: 500Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: efs-wave-sc + claimRef: + namespace: wave + name: wave-build-pvc + csi: + driver: efs.csi.aws.com + volumeHandle: "" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: wave + name: wave-build-pvc + labels: + app: wave-app +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 500Gi + storageClassName: efs-wave-sc + volumeName: wave-build-pv +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: wave-build-pv-jobs +spec: + capacity: + storage: 500Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: efs-wave-sc + claimRef: + namespace: wave-build + name: wave-build-pvc + csi: + driver: efs.csi.aws.com + volumeHandle: "" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + namespace: wave-build + name: wave-build-pvc + labels: + app: wave-app +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 500Gi + storageClassName: efs-wave-sc + volumeName: wave-build-pv-jobs +``` + +Configuration notes: + +- Replace `` with your EFS file system ID in both volumes. Both claims keep the name `wave-build-pvc`, because `wave.build.k8s.storage.claim-name` is a single value used in both namespaces. +- The EFS security group must allow inbound and outbound NFS traffic (port `2049`) from the EKS worker nodes. + +## Create the build RBAC + +The Wave build service creates and manages build pods. The following manifest creates two resources: + +- `wave-build-sa`: the service account the build, scan, and mirror pods run as. The `wave.build.k8s.service-account` setting in [Enable build features](#enable-build-features) references it. It needs no Kubernetes API permissions of its own. +- A ClusterRole and binding that grant `wave-sa` (the Wave service, in the `wave` namespace) permission to create and monitor build jobs: + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: wave-build-sa + namespace: wave-build +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: wave-role +rules: + - apiGroups: [""] + resources: [pods, pods/status, pods/log, pods/exec] + verbs: [get, list, watch, create, delete] + - apiGroups: ["batch"] + resources: [jobs, jobs/status] + verbs: [get, list, watch, create, delete] + - apiGroups: [""] + resources: [configmaps, secrets] + verbs: [get, list] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: wave-rolebind +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: wave-role +subjects: + - kind: ServiceAccount + name: wave-sa + namespace: wave +``` + +:::note +The ClusterRole grants these permissions cluster-wide. For least privilege, replace it with a namespaced `Role` and `RoleBinding` in the `wave-build` namespace, because Wave only manages jobs there. +::: + +## Enable build features + +Update the `wave-cfg` ConfigMap from your Wave Lite install to enable build, mirror, and scan and to configure the build subsystem. Keep the existing database, Redis, and Platform settings. Set the build repositories to the ECR repositories you created and point the workspace at the EFS mount. Add a `wave.registries` entry for the ECR host so Wave can authenticate pushes to the build and cache repositories. Without this entry, builds fail with a `Missing credentials for container repository` error: + +```yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: wave-cfg + namespace: wave + labels: + app: wave-cfg +data: + config.yml: | + wave: + build: + enabled: true + repo: ".dkr.ecr..amazonaws.com/wave/build" + cache: ".dkr.ecr..amazonaws.com/wave/cache" + workspace: "/efs/wave/build" + k8s: + namespace: "wave-build" + service-account: "wave-build-sa" + storage: + claim-name: "wave-build-pvc" + mount-path: "/efs/wave/build" + node-selector: + # Keys are container platforms. Values are 'label=value' applied to your build node groups. + # 'noarch' is required: mirror and blob cache pods are architecture-independent and + # get no node selector at all without it. + 'linux/amd64': 'service=wave-build' + 'linux/arm64': 'service=wave-build-arm64' + 'noarch': 'service=wave-build' + logs: + path: "s3:///wave/build-logs" + locks: + path: "s3:///wave/build-locks" + # Independent toggles. Enable only what you need. + mirror: + enabled: true + scan: + enabled: true + # Required whenever scan is enabled: Wave fails to start without it. + reports: + path: "s3:///wave/scan-reports" + blobCache: + enabled: false # Enabling blob cache needs S3. See Configure Wave. + # ECR registry credentials. Wave exchanges these for ECR auth tokens using + # its IRSA identity. See "Grant Wave access to AWS APIs with IRSA". + registries: + .dkr.ecr..amazonaws.com: + username: "" + password: "" + # Database, Redis, and Platform settings (unchanged from the Wave Lite install). + server: + url: "https://wave.example.com" + db: + uri: "jdbc:postgresql://postgres.example.com:5432/wave" + user: "wave_user" + password: "" + redis: + uri: "rediss://redis.example.com:6379" + tower: + endpoint: + url: "https://platform.example.com/api" + endpoints: + health: + enabled: true + disk-space: + enabled: false + jdbc: + enabled: false +``` + +Because Wave ships working defaults for the build tool images and the build timeout, the ConfigMap does not set them. To override one, or to tune the build subsystem further, see [Container build process](reference.md#container-build-process). + +To build ARM (Graviton) images, route `linux/arm64` builds to an ARM node group with the `node-selector` shown earlier. For cache setup (ECR cache repository, S3 cache authentication), see [Configure Wave](configure-wave.md). + +:::note +If your build nodes run Bottlerocket, BuildKit needs user namespaces enabled before any build succeeds. See [Builds fail on Bottlerocket nodes](../troubleshoot.md#builds-fail-on-bottlerocket-nodes). +::: + +## Update the Wave deployment + +Update your Wave Lite deployment to use the IRSA service account, mount the EFS workspace, and run with the build Micronaut environments. The image pull still uses the `seqera-reg-creds` secret created during the Wave Lite install: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: wave + namespace: wave + labels: + app: wave-app +spec: + replicas: 1 + selector: + matchLabels: + app: wave-app + template: + metadata: + labels: + app: wave-app + spec: + serviceAccountName: wave-sa + imagePullSecrets: + - name: seqera-reg-creds + containers: + - image: cr.seqera.io/: # Use the image path and tag provided by Seqera. + name: wave-app + ports: + - containerPort: 9090 + name: http + env: + - name: MICRONAUT_ENVIRONMENTS + # lite is dropped from the Lite set; k8s enables the in-cluster build client. + value: "postgres,redis,k8s,rate-limit" + # The image defaults to an 850 MB heap regardless of the container limit. + # Setting this variable replaces the whole default option set, so keep the + # flags below alongside your own. + - name: WAVE_JVM_OPTS + value: >- + -XX:+UseG1GC + -Xms1g + -Xmx3g + -XX:MaxDirectMemorySize=100m + -Dio.netty.maxDirectMemory=0 + -Dio.netty.allocator.type=pooled + resources: + requests: + memory: "4Gi" + cpu: "1000m" + limits: + memory: "4Gi" + cpu: "2000m" + workingDir: "/work" + volumeMounts: + - name: wave-cfg + mountPath: /work/config.yml + subPath: "config.yml" + - name: build-storage + mountPath: /efs/wave/build + readinessProbe: + httpGet: + path: /health + port: 9090 + initialDelaySeconds: 30 + timeoutSeconds: 10 + livenessProbe: + httpGet: + path: /health + port: 9090 + initialDelaySeconds: 60 + timeoutSeconds: 10 + volumes: + - name: wave-cfg + configMap: + name: wave-cfg + - name: build-storage + persistentVolumeClaim: + claimName: wave-build-pvc + restartPolicy: Always +``` + +Apply the assembled manifest and confirm the build subsystem starts: + +```bash +kubectl apply -f wave-build.yaml +kubectl logs -f deployment/wave -n wave | grep -i build +``` + +## Freeze and user-supplied build repositories + +In freeze mode, a pipeline sets `wave.build.repository` (the Nextflow-side setting) to choose its own push target. Wave treats the value as custom only if it sits outside the operator's `wave.build.repo`, `wave.build.public-repo`, and `wave.build.cache` prefixes. If it starts with one of those prefixes, Wave rejects the freeze with a `must be specified when using freeze mode` error (with a numbered suffix such as `[1]`), even though the pipeline did supply a value. + +To let users freeze to their own repositories, reserve a registry namespace outside your operator prefixes and distribute push credentials through Platform workspaces. See [Registry pre-creation](reference.md#registry-pre-creation) for whether the target registry needs the repository to exist first. + +## Verify your installation + +Run the build, mirror, and scan functional checks in [Verify your installation](post-install.md), then continue to the [production checklist](configure-wave.md#production-checklist) to prepare the deployment for production. diff --git a/docs/install/configure-wave-build.md b/docs/install/configure-wave-build.md deleted file mode 100644 index 1d61cf708..000000000 --- a/docs/install/configure-wave-build.md +++ /dev/null @@ -1,407 +0,0 @@ ---- -title: Configure Wave build ---- - -This guide covers extending your existing Wave installation on Kubernetes to support container build capabilities. This enables Wave's full feature set including container building, freezing, and advanced caching. - -## Prerequisites - -Before extending Wave for build support, ensure you have: - -- **Existing Wave installation** - Basic Wave deployment already running in augmentation-only mode -- **AWS EKS cluster** - Build capabilities require AWS-specific integrations -- **EFS filesystem** - Configured and accessible from your EKS cluster for shared build storage -- **Cluster admin permissions** - Required to create RBAC policies and storage resources - -## Create Kubernetes Service Account & RBAC Policies - -Wave's build service needs permissions to create and manage build pods. Create the necessary RBAC configuration: - -```yaml ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: wave-sa - namespace: wave ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: wave-role -rules: - - apiGroups: [""] - resources: [pods, pods/status, pods/log, pods/exec] - verbs: [get, list, watch, create, delete] - - apiGroups: ["batch"] - resources: [jobs, jobs/status] - verbs: [get, list, watch, create, delete] - - apiGroups: [""] - resources: [configmaps, secrets] - verbs: [get, list] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: wave-rolebind -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: wave-role -subjects: - - kind: ServiceAccount - name: wave-sa - namespace: wave -``` - -## Configure EFS Storage - -Wave builds require shared storage accessible across multiple pods. Configure EFS with the AWS EFS CSI driver: - -### Storage Class - -```yaml -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: efs-wave-sc -provisioner: efs.csi.aws.com -parameters: - provisioningMode: efs-ap - fileSystemId: "REPLACE_ME_EFS_ID" - directoryPerms: "0755" -``` - -### Persistent Volume - -```yaml -apiVersion: v1 -kind: PersistentVolume -metadata: - name: wave-build-pv -spec: - capacity: - storage: 500Gi - volumeMode: Filesystem - accessModes: - - ReadWriteMany - persistentVolumeReclaimPolicy: Retain - storageClassName: efs-wave-sc - csi: - driver: efs.csi.aws.com - volumeHandle: "REPLACE_ME_EFS_ID" -``` - -### Persistent Volume Claim - -```yaml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - namespace: wave - name: wave-build-pvc - labels: - app: wave-app -spec: - accessModes: - - ReadWriteMany - resources: - requests: - storage: 500Gi - storageClassName: efs-wave-sc -``` - -**Configuration Notes:** -- Replace `REPLACE_ME_EFS_ID` with your actual EFS filesystem ID -- EFS must be in the same VPC as your EKS cluster -- Ensure EFS security groups allow NFS traffic from EKS worker nodes - -## Update Wave Configuration - -Update your existing Wave ConfigMap to enable build features and configure storage paths: - -```yaml -kind: ConfigMap -apiVersion: v1 -metadata: - name: wave-cfg - namespace: wave - labels: - app: wave-cfg -data: - config.yml: | - wave: - # Enable build service - build: - enabled: true - workspace: '/build/workspace' - # Optional: Retain failed builds to gather logs & inspect - cleanup: "OnSuccess" - # Optional: Configure build timeouts - timeout: '15m' - # Example additional kubernetes configuration for wave-build - k8s: - dns: - servers: - - "1.1.1.1" - - "8.8.8.8" - namespace: "wave-build" - storage: - mountPath: "/build" - # Relevant volume claim name should match the - claimName: "wave-build-pvc" - serviceAccount: "wave-build-sa" - resources: - requests: - memory: '1Gi' - cpu: '500m' - limits: - memory: '4Gi' - cpu: '2000m' - nodeSelector: - # this node selector binds the build pods to a separate cluster node group - linux/amd64: 'service=wave-build' - linux/arm64: 'service=wave-build-arm64' - # Enable other build-dependent features - mirror: - enabled: true - scan: - enabled: true - blobCache: - enabled: true - - # Existing database, redis, and platform configuration... - db: - uri: "jdbc:postgresql://your-postgres-host:5432/wave" - user: "wave_user" - password: "your_secure_password_here" - - redis: - uri: "redis://your-redis-host:6379" - - tower: - endpoint: - url: "https://your-platform-instance.com/api" - -``` - -## Update Wave Deployment - -Modify your existing Wave deployment to include the service account and EFS storage: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: wave - namespace: wave - labels: - app: wave-app -spec: - replicas: 1 - selector: - matchLabels: - app: wave-app - template: - metadata: - labels: - app: wave-app - spec: - serviceAccountName: wave-sa # Add service account - containers: - - image: your-registry.com/wave:latest - name: wave-app - ports: - - containerPort: 9090 - name: http - env: - - name: MICRONAUT_ENVIRONMENTS - value: "postgres,redis,k8s" # Add k8s environment - - name: WAVE_JVM_OPTS - value: "-Xmx3g -Xms1g -XX:+UseG1GC" - resources: - requests: - memory: "4Gi" - cpu: "1000m" - limits: - memory: "4Gi" - cpu: "2000m" - workingDir: "/work" - volumeMounts: - - name: wave-cfg - mountPath: /work/config.yml - subPath: "config.yml" - - name: build-storage - mountPath: /build - readinessProbe: - httpGet: - path: /health - port: 9090 - initialDelaySeconds: 30 - timeoutSeconds: 10 - livenessProbe: - httpGet: - path: /health - port: 9090 - initialDelaySeconds: 60 - timeoutSeconds: 10 - volumes: - - name: wave-cfg - configMap: - name: wave-cfg - - name: build-storage # Add EFS volume - persistentVolumeClaim: - claimName: wave-build-pvc - restartPolicy: Always -``` - -## Deploy the Updates - -Apply the configuration changes to enable build support: - -```bash -# Apply RBAC configuration -kubectl apply -f wave-rbac.yaml - -# Apply storage configuration -kubectl apply -f wave-storage.yaml - -# Update the ConfigMap -kubectl apply -f wave-configmap.yaml - -# Update the deployment -kubectl apply -f wave-deployment.yaml - -# Verify the deployment -kubectl get pods -n wave -kubectl logs -f deployment/wave -n wave - -# Check that EFS is mounted correctly -kubectl exec -it deployment/wave -n wave -- df -h /build -``` - -## Verify Build Functionality - -Test that Wave build capabilities are working: - -1. **Check Wave health endpoint** for build service status -2. **Monitor logs** for build service initialization messages -3. **Test a simple build** through the Wave API or Platform integration - -```bash -curl http://wave-service.wave.svc.cluster.local:9090/health - -kubectl logs -f deployment/wave -n wave | grep -i build -``` - -## Recommended Production Enhancements - -### Dedicated Node Pools - -Create dedicated node pools for Wave build workloads to isolate build processes and optimize resource allocation: - - -### Build Pod Resource Management - -Configure resource quotas and limits for build pods: - -```yaml -apiVersion: v1 -kind: ResourceQuota -metadata: - name: wave-build-quota - namespace: wave -spec: - hard: - requests.cpu: "10" - requests.memory: 20Gi - limits.cpu: "20" - limits.memory: 40Gi - pods: "10" -``` - -### Monitoring and Alerting - -Set up monitoring for build operations: - -- **Build success/failure rates** -- **Build duration metrics** -- **EFS storage usage** -- **Node resource utilization** -- **Build queue length** - -## Security Considerations - -- **EFS Access Points** - Use EFS access points to isolate build workspaces -- **Network Policies** - Restrict network access for build pods -- **Pod Security Standards** - Apply appropriate security contexts to build pods -- **Image Scanning** - Enable security scanning for built images -- **RBAC Minimization** - Regularly review and minimize Wave's cluster permissions - -## Troubleshooting - -**Common issues and solutions:** - -- **EFS mount failures** - Check security groups and VPC configuration -- **Build pod creation failures** - Verify RBAC permissions and node selectors -- **Storage access issues** - Ensure EFS access points are configured correctly -- **Build timeouts** - Adjust build timeout settings based on workload requirements - -For additional configuration options and advanced features, see [Configuring Wave](../configure-wave.md). - -## Bottlerocket support - -Buildkit requires user namespaces. However, Bottlerocket sets `user.max_user_namespaces=0` by default for security. - -To use Buildkit with Bottlerocket, enable user namespaces for container builds by setting `user.max_user_namespaces=N` on your host nodes, where `N` is a positive integer. Use a sufficiently high value (e.g., `62000`) to avoid build failures. Values that are too low (e.g., `10`) will limit concurrent build capacity and may cause build failures. - -You can configure this setting in two ways: - -#### Recommended: Node startup configuration - -Configure the user namespace setting in your node group's startup script or user data. This approach applies the configuration at boot time and doesn't require privileged containers in your cluster. - -#### Alternative: DaemonSet - -If you can't control the node configuration directly, use DaemonSet. This approach requires running a privileged container. We recommend you deploy it only on wave-build nodes and use a dedicated namespace for isolation. - -**Example manifest:** - -```yaml -apiVersion: apps/v1 -kind: DaemonSet -metadata: - labels: - app: sysctl-userns - name: sysctl-userns -spec: - selector: - matchLabels: - app: sysctl-userns - template: - metadata: - labels: - app: sysctl-userns - spec: - containers: - - name: sysctl-userns - image: busybox - command: ["sh", "-euxc", "sysctl -w user.max_user_namespaces=63359 && sleep infinity"] - securityContext: - privileged: true - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: service - operator: In - values: ["wave-build", "wave-build-arm64"] -``` - -#### Additional resources - -For more information about Bottlerocket, see: - -- [Bottlerocket FAQs](https://bottlerocket.dev/en/faq/) -- [Amazon Bottlerocket FAQs](https://aws.amazon.com/bottlerocket/faqs/) diff --git a/docs/install/configure-wave.md b/docs/install/configure-wave.md new file mode 100644 index 000000000..2efbad4c4 --- /dev/null +++ b/docs/install/configure-wave.md @@ -0,0 +1,194 @@ +--- +title: Configure Wave +description: Prepare a self-hosted Wave deployment for production and configure optional features. +--- + +Prepare a self-hosted Wave deployment for production and add optional features such as email notifications and build caching. Complete the production checklist before you serve traffic. + +:::info +See the [Configuration reference](reference.md) for the full list of configuration options for self-hosted Wave deployments. +::: + +## Production checklist + +A freshly installed Wave service boots and returns `200` on `/service-info`, but it is not yet production-ready. Complete this checklist after [verifying your installation](post-install.md) and before serving production traffic. For the underlying options, see the [Configuration reference](reference.md). + +### Require authentication + +By default Wave allows anonymous pulls. In production, require authentication so only paired Platform clients can request containers: + +```yaml +wave: + capabilities: + anonymous-access: false +``` + +With anonymous access disabled, every request must carry a valid Platform-issued token. + +For a regulated deployment that must also stop Wave from serving images directly, brokering registry credentials, or exposing its HTML pages, add `strict` to `MICRONAUT_ENVIRONMENTS` instead. That disables all four capability toggles at once. See [Capabilities](reference.md#capabilities). + +### Terminate TLS + +Wave does not terminate TLS itself. Front it with an ingress or load balancer that holds the certificate. For example, use an Application Load Balancer (ALB) with an AWS Certificate Manager (ACM) certificate that matches the Wave hostname, and a Route 53 alias record that points at the load balancer. Confirm `wave.server.url` uses the `https://` hostname clients reach. + +:::note +Private CA and self-signed certificate handling is not yet documented. If your registries or Platform use a private CA, contact Seqera support. +::: + +### Set rate limits + +Wave rate-limits container pulls. Tune the limits for your expected load so anonymous or runaway clients cannot exhaust the service: + +```yaml +rate-limit: + pull: + anonymous: 250/1h + authenticated: 2000/1m + timeout-errors: + max-rate: 100/1m +``` + +:::warning +Rate limiting is active only when the `rate-limit` entry is present in the `MICRONAUT_ENVIRONMENTS` variable of your deployment. The install guides include it. Without it, `rate-limit.*` settings are silently ignored. +::: + +For the full list of limits, see [Rate limits](reference.md#rate-limits) in the Configuration reference. + +### Configure cleanup and retention + +Builds and augmented images accumulate. Set cleanup and retention so storage stays bounded. See `wave.cleanup.*` in the [Configuration reference](reference.md). Also set a retention or lifecycle policy on your build and cache registries. + +### Right-size resources + +Reserve about 2 GB memory and 0.2 CPU per Wave instance, with limits of 4 GB and 1 CPU, matching the sizing in the install paths. Run multiple replicas behind the load balancer for availability. + +Set `WAVE_JVM_OPTS` to match the container limit. The image defaults to an 850 MB heap regardless of the container limit. A 4 GB container leaves most of its memory unused until you override it. Setting the variable replaces the whole default option set rather than adding to it. Copy the defaults from `src/main/jib/launch.sh` and adjust `-Xmx`. + +Size the build node pool and cap concurrency with `wave.job-manager.max-running-jobs` and a build-namespace `ResourceQuota`. + +### Restrict build-pod egress + +Build pods run user-supplied Dockerfiles. On a build-enabled deployment, apply a `NetworkPolicy` to the build namespace that limits egress to your registries and S3. + +### Review security headers + +Wave sends HTTP security headers by default: HTTP Strict Transport Security (HSTS), frame options, content-type options, referrer policy, permissions policy, and a content security policy. Review them against your environment and adjust the content security policy if you front Wave with additional origins. See [Security headers](reference.md#security-headers) in the Configuration reference. + +## Email notifications + +Wave sends email notifications for build-related events. Configure delivery through SMTP or Amazon Simple Email Service (SES). + +### SMTP + +Append `mail` to the `MICRONAUT_ENVIRONMENTS` value your install path already sets, then configure the SMTP settings in your Wave configuration: + +```yaml +mail: + from: "wave-notifications@your-domain.com" + smtp: + host: "smtp.your-provider.com" + port: "587" + user: "your-smtp-username" + password: "your-smtp-password" + auth: true + starttls: + enable: true + required: true + ssl: + protocols: "TLSv1.2" +``` + +For every `mail.*` setting, see [Email configuration](reference.md#email-configuration). + +### SES + +In AWS environments, Wave integrates directly with SES using Identity and Access Management (IAM) authentication instead of SMTP credentials. Wave uses the AWS SDK to send emails through the SES API. + +:::info[**Prerequisites**] + +You need the following: + +- SES configured in the same AWS region as your Wave deployment. +- An IAM role or user with `ses:SendEmail` and `ses:SendRawEmail` permissions. +- A verified sending domain or address for your `from` email address. +- SES out of sandbox mode if you send to unverified addresses. + +::: + +Append both `mail` and `aws-ses` to the `MICRONAUT_ENVIRONMENTS` value your install path already sets, then set the sender address in your Wave configuration: + +```yaml +mail: + from: "wave-notifications@your-domain.com" +``` + +Grant Wave the following IAM permissions: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["ses:SendEmail", "ses:SendRawEmail"], + "Resource": "*" + } + ] +} +``` + +Wave uses SES in the AWS region where it runs. Verify your sending domain in the AWS SES console and set sending limits for your expected volume. + +## Security scanning + +Wave scans container builds for vulnerabilities. This feature requires the build service and additional scanning infrastructure. + +:::info[**Prerequisites**] + +You need the following: + +- The Wave build service enabled (`wave.build.enabled: true`). +- Compute resources for scan jobs, which run on the build infrastructure. +- An S3 bucket path for scan reports. + +::: + +Enable scanning in your Wave configuration and set the report location: + +```yaml +wave: + build: + enabled: true + scan: + enabled: true + reports: + path: "s3:///wave/scan-reports" +``` + +Wave runs scans with its bundled Trivy-based scanner image. Override the image with `wave.scan.image.name` if you mirror it to your own registry. For all scan options, see [Container scan process](reference.md#container-scan-process). + +## Build layer cache + +`wave.build.cache` takes either a container repository or an S3 path. [Enable Wave builds](aws-build.md) sets it to the ECR repository created there, which is the default choice on AWS. Add a [lifecycle policy](https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html) that expires untagged images to keep its storage bounded. + +To use S3 as the BuildKit cache backend instead, point `wave.build.cache` at a bucket path: + +```yaml +wave: + build: + cache: "s3://wave-cache-bucket/buildkit" + cache-bucket-region: "us-east-1" # Optional if AWS_REGION is set + cache-bucket-upload-parallelism: 8 # Optional, controls parallel S3 uploads +``` + +S3 cache needs no static credentials. Build pods pick up the AWS identity of their node or service account. Extend the IRSA policy from [Enable Wave builds](aws-build.md#grant-wave-access-to-aws-apis-with-irsa) with `s3:PutObject`, `s3:GetObject`, `s3:DeleteObject`, `s3:ListBucket`, `s3:AbortMultipartUpload`, `s3:ListMultipartUploadParts`, and `s3:ListBucketMultipartUploads` on the cache path. For the full set of build cache settings, see [Container build process](reference.md#container-build-process). + +## Client IP address resolution + +Wave uses client IP addresses for rate limiting. By default, Wave reads the socket address, which clients cannot spoof. + +For AWS ALB deployments, append `alb` to the `MICRONAUT_ENVIRONMENTS` value your install path already sets. The `alb` profile trusts the `X-Forwarded-For` header from the ALB to resolve the client IP. + +:::warning +Enable the `alb` profile only when Wave runs behind a trusted ALB. If Wave is exposed directly to the internet, trusting `X-Forwarded-For` lets clients spoof their IP address and bypass rate limiting. +::: diff --git a/docs/install/docker-compose.md b/docs/install/docker-compose.md index 3da9a2f8c..f52e1568d 100644 --- a/docs/install/docker-compose.md +++ b/docs/install/docker-compose.md @@ -1,43 +1,31 @@ --- -title: Docker Compose installation +title: Install Wave Lite with Docker Compose +description: Deploy Wave Lite on a single Docker host with external PostgreSQL and Redis. --- -Wave enables you to provision container images on demand, removing the need to build and upload them manually to a container registry. Wave can provision both ephemeral and regular registry-persisted container images. +Install Wave Lite with Docker Compose when you want the Lite configuration without Kubernetes, for example a compliance-constrained site that cannot run Amazon EKS. This installs container augmentation, inspection, and private registry authentication. A Docker Compose deployment cannot be extended to the full Wave configuration, which requires Kubernetes on Amazon EKS. -Docker Compose installations support Wave Lite, the self-hosted Wave configuration that includes container augmentation and inspection only and enables the use of the Fusion file system in Nextflow pipelines. +:::info[**Prerequisites**] -## Prerequisites +You need the following: -Before installing Wave, you need the following infrastructure components: +- Current, supported versions of Docker Engine and Docker Compose. +- A host with capacity for each Wave replica you run. One replica reserves 2 GB RAM and 0.2 CPU and is limited to 4 GB and 1 CPU. Budget 6 GB and 2 cores per replica, including headroom for the OS and Docker. On AWS EC2, an `m5a.xlarge` runs one replica comfortably. +- 10 GB storage, plus disk space for container images and temporary files. +- PostgreSQL 16 or later, reachable from the host. +- Redis 6.2 or later, reachable from the host. +- A Seqera Platform deployment and its endpoint URL. +- Access to the Wave container image from `cr.seqera.io`, using credentials provided by Seqera. -- **PostgreSQL instance** - Version 12, or higher -- **Redis instance** - Version 6.2, or higher - -:::note -Use managed services for PostgreSQL and Redis (e.g., Amazon RDS, Amazon ElastiCache, or equivalent) rather than running them in Docker Compose. Managed services provide automated backups, failover, patching, and monitoring that are difficult to replicate with containerized databases. Running PostgreSQL or Redis in Docker Compose is suitable only for local development and testing. ::: -## System requirements - -The minimum system requirements for self-hosted Wave in Docker Compose are: - -- Current, supported versions of **Docker Engine** and **Docker Compose**. -- Compute instance minimum requirements: - - **Memory**: 12 GB RAM available on the host system (8 GB for Wave replicas + headroom for OS and Docker). - - **CPU**: 4 CPU cores available on the host system (2 CPU cores for Wave replicas + headroom for OS and Docker). - - **Storage**: 10 GB in addition to sufficient disk space for your container images and temporary files. - - For example, in AWS EC2, `m5a.xlarge` or greater - - **Network**: Connectivity to your PostgreSQL and Redis instances. +## Create the database -## Database configuration - -Wave requires a PostgreSQL database to operate. - -Create a dedicated `wave` database and user account with the appropriate privileges: +Create a dedicated `wave` database and a `wave_user` role on your managed PostgreSQL instance: ```sql -- Create a dedicated user for Wave -CREATE ROLE wave_user LOGIN PASSWORD 'your_secure_password'; +CREATE ROLE wave_user LOGIN PASSWORD ''; -- Create the Wave database CREATE DATABASE wave; @@ -60,67 +48,53 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO wave_user; ``` -Wave will automatically handle schema migrations on startup and create the required database objects. +Wave applies its schema migrations on startup. + +## Set the connection environment -## Wave config +Create a `wave.env` file with the values Wave needs to reach its database, Redis, and Seqera Platform: -Create a configuration file that defines Wave's behavior and integrations. Save this as `config/wave-config.yml` in your Docker Compose directory. +```bash +# Base URL clients use to reach the Wave service. +WAVE_SERVER_URL=https://wave.example.com + +# PostgreSQL connection. +WAVE_DB_URI=jdbc:postgresql://postgres.example.com:5432/wave +WAVE_DB_USER=wave_user +WAVE_DB_PASSWORD= + +# Redis connection. Use rediss:// for TLS (typical for managed Redis), +# or redis:// for a plain connection. +REDIS_URI=rediss://redis.example.com:6379 + +# Seqera Platform endpoint to pair with. +TOWER_ENDPOINT_URL=https://platform.example.com/api +``` + +:::warning +Set `WAVE_SERVER_URL` to the address your clients use to reach Wave. If you leave it unset, Wave issues container tokens pointing at `http://localhost:9090`, which clients cannot reach. +::: -Database, Redis, and Platform connection settings are provided via the `wave.env` environment file (see [Deploy Wave](#deploy-wave) below). +## Configure Wave + +Wave requires a `config.yml` in its working directory and fails to start without one. Create it alongside `wave.env`: ```yaml wave: - debug: false - tokens: - cache: - duration: "36h" - # Bind workflow-bound token lifetime to the Platform run lifecycle. - # Set enabled to false to keep the fixed cache.duration for all requests. - watcher: - enabled: true - metrics: - enabled: true - -# Rate limiting configuration -rate-limit: - pull: - anonymous: 250/1h - authenticated: 2000/1m - timeout-errors: - max-rate: 100/1m - -# Micronaut framework configuration -micronaut: - # Netty HTTP server configuration - netty: - event-loops: - default: - num-threads: 64 - # HTTP client configuration - http: - services: - stream-client: - read-timeout: '30s' - read-idle-timeout: '5m' - -# Management endpoints configuration -endpoints: - env: - enabled: false - bean: - enabled: false - caches: + # Wave Lite: build, mirror, scan, and blob cache disabled. + build: enabled: false - refresh: + mirror: enabled: false - loggers: + scan: enabled: false - info: + blobCache: enabled: false - # Enable metrics for monitoring - metrics: - enabled: true - # Enable health checks + +# Keep the JDBC and disk-space indicators out of /health. Micronaut enables +# them by default, so a brief database blip turns the healthcheck below red +# and, under Swarm, gets the task replaced. +endpoints: health: enabled: true disk-space: @@ -129,38 +103,73 @@ endpoints: enabled: false ``` -Configuration notes: +The `lite` entry in `MICRONAUT_ENVIRONMENTS`, set in the Compose file in a later step, already applies the four feature toggles. The file restates them explicitly and gives you a place to add further configuration. For every available setting, see the [Configuration reference](reference.md). Before serving production traffic, complete the [production checklist](configure-wave.md#production-checklist). + +## Authenticate to private registries + +Wave Lite pulls images during augmentation. To augment images from a private registry, give Wave credentials for that registry. Wave uses one of two credential sources per request: + +- **Platform workspace credentials**: credentials a user adds to their Seqera Platform workspace. Wave uses these for requests that carry a Platform identity. +- **Server-side static credentials**: credentials the operator sets under `wave.registries.`. Wave uses these for anonymous requests and for registries the operator owns. + +Add an entry per registry to `config.yml`. Wave reads static credentials only from `wave.registries`. Keep the values out of `wave.env` unless you interpolate them here: + +```yaml +wave: + registries: + docker.io: + username: "${DOCKER_USER:}" + password: "${DOCKER_PAT:}" + quay.io: + username: "${QUAY_USER:}" + password: "${QUAY_PAT:}" + myregistry.example.com: + username: "" + password: "" +``` + +The `${VAR:}` form reads the value from the environment. With this block, you can put `DOCKER_USER` and `DOCKER_PAT` in `wave.env` and keep the secrets out of `config.yml`. Configure credentials for every private registry Wave pulls from. Public images need none. For all registry options, see [Container registry](reference.md#container-registry). + +:::warning +Anonymous access is enabled by default. Any client that can reach Wave can use these operator credentials to pull through it. Disable it with `wave.capabilities.anonymous-access: false` before you expose the service. See [Require authentication](configure-wave.md#require-authentication). +::: + +## Log in to the Seqera container registry + +The Wave image is hosted on `cr.seqera.io`, which requires authentication. Log in with the credentials provided by Seqera before starting the service: -- Adjust `num-threads` (64) based on your CPU cores. Use between 2x and 4x your CPU core count. +```bash +docker login cr.seqera.io -u +``` -## Docker Compose +## Create the Compose file -Add the following to your `docker-compose.yml`: +Define the Wave service in `docker-compose.yml`. Replace `` and `` with the image path and tag provided by Seqera: ```yaml services: wave: - # Replace with your Wave image registry path - image: + image: cr.seqera.io/: ports: - "9090:9090" - environment: - - MICRONAUT_ENVIRONMENTS=lite,rate-limit,redis,postgres,prometheus volumes: - - ./config/wave-config.yml:/work/config.yml:ro + - ./config.yml:/work/config.yml:ro + environment: + # prometheus exposes metrics for scraping. Remove it if you do not collect metrics. + - MICRONAUT_ENVIRONMENTS=lite,postgres,redis,rate-limit,prometheus env_file: - wave.env working_dir: /work deploy: mode: replicated - replicas: 2 + replicas: 1 resources: limits: - memory: 1500M + memory: 4G + cpus: '1.0' reservations: - memory: 1500M + memory: 2G cpus: '0.2' - # Health check configuration healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9090/health"] interval: 30s @@ -170,45 +179,24 @@ services: restart: unless-stopped ``` -## Deploy Wave - -1. Download and populate the [wave.env](./_templates/wave.env) file with the settings corresponding to your system. - -1. Use Docker Swarm to deploy Wave Lite. See [Create a swarm](https://docs.docker.com/engine/swarm/swarm-tutorial/create-swarm/) for detailed setup instructions. - - -1. Deploy the Wave service, running two replicas: +## Start Wave - ```bash - docker stack deploy -c docker-compose.yml mystack - ``` +Start the service: - :::note - Wave is available at `http://localhost:9090` once the container is running and healthy. The application may take 30-60 seconds to fully initialize on first startup, as it performs database migrations. - ::: - -1. Check the current status: - - ```bash - docker service ls - ``` - -1. Check the logs: +```bash +docker compose up -d +``` - ```bash - docker service logs mystack_wave - ``` +On first startup, Wave takes 30 to 60 seconds to initialize while it applies database migrations. -1. Tear down the service when it's no longer needed: +For two or more replicas, raise `replicas` and deploy the same file as a Swarm stack instead. See [Deploy a stack to a swarm](https://docs.docker.com/engine/swarm/stack-deploy/). - ```bash - docker stack rm mystack - ``` +:::warning +If Wave Lite runs in the same Swarm as Platform Connect for [Studios](https://docs.seqera.io/platform-enterprise/25.2/enterprise/studios#docker-compose), removing the stack also interrupts Connect services. +::: - :::warning - If Wave Lite is running in the same container as Platform Connect for [Studios](https://docs.seqera.io/platform-enterprise/25.2/enterprise/studios#docker-compose), tearing down the service will also interrupt Connect services. - ::: +## Verify your installation -### Advanced configuration +Confirm the service is live and functional. See [Verify your installation](post-install.md) for the `/service-info` check and the Wave CLI functional checks. -See [Configure Wave](../configure-wave.md) for advanced Wave features, scaling guidance, and integration options. +When Wave is running and verified, continue to the [production checklist](configure-wave.md#production-checklist) to prepare the deployment for production, including TLS termination. This procedure assumes managed PostgreSQL and Redis, and that you front Wave with your own load balancer. diff --git a/docs/install/index.md b/docs/install/index.md new file mode 100644 index 000000000..2078211a1 --- /dev/null +++ b/docs/install/index.md @@ -0,0 +1,31 @@ +--- +title: Self-hosted Wave +description: When to self-host Wave, the two self-hosted configurations, and how to choose an install path. +--- + +Self-hosting runs the Wave container provisioning service inside your own infrastructure instead of using Seqera-hosted Wave. Teams self-host when they cannot grant Seqera-hosted Wave access to a private registry, or when compliance requires the service to run inside their own boundary. + +You can self-host Wave in two configurations: + +- **Wave Lite**: Container augmentation, inspection, and private registry authentication. +- **Wave**: Everything in Wave Lite, plus on-demand container builds, freeze, mirroring, and security scanning. + +Every deployment starts with Wave Lite. The full configuration is a Wave Lite deployment on Amazon EKS with build, mirror, and scan enabled on top. + +For the full capability comparison, see the [feature matrix](../features/index.mdx). For how Wave provisions containers, see [How Wave works](../how-wave-works.md). + +## Install path + +Choose the path that matches your infrastructure. + +### Docker Compose + +Run Wave Lite on a single Docker host without Kubernetes. Choose this path for a compliance-constrained site that cannot run Amazon EKS, or a deployment too small to need a cluster. + +[Install Wave Lite with Docker Compose](docker-compose.md) + +### Kubernetes + +Run Wave Lite on a Kubernetes cluster you already operate. Wave Lite has no AWS dependency and runs on any conformant distribution. This is also the path to the full Wave configuration. Install Wave Lite on Amazon EKS, then [enable Wave builds](aws-build.md) to add on-demand builds, freeze, mirroring, and scanning. + +[Install Wave Lite on Kubernetes](kubernetes-lite.md) diff --git a/docs/install/kubernetes-lite.md b/docs/install/kubernetes-lite.md new file mode 100644 index 000000000..cfba4b34e --- /dev/null +++ b/docs/install/kubernetes-lite.md @@ -0,0 +1,304 @@ +--- +title: Install Wave Lite on Kubernetes +description: Deploy Wave Lite on any Kubernetes cluster with external PostgreSQL and Redis. +--- + +Install Wave Lite on a Kubernetes cluster you already operate. This installs container augmentation, inspection, and private registry authentication. Build, mirror, and scan are not part of Wave Lite. The full Wave configuration adds them to a Wave Lite install on Amazon EKS. After you install Wave Lite on an EKS cluster, follow [Enable Wave builds](aws-build.md). + +:::info[**Prerequisites**] + +You need the following: + +- A Kubernetes cluster, version 1.31 or later, with permission to create namespaces, deployments, and services. +- Cluster capacity for each Wave pod. The deployment in this guide requests 2 GB RAM and 0.2 CPU per pod and limits it to 4 GB and 1 CPU. Scale that by your replica count. +- 10 GB storage, plus disk space for container images and temporary files. +- PostgreSQL 16 or later, reachable from the cluster. +- Redis 6.2 or later, reachable from the cluster. +- A Seqera Platform deployment and its endpoint URL. +- Access to the Wave container image from `cr.seqera.io`, using credentials provided by Seqera. +::: + +:::tip[Install with the Helm chart] +Seqera publishes an official [Wave Helm chart](https://artifacthub.io/packages/helm/seqera/wave) that deploys Wave Lite as an alternative to the raw manifests in this guide. Follow the chart's documentation to install it, and note the following: + +- Create the database first, as described in [Create the database](#create-the-database), and verify the deployment with the same [post-install checks](post-install.md). +- Add `rate-limit` to the chart's `micronautEnvironments` value to activate the rate limits described in [Set rate limits](configure-wave.md#set-rate-limits). +- Confirm the `WAVE_SERVER_URL` environment variable on the running pod resolves to the hostname clients use to reach Wave. Override it with the chart's `extraEnvVars` value if it does not. +- The chart deploys Wave Lite only. To run the full Wave configuration, deploy on Amazon EKS and follow [Enable Wave builds](aws-build.md) on top of the chart release. +::: + +## Create the database + +Create a dedicated `wave` database and a `wave_user` role on your managed PostgreSQL instance: + +```sql +-- Create a dedicated user for Wave +CREATE ROLE wave_user LOGIN PASSWORD ''; + +-- Create the Wave database +CREATE DATABASE wave; + +-- Connect to the wave database +\c wave; + +-- Grant basic schema access +GRANT USAGE, CREATE ON SCHEMA public TO wave_user; + +-- Grant privileges on existing tables and sequences +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO wave_user; +GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO wave_user; + +-- Grant privileges on future tables and sequences +ALTER DEFAULT PRIVILEGES IN SCHEMA public +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO wave_user; + +ALTER DEFAULT PRIVILEGES IN SCHEMA public +GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO wave_user; +``` + +Wave applies its schema migrations on startup. + +## Create the namespace + +Create a dedicated `wave` namespace to hold the Wave service and its resources: + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: "wave" + labels: + app: wave-app +``` + +## Create the registry credentials secret + +The Wave image is hosted on `cr.seqera.io`, which requires authentication. Create a pull secret in the `wave` namespace with the credentials provided by Seqera. The deployment in a later step references it as `seqera-reg-creds`: + +```bash +kubectl create secret docker-registry seqera-reg-creds \ + --namespace wave \ + --docker-server=cr.seqera.io \ + --docker-username= \ + --docker-password= +``` + +## Configure Wave + +Create a ConfigMap that holds the Wave configuration. The ConfigMap is the entire `config.yml` because Wave loads a single YAML document. Add settings inside this block rather than appending a second `wave:` section. Update the database, Redis, Platform, and registry values to match your environment. + +:::warning +This ConfigMap contains sensitive values. Use a Kubernetes Secret for credentials and reference it from the deployment rather than embedding secrets in the ConfigMap. See the [Kubernetes Secrets documentation](https://kubernetes.io/docs/concepts/configuration/secret/). +::: + +```yaml +kind: ConfigMap +apiVersion: v1 +metadata: + name: wave-cfg + namespace: "wave" + labels: + app: wave-cfg +data: + config.yml: | + wave: + # Wave Lite: build, mirror, scan, and blob cache disabled. + build: + enabled: false + mirror: + enabled: false + scan: + enabled: false + blobCache: + enabled: false + server: + url: "https://wave.example.com" + db: + uri: "jdbc:postgresql://postgres.example.com:5432/wave" + user: "wave_user" + password: "" + # One entry per private registry Wave pulls from. Public images need none. + registries: + docker.io: + username: "" + password: "" + quay.io: + username: "" + password: "" + redis: + # Use rediss:// for TLS (typical for managed Redis), or redis:// for a plain connection. + uri: "rediss://redis.example.com:6379" + tower: + endpoint: + url: "https://platform.example.com/api" + # Keep the JDBC and disk-space indicators out of /health. Micronaut enables them + # by default, and the liveness probe below would restart every pod on a brief + # database blip. + endpoints: + health: + enabled: true + disk-space: + enabled: false + jdbc: + enabled: false +``` + +:::warning +Set `wave.server.url` to the address clients use to reach Wave. If you leave it unset, Wave issues container tokens pointing at `http://localhost:9090`, which clients cannot reach. +::: + +The `lite` entry in `MICRONAUT_ENVIRONMENTS`, set in the deployment in a later step, already applies the four feature toggles. The ConfigMap restates them explicitly and gives you a place to add further configuration. For every available setting, see the [Configuration reference](reference.md). Before serving production traffic, complete the [production checklist](configure-wave.md#production-checklist). + +## Registry credentials + +Wave Lite pulls images during augmentation and uses one of two credential sources per request: + +- **Platform workspace credentials**: credentials a user adds to their Seqera Platform workspace. Wave uses these for requests that carry a Platform identity. +- **Server-side static credentials**: the `wave.registries.` entries in the `wave-cfg` ConfigMap. Wave uses these for anonymous requests and for registries the operator owns. + +For all registry options, see [Container registry](reference.md#container-registry). + +:::warning +Anonymous access is enabled by default. Any client that can reach Wave can use the operator credentials to pull through it. Disable it with `wave.capabilities.anonymous-access: false` before you expose the service. See [Require authentication](configure-wave.md#require-authentication). +::: + +## Create the deployment + +Deploy Wave with a Deployment that pulls the Wave image and mounts the `wave-cfg` ConfigMap: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: wave + namespace: "wave" + labels: + app: wave-app +spec: + replicas: 1 + selector: + matchLabels: + app: wave-app + template: + metadata: + labels: + app: wave-app + spec: + imagePullSecrets: + - name: seqera-reg-creds + containers: + - image: cr.seqera.io/: # Use the image path and tag provided by Seqera. + name: wave-app + ports: + - containerPort: 9090 + env: + - name: MICRONAUT_ENVIRONMENTS + # rate-limit activates the rate-limit.* settings. Add prometheus to expose metrics. + # The k8s env is only needed for the in-cluster build client (see aws-build). + value: "lite,postgres,redis,rate-limit" + resources: + requests: + memory: "2Gi" + cpu: "0.2" + limits: + memory: "4Gi" + cpu: "1" + workingDir: "/work" + volumeMounts: + - name: wave-cfg + mountPath: /work/config.yml + subPath: "config.yml" + readinessProbe: + httpGet: + path: /health + port: 9090 + initialDelaySeconds: 5 + timeoutSeconds: 3 + livenessProbe: + httpGet: + path: /health + port: 9090 + initialDelaySeconds: 5 + timeoutSeconds: 3 + failureThreshold: 10 + volumes: + - name: wave-cfg + configMap: + name: wave-cfg + restartPolicy: Always +``` + +## Create the service + +Expose the Wave pods inside the cluster with a Service that the ingress routes to: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: wave-service + namespace: "wave" + labels: + app: wave-app +spec: + selector: + app: wave-app + ports: + - name: http + port: 9090 + targetPort: 9090 + protocol: TCP + type: ClusterIP +``` + +## Expose Wave + +Wave must be reachable from Seqera Platform and from your Nextflow compute environments. Front the service with an ingress and terminate TLS at the ingress or load balancer. Wave does not terminate TLS itself. + +This example uses the AWS Load Balancer Controller. `target-type: ip` lets it route to the `ClusterIP` service defined earlier. With the default `instance` target type, change that service to `NodePort`. Replace the certificate ARN with your own: + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: wave-ingress + namespace: wave + annotations: + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]' + alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:::certificate/" + alb.ingress.kubernetes.io/healthcheck-path: /health +spec: + ingressClassName: alb + rules: + - host: wave.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: wave-service + port: + number: 9090 +``` + +For the certificate and the DNS record that points `wave.example.com` at the load balancer, see [Terminate TLS](configure-wave.md#terminate-tls). For NGINX, GCE, or Traefik, swap `ingressClassName` and the annotations for that controller's equivalents. + +## Apply the manifests + +Apply the assembled file and wait for the rollout: + +```bash +kubectl apply -f wave.yaml +kubectl rollout status deployment/wave -n wave +``` + +Then configure your Seqera Platform deployment to use the Wave endpoint by setting the Wave server URL in `tower.yml`. See [Platform Wave configuration](https://docs.seqera.io/platform-enterprise/latest/enterprise/configuration/wave). + +## Verify your installation + +Confirm the service is live and functional. See [Verify your installation](post-install.md) for the `/service-info` check and the Wave CLI functional checks. + +When Wave is running and verified, continue to the [production checklist](configure-wave.md#production-checklist) to prepare the deployment for production. Wave Lite has no AWS dependency and runs on any conformant Kubernetes distribution, though only EKS is validated. Only an EKS deployment can be extended to the full Wave configuration. diff --git a/docs/install/kubernetes.md b/docs/install/kubernetes.md deleted file mode 100644 index f6cd0eed8..000000000 --- a/docs/install/kubernetes.md +++ /dev/null @@ -1,371 +0,0 @@ ---- -title: Kubernetes installation ---- - -Wave enables you to provision container images on-demand, removing the need to build and upload them manually to a container registry. Wave can can provision both disposable containers that are only accessible for a short period, and regular registry-persisted container images. - -This installation guide covers the [Wave Lite](../features/index.mdx) configuration. Wave Lite provides container augmentation and inspection on AWS, Azure, and GCP and enables the use of the Fusion file system in Nextflow pipelines. - -:::info -Wave's full build capabilities require specific integrations with Kubernetes and AWS EFS Storage, making EKS and AWS a hard dependency for fully-featured deployments. After you have configured a base Wave Lite installation on AWS with this guide, see [Configure Wave Build](./configure-wave-build.md) to extend your installation to support build capabilities. -::: - -## Prerequisites - -**Required infrastructure:** -- **Kubernetes cluster** - Version 1.31 or higher (any distribution) -- **PostgreSQL instance** - Version 12 or higher (managed externally) -- **Redis instance** - Version 6.0 or higher (managed externally) - -## System requirements - -The minimum system requirements for a Wave Kubernetes installation are: - -- **Memory**: Minimum 1500 MiB RAM per Wave pod -- **CPU**: Minimum 0.2 CPU core per pod -- **Network**: Connectivity to your external PostgreSQL and Redis instances -- **Storage**: Sufficient storage for your container images and temporary files - -:::info -See [Configure Wave](../configure-wave.md) for detailed scaling and performance tuning guidance. -::: - -## Assumptions - -This guide assumes: -- You have already deployed Seqera Platform Enterprise -- You will deploy Wave into the `wave` namespace -- You have appropriate cluster permissions to create namespaces, deployments, and services -- Your PostgreSQL and Redis instances are accessible from the Kubernetes cluster - -## Database configuration - -Wave requires a PostgreSQL database to operate. - -Create a dedicated `wave` database and user account with the appropriate privileges: - -```sql --- Create a dedicated user for Wave -CREATE ROLE wave_user LOGIN PASSWORD 'your_secure_password'; - --- Create the Wave database -CREATE DATABASE wave; - --- Connect to the wave database -\c wave; - --- Grant basic schema access -GRANT USAGE, CREATE ON SCHEMA public TO wave_user; - --- Grant privileges on existing tables and sequences -GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO wave_user; -GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO wave_user; - --- Grant privileges on future tables and sequences -ALTER DEFAULT PRIVILEGES IN SCHEMA public -GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO wave_user; - -ALTER DEFAULT PRIVILEGES IN SCHEMA public -GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO wave_user; -``` - -## Create namespace - -```yaml ---- -apiVersion: v1 -kind: Namespace -metadata: - name: "wave" - labels: - app: wave-app -``` - -## Configure Wave - -Create a ConfigMap containing Wave's configuration. Update the following values to match your environment: - -- Database connection details (`uri`, `user`, `password`) -- Redis connection string -- Seqera Platform API endpoint - -:::warning -This configuration contains sensitive values. It is recommended to use Kubernetes Secrets for sensitive data, instead of embedding them directly in the ConfigMap. See the [Kubernetes Secrets documentation](https://kubernetes.io/docs/concepts/configuration/secret/) for more details. - -Example using environment variables with secrets: - -```yaml -env: - - name: WAVE_DB_PASSWORD - valueFrom: - secretKeyRef: - name: wave-secrets - key: db-password -``` -::: - -```yaml -kind: ConfigMap -apiVersion: v1 -metadata: - name: wave-cfg - namespace: "wave" - labels: - app: wave-cfg -data: - config.yml: | - wave: - # Build service configuration - disabled for Wave base installation - build: - enabled: false - # Mirror service configuration - disabled for Wave base installation - mirror: - enabled: false - # Security scanning configuration - disabled for Wave base installation - scan: - enabled: false - # Blob caching configuration - disabled by default (omit the blobCache stanza entirely when not in use) - # Database connection settings - db: - uri: "jdbc:postgresql://your-postgres-host:5432/wave" - user: "wave_user" - password: "your_secure_password" - - # Redis configuration for caching and session management - redis: - uri: "rediss://your-redis-host:6379" - - # Platform integration (optional) - tower: - endpoint: - url: "https://your-platform-server.com/api" - - # Micronaut framework configuration - micronaut: - # Executor configuration for handling concurrent requests - executors: - stream-executor: - type: FIXED - number-of-threads: 16 - # Netty HTTP server configuration - netty: - event-loops: - default: - num-threads: 64 - stream-pool: - executor: stream-executor - # HTTP client configuration - http: - services: - stream-client: - read-timeout: 30s - read-idle-timeout: 5m - event-loop-group: stream-pool - - # Management endpoints configuration - loggers: - # Enable metrics for monitoring - metrics: - enabled: true - # Enable health checks - health: - enabled: true - disk-space: - enabled: false - jdbc: - enabled: false -``` - -## Create deployment - -Deploy Wave using the following Deployment manifest: - -```yaml ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: wave - namespace: "wave" - labels: - app: wave-app -spec: - replicas: 1 - selector: - matchLabels: - app: wave-app - template: - metadata: - labels: - app: wave-app - spec: - containers: - - image: REPLACE_ME_AWS_ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/nf-tower-enterprise/wave:REPLACE_ME_WAVE_IMAGE_TAG - name: wave-app - ports: - - containerPort: 9090 - env: - - name: MICRONAUT_ENVIRONMENTS - value: "postgres,redis,lite" - resources: - requests: - memory: "1500Mi" - limits: - memory: "1500Mi" - workingDir: "/work" - volumeMounts: - - name: wave-cfg - mountPath: /work/config.yml - subPath: "config.yml" - readinessProbe: - httpGet: - path: /health - port: 9090 - initialDelaySeconds: 5 - timeoutSeconds: 3 - livenessProbe: - httpGet: - path: /health - port: 9090 - initialDelaySeconds: 5 - timeoutSeconds: 3 - failureThreshold: 10 - volumes: - - name: wave-cfg - configMap: - name: wave-cfg - restartPolicy: Always -``` - -## Create Service - -Expose Wave within the cluster using a Service: - -```yaml ---- -apiVersion: v1 -kind: Service -metadata: - name: wave-service - namespace: "wave" - labels: - app: wave-app -spec: - selector: - app: wave-app - ports: - - name: http - port: 9090 - targetPort: 9090 - protocol: TCP - type: ClusterIP -``` - - -## Next steps - -### Configure Seqera Platform to integrate with Wave - -Configure your Seqera Platform Enterprise deployment to integrate with Wave by setting the Wave server endpoint in your `tower.yml` [configuration](https://docs.seqera.io/platform-enterprise/latest/enterprise/configuration/wave). - -### Networking - -Wave must be accessible from: - -- Seqera Platform services -- Compute environments (for container image access) - -Configure external access using a Kubernetes ingress. - -Update the following example ingress with your provider-specific annotations: - -```yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: wave-ingress - namespace: wave -spec: - rules: - - host: wave.your-domain.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: wave-service - port: - number: 9090 -``` - -### TLS - -Wave does not handle TLS termination directly. Configure TLS at your ingress controller or load balancer level. Most ingress controllers support automatic certificate provisioning through provider integrations. - - -### Production environments - -Consider implementing the following for production deployments: - -**Reliability:** -- Pod Disruption Budgets, for availability during cluster maintenance -- Horizontal Pod Autoscaler, for automatic scaling based on load -- Multiple replicas with anti-affinity rules, for high availability - -**Resource management:** -- Node selectors or affinity rules for optimal pod placement -- Resource quotas and limit ranges for the Wave namespace - -### AWS credentials to access ECR -Wave requires access to AWS ECR for container image management. Create an IAM role with the following permissions: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": "ecr:GetAuthorizationToken", - "Resource": "*" - }, - { - "Sid": "CorePermissionsForBuildAndCache", - "Action": [ - "ecr:BatchCheckLayerAvailability", - "ecr:BatchGetImage", - "ecr:CompleteLayerUpload", - "ecr:GetDownloadUrlForLayer", - "ecr:InitiateLayerUpload", - "ecr:PutImage", - "ecr:UploadLayerPart" - ], - "Effect": "Allow", - "Resource": [ - "/wave/*" - ] - }, - { - "Sid": "ExtraPermissionsForBuild", - "Action": [ - "ecr:DescribeImageScanFindings", - "ecr:DescribeImages", - "ecr:DescribeRepositories", - "ecr:GetLifecyclePolicy", - "ecr:GetLifecyclePolicyPreview", - "ecr:GetRepositoryPolicy", - "ecr:ListImages", - "ecr:ListTagsForResource" - ], - "Effect": "Allow", - "Resource": [ - "/wave/" - ] - }, - ] -} -``` - -### Advanced configuration - -See [Configure Wave](../configure-wave.md) for advanced Wave features, scaling guidance, and integration options. diff --git a/docs/install/post-install.md b/docs/install/post-install.md new file mode 100644 index 000000000..fd5737cf4 --- /dev/null +++ b/docs/install/post-install.md @@ -0,0 +1,107 @@ +--- +title: Verify your installation +description: Confirm a self-hosted Wave service is live and functional after installation. +--- + +After installing Wave with any method, confirm the service is live and provisions containers. Run the liveness check first, then the functional checks for your configuration. + +## Check the service is live + +Wave exposes `/service-info`. A healthy service returns its version and commit ID: + +```bash +curl -s https://wave.example.com/service-info +``` + +```json +{ + "serviceInfo": { + "version": "", + "commitId": "" + } +} +``` + +If this returns a connection error, check that the service is running and that your ingress or load balancer routes to it. If it returns the wrong version, confirm the deployed image tag. + +## Install the Wave CLI + +The functional checks use the Wave CLI. + +```bash +# Homebrew +brew install seqeralabs/tap/wave-cli + +# Or download a binary from the releases page +# https://github.com/seqeralabs/wave-cli/releases +``` + +Point the CLI at your service with the `--wave-endpoint` flag or the `WAVE_ENDPOINT` environment variable: + +```bash +export WAVE_ENDPOINT=https://wave.example.com +``` + +:::note +If you disabled anonymous access in [Configure Wave](configure-wave.md#require-authentication), every CLI check needs a Seqera Platform access token. Pass it with the `--tower-token` flag or the `TOWER_ACCESS_TOKEN` environment variable. +::: + +## Functional checks + +### Wave Lite + +Augment an existing image: + +```bash +wave -i ubuntu:22.04 +``` + +A successful augmentation prints a Wave image reference pointing at your service host. + +### Wave + +In addition to the augmentation check, verify the build features you enabled. + +Build a container from a Conda package: + +```bash +wave --conda-package bcftools +``` + +Build from a Dockerfile and freeze it to a persistent repository. Any small Dockerfile works for this check: + +```bash +echo "FROM ubuntu:22.04" > Dockerfile +wave --containerfile Dockerfile --freeze --build-repo --tower-token +``` + +A successful build returns a reference in your configured build repository. + +If you enabled mirroring, copy an image into your build repository. `--mirror` requires a build repository and cannot be combined with `--containerfile`, `--conda-package`, or `--freeze`: + +```bash +wave -i ubuntu:22.04 --mirror --build-repo +``` + +If you enabled scanning, request a build that must pass a scan before it is returned: + +```bash +wave --conda-package bcftools --scan-mode required +``` + +A scan failure returns the vulnerabilities found rather than an image reference. Use `--scan-level` to set which severities to tolerate. + +:::note +Freeze builds push to the repository you name. They always need a Platform access token, and the associated Platform workspace must hold registry credentials with push access to ``. See [Freeze and user-supplied build repositories](aws-build.md#freeze-and-user-supplied-build-repositories). +::: + +## If a check fails + +If a check does not pass, match the symptom to one of these common causes: + +- **Augmentation or build fails with an authentication error**: Wave cannot authenticate to the target registry. See [Registry push and authentication failures](../troubleshoot.md#registry-push-and-authentication-failures). +- **Build fails partway through a push**: The target repository may not exist or may lack push scope. See [Registry pre-creation](reference.md#registry-pre-creation). +- **Freeze is rejected even with a build repository set**: The repository sits inside an operator-reserved prefix. See [Freeze and user-supplied build repositories](aws-build.md#freeze-and-user-supplied-build-repositories). + +When the checks pass, continue to the [production checklist](configure-wave.md#production-checklist) to prepare the deployment for production. + diff --git a/docs/configuration.md b/docs/install/reference.md similarity index 67% rename from docs/configuration.md rename to docs/install/reference.md index 118c6702f..8f2a1ffe6 100644 --- a/docs/configuration.md +++ b/docs/install/reference.md @@ -1,10 +1,11 @@ --- title: Configuration reference +description: Configuration options for self-hosted Wave deployments. tags: [configuration, reference, wave] --- -This page documents configuration options for self-hosted Wave deployments. -If you are using Wave as a service, these configuration options do not apply to your setup. +The following options configure self-hosted Wave deployments. +If you use Wave as a hosted service, these options do not apply. Configure Wave by setting YAML values in the [`config.yml`](https://github.com/seqeralabs/wave/blob/master/config.yml) configuration file: @@ -14,16 +15,16 @@ wave: from: "wave-app@seqera.io" ``` -Configuration paths in this reference use dot notation to represent nested YAML keys. In the example above, the `from` value nested under the `mail` section is referenced as `wave.mail.from`. +Configuration paths in this reference use dot notation to represent nested YAML keys. In this example, the `from` value nested under the `mail` section is referenced as `wave.mail.from`. You can configure Wave using either the `config.yml` file or environment variables. Environment variables are provided where available, though not all configuration options support them. ## General -Configure general Wave application settings, such as application name, port, anonymous access permissions, and platform URLs with the following options: +Configure general Wave application settings. `tower.endpoint.url` *(optional)* -: URL of the Seqera platform API service (default: [`https://api.cloud.seqera.io`](https://api.cloud.seqera.io)). +: URL of the Seqera Platform API service (default: [`https://api.cloud.seqera.io`](https://api.cloud.seqera.io)). Can be set using the `${TOWER_ENDPOINT_URL}` environment variable. `wave.deny-hosts` *(optional)* @@ -61,13 +62,30 @@ Configure general Wave application settings, such as application name, port, ano `wave.tokens.watcher.count` *(optional)* : Maximum number of container requests processed in a single watcher cycle (default: `250`). +## Feature toggles + +Each Wave feature is an independent toggle. Wave Lite runs with all four off, which is what the `lite` Micronaut environment applies. + +`wave.build.enabled` *(optional)* +: When `true`, Wave provisions containers with on-demand builds (default: `true`). Freeze and scanning both depend on the build pipeline and are unavailable when this is `false`. + +`wave.mirror.enabled` *(optional)* +: When `true`, Wave can mirror images into a target repository (default: `true`). + +`wave.scan.enabled` *(optional)* +: When `true`, activates vulnerability scanning (default: `false`). Requires `wave.build.enabled` and `wave.scan.reports.path`. + +`wave.blob-cache.enabled` *(optional)* +: When `true`, Wave caches container layers in object storage (default: `false`). Requires the blob cache storage settings. + ## Capabilities The `wave.capabilities.*` flags are installation-level toggles for optional Wave capabilities. All default to `true` (permissive). Set a flag to `false` to lock down that capability, or enable the `strict` environment (`MICRONAUT_ENVIRONMENTS=strict`) to disable all of them at once — intended for regulated deployments where Wave must not serve images directly, broker credentials, expose its HTML pages, or accept anonymous requests. `wave.capabilities.anonymous-access` *(optional)* : When `true`, anonymous (unauthenticated) users can access the Wave server (default: `true`). - Set to `false` to require authenticated access. Modify this option based on your security requirements. + Set to `false` to require authenticated access, so that every request must carry a Platform-issued token. + Legacy alias: `wave.allowAnonymous`. Wave still honors the alias, but prefer the canonical key. `wave.capabilities.ephemeral-token` *(optional)* : When `true`, Wave can provision a container by pulling an existing image directly, applying any container configuration on the fly (the augmentation path) (default: `true`). @@ -86,14 +104,11 @@ The `wave.capabilities.*` flags are installation-level toggles for optional Wave Wave uses the generic format `wave.registries..username` and `wave.registries..password` for registry authentication. You must specify all repositories used in your Wave installation. -The examples below show standard formats for known registries, but you can customize the registry name (for example, change `azurecr.io` to `seqeralabs.azurecr.io`). +The following examples show standard formats for known registries, but you can customize the registry name (for example, change `azurecr.io` to `seqeralabs.azurecr.io`). -:::note -You can also define container registry credentials in the [`config.yml`](https://github.com/seqeralabs/wave/blob/master/config.yml) file. -These configurations enable Wave authentication for repositories used to push or pull artifacts. -::: +Wave does not read the environment variables in this section directly. They take effect only where your `config.yml` interpolates them, as in `username: "${DOCKER_USER:}"`. Setting `DOCKER_USER` without such an entry has no effect. -Configure container registry authentication with the following options: +Configure container registry authentication with the following options. `wave.registries..password` *(optional)* : AWS ECR password for authentication. @@ -124,15 +139,29 @@ Configure container registry authentication with the following options: : Quay.io username for authentication. Can be set using the `${QUAY_USER}` environment variable. +## Registry pre-creation + +Wave pushes with BuildKit for builds and Skopeo for mirrors. The registry, not Wave, determines whether a target repository must exist before the first push. If the registry requires pre-creation and the path is missing, the push fails partway through the layer upload. See [Registry push and authentication failures](../troubleshoot.md#registry-push-and-authentication-failures) to diagnose one. + +| Registry | Pre-creation | Notes | +| --- | --- | --- | +| Amazon ECR | Required | Every repository must exist before push. Registry-level auto-create policies exist but are off by default. | +| Docker Hub | Not required | Repositories auto-create in your user or organization namespace. Repository-count and pull rate limits apply. | +| GitHub Container Registry | Not required | Auto-creates under the user or organization namespace; visibility inherits from the organization's package settings. | +| Google Artifact Registry | Partial | Create the repository with `gcloud artifacts repositories create`; image paths inside it auto-create. | +| Google Container Registry | Not required | Auto-creates on push. Being phased out. Target Artifact Registry for new deployments. | +| Azure Container Registry | Partial | The ACR instance must exist; image paths inside it auto-create. Wave needs the `AcrPush` role. | +| Harbor | Partial | Create the project through the UI or API; images inside it auto-create if project policy permits. | + ## AWS cross-account role chaining -When Wave needs to access ECR registries in customer AWS accounts using IAM role credentials provided by the Seqera Platform, you can configure an intermediate "jump role" for cross-account access. When configured, Wave first assumes the jump role using its own credentials, then uses the jump role's temporary credentials to assume the target role received from the Seqera Platform. +To access ECR registries in customer AWS accounts using IAM role credentials from the Seqera Platform, configure an intermediate "jump role" for cross-account access. Wave first assumes the jump role using its own credentials, then uses the jump role's temporary credentials to assume the target role from the Seqera Platform. -This enables a two-hop role chaining pattern: +This is a two-hop role chaining pattern: -1. Wave assumes the jump role (using its default credentials) -2. Wave uses the jump role's temporary credentials to assume the customer's target role -3. The target role's temporary credentials are used to authenticate with ECR +1. Wave assumes the jump role (using its default credentials). +2. Wave uses the jump role's temporary credentials to assume the customer's target role. +3. The target role's temporary credentials authenticate with ECR. Configure jump role chaining with the following options: @@ -148,7 +177,7 @@ Configure jump role chaining with the following options:
:::note -When the jump role is not configured, Wave assumes target roles directly using its default credentials (the previous behavior). The jump role is only used for role-based ECR authentication, not for static AWS credential flows. +When the jump role is not configured, Wave assumes target roles directly using its default credentials. The jump role is only used for role-based ECR authentication, not for static AWS credential flows. :::
@@ -184,7 +213,7 @@ Configure caching for jump role temporary credentials to avoid redundant STS cal ## HTTP client -Configure the HTTP client with the following options: +Configure the HTTP client with the following options. `wave.httpclient.connect-timeout` *(optional)* : Connection timeout for the HTTP client (default: `20s`). @@ -193,7 +222,7 @@ Configure the HTTP client with the following options: : Number of HTTP client retry attempts (default: `5`). `wave.httpclient.retry.delay` *(optional)* -: Delay between HTTP client retries (default: `1s`). +: Delay between HTTP client retries (default: `500ms`). `wave.httpclient.retry.jitter` *(optional)* : Jitter factor for HTTP client retries (default: `0.25`). @@ -206,14 +235,14 @@ Configure the HTTP client with the following options: ## Container build process -Configure how Wave builds container images and manages associated logs for monitoring, troubleshooting, and delivery with the following options: +Configure how Wave builds container images and manages build logs. -`wave.build.buildkit-image` *(required)* +`wave.build.buildkit-image` *(optional)* : [Buildkit](https://github.com/moby/buildkit) container image used in the Wave build process (default: `public.cr.seqera.io/wave/buildkit:v0.25.2-rootless`). `wave.build.cache` *(optional)* : Cache repository for images built by Wave. Supports both container registry paths and S3 bucket paths. - For Example: + For example:
@@ -235,13 +264,9 @@ Configure how Wave builds container images and manages associated logs for monit For example, `8`. This setting is only used when `wave.build.cache` is configured with an S3 bucket path. -`wave.cleanup.strategy` *(optional)* -: Cleanup strategy after the build process. - For example, set to `OnSuccess` for cleanup only if a build is successful. - `wave.build.compression` *(optional)* : Compression type applied to cache layers (default: `gzip`). - Options include: `uncompressed`,`estargz`, and `zstd`. + Options include: `uncompressed`, `estargz`, and `zstd`. `wave.build.force-compression` *(optional)* : When `true`, forces compression for each cache layer produced by the build process (default: `false`). @@ -250,9 +275,9 @@ Configure how Wave builds container images and manages associated logs for monit : When `true`, includes OCI media types in exported manifests (default: `true`). `wave.build.public-repo` *(optional)* -: Public repository for container images built by Wave. Wave uses this repository as the build target when a freeze mode build is requested with Conda or pip packages via the v2 API and no `buildRepository` is provided. Images stored here default to the `imageSuffix` naming strategy. The registry hostname must be unique so that Wave can resolve default credentials correctly (i.e, it cannot be shared with `wave.build.repo` or `wave.build cache-repo`). If not set, a `buildRepository` must be specified in each freeze mode operations with packages. +: Public repository for container images built by Wave. Wave uses this repository as the build target when a freeze mode build is requested with Conda or pip packages via the v2 API and no `buildRepository` is provided. Images stored here default to the `imageSuffix` naming strategy. The registry hostname must be unique so that Wave can resolve default credentials correctly (it cannot be shared with `wave.build.repo` or `wave.build.cache`). If not set, you must specify a `buildRepository` in each freeze mode operation with packages. -`wave.build.repo` *(required)* +`wave.build.repo` *(required when builds are enabled)* : Docker container repository for container images built by Wave. `wave.build.singularity-image` *(optional)* @@ -262,44 +287,69 @@ Configure how Wave builds container images and manages associated logs for monit : Delay between build status checks (default: `5s`). `wave.build.status.duration` *(optional)* -: Duration for build status checks (default: `1d`). +: Duration for build status checks (default: `90m`). `wave.build.timeout` *(optional)* -: Maximum duration for the build process (default: `5m`). +: Maximum duration for the build process (default: `900s`). + Keep `micronaut.server.idle-timeout` (default: `910s`) equal to or longer than this value. `wave.build.trusted-timeout` *(optional)* : Maximum duration for the build process when you are authenticated and freeze mode is enabled (default: `10m`). If set to less than `wave.build.timeout`, the longer `wave.build.timeout` value is used. -`wave.build.workspace` *(required)* +`wave.build.workspace` *(required when builds are enabled)* : Path to the directory used by Wave to store artifacts such as Containerfiles, Trivy cache for scan, Buildkit context, and authentication configuration files. For example, `/efs/wave/build`. :::tip -For S3 cache authentication setup (IAM roles, service accounts, and deployment examples), see [S3 cache authentication](./configure-wave.md#s3-cache-authentication). +For S3 cache authentication setup, see [Build layer cache](configure-wave.md#build-layer-cache). ::: +### Cleanup + +Wave deletes build job resources and work directories after builds complete. Configure the cleanup behavior with the following options. + +`wave.cleanup.strategy` *(optional)* +: Cleanup strategy for build resources. + Options include: `always`, `never`, and `onsuccess` (clean up only when the build succeeds). + When unset, Wave cleans up unless `wave.debug` is `true`. + +`wave.cleanup.succeeded` *(optional)* +: How long the job resources and work directory of a successful build are retained before deletion (default: `30m`). + +`wave.cleanup.failed` *(optional)* +: How long the job resources and work directory of a failed build are retained before deletion (default: `1d`). + +`wave.cleanup.range` *(optional)* +: Maximum number of expired entries processed in each cleanup run (default: `200`). + +`wave.cleanup.startup-delay` *(optional)* +: Delay before the cleanup service starts after boot. The actual delay is randomized around this value (default: `10s`). + +`wave.cleanup.run-interval` *(optional)* +: Interval between cleanup runs (default: `30s`). + ### Build process logs -Configure how Wave stores and delivers build logs from containers and Kubernetes pods, which can be retrieved later or included in build completion emails, with the following options: +Configure how Wave stores and delivers build logs from containers and Kubernetes pods. You can retrieve these logs later or include them in build completion emails. -`wave.build.locks.path` *(required)* -: Path where Wave stores Conda lock files. Can be an S3 URI (e.g., `s3://my-bucket/wave/locks`) or a local filesystem path. +`wave.build.locks.path` *(required when builds are enabled)* +: Path where Wave stores Conda lock files. Can be an S3 URI (for example, `s3://my-bucket/wave/locks`) or a local filesystem path. `wave.build.logs.max-length` *(optional)* : Maximum number of bytes read from a log file. If a log file exceeds this limit, it is truncated (default: `100000` (100 KB)). -`wave.build.logs.path` *(required)* -: Path where Wave stores build logs. Can be an S3 URI (e.g., `s3://my-bucket/wave/logs`) or a local filesystem path. When using an S3 URI, Wave automatically extracts the key prefix for log file organization. +`wave.build.logs.path` *(required when builds are enabled)* +: Path where Wave stores build logs. Can be an S3 URI (for example, `s3://my-bucket/wave/logs`) or a local filesystem path. When using an S3 URI, Wave automatically extracts the key prefix for log file organization. ### Kubernetes container build process -Configure Kubernetes-specific settings for Wave, where build and scan processes share most configurations except for CPU and memory requirements, with the following options: +Configure Kubernetes-specific settings for Wave. Build and scan processes share most configurations except for CPU and memory requirements. `wave.build.k8s.labels` *(optional)* : Labels for Wave build Kubernetes pods. -`wave.build.k8s.namespace` *(required)* +`wave.build.k8s.namespace` *(required for Kubernetes builds)* : Kubernetes namespace where Wave runs build pods. `wave.build.k8s.dns.policy` *(optional)* @@ -319,7 +369,7 @@ Configure Kubernetes-specific settings for Wave, where build and scan processes
```yaml - wave.build.k8s.nodeSelector: + wave.build.k8s.node-selector: linux/amd64: 'seqera.io/wave-build-amd64=true' linux/arm64: 'seqera.io/wave-build-arm64=true' noarch: 'seqera.io/wave-build=true' @@ -346,28 +396,25 @@ Configure Kubernetes-specific settings for Wave, where build and scan processes ## Container scan process -Configure how Wave's vulnerability scanning process uses a [Trivy Docker image](https://hub.docker.com/r/aquasec/trivy) with customizable tags and severity levels with the following options: - -`wave.scan.enabled` *(optional)* -: When `true`, activates vulnerability scanning (default: `false`). +Configure the Wave vulnerability scanning process, which uses a [Trivy Docker image](https://hub.docker.com/r/aquasec/trivy) with customizable tags and severity levels. `wave.scan.image.name` *(optional)* : Container image used for security scanning (default: `public.cr.seqera.io/wave/scanner:v1-0.65.0-oras-1.3.0`). -`wave.scan.reports.path` *(required)* +`wave.scan.reports.path` *(required when scanning is enabled)* : S3 bucket path where Wave stores SBOM reports. For example, `s3://wave-store/scan-reports`. `wave.scan.severity` *(optional)* : [Severity levels](https://aquasecurity.github.io/trivy/v0.22.0/vulnerability/examples/filter/) to report in vulnerability scanning. -: Options include: `MEDIUM`,`HIGH`, and `CRITICAL`. +: Options include: `MEDIUM`, `HIGH`, and `CRITICAL`. `wave.scan.status.duration` *(optional)* : Duration for which scan status records are retained (default: `5d`). ### Kubernetes Wave scan process -Configure Wave scanning process resource requirements for Kubernetes deployments with the following options: +Configure Wave scan process resource requirements for Kubernetes deployments. `wave.scan.k8s.resources.requests.cpu` *(optional)* : [CPU resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes) allocated to Wave scan processes. @@ -379,7 +426,7 @@ Configure Wave scanning process resource requirements for Kubernetes deployments ## Container mirror process -Configure Kubernetes resource requirements for Wave's container mirroring operations with the following options: +Configure Kubernetes resource requirements for Wave container mirroring operations. `wave.mirror.k8s.resources.requests.cpu` *(optional)* : CPU resources requested for mirror Kubernetes pods. @@ -395,7 +442,7 @@ Configure Kubernetes resource requirements for Wave's container mirroring operat ## Proxy cache -Configure Wave's in-memory proxy cache for registry responses with the following options: +Configure the Wave in-memory proxy cache for registry responses. `wave.proxy-cache.enabled` *(optional)* : When `true`, activates the proxy cache (default: `false`). @@ -408,22 +455,54 @@ Configure Wave's in-memory proxy cache for registry responses with the following ## Rate limits -Configure how Wave controls rate limits for anonymous and authenticated user access with the following options: +Configure rate limits for anonymous and authenticated user access. -`rate-limit.build.anonymous` *(required)* +:::note +These options take effect only when the `rate-limit` entry is included in the `MICRONAUT_ENVIRONMENTS` variable of your deployment. +::: + +`rate-limit.build.anonymous` *(optional)* : Rate limit for build requests from anonymous users (default: `10/1h`). -`rate-limit.build.authenticated` *(required)* +`rate-limit.build.authenticated` *(optional)* : Rate limit for build requests from authenticated users (default: `10/1m`). -`rate-limit.pull.anonymous` *(required)* +`rate-limit.pull.anonymous` *(optional)* : Rate limit for pull requests from anonymous users (default: `100/1h`). -`rate-limit.pull.authenticated` *(required)* +`rate-limit.pull.authenticated` *(optional)* : Rate limit for pull requests from authenticated users (default: `100/1m`). `rate-limit.timeout-errors.max-rate` *(optional)* -: Maximum rate of timeout errors before Wave begins rejecting requests (default: `10/1m`). +: Maximum rate of timeout errors before Wave begins rejecting requests (default: `20/2m`). + +## Security headers + +Wave sends HTTP security headers on all responses by default. Configure them with the following options. + +`wave.security.http-headers.enabled` *(optional)* +: When `true`, Wave adds security headers to HTTP responses (default: `true`). + +`wave.security.http-headers.hsts.max-age` *(optional)* +: `Strict-Transport-Security` max age in seconds (default: `31536000`). + +`wave.security.http-headers.hsts.include-sub-domains` *(optional)* +: When `true`, applies HSTS to subdomains (default: `true`). + +`wave.security.http-headers.frame-options` *(optional)* +: `X-Frame-Options` header value (default: `DENY`). + +`wave.security.http-headers.content-type-options` *(optional)* +: `X-Content-Type-Options` header value (default: `nosniff`). + +`wave.security.http-headers.referrer-policy` *(optional)* +: `Referrer-Policy` header value (default: `strict-origin-when-cross-origin`). + +`wave.security.http-headers.permissions-policy` *(optional)* +: `Permissions-Policy` header value (default: `camera=(), microphone=(), geolocation=()`). + +`wave.security.http-headers.content-security-policy` *(optional)* +: `Content-Security-Policy` header value. Adjust this if you front Wave with additional origins (default: `default-src 'self'; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data:; frame-ancestors 'none'`). ## Database and cache @@ -431,7 +510,7 @@ Wave uses external database and caching services to store application data and i ### Redis -Configure Redis with the following options: +Configure Redis with the following options. `redis.client.timeout` *(optional)* : Timeout duration (in milliseconds) for Redis client operations (default: `5000` (5 seconds)). @@ -458,7 +537,7 @@ Configure Redis with the following options: ### PostgreSQL -Configure PostgreSQL with the following options: +Configure PostgreSQL with the following options. `wave.db.password` *(required)* : Password for the PostgreSQL database user. @@ -475,7 +554,7 @@ Configure PostgreSQL with the following options: ## Blob cache -Configure how Wave caches container blobs to improve client performance and optionally delegates transfer tasks to Kubernetes pods for scalability with the following options: +Configure how Wave caches container blobs to improve client performance. Wave can also delegate transfer tasks to Kubernetes pods for scalability. `wave.blob-cache.base-url` *(optional)* : URL that overrides the base URL (the part before the blob path) of blobs sent to the client. @@ -505,7 +584,7 @@ Configure how Wave caches container blobs to improve client performance and opti : Memory resource [limit](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes) for the Kubernetes pod used for blob binary transfers. `wave.blob-cache.s5cmd-image` *(optional)* -: Container image that supplies the [s5cmd tool](https://github.com/peak/s5cmd) for uploading blob binaries to the S3 bucket (default: `public.cr.seqera.io/wave/s5cmd:v2.2.2`). +: Container image that supplies the [s5cmd tool](https://github.com/peak/s5cmd) for uploading blob binaries to the S3 bucket (default: `public.cr.seqera.io/wave/s5cmd:v2.3.0`). `wave.blob-cache.signing-strategy` *(optional)* : URL signing strategy for different services. @@ -518,20 +597,20 @@ Configure how Wave caches container blobs to improve client performance and opti `wave.blob-cache.status.duration` *(optional)* : Duration for which blob transfer status records are retained in cache (default: `1h`). -`wave.blob-cache.storage.access-key` *(required)* +`wave.blob-cache.storage.access-key` *(required when blob cache is enabled)* : Access key credential for the caching service. -`wave.blob-cache.storage.bucket` *(required)* +`wave.blob-cache.storage.bucket` *(required when blob cache is enabled)* : Name of the Cloudflare or S3 bucket. For example, `s3://wave-blob-cache`. `wave.blob-cache.storage.endpoint` *(optional)* : Storage endpoint URL for blob binary downloads and uploads. -`wave.blob-cache.storage.region` *(required)* +`wave.blob-cache.storage.region` *(required when blob cache is enabled)* : AWS region of the bucket. -`wave.blob-cache.storage.secret-key` *(required)* +`wave.blob-cache.storage.secret-key` *(required when blob cache is enabled)* : Secret key credential for the caching service.
@@ -547,26 +626,63 @@ Static credentials (`access-key` and `secret-key`) are currently required for bl ## Email configuration -Configure how Wave sends email notifications on behalf of the service with the following options: +Configure how Wave sends email notifications. -`mail.from` *(required)* +Email delivery requires `mail` in `MICRONAUT_ENVIRONMENTS`. Add `aws-ses` as well to send through Amazon SES with IAM authentication. In that case, only `mail.from` applies and the `mail.smtp.*` settings are ignored. + +`mail.from` *(required when mail is enabled)* : Sender email address for Wave notifications. Can be set using the `${MAIL_FROM}` environment variable. +`mail.smtp.host` *(required when SMTP is used)* +: SMTP server hostname. + +`mail.smtp.port` *(required when SMTP is used)* +: SMTP server port, typically `587` for STARTTLS or `465` for implicit TLS. + +`mail.smtp.user` *(optional)* +: Username for SMTP authentication. + +`mail.smtp.password` *(optional)* +: Password for SMTP authentication. + +`mail.smtp.auth` *(optional)* +: When `true`, authenticate to the SMTP server. + +`mail.smtp.starttls.enable` *(optional)* +: When `true`, upgrade the connection with STARTTLS. + +`mail.smtp.starttls.required` *(optional)* +: When `true`, fail rather than fall back to an unencrypted connection. + +`mail.smtp.ssl.protocols` *(optional)* +: Space-separated list of permitted TLS protocols, for example `TLSv1.2 TLSv1.3`. + ## Metrics -Configure how Wave Metrics service provides data about container builds and pulls per organization and date with the following options: +Configure the Wave Metrics service, which provides data about container builds and pulls per organization and date. `wave.metrics.enabled` *(optional)* : When `true`, activates Wave metrics (default: `false`). ## Accounts -Configure user credentials for accessing authenticated Wave APIs and services with the following options: +Configure user credentials for accessing authenticated Wave APIs and services. + +`wave.accounts` *(optional)* +: Credentials for accessing authenticated Wave APIs such as the metrics API. + A map of usernames to SHA-256 hex checksums of the corresponding passwords: -`wave.accounts` *(required)* -: List of credentials for accessing authenticated Wave APIs such as the metrics API. - Format of the credential list: `- :` +
+ + ```yaml + wave: + accounts: + # SHA-256 checksum of the password 'bar' + foo: "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9" + ``` + +
## License server @@ -581,6 +697,10 @@ Configure the connection to the Seqera license management server: Configure polling and scheduling intervals for Wave's asynchronous job processing. +`wave.job-manager.max-running-jobs` *(optional)* +: Maximum number of build, scan, and mirror jobs Wave runs concurrently (default: `20`). + Pair this with a `ResourceQuota` on the build namespace to bound build resource usage. + `wave.job-manager.poll-interval` *(optional)* : Polling interval for checking job status (default: `1s`). diff --git a/docs/sidebar.json b/docs/sidebar.json index b95592ed4..f521d3daf 100644 --- a/docs/sidebar.json +++ b/docs/sidebar.json @@ -9,7 +9,7 @@ }, "collapsed": true, "items": [ - "how-it-works", + "how-wave-works", { "type": "category", "label": "Features", @@ -33,20 +33,26 @@ }, { "type": "category", - "label": "Installation", - "collapsed": true, - "items": [ - "install/docker-compose", - "install/kubernetes" - ] - }, - { - "type": "category", - "label": "Configuration", - "collapsed": true, + "label": "Self-hosted Wave", + "link": { + "type": "doc", + "id": "install/index" + }, + "collapsed": false, "items": [ - "configure-wave", - "install/configure-wave-build" + { + "type": "category", + "label": "Wave Lite", + "collapsed": false, + "items": [ + "install/docker-compose", + "install/kubernetes-lite" + ] + }, + "install/post-install", + "install/aws-build", + "install/configure-wave", + "install/reference" ] }, "nextflow/index", @@ -62,10 +68,11 @@ ] }, "faq", + "troubleshoot", { "type": "link", "label": "Changelog", "href": "/changelog/tags/wave" } ] -} \ No newline at end of file +} diff --git a/docs/troubleshoot.md b/docs/troubleshoot.md index 16a7c36e1..55e832b69 100644 --- a/docs/troubleshoot.md +++ b/docs/troubleshoot.md @@ -1,3 +1,8 @@ +--- +title: Troubleshoot +description: Diagnose common Wave build, registry, and node configuration failures. +--- + ## Troubleshoot guide 1. How to troubleshoot container build failure? @@ -24,3 +29,53 @@ sh: can't kill pid 14: No such process - In case of wave cli use `--platform linux/arm64` flag with wave build command. - In case of API call use `containerPlatform: linux/arm64` in the request body. +## Registry push and authentication failures + +When Wave cannot push a built or mirrored image, or cannot authenticate to a registry, the failure usually matches one of these symptoms: + +- **No credentials match the target host.** Wave returns an authentication error at token-request time, before BuildKit or Skopeo launches. This is the fastest failure to diagnose. Confirm you configured credentials for the target registry. +- **The repository does not exist and the registry requires pre-creation.** The push fails with `403 Forbidden` or `404 Not Found` partway through the layer upload, often after an initial `HEAD` succeeds but the final manifest `PUT` fails. Pre-create the repository. For per-registry rules, see [Registry pre-creation](install/reference.md#registry-pre-creation). +- **Credentials exist but lack push scope.** The push typically returns a `403` on the final manifest `PUT` even though layer uploads appear to work. Check the credential's scope for `push`, `write`, or `deploy` permission. +- **The repository key is missing from the path (JFrog Artifactory).** The push fails with `404`. Confirm the repository key is the first path segment after the host, for example `artifactory.example.com/docker-local/...`. +- **The registry exists but the configured AWS credentials or role target the wrong region or account.** For ECR, a misaligned `aws.region` or jump-role configuration produces STS `AccessDenied` errors in the Wave service logs. These errors appear when Wave exchanges the configured credentials for an ECR auth token, before the build pod launches. + +## Builds fail on Bottlerocket nodes + +BuildKit requires user namespaces, and Bottlerocket sets `user.max_user_namespaces=0` by default. Every build fails on an otherwise correct [Wave build deployment](install/aws-build.md). To resolve, raise the limit on your build nodes. A value of `63359` is reasonable. Values that are too low cap concurrent build capacity and cause intermittent failures. + +Set it at boot through the node group's startup script or user data. That route is preferred because it needs no privileged containers. If you cannot change node configuration directly, apply it with a DaemonSet scoped to the build nodes: + +```yaml +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + app: sysctl-userns + name: sysctl-userns +spec: + selector: + matchLabels: + app: sysctl-userns + template: + metadata: + labels: + app: sysctl-userns + spec: + containers: + - name: sysctl-userns + image: busybox + command: ["sh", "-euxc", "sysctl -w user.max_user_namespaces=63359 && sleep infinity"] + securityContext: + privileged: true + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: service + operator: In + values: ["wave-build", "wave-build-arm64"] +``` + +For more on Bottlerocket settings, see the [Bottlerocket FAQs](https://bottlerocket.dev/en/faq/). +