Skip to content

Commit c064d74

Browse files
committed
Addiing in zookeeper example, fixes #159
1 parent e9cc28c commit c064d74

File tree

6 files changed

+361
-0
lines changed

6 files changed

+361
-0
lines changed

aws-ts-zookeeper/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
/bin/
3+
/node_modules/

aws-ts-zookeeper/Pulumi.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: aws-ts-zookeeper
2+
runtime:
3+
name: nodejs
4+
options:
5+
packagemanager: yarn
6+
description: Zookeeper on AWS with managed node group, launch template, and load balancer

aws-ts-zookeeper/README.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# ZooKeeper cluster on AWS
2+
3+
## Components and Features
4+
5+
1. Networking:
6+
- VPC with 3 availability zones
7+
- Private subnets with NAT gateways
8+
- Security groups for ZooKeeper traffic
9+
10+
2. Compute:
11+
- Auto Scaling Group with 3 nodes
12+
- Launch template with Ubuntu 24.04
13+
- IAM roles and instance profile
14+
- CloudWatch integration
15+
16+
3. Load Balancing:
17+
- Internal Application Load Balancer
18+
- Target group with health checks
19+
- Listener configuration
20+
21+
4. Security:
22+
- Security groups with minimal required ports
23+
- IMDSv2 requirement
24+
- Encrypted EBS volumes
25+
- SSM access for management
26+
27+
5. Monitoring:
28+
- CloudWatch agent configuration
29+
- CPU utilization alarms
30+
- Memory and disk monitoring
31+
32+
## Deployment
33+
34+
1. Set up your AWS credentials
35+
2. Create a Pulumi stack
36+
37+
3. Configure the environment:
38+
39+
```bash
40+
41+
pulumi config set environment production
42+
```
43+
44+
4. Deploy:
45+
46+
```bash
47+
48+
pulumi up
49+
```

aws-ts-zookeeper/index.ts

+276
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
// Copyright 2016-2025, Pulumi Corporation. All rights reserved.
2+
3+
import * as aws from "@pulumi/aws";
4+
import * as awsx from "@pulumi/awsx";
5+
import * as pulumi from "@pulumi/pulumi";
6+
7+
8+
const config = new pulumi.Config();
9+
const projectName = "zookeeper";
10+
const environment = config.require("environment");
11+
12+
// VPC Configuration
13+
const vpc = new awsx.ec2.Vpc(`${projectName}-vpc`, {
14+
numberOfAvailabilityZones: 3,
15+
natGateways: {
16+
strategy: environment === "production" ? "OnePerAz" : "Single",
17+
},
18+
tags: {
19+
Name: `${projectName}-vpc`,
20+
Environment: environment,
21+
},
22+
});
23+
24+
// Security Groups
25+
const zookeeperSG = new aws.ec2.SecurityGroup(`${projectName}-sg`, {
26+
vpcId: vpc.vpcId,
27+
description: "Security group for ZooKeeper nodes",
28+
ingress: [
29+
{ protocol: "tcp", fromPort: 22, toPort: 22, cidrBlocks: ["10.0.0.0/8"] }, // SSH
30+
{ protocol: "tcp", fromPort: 2181, toPort: 2181, cidrBlocks: ["10.0.0.0/8"] }, // Client port
31+
{ protocol: "tcp", fromPort: 2888, toPort: 2888, cidrBlocks: ["10.0.0.0/8"] }, // Follower port
32+
{ protocol: "tcp", fromPort: 3888, toPort: 3888, cidrBlocks: ["10.0.0.0/8"] }, // Election port
33+
],
34+
egress: [{
35+
protocol: "-1",
36+
fromPort: 0,
37+
toPort: 0,
38+
cidrBlocks: ["0.0.0.0/0"],
39+
}],
40+
tags: {
41+
Name: `${projectName}-sg`,
42+
Environment: environment,
43+
},
44+
});
45+
46+
// IAM Role and Instance Profile
47+
const zookeeperRole = new aws.iam.Role(`${projectName}-role`, {
48+
assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({
49+
Service: "ec2.amazonaws.com",
50+
}),
51+
});
52+
53+
const zookeeperInstanceProfile = new aws.iam.InstanceProfile(`${projectName}-instance-profile`, {
54+
role: zookeeperRole.name,
55+
});
56+
57+
// SSM Policy Attachment
58+
const ssmPolicy = new aws.iam.RolePolicyAttachment(`${projectName}-ssm-policy`, {
59+
role: zookeeperRole.name,
60+
policyArn: "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
61+
});
62+
63+
// CloudWatch Policy
64+
const cloudwatchPolicy = new aws.iam.RolePolicy(`${projectName}-cloudwatch-policy`, {
65+
role: zookeeperRole.name,
66+
policy: JSON.stringify({
67+
Version: "2012-10-17",
68+
Statement: [{
69+
Effect: "Allow",
70+
Action: [
71+
"cloudwatch:PutMetricData",
72+
"cloudwatch:GetMetricData",
73+
"cloudwatch:ListMetrics",
74+
],
75+
Resource: "*",
76+
}],
77+
}),
78+
});
79+
80+
// Launch Template User Data Script
81+
const getUserData = (id: number) => `#!/bin/bash
82+
apt-get update
83+
apt-get install -y openjdk-11-jdk
84+
85+
# Install ZooKeeper
86+
ZOOKEEPER_VERSION="3.9.3"
87+
wget https://dlcdn.apache.org/zookeeper/zookeeper-$ZOOKEEPER_VERSION/apache-zookeeper-$ZOOKEEPER_VERSION-bin.tar.gz
88+
tar -xzf apache-zookeeper-$ZOOKEEPER_VERSION-bin.tar.gz
89+
mv apache-zookeeper-$ZOOKEEPER_VERSION-bin /opt/zookeeper
90+
91+
# Configure ZooKeeper
92+
cat > /opt/zookeeper/conf/zoo.cfg << EOF
93+
tickTime=2000
94+
initLimit=10
95+
syncLimit=5
96+
dataDir=/var/lib/zookeeper
97+
clientPort=2181
98+
server.1=zk1.internal:2888:3888
99+
server.2=zk2.internal:2888:3888
100+
server.3=zk3.internal:2888:3888
101+
EOF
102+
103+
mkdir -p /var/lib/zookeeper
104+
echo "${id}" > /var/lib/zookeeper/myid
105+
106+
# Create systemd service
107+
cat > /etc/systemd/system/zookeeper.service << EOF
108+
[Unit]
109+
Description=ZooKeeper Service
110+
After=network.target
111+
112+
[Service]
113+
Type=forking
114+
User=root
115+
Group=root
116+
ExecStart=/opt/zookeeper/bin/zkServer.sh start
117+
ExecStop=/opt/zookeeper/bin/zkServer.sh stop
118+
ExecReload=/opt/zookeeper/bin/zkServer.sh restart
119+
WorkingDirectory=/opt/zookeeper
120+
121+
[Install]
122+
WantedBy=multi-user.target
123+
EOF
124+
125+
# Start ZooKeeper
126+
systemctl daemon-reload
127+
systemctl enable zookeeper
128+
systemctl start zookeeper
129+
130+
# Install CloudWatch agent
131+
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
132+
dpkg -i amazon-cloudwatch-agent.deb
133+
134+
# Configure CloudWatch agent
135+
cat > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json << EOF
136+
{
137+
"metrics": {
138+
"metrics_collected": {
139+
"mem": {
140+
"measurement": ["mem_used_percent"]
141+
},
142+
"disk": {
143+
"measurement": ["disk_used_percent"],
144+
"resources": ["/"]
145+
}
146+
}
147+
}
148+
}
149+
EOF
150+
151+
systemctl enable amazon-cloudwatch-agent
152+
systemctl start amazon-cloudwatch-agent`;
153+
154+
// Launch Template
155+
const launchTemplate = new aws.ec2.LaunchTemplate(`${projectName}-launch-template`, {
156+
namePrefix: `${projectName}-`,
157+
imageId: "ami-0c7217cdde317cfec", // Ubuntu 24.04 LTS AMI ID
158+
instanceType: "t3.medium",
159+
userData: Buffer.from(getUserData(1)).toString("base64"),
160+
vpcSecurityGroupIds: [zookeeperSG.id],
161+
iamInstanceProfile: {
162+
name: zookeeperInstanceProfile.name,
163+
},
164+
blockDeviceMappings: [{
165+
deviceName: "/dev/sda1",
166+
ebs: {
167+
volumeSize: 50,
168+
volumeType: "gp3",
169+
deleteOnTermination: "true",
170+
encrypted: "true",
171+
},
172+
}],
173+
tags: {
174+
Name: `${projectName}-launch-template`,
175+
Environment: environment,
176+
},
177+
metadataOptions: {
178+
httpEndpoint: "enabled",
179+
httpTokens: "required",
180+
httpPutResponseHopLimit: 1,
181+
},
182+
});
183+
184+
// Auto Scaling Group
185+
const asg = new aws.autoscaling.Group(`${projectName}-asg`, {
186+
vpcZoneIdentifiers: vpc.privateSubnetIds,
187+
desiredCapacity: 3,
188+
maxSize: 3,
189+
minSize: 3,
190+
healthCheckType: "ELB",
191+
healthCheckGracePeriod: 300,
192+
launchTemplate: {
193+
id: launchTemplate.id,
194+
version: "$Latest",
195+
},
196+
tags: [{
197+
key: "Name",
198+
value: `${projectName}-node`,
199+
propagateAtLaunch: true,
200+
}, {
201+
key: "Environment",
202+
value: environment,
203+
propagateAtLaunch: true,
204+
}],
205+
});
206+
207+
// Application Load Balancer
208+
const alb = new aws.lb.LoadBalancer(`${projectName}-alb`, {
209+
internal: true,
210+
loadBalancerType: "application",
211+
securityGroups: [zookeeperSG.id],
212+
subnets: vpc.privateSubnetIds,
213+
tags: {
214+
Name: `${projectName}-alb`,
215+
Environment: environment,
216+
},
217+
});
218+
219+
// Target Group
220+
const targetGroup = new aws.lb.TargetGroup(`${projectName}-tg`, {
221+
port: 2181,
222+
protocol: "HTTP",
223+
vpcId: vpc.vpcId,
224+
targetType: "instance",
225+
healthCheck: {
226+
enabled: true,
227+
path: "/",
228+
port: "2181",
229+
protocol: "HTTP",
230+
healthyThreshold: 2,
231+
unhealthyThreshold: 3,
232+
timeout: 5,
233+
interval: 30,
234+
},
235+
tags: {
236+
Name: `${projectName}-tg`,
237+
Environment: environment,
238+
},
239+
});
240+
241+
// ALB Listener
242+
const listener = new aws.lb.Listener(`${projectName}-listener`, {
243+
loadBalancerArn: alb.arn,
244+
port: 2181,
245+
protocol: "HTTP",
246+
defaultActions: [{
247+
type: "forward",
248+
targetGroupArn: targetGroup.arn,
249+
}],
250+
});
251+
252+
// Attach ASG to Target Group
253+
const asgAttachment = new aws.autoscaling.Attachment(`${projectName}-asg-attachment`, {
254+
autoscalingGroupName: asg.name,
255+
lbTargetGroupArn: targetGroup.arn,
256+
});
257+
258+
// CloudWatch Alarms
259+
const cpuAlarm = new aws.cloudwatch.MetricAlarm(`${projectName}-cpu-alarm`, {
260+
comparisonOperator: "GreaterThanThreshold",
261+
evaluationPeriods: 2,
262+
metricName: "CPUUtilization",
263+
namespace: "AWS/EC2",
264+
period: 300,
265+
statistic: "Average",
266+
threshold: 80,
267+
alarmDescription: "This metric monitors ec2 cpu utilization",
268+
dimensions: {
269+
AutoScalingGroupName: asg.name,
270+
},
271+
});
272+
273+
// Export values
274+
export const vpcId = vpc.vpcId;
275+
export const albDnsName = alb.dnsName;
276+
export const asgName = asg.name;

aws-ts-zookeeper/package.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "aws-ts-zookeeper",
3+
"description": "Zookeeper on AWS with managed node group, launch template, and load balancer",
4+
"dependencies": {
5+
"@pulumi/pulumi": "3.145.0",
6+
"@pulumi/aws": "6.66.3",
7+
"@pulumi/awsx": "2.19.0"
8+
}
9+
}

aws-ts-zookeeper/tsconfig.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"compilerOptions": {
3+
"strict": true,
4+
"outDir": "bin",
5+
"target": "es2016",
6+
"module": "commonjs",
7+
"moduleResolution": "node",
8+
"sourceMap": true,
9+
"experimentalDecorators": true,
10+
"pretty": true,
11+
"noFallthroughCasesInSwitch": true,
12+
"noImplicitReturns": true,
13+
"forceConsistentCasingInFileNames": true
14+
},
15+
"files": [
16+
"index.ts"
17+
]
18+
}

0 commit comments

Comments
 (0)