| 1 | /** |
| 2 | * An EKS-orchestrated SageMaker HyperPod cluster with every DX tier: |
| 3 | * |
| 4 | * - `src/eks-infra.ts` — network → EKS control plane → HyperPod cluster → |
| 5 | * task governance (the amazon-sagemaker-hyperpod-taskgovernance add-on, |
| 6 | * an `AWS.SageMaker.ClusterSchedulerConfig` policy, and a team |
| 7 | * `AWS.SageMaker.ComputeQuota`), |
| 8 | * - LOW LEVEL: a raw batch/v1 Job applied via `Kubernetes.Manifest`, pinned |
| 9 | * to HyperPod nodes with the well-known node labels and submitted |
| 10 | * through task governance with the Kueue labels (below), |
| 11 | * - HIGH LEVEL: `src/TrainJob.ts`, an effectful `Kubernetes.Job` bundled from |
| 12 | * TypeScript and pinned + governed through HyperPod resource attributes |
| 13 | * (`quota.namespace`, `quota.queueName`, the group's `nodeSelector`). |
| 14 | * |
| 15 | * Deploy with `bun alchemy deploy --config ./eks.run.ts`. The EKS control plane |
| 16 | * takes ~10-15 minutes and the HyperPod cluster another ~10-20. |
| 17 | */ |
| 18 | import * as Alchemy from "alchemy"; |
| 19 | import * as AWS from "alchemy/AWS"; |
| 20 | import * as Kubernetes from "alchemy/Kubernetes"; |
| 21 | import * as Effect from "effect/Effect"; |
| 22 | import * as Layer from "effect/Layer"; |
| 23 | import { HyperPodEksInfra } from "./src/eks-infra.ts"; |
| 24 | import TrainJob from "./src/TrainJob.ts"; |
| 25 | |
| 26 | export default Alchemy.Stack( |
| 27 | "AwsHyperPodEksExample", |
| 28 | { |
| 29 | providers: Layer.mergeAll(AWS.providers(), Kubernetes.providers()), |
| 30 | state: Alchemy.localState(), |
| 31 | }, |
| 32 | Effect.gen(function* () { |
| 33 | const { eks, hyperpod, scheduler, researchQuota } = yield* HyperPodEksInfra; |
| 34 | |
| 35 | // ── LOW LEVEL: any Kubernetes object, applied as data. This one is |
| 36 | // governed: it runs in the research team's namespace (created by the |
| 37 | // ComputeQuota) and carries the Kueue queue + priority labels, so |
| 38 | // HyperPod task governance arbitrates it against the team's quota. |
| 39 | const governedJob = yield* Kubernetes.Manifest("GovernedJob", { |
| 40 | cluster: eks, |
| 41 | manifest: { |
| 42 | apiVersion: "batch/v1", |
| 43 | kind: "Job", |
| 44 | metadata: { |
| 45 | name: "governed-hello", |
| 46 | namespace: researchQuota.namespace, |
| 47 | labels: { |
| 48 | [AWS.SageMaker.KUEUE_QUEUE_NAME_LABEL]: researchQuota.queueName, |
| 49 | [AWS.SageMaker.KUEUE_PRIORITY_CLASS_LABEL]: "training-priority", |
| 50 | }, |
| 51 | }, |
| 52 | spec: { |
| 53 | backoffLimit: 1, |
| 54 | template: { |
| 55 | spec: { |
| 56 | nodeSelector: hyperpod.instanceGroups.workers.nodeSelector, |
| 57 | containers: [ |
| 58 | { |
| 59 | name: "hello", |
| 60 | image: "public.ecr.aws/docker/library/busybox:stable", |
| 61 | command: ["sh", "-c", "echo hello from HyperPod"], |
| 62 | }, |
| 63 | ], |
| 64 | restartPolicy: "Never", |
| 65 | }, |
| 66 | }, |
| 67 | }, |
| 68 | }, |
| 69 | }); |
| 70 | |
| 71 | // ── HIGH LEVEL: the effectful Job (bundled TypeScript, pinned + |
| 72 | // governed through the same attributes). See src/TrainJob.ts. |
| 73 | const trainJob = yield* TrainJob; |
| 74 | |
| 75 | return { |
| 76 | eksClusterName: eks.clusterName, |
| 77 | hyperpodClusterArn: hyperpod.clusterArn, |
| 78 | hyperpodStatus: hyperpod.clusterStatus, |
| 79 | schedulerPolicyId: scheduler.clusterSchedulerConfigId, |
| 80 | researchTeam: researchQuota.teamName, |
| 81 | governedJobName: governedJob.name, |
| 82 | trainJobName: trainJob.jobName, |
| 83 | }; |
| 84 | }), |
| 85 | ); |