feat(evidently): dump collected notes

This commit is contained in:
Michele Cereda
2024-09-25 18:21:01 +02:00
parent 20fe9d609e
commit ff76d4505a
23 changed files with 732 additions and 150 deletions

View File

@@ -0,0 +1,76 @@
#cloud-config
# Tested on:
# - Amazon Linux 2023
bootcmd:
# `cloud-init` has issues with `firewall-cmd`, using the offline version.
- firewall-offline-cmd --add-port='8000/tcp' --zone='public'
users:
- default
- evidently
packages:
- make
write_files:
- path: /home/evidently/requirements.txt
owner: evidently:evidently
defer: true
content: |
evidently[llm]==0.4.37
tracely==0.1.0
s3fs==2024.9.0
- path: /home/evidently/Makefile
owner: evidently:evidently
defer: true
content:
# make sure to keep the tab characters in the targets' definitions
|
#!/usr/bin/env make
override venv ?= ${HOME}/venv
create-venv: override python_version ?= 3.9
create-venv: python_executable = ${shell which --tty-only --show-dot --show-tilde 'python${python_version}'}
create-venv: ${python_executable}
@${python_executable} -m 'venv' '${venv}'
@${venv}/bin/pip --require-virtualenv install -r 'requirements.txt'
recreate-venv:
@rm -rf '${venv}'
@${MAKE} create-venv
start-evidently-ui: override host ?= 0.0.0.0
start-evidently-ui: override port ?= 8000
start-evidently-ui: override workspace ?= s3://evidently-ui/workspace
start-evidently-ui: create-venv ${venv}/bin/evidently
@${venv}/bin/evidently ui --host='${host}' --port='${port}' --workspace='${workspace}'
- path: /etc/systemd/system/evidently-ui.service
owner: root:root
permissions: 0755
defer: true
content: |
[Unit]
Description=Evidently UI
Documentation=https://docs.evidentlyai.com/
Wants=network-online.target
After=network-online.target
[Service]
SyslogIdentifier=evidently-ui
Type=simple
User=evidently
Group=evidently
WorkingDirectory=/home/evidently
ExecStart=/usr/bin/env make start-evidently-ui
Restart=always
[Install]
WantedBy=multi-user.target
runcmd:
- systemctl reload 'firewalld.service'
- systemctl daemon-reload
- systemctl enable --now 'evidently-ui.service'

View File

@@ -1,8 +0,0 @@
encryptionsalt: v1:55yDA5Kuyzs=:v1:+kFXkziA9Bd7nNZQ:OSBtNRAVCGBXwzTtHOGA5Ti9Dz+FTQ==
config:
aws:region: eu-west-1
aws:defaultTags:
tags:
ManagedByPulumi: true
Owner: "somebody@example.com"
PulumiProject: ec2-instance

View File

@@ -1,9 +0,0 @@
name: ec2-instance
runtime: nodejs
description: AWS EC2 instance example
config:
pulumi:tags:
value:
pulumi:template: aws-typescript
backend:
url: file://.

View File

@@ -1,123 +0,0 @@
import * as aws from "@pulumi/aws";
import * as cloudinit from "@pulumi/cloudinit";
import * as yaml from "yaml";
const ami = aws.ec2.getAmiOutput({
owners: [ "amazon", ],
nameRegex: "^al2023-ami-2023.*",
filters: [
{
name: "architecture",
values: [ "arm64" ],
},
{
name: "state",
values: [ "available" ],
},
],
mostRecent: true,
});
const keyPair = aws.ec2.getKeyPairOutput({ keyName: "somebody-ec2Instances" });
const subnet = aws.ec2.getSubnetOutput({
filters: [{
name: "tag:Name",
values: [ "Private C" ],
}],
});
const securityGroup = new aws.ec2.SecurityGroup(
"ec2-instance-example",
{
name: "Ec2InstanceExample",
description: "Regulate communications to and from the EC2 Instance",
tags: {
Name: "EC2 Instance Example",
},
},
);
const role = new aws.iam.Role(
"ec2-instance-example",
{
name: "Ec2InstanceExample",
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Action: "sts:AssumeRole",
Principal: {
Service: "ec2.amazonaws.com",
},
}],
}),
managedPolicyArns: [ "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" ],
},
);
const instanceProfile = new aws.iam.InstanceProfile(
"ec2-instance-example",
{
name: "Ec2InstanceExample",
role: role.name,
},
);
const userData = new cloudinit.Config(
"ec2-instance-example",
{
gzip: true,
base64Encode: true,
parts: [
{
// only useful on minimal al2023 base images or other images with no aws-ssm
contentType: "text/cloud-config",
content: yaml.stringify({
package_upgrade: false,
packages: [ "amazon-ssm-agent" ],
runcmd: [
"systemctl daemon-reload",
"systemctl enable --now 'amazon-ssm-agent.service'",
]
}),
filename: "cloud-config.managed-by.ssm.yml",
},
{
contentType: "text/cloud-config",
content: yaml.stringify({
package_upgrade: false,
packages: [ "python" ],
}),
filename: "cloud-config.managed-by.ansible.yml",
mergeType: "dict(recurse_array,no_replace)+list(append)",
},
],
},
);
new aws.ec2.Instance(
"ec2-instance-example",
{
ami: ami.apply(ami => ami.id),
iamInstanceProfile: instanceProfile.name,
instanceType: "t4g.small",
keyName: keyPair.apply(keyPair => keyPair.keyName!),
rootBlockDevice: {
volumeType: "gp3",
volumeSize: 20,
tags: {
Description: "Instance root disk",
Name: "EC2 Instance Example",
},
},
subnetId: subnet.apply(subnet => subnet.id),
tags: {
Name: "EC2 Instance Example",
ManagedBySsm: "true",
ManagedByAnsible: "true",
},
userData: userData.rendered,
vpcSecurityGroupIds: [ securityGroup.id ],
},
{
ignoreChanges: [
// avoid being replaced just because a new version of the base image came out
"ami",
],
}
);

View File

@@ -0,0 +1,4 @@
---
encryptionsalt: v1:pAW9r3TNSV0=:v1:WNIhJ97VgKWPVLTi:IypZBR+LreWLhasHqWGTNCuabKf3Ew==
config:
aws:region: eu-west-1

View File

@@ -0,0 +1,11 @@
---
name: evidently-ui-on-ec2-via-alb
runtime:
name: nodejs
options:
packagemanager: npm
description: 'AWS example: Evidently-UI on EC2 instance via ALB'
config:
pulumi:tags:
value:
pulumi:template: aws-typescript

View File

@@ -0,0 +1,85 @@
#cloud-config
# Tested on:
# - Amazon Linux 2023
bootcmd:
# `cloud-init` has issues with `firewall-cmd`, using the offline version.
- firewall-offline-cmd --add-port='8000/tcp' --zone='public'
users:
- default
- evidently
packages:
- make
write_files:
- path: /home/evidently/requirements.txt
owner: evidently:evidently
defer: true
content: |
evidently[llm]==0.4.37
tracely==0.1.0
s3fs==2024.9.0
- path: /home/evidently/Makefile
owner: evidently:evidently
defer: true
content:
# make sure to keep the tab characters in the targets' definitions
|
#!/usr/bin/env make
# '/tmp' is on tmpfs (uses and is limited by the instance's RAM)
# Use a local 'tmp' directory for PIP to avoid exhausting the space during installation
override tmpdir ?= ${HOME}/tmp
override venv ?= ${HOME}/venv
create-venv: override python_version ?= 3
create-venv: python_executable = ${shell which --tty-only --show-dot --show-tilde 'python${python_version}'}
create-venv: ${python_executable}
@mkdir -p ${tmpdir}
@${python_executable} -m 'venv' '${venv}'
@TMPDIR=${tmpdir} ${venv}/bin/pip --require-virtualenv install -r 'requirements.txt'
recreate-venv:
@rm -rf '${venv}'
@${MAKE} create-venv
update-venv: ${venv}/bin/pip
@${venv}/bin/pip freeze -l --require-virtualenv | sed 's/==/>=/' \
| xargs ${venv}/bin/pip --require-virtualenv install -U
start-evidently-ui: override host ?= 0.0.0.0
start-evidently-ui: override port ?= 8000
start-evidently-ui: override workspace ?= s3://evidentlyUi/workspace
start-evidently-ui: create-venv ${venv}/bin/evidently
@${venv}/bin/evidently ui --host='${host}' --port='${port}' --workspace='${workspace}'
- path: /etc/systemd/system/evidently-ui.service
owner: root:root
permissions: 0755
defer: true
content: |
[Unit]
Description=Evidently UI
Documentation=https://docs.evidentlyai.com/
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=evidently
Group=evidently
WorkingDirectory=/home/evidently
ExecStart=/usr/bin/env make start-evidently-ui
SyslogIdentifier=evidently-ui
Restart=always
[Install]
WantedBy=multi-user.target
runcmd:
- systemctl reload 'firewalld.service'
- systemctl daemon-reload
- systemctl enable --now 'evidently-ui.service'

View File

@@ -0,0 +1,291 @@
import * as aws from "@pulumi/aws";
import * as cloudinit from "@pulumi/cloudinit";
import * as fs from "fs";
import * as pulumi from "@pulumi/pulumi";
/**
* EvidentlyUi requirements - start
* -------------------------------------
*/
const evidentlyUi_s3bucket = new aws.s3.Bucket(
"evidentlyUi",
{ bucket: "evidentlyUi" },
);
const evidentlyUi_securityGroup_internal = new aws.ec2.SecurityGroup(
"evidentlyUi",
{
name: "EvidentlyUiInternal",
tags: { Name: "EvidentlyUiInternal" },
ingress: [{
description: "Allow connections between ALB and instance",
self: true,
protocol: "tcp",
fromPort: 8000,
toPort: 8000,
}],
egress: [{
cidrBlocks: [ "0.0.0.0/0" ],
protocol: "-1",
fromPort: 0,
toPort: 0,
}],
},
);
const evidentlyUi_securityGroup_external = new aws.ec2.SecurityGroup(
"evidentlyUi-external",
{
name: "EvidentlyUiExternal",
tags: { Name: "EvidentlyUiExternal" },
ingress: [
{
description: "Allow connections to the ALB from outside via HTTP",
cidrBlocks: [ "0.0.0.0/0" ],
protocol: "tcp",
fromPort: 80,
toPort: 80,
},
{
description: "Allow connections to the ALB from outside via HTTPS",
cidrBlocks: [ "0.0.0.0/0" ],
protocol: "tcp",
fromPort: 443,
toPort: 443,
},
],
egress: [{
cidrBlocks: [ "0.0.0.0/0" ],
protocol: "-1",
fromPort: 0,
toPort: 0,
}],
},
);
const evidentlyUi_publicSubnet_output = aws.ec2.getSubnetOutput({
filters: [{
name: "tag:Name",
values: [ "Public Subnet in AZ B" ],
}],
});
/**
* -------------------------------------
* EvidentlyUi requirements - end
*/
/**
* EC2 Instance - start
* -------------------------------------
*/
const evidentlyUi_instanceAmi_output = aws.ec2.getAmiOutput({
owners: [ "amazon", ],
nameRegex: "^al2023-ami-2023.*",
filters: [
{
name: "architecture",
values: [ "arm64" ],
},
{
name: "state",
values: [ "available" ],
},
],
mostRecent: true,
});
const evidentlyUi_instanceSubnet_output = aws.ec2.getSubnetOutput({
filters: [{
name: "tag:Name",
values: [ "Private Subnet in AZ C" ],
}],
});
const evidentlyUi_instanceKeyPair = new aws.ec2.KeyPair(
"evidentlyUi",
{
keyName: "EvidentlyUi",
publicKey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1CBr/1GqUX+/rUR00TK34+2sMWbRNkqbckGvYmtypu openpgp:0xE742BC48",
},
);
const evidentlyUi_instanceRole = evidentlyUi_s3bucket.arn.apply(
bucketArn => new aws.iam.Role(
"evidentlyUi-instance",
{
name: "EvidentlyUiInstanceRole",
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Principal: { Service: "ec2.amazonaws.com" },
Action: "sts:AssumeRole",
}],
}),
inlinePolicies: [{
name: "AllowManagingOwnBucket",
policy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Action: "s3:*",
Resource: [
`${bucketArn}`,
`${bucketArn}/*`,
],
}],
}),
}],
},
),
);
const evidentlyUi_instanceProfile = new aws.iam.InstanceProfile(
"evidentlyUi-ec2Instance",
{
name: "EvidentlyUiInstanceProfile",
role: evidentlyUi_instanceRole.name,
},
);
const evidentlyUi_instanceUserData = new cloudinit.Config(
"evidentlyUi-instance",
{
gzip: true,
base64Encode: true,
parts: [{
contentType: "text/cloud-config",
content: fs.readFileSync("cloud-config.evidently-ui.yml", "utf8"),
filename: "cloud-config.evidently-ui.yml",
}],
},
);
const evidentlyUi_instance = new aws.ec2.Instance(
"evidentlyUi",
{
ami: evidentlyUi_instanceAmi_output.id,
iamInstanceProfile: evidentlyUi_instanceProfile.name,
instanceType: "t4g.micro",
keyName: evidentlyUi_instanceKeyPair.keyName!,
rootBlockDevice: {
volumeType: "gp3",
volumeSize: 20,
tags: {
Description: "Instance root disk",
Name: "EvidentlyUi-instanceRootDisk",
},
},
subnetId: evidentlyUi_instanceSubnet_output.id,
tags: {
Name: "EvidentlyUi",
ManagedBySsm: "true",
ManagedByAnsible: "true",
},
userData: evidentlyUi_instanceUserData.rendered,
userDataReplaceOnChange: true,
vpcSecurityGroupIds: [ evidentlyUi_securityGroup_internal.id ],
},
{
ignoreChanges: [
"ami", // avoid replacing just because a new version of the base image came out
],
},
);
/**
* -------------------------------------
* EC2 Instance - end
*/
/**
* Application Load Balancer - start
* -------------------------------------
*/
const evidentlyUi_targetGroup = new aws.alb.TargetGroup(
"evidentlyUi",
{
name: "EvidentlyUi",
vpcId: evidentlyUi_instanceSubnet_output.vpcId,
targetType: "instance",
port: 8000,
protocol: "HTTP",
protocolVersion: "HTTP1",
},
);
new aws.lb.TargetGroupAttachment(
"evidentlyUi",
{
targetGroupArn: evidentlyUi_targetGroup.arn,
targetId: evidentlyUi_instance.id,
},
);
const evidentlyUi_applicationLoadBalancer = new aws.alb.LoadBalancer(
"evidentlyUi",
{
name: "EvidentlyUi",
ipAddressType: "ipv4",
subnets: [
evidentlyUi_publicSubnet_output.id, // external
evidentlyUi_instanceSubnet_output.id, // internal
],
securityGroups: [
evidentlyUi_securityGroup_external.id,
evidentlyUi_securityGroup_internal.id,
],
accessLogs: { bucket: "" },
},
);
new aws.alb.Listener(
"evidentlyUi-http2https",
{
loadBalancerArn: evidentlyUi_applicationLoadBalancer.arn,
port: 80,
defaultActions: [{
order: 1,
redirect: {
port: "443",
protocol: "HTTPS",
statusCode: "HTTP_301",
},
type: "redirect",
}],
},
);
new aws.alb.Listener(
"evidentlyUi-https",
{
loadBalancerArn: evidentlyUi_applicationLoadBalancer.arn,
port: 443,
protocol: "HTTPS",
certificateArn: "arn:aws:acm:eu-west-1:012345678901:certificate/01234567-abcd-8901-abcd-abcdef012345",
defaultActions: [{
order: 1,
targetGroupArn: evidentlyUi_targetGroup.arn,
type: "forward",
}],
},
);
new aws.route53.Record(
"evidentlyUi",
{
aliases: [{
evaluateTargetHealth: true,
name: pulumi.interpolate`dualstack.${evidentlyUi_applicationLoadBalancer.dnsName}`,
zoneId: "Z012345678ABCD",
}],
name: "evidently-ui.dev.company.com",
type: "A",
zoneId: "Z9ABCD12345678",
},
);
/**
* -------------------------------------
* Application Load Balancer - end
*/

View File

@@ -1,5 +1,5 @@
{
"name": "ec2-instance",
"name": "evidently-ui-on-ec2-via-alb",
"main": "index.ts",
"devDependencies": {
"@types/node": "^18",
@@ -8,6 +8,6 @@
"dependencies": {
"@pulumi/aws": "^6.0.0",
"@pulumi/cloudinit": "^1.0.0",
"yaml": "^2.0.0"
"fs": "^0.0.0"
}
}
}

View File

@@ -1,5 +1,6 @@
import * as cloudinit from "@pulumi/cloudinit";
import * as fs from 'fs';
import * as yaml from 'yaml';
export const userData = new cloudinit.Config(
"userData",
@@ -18,6 +19,22 @@ export const userData = new cloudinit.Config(
filename: "cloud-config.docker.yml",
mergeType: "dict(recurse_array,no_replace)+list(append)",
},
{
contentType: "text/cloud-config",
content: yaml.stringify({
write_files: [{
path: "/etc/cron.daily/security-updates",
permissions: "0755",
content: [
"#!/bin/bash",
"dnf -y upgrade --security --nobest",
].join("\n"),
defer: true,
}],
}),
filename: "cloud-config.security-updates.yml",
mergeType: "dict(recurse_array,no_replace)+list(append)",
},
],
},
);

View File

@@ -2,10 +2,11 @@
"name": "cloud-init.multi-part",
"main": "index.ts",
"devDependencies": {
"@types/node": "^18"
"@types/node": "^18",
"typescript": "^5.0.0"
},
"dependencies": {
"@pulumi/cloudinit": "^1.4.1",
"typescript": "^5.0.0"
"@pulumi/cloudinit": "^1.4.6",
"yaml": "^2.5.1"
}
}
}