Skip to content

Commit ebb07d6

Browse files
authored
Merge pull request #192 from aws-samples/feat/union-pattern
add union pattern
2 parents 7af3d9f + af84deb commit ebb07d6

File tree

9 files changed

+244
-14
lines changed

9 files changed

+244
-14
lines changed

.github/workflows/ci.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ jobs:
1212

1313
strategy:
1414
matrix:
15-
node-version: [18]
15+
node-version: [22]
1616

1717
steps:
18-
- uses: actions/checkout@v2
18+
- uses: actions/checkout@v3
1919

2020
- name: Use Node.js ${{ matrix.node-version }}
21-
uses: actions/setup-node@v1
21+
uses: actions/setup-node@v3
2222
with:
2323
node-version: ${{ matrix.node-version }}
2424

2525
- name: Cache node modules
26-
uses: actions/cache@v2
26+
uses: actions/cache@v5
2727
env:
2828
cache-name: cache-node-modules
2929
with:
@@ -48,4 +48,4 @@ jobs:
4848
run: make list
4949

5050
- name: Run CDK Synth
51-
run: make test-all
51+
run: make test-all

.github/workflows/markdown-link-check.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- uses: actions/checkout@v3
2121
- uses: actions/setup-node@v3
2222
with:
23-
node-version: '18.x'
23+
node-version: '20.x'
2424
- name: install markdown-link-check
2525
run: npm install -g markdown-link-check@3.10.2
2626
- name: markdown-link-check version

bin/unionai.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import UnionDataplaneConstruct from '../lib/union-dataplane-construct';
2+
import { configureApp, errorHandler } from '../lib/common/construct-utils';
3+
4+
const app = configureApp();
5+
6+
new UnionDataplaneConstruct().buildAsync(app, 'union-ai-datplane').catch((e) => {
7+
errorHandler(app, "Union Dataplane Construct pattern is not setup due to missing secrets for Union client. See Union Dataplane Construct in the readme for instructions", e);
8+
});

docs/patterns/union.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Union.ai on EKS Pattern
2+
3+
Union.ai empowers AI development teams to rapidly ship high-quality code to production by offering optimized performance, resource efficiency, and workflow authoring experience. With Union.ai your team can:
4+
5+
- Run complex AI workloads with performance, scale, and efficiency.
6+
- Scale out to multiple regions, clusters, and clouds as needed for resource availability, scale, or compliance.
7+
8+
Union.ai’s modular architecture allows for great flexibility and control. The customer can decide how many clusters to have, their shape, and who has access to what. All communication is encrypted.
9+
10+
<p align="center">
11+
<a href="https://www.union.ai">
12+
<img alt="Union Self-managed Architecture" src="https://www.union.ai/docs/v1/selfmanaged/_static/images/deployment/architecture.svg" width="600" />
13+
</a>
14+
</p>
15+
16+
17+
## Prerequisites
18+
19+
Ensure that you have installed the following tools on your machine:
20+
21+
- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) (also ensure it is [configured](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html#getting-started-quickstart-new))
22+
- [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)
23+
- [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)
24+
- [tsc](https://www.typescriptlang.org/download)
25+
- [make](https://www.gnu.org/software/make/)
26+
- uctl
27+
28+
### Installing `uctl`
29+
30+
On Mac:
31+
```bash
32+
brew tap unionai/homebrew-tap
33+
brew install uctl
34+
```
35+
36+
With cURL:
37+
```bash
38+
curl -sL https://raw.githubusercontent.com/unionai/uctl/main/install.sh | bash
39+
```
40+
41+
## Deployment
42+
43+
### Setup Union Credentials
44+
45+
Both the control plane URL and cluster name will be provided by Union. Union will also provide authentication information for your account to access the hosted control plane.
46+
47+
```bash
48+
export UNION_CONTROL_PLANE_URL=<YOUR_UNION_CONTROL_PLANE_URL>
49+
export UNION_CLUSTER_NAME=<YOUR_SELECTED_CLUSTER_NAME>
50+
export UNION_ORG_NAME=<YOUR_SELECTED_ORG_NAME>
51+
52+
uctl config init --host=$UNION_CONTROL_PLANE_URL
53+
uctl selfserve provision-dataplane-resources --clusterName $UNION_CLUSTER_NAME --provider aws
54+
```
55+
56+
This command will output the ID, name, and secret used by Union services to communicate with the control plane.
57+
58+
### Create Union Secrets in AWS Secrets Manager
59+
60+
```bash
61+
export UNION_SECRET_NAME=union-secret
62+
aws secretsmanager create-secret --name $UNION_SECRET_NAME \
63+
--secret-string "{\"host\":\"$UNION_CONTROL_PLANE_URL\",\"clusterName\":\"$UNION_CLUSTER_NAME\",\"orgName\":\"$UNION_ORG_NAME\"}"
64+
65+
export UNION_CLIENT_SECRET_NAME=union-client-secret
66+
export UNION_CLIENT_ID_SECRET_VALUE=<CLUSTERAUTHCLIENTID_FROM_SELFSERVE_COMMAND>
67+
export UNION_SECRET_SECRET_VALUE=<CLUSTERAUTHCLIENTSECRET_FROM_SELFSERVE_COMMAND>
68+
69+
aws secretsmanager create-secret --name $UNION_CLIENT_SECRET_NAME \
70+
--secret-string "{\"clientId\":\"$UNION_CLIENT_ID_SECRET_VALUE\",\"clientSecret\":\"$UNION_SECRET_SECRET_VALUE\"}"
71+
```
72+
73+
### Clone the repository:
74+
75+
```sh
76+
git clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git
77+
cd cdk-eks-blueprints-patterns
78+
```
79+
80+
### Run the following commands:
81+
82+
```sh
83+
make deps
84+
make build
85+
make pattern unionai deploy
86+
```
87+
88+
### Validation
89+
90+
Run the command:
91+
```bash
92+
kubectl get deploy -A
93+
```
94+
95+
Output should be:
96+
```bash
97+
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
98+
kube-system blueprints-addon-metrics-server 1/1 1 1 57d
99+
kube-system blueprints-addon-union-dataplane-kube-state-metrics 1/1 1 1 57d
100+
unionai executor 1/1 1 1 57d
101+
unionai flytepropeller 1/1 1 1 57d
102+
unionai flytepropeller-webhook 1/1 1 1 57d
103+
unionai opencost 1/1 1 1 57d
104+
unionai prometheus-operator 1/1 1 1 57d
105+
unionai syncresources 1/1 1 1 57d
106+
unionai union-operator 1/1 1 1 57d
107+
unionai union-operator-proxy 1/1 1 1 57d
108+
```
109+
110+
To validate the cluster has been successfully registered to the Union control plane run the command:
111+
```bash
112+
uctl get cluster
113+
```
114+
115+
Output should be:
116+
```bash
117+
----------- ------- --------------- -----------
118+
| NAME | ORG | STATE | HEALTH |
119+
----------- ------- --------------- -----------
120+
| <cluster> | <org> | STATE_ENABLED | HEALTHY |
121+
----------- ------- --------------- -----------
122+
1 rows
123+
```
124+
125+
### 8. Register and run example workflows
126+
127+
```bash
128+
uctl register examples --project=union-health-monitoring --domain=development
129+
uctl validate snacks --project=union-health-monitoring --domain=development
130+
---------------------- ----------------------------------- ---------- -------------------------------- -------------- ----------- ---------------
131+
| NAME | LAUNCH PLAN NAME | VERSION | STARTED AT | ELAPSED TIME | RESULT | ERROR MESSAGE |
132+
---------------------- ----------------------------------- ---------- -------------------------------- -------------- ----------- ---------------
133+
| alskkhcd6wx5m6cqjlwm | basics.hello_world.hello_world_wf | v0.3.341 | 2025-05-09T18:30:02.968183352Z | 4.452440953s | SUCCEEDED | |
134+
---------------------- ----------------------------------- ---------- -------------------------------- -------------- ----------- ---------------
135+
1 rows
136+
```

lib/common/construct-utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ export async function prevalidateSecrets(pattern: string, region?: string, ...se
3636
}
3737
}
3838

39+
export function getJsonSecret(secretString: string, key?: string): string {
40+
const parsed = JSON.parse(secretString);
41+
return key ? parsed[key] : parsed;
42+
}
43+
3944
export class EmptyStack extends cdk.Stack {
4045
constructor(scope: cdk.App, ...message: string[]) {
4146
super(scope, "empty-error-stack");

lib/kubeshark-construct/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export default class KubesharkConstruct {
1111
EksBlueprint.builder()
1212
.account(process.env.CDK_DEFAULT_ACCOUNT!)
1313
.region(process.env.CDK_DEFAULT_REGION)
14-
.addOns(new KubesharkAddOn())
14+
.addOns(new KubesharkAddOn({repository: "https://helm.kubeshark.com"}))
1515
.version('auto')
1616
.build(scope, stackId);
1717
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import * as cdk from 'aws-cdk-lib';
2+
import * as blueprints from '@aws-quickstart/eks-blueprints';
3+
import * as union from '@unionai/union-eks-blueprints-addon';
4+
import { prevalidateSecrets, getJsonSecret } from '../common/construct-utils';
5+
6+
const BUCKET_PROVIDER_NAME = "union-s3-bucket";
7+
8+
export default class UnionDataplaneConstruct {
9+
async buildAsync(scope: cdk.App, id: string) {
10+
11+
await prevalidateSecrets(UnionDataplaneConstruct.name, undefined, "union-client-secret", "union-secret");
12+
const unionSecretString = await blueprints.utils.getSecretValue("union-secret", process.env.CDK_DEFAULT_REGION!);
13+
14+
const unionConfig: union.UnionDataplaneAddOnProps = {
15+
orgName: getJsonSecret(unionSecretString, "orgName"),
16+
unionSecretName: "union-client-secret",
17+
clusterName: getJsonSecret(unionSecretString, "clusterName"),
18+
s3BucketProviderName: BUCKET_PROVIDER_NAME,
19+
host: getJsonSecret(unionSecretString, "host"),
20+
};
21+
22+
const stackId = `${id}-blueprint`;
23+
24+
const nodeClassSpec: blueprints.Ec2NodeClassV1Spec = {
25+
amiFamily: "Bottlerocket",
26+
amiSelectorTerms: [
27+
{ alias: "bottlerocket@1.50.0" }, // alias allows to specify ami family and version. @latest also supported
28+
],
29+
subnetSelectorTerms: [
30+
{ tags: { Name: `${stackId}/${stackId}-vpc/PrivateSubnet*` } },
31+
],
32+
securityGroupSelectorTerms: [
33+
{ tags: { "aws:eks:cluster-name": `${stackId}` } },
34+
],
35+
};
36+
37+
const nodePoolSpec: blueprints.NodePoolV1Spec = {
38+
labels: { type: "karpenter-test" },
39+
requirements: [
40+
{ key: "node.kubernetes.io/instance-type", operator: "In", values: ["m5.2xlarge", "m5.xlarge"] },
41+
{
42+
key: "topology.kubernetes.io/zone",
43+
operator: "In",
44+
values: [`${process.env.CDK_DEFAULT_REGION}a`, `${process.env.CDK_DEFAULT_REGION}b`],
45+
},
46+
{ key: "kubernetes.io/arch", operator: "In", values: ["amd64"] },
47+
{ key: "karpenter.sh/capacity-type", operator: "In", values: ["on-demand"] },
48+
],
49+
expireAfter: "20m",
50+
disruption: { consolidationPolicy: "WhenEmpty", consolidateAfter: "30s" },
51+
limits: { cpu: 400 }
52+
};
53+
54+
55+
const addOns: Array<blueprints.ClusterAddOn> = [
56+
new blueprints.addons.MetricsServerAddOn,
57+
new blueprints.addons.VpcCniAddOn,
58+
new blueprints.addons.KubeProxyAddOn,
59+
new blueprints.addons.CoreDnsAddOn,
60+
new blueprints.addons.EksPodIdentityAgentAddOn,
61+
new blueprints.addons.KarpenterV1AddOn({
62+
ec2NodeClassSpec: nodeClassSpec,
63+
nodePoolSpec: nodePoolSpec,
64+
interruptionHandling: true,
65+
podIdentity: true
66+
}),
67+
new union.UnionDataplaneCRDsAddOn,
68+
new union.UnionDataplaneAddOn(unionConfig)
69+
];
70+
blueprints.EksBlueprint.builder()
71+
.account(process.env.CDK_DEFAULT_ACCOUNT)
72+
.region(process.env.CDK_DEFAULT_REGION)
73+
.resourceProvider(BUCKET_PROVIDER_NAME, new blueprints.CreateS3BucketProvider({ name: `union-bucket-${process.env.CDK_DEFAULT_ACCOUNT!}`, id: "union-bucket" }))
74+
.addOns(...addOns)
75+
.version('auto')
76+
.build(scope, stackId);
77+
}
78+
}
79+
80+

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ nav:
5151
- Graviton Pattern: 'patterns/graviton.md'
5252
- Windows Pattern: 'patterns/windows.md'
5353
- gMaestro Pattern: 'patterns/gmaestro.md'
54+
- Union.ai Pattern : 'patterns/union.md'
5455
markdown_extensions:
5556
- def_list
5657
- pymdownx.highlight

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"typescript": "^5.3.3"
2525
},
2626
"dependencies": {
27-
"@aws-quickstart/eks-blueprints": "1.16.3",
27+
"@aws-quickstart/eks-blueprints": "1.17.4",
2828
"@aws-sdk/client-config-service": "^3.576.0",
2929
"@aws-sdk/client-eks": "^3.478.0",
3030
"@claranet-ch/konveyor-eks-blueprint-addon": "^1.0.2",
@@ -40,18 +40,18 @@
4040
"@paralus/paralus-eks-blueprints-addon": "^0.1.5",
4141
"@rafaysystems/rafay-eks-blueprints-addon": "^0.0.2",
4242
"@snyk-partners/snyk-monitor-eks-blueprints-addon": "^1.1.1",
43-
"aws-cdk": "2.173.4",
44-
"aws-cdk-lib": "2.173.4",
43+
"@unionai/union-eks-blueprints-addon": "^0.0.5",
44+
"aws-cdk": "2.1029.2",
45+
"aws-cdk-lib": "2.215.0",
4546
"eks-blueprints-cdk-kubeflow-ext": "0.1.9",
4647
"kubeshark": "^0.0.9",
4748
"source-map-support": "^0.5.21"
4849
},
4950
"overrides": {
50-
"@aws-quickstart/eks-blueprints": "1.16.3",
51-
"aws-cdk": "2.173.4",
52-
"aws-cdk-lib": "2.173.4",
51+
"@aws-quickstart/eks-blueprints": "1.17.4",
52+
"aws-cdk": "2.1029.2",
53+
"aws-cdk-lib": "2.215.0",
5354
"xml2js": "0.5.0",
54-
"@aws-cdk/core": "../_EXCLUDED_",
5555
"axios": "^1.6.2"
5656
}
5757
}

0 commit comments

Comments
 (0)