mirror of
https://gitea.com/mcereda/oam.git
synced 2026-02-08 21:34:25 +00:00
feat(evidently): dump collected notes
This commit is contained in:
76
examples/cloud-init/evidently-ui.s3.yml
Normal file
76
examples/cloud-init/evidently-ui.s3.yml
Normal 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'
|
||||||
@@ -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
|
|
||||||
@@ -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://.
|
|
||||||
@@ -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",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
encryptionsalt: v1:pAW9r3TNSV0=:v1:WNIhJ97VgKWPVLTi:IypZBR+LreWLhasHqWGTNCuabKf3Ew==
|
||||||
|
config:
|
||||||
|
aws:region: eu-west-1
|
||||||
@@ -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
|
||||||
@@ -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'
|
||||||
@@ -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
|
||||||
|
*/
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "ec2-instance",
|
"name": "evidently-ui-on-ec2-via-alb",
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18",
|
"@types/node": "^18",
|
||||||
@@ -8,6 +8,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pulumi/aws": "^6.0.0",
|
"@pulumi/aws": "^6.0.0",
|
||||||
"@pulumi/cloudinit": "^1.0.0",
|
"@pulumi/cloudinit": "^1.0.0",
|
||||||
"yaml": "^2.0.0"
|
"fs": "^0.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as cloudinit from "@pulumi/cloudinit";
|
import * as cloudinit from "@pulumi/cloudinit";
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import * as yaml from 'yaml';
|
||||||
|
|
||||||
export const userData = new cloudinit.Config(
|
export const userData = new cloudinit.Config(
|
||||||
"userData",
|
"userData",
|
||||||
@@ -18,6 +19,22 @@ export const userData = new cloudinit.Config(
|
|||||||
filename: "cloud-config.docker.yml",
|
filename: "cloud-config.docker.yml",
|
||||||
mergeType: "dict(recurse_array,no_replace)+list(append)",
|
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)",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
"name": "cloud-init.multi-part",
|
"name": "cloud-init.multi-part",
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18"
|
"@types/node": "^18",
|
||||||
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pulumi/cloudinit": "^1.4.1",
|
"@pulumi/cloudinit": "^1.4.6",
|
||||||
"typescript": "^5.0.0"
|
"yaml": "^2.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
97
knowledge base/evidently.md
Normal file
97
knowledge base/evidently.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Evidently
|
||||||
|
|
||||||
|
AI observability tool written in [Python].
|
||||||
|
|
||||||
|
1. [TL;DR](#tldr)
|
||||||
|
1. [ML monitoring dashboard](#ml-monitoring-dashboard)
|
||||||
|
1. [Remote snapshot storage](#remote-snapshot-storage)
|
||||||
|
1. [Collector](#collector)
|
||||||
|
1. [Further readings](#further-readings)
|
||||||
|
1. [Sources](#sources)
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Setup</summary>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip install 'evidently'
|
||||||
|
pip install 'evidently[llm]' 's3fs' 'tracely'
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Usage</summary>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
evidently ui
|
||||||
|
evidently ui … --host='0.0.0.0' --port='8080'
|
||||||
|
FSSPEC_S3_KEY='AKIA2HKHF74L0123ABCD' FSSPEC_S3_SECRET='jlc/m1sO…' evidently ui … --workspace='s3://bucket/prefix'
|
||||||
|
|
||||||
|
evidently collector
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## ML monitoring dashboard
|
||||||
|
|
||||||
|
Visualizes ML system performance over time and allows for issues detection.
|
||||||
|
|
||||||
|
Available as cloud service or self-hosted.<br/>
|
||||||
|
Evidently Cloud offers extra features (e.g. user authentication and roles, built-in alerting, no-code interface).
|
||||||
|
|
||||||
|
### Remote snapshot storage
|
||||||
|
|
||||||
|
One can store snapshots in a remote data store such as S3 buckets.<br/>
|
||||||
|
The Monitoring UI service will interface with it to read the snapshots' data.
|
||||||
|
|
||||||
|
Evidently connects to data stores using [`fsspec`][fsspec].<br/>
|
||||||
|
It allows accessing data on remote file systems via standard Python interfaces
|
||||||
|
([built-in implementations][fsspec built-in implementations], [other implementations][fsspec other implementations]).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip install 'evidently[llm]' 's3fs'
|
||||||
|
evidently ui --host='0.0.0.0' --port='8000' --workspace='s3://bucket/prefix'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Collector
|
||||||
|
|
||||||
|
```sh
|
||||||
|
evidently collector
|
||||||
|
```
|
||||||
|
|
||||||
|
## Further readings
|
||||||
|
|
||||||
|
- [Website]
|
||||||
|
- [Main repository]
|
||||||
|
- [Docker image]
|
||||||
|
- [Python]
|
||||||
|
- [`fsspec`][fsspec]
|
||||||
|
- [ML observability course]
|
||||||
|
|
||||||
|
### Sources
|
||||||
|
|
||||||
|
- [Documentation]
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Reference
|
||||||
|
═╬═Time══
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- In-article sections -->
|
||||||
|
<!-- Knowledge base -->
|
||||||
|
[python]: python.md
|
||||||
|
|
||||||
|
<!-- Files -->
|
||||||
|
<!-- Upstream -->
|
||||||
|
[docker image]: https://hub.docker.com/r/evidently/evidently-service
|
||||||
|
[documentation]: https://docs.evidentlyai.com/
|
||||||
|
[main repository]: https://github.com/evidentlyai/evidently
|
||||||
|
[ml observability course]: https://learn.evidentlyai.com/
|
||||||
|
[website]: https://www.evidentlyai.com/
|
||||||
|
|
||||||
|
<!-- Others -->
|
||||||
|
[fsspec built-in implementations]: https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations
|
||||||
|
[fsspec other implementations]: https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations
|
||||||
|
[fsspec]: https://filesystem-spec.readthedocs.io/en/latest/
|
||||||
@@ -8,6 +8,25 @@ Package installer for Python.
|
|||||||
|
|
||||||
## TL;DR
|
## TL;DR
|
||||||
|
|
||||||
|
Stores the cache in:
|
||||||
|
|
||||||
|
- `$XDG_CACHE_HOME` (default: `~/.cache/pip`) on Linux.
|
||||||
|
- `~/Library/Caches/pip` on Mac OS X.
|
||||||
|
- `%LocalAppData%\pip\Cache` on Windows.
|
||||||
|
|
||||||
|
pip will also respect XDG_CACHE_HOME.
|
||||||
|
|
||||||
|
Creates **temporary** files to unpack/build/doOtherStuff to packages, then deletes them after installation.<br/>
|
||||||
|
It checks, in order, if any of the directories used by `tempfile.gettempdir()` **exists**, and is **readable** and
|
||||||
|
**writable**; the first one matching all the requirements is used.<br/>
|
||||||
|
The directory priority at the time of writing is as follows:
|
||||||
|
|
||||||
|
1. `$TMPDIR`
|
||||||
|
1. `$TEMP`
|
||||||
|
1. `$TMP`
|
||||||
|
1. `C:\TEMP`, `C:\TMP`, `\TEMP`, and `\TMP` on Windows; `/tmp`, `/var/tmp`, and `/usr/tmp` on any other platform.
|
||||||
|
1. The current working directory
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Install packages.
|
# Install packages.
|
||||||
pip install 'yamllint'
|
pip install 'yamllint'
|
||||||
@@ -26,6 +45,23 @@ pip install --requirement <(pip freeze | sed 's/==/>=/') --upgrade
|
|||||||
# Generate a list of the outdated packages.
|
# Generate a list of the outdated packages.
|
||||||
pip list --outdated
|
pip list --outdated
|
||||||
|
|
||||||
|
# Get the currently configured cache directory.
|
||||||
|
pip cache dir
|
||||||
|
|
||||||
|
# Provide an overview of the contents of the cache.
|
||||||
|
pip cache info
|
||||||
|
|
||||||
|
# List files from the 'wheel' cache.
|
||||||
|
pip cache list
|
||||||
|
pip cache list 'ansible'
|
||||||
|
|
||||||
|
# Removes files from the 'wheel' cache.
|
||||||
|
# Files from the 'HTTP' cache are left untouched at this time.
|
||||||
|
pip cache remove 'setuptools'
|
||||||
|
|
||||||
|
# Clear all files from the 'wheel' and 'HTTP' caches.
|
||||||
|
pip cache purge
|
||||||
|
|
||||||
# Remove orphaned dependencies.
|
# Remove orphaned dependencies.
|
||||||
# Requires `pip-autoremove`.
|
# Requires `pip-autoremove`.
|
||||||
pip-autoremove
|
pip-autoremove
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# Python
|
# Python
|
||||||
|
|
||||||
## Table of contents <!-- omit in toc -->
|
|
||||||
|
|
||||||
1. [TL;DR](#tldr)
|
1. [TL;DR](#tldr)
|
||||||
1. [Dictionaries](#dictionaries)
|
1. [Dictionaries](#dictionaries)
|
||||||
1. [F-strings](#f-strings)
|
1. [F-strings](#f-strings)
|
||||||
@@ -235,7 +233,8 @@ All the references in the [further readings] section, plus the following:
|
|||||||
- [Click]
|
- [Click]
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
References
|
Reference
|
||||||
|
═╬═Time══
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- Upstream -->
|
<!-- Upstream -->
|
||||||
|
|||||||
@@ -122,6 +122,13 @@ aws ec2 describe-instances --output 'text' \
|
|||||||
|
|
||||||
watch -n '1' aws ec2 describe-instances --instance-ids 'i-0123456789abcdef0' --query 'Reservations[].Instances[].[State,StateTransitionReason]'
|
watch -n '1' aws ec2 describe-instances --instance-ids 'i-0123456789abcdef0' --query 'Reservations[].Instances[].[State,StateTransitionReason]'
|
||||||
|
|
||||||
|
# Retrieve the security credentials for an IAM role named 's3access'
|
||||||
|
# IMDSv2
|
||||||
|
TOKEN=$(curl -X PUT 'http://169.254.169.254/latest/api/token' -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600') \
|
||||||
|
&& curl -H "X-aws-ec2-metadata-token: ${TOKEN}" 'http://169.254.169.254/latest/meta-data/iam/security-credentials/s3access'
|
||||||
|
# IMDSv1
|
||||||
|
curl 'http://169.254.169.254/latest/meta-data/iam/security-credentials/s3access'
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
# ECR
|
# ECR
|
||||||
|
|||||||
35
snippets/pip.sh
Normal file
35
snippets/pip.sh
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Install packages
|
||||||
|
pip install 'yamllint'
|
||||||
|
pip install --user 'ansible==10.1.0'
|
||||||
|
pip install -U --require-virtualenv -r 'requirements.txt' --no-cache-dir
|
||||||
|
|
||||||
|
# Upgrade packages
|
||||||
|
pip install -U 'pip'
|
||||||
|
|
||||||
|
# Upgrade the included `pip` executable on Mac OS X
|
||||||
|
~/Library/Python/3.8/bin/pip3 install --user --upgrade 'pip'
|
||||||
|
|
||||||
|
# Upgrade all currently installed packages
|
||||||
|
pip install --requirement <(pip freeze | sed 's/==/>=/') --upgrade
|
||||||
|
|
||||||
|
# Generate a list of the outdated packages
|
||||||
|
pip list --outdated
|
||||||
|
|
||||||
|
# Get the currently configured cache directory
|
||||||
|
pip cache dir
|
||||||
|
|
||||||
|
# Provide an overview of the contents of the cache
|
||||||
|
pip cache info
|
||||||
|
|
||||||
|
# List files from the 'wheel' cache
|
||||||
|
pip cache list
|
||||||
|
pip cache list 'ansible'
|
||||||
|
|
||||||
|
# Removes files from the 'wheel' cache
|
||||||
|
# Files from the 'HTTP' cache are left untouched at this time
|
||||||
|
pip cache remove 'setuptools'
|
||||||
|
|
||||||
|
# Clear all files from the 'wheel' and 'HTTP' caches
|
||||||
|
pip cache purge
|
||||||
63
snippets/pulumi/aws/get existing resources information.ts
Normal file
63
snippets/pulumi/aws/get existing resources information.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import * as aws from "@pulumi/aws";
|
||||||
|
import * as pulumi from "@pulumi/pulumi";
|
||||||
|
|
||||||
|
const ec2Ami_amazonLinux2023_arm64_available_latest_output: pulumi.Output<aws.ec2.GetAmiResult> = aws.ec2.getAmiOutput({
|
||||||
|
owners: [ "amazon", ],
|
||||||
|
nameRegex: "^al2023-ami-2023.*",
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "architecture",
|
||||||
|
values: [ "arm64" ],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "state",
|
||||||
|
values: [ "available" ],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mostRecent: true,
|
||||||
|
});
|
||||||
|
const ec2KeyPair_output: pulumi.Output<aws.ec2.GetKeyPairResult> = aws.ec2.getKeyPairOutput({ keyName: "john-ec2KeyPair" });
|
||||||
|
const ec2Subnet: pulumi.Output<aws.ec2.GetSubnetResult> = aws.ec2.getSubnetOutput({
|
||||||
|
filters: [{
|
||||||
|
name: "tag:Name",
|
||||||
|
values: [ "Private Subnet in AZ C" ],
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
pulumi.all([
|
||||||
|
ec2Ami_amazonLinux2023_arm64_available_latest_output,
|
||||||
|
ec2KeyPair_output,
|
||||||
|
ec2Subnet,
|
||||||
|
]).apply( ([ ami, keyPair, subnet ]) => console.log(`${ami.arn}, ${keyPair.arn}, ${subnet.arn}`) );
|
||||||
|
|
||||||
|
|
||||||
|
const s3bucket_output = aws.s3.getBucketOutput({ bucket: "some-bucket" });
|
||||||
|
s3bucket_output.arn.apply(
|
||||||
|
(bucketArn: aws.ARN) => new aws.iam.Role(
|
||||||
|
"bucket",
|
||||||
|
{
|
||||||
|
name: "BucketInstanceRole",
|
||||||
|
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}/*`,
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user