Skip to content

Commit

Permalink
[ENH]: DO deployment blueprint (#1171)
Browse files Browse the repository at this point in the history
## Description of changes

*Summarize the changes made by this PR.*
 - Improvements & Bug fixes
	 - Full deployment of a Chroma on a DO droplet with attached volume

## Test plan
*How are these changes tested?*

Manually tested.

## Documentation Changes

Docs are in the deployment README.md
  • Loading branch information
tazarov authored Oct 2, 2023
1 parent 2dcffca commit 0d67509
Show file tree
Hide file tree
Showing 4 changed files with 475 additions and 0 deletions.
53 changes: 53 additions & 0 deletions examples/deployments/common/startup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#! /bin/bash

# Note: This is run as root

cd ~
export enable_auth="${enable_auth}"
export basic_auth_credentials="${basic_auth_credentials}"
export auth_type="${auth_type}"
export token_auth_credentials="${token_auth_credentials}"
apt-get update -y
apt-get install -y ca-certificates curl gnupg lsb-release
mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update -y
chmod a+r /etc/apt/keyrings/docker.gpg
apt-get update -y
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin git
usermod -aG docker ubuntu
git clone https://github.com/chroma-core/chroma.git && cd chroma
git fetch --tags
git checkout tags/${chroma_release}

if [ "$${enable_auth}" = "true" ] && [ "$${auth_type}" = "basic" ] && [ ! -z "$${basic_auth_credentials}" ]; then
username=$(echo $basic_auth_credentials | cut -d: -f1)
password=$(echo $basic_auth_credentials | cut -d: -f2)
docker run --rm --entrypoint htpasswd httpd:2 -Bbn $username $password > server.htpasswd
cat <<EOF > .env
CHROMA_SERVER_AUTH_CREDENTIALS_FILE="/chroma/server.htpasswd"
CHROMA_SERVER_AUTH_CREDENTIALS_PROVIDER='chromadb.auth.providers.HtpasswdFileServerAuthCredentialsProvider'
CHROMA_SERVER_AUTH_PROVIDER='chromadb.auth.basic.BasicAuthServerProvider'
EOF
fi

if [ "$${enable_auth}" = "true" ] && [ "$${auth_type}" = "token" ] && [ ! -z "$${token_auth_credentials}" ]; then
cat <<EOF > .env
CHROMA_SERVER_AUTH_CREDENTIALS="$${token_auth_credentials}" \
CHROMA_SERVER_AUTH_CREDENTIALS_PROVIDER='chromadb.auth.token.TokenConfigServerAuthCredentialsProvider'
CHROMA_SERVER_AUTH_PROVIDER='chromadb.auth.token.TokenAuthServerProvider'
EOF
fi

cat <<EOF > docker-compose.override.yaml
version: '3.8'
services:
server:
volumes:
- /chroma-data:/chroma/chroma
EOF

COMPOSE_PROJECT_NAME=chroma docker compose up -d --build
163 changes: 163 additions & 0 deletions examples/deployments/do-terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Digital Ocean Droplet Deployment

This is an example deployment using Digital Ocean Droplet using [terraform](https://www.terraform.io/).

This deployment will do the following:

- 🔥 Create a firewall with required ports open (22 and 8000)
- 🐳 Create Droplet with Ubuntu 22 and deploy Chroma using docker compose
- 💿 Create a data volume for Chroma data
- 🗻 Mount the data volume to the Droplet instance
- ✏️ Format the data volume with ext4
- 🏃‍ Start Chroma

## Requirements

- [Terraform CLI v1.3.4+](https://developer.hashicorp.com/terraform/tutorials/gcp-get-started/install-cli)

## Deployment with terraform

This deployment uses Ubuntu 22 as foundation, but you'd like to use a different image for your Droplet (
see https://slugs.do-api.dev/ for a list of available images)

### Configuration Options


### 1. Init your terraform state

```bash
terraform init
```

### 2. Deploy your application

Generate SSH key to use with your chroma instance (so you can log in to the Droplet):

> Note: This is optional. You can use your own existing SSH key if you prefer.
```bash
ssh-keygen -t RSA -b 4096 -C "Chroma DO Key" -N "" -f ./chroma-do && chmod 400 ./chroma-do
```

Set up your Terraform variables and deploy your instance:

```bash
#take note of this as it must be present in all of the subsequent steps
export TF_VAR_do_token=<DIGITALOCEAN_TOKEN>
#path to the public key you generated above (or can be different if you want to use your own key)
export TF_ssh_public_key="./chroma-do.pub"
#path to the private key you generated above (or can be different if you want to use your own key) - used for formatting the Chroma data volume
export TF_ssh_private_key="./chroma-do"
#set the chroma release to deploy
export TF_VAR_chroma_release="0.4.12"
# DO region to deploy the chroma instance to
export TF_VAR_region="ams2"
#enable public access to the chroma instance on port 8000
export TF_VAR_public_access="true"
#enable basic auth for the chroma instance
export TF_VAR_enable_auth="true"
#The auth type to use for the chroma instance (token or basic)
export TF_VAR_auth_type="token"
terraform apply -auto-approve
```

> Note: Basic Auth is supported by Chroma v0.4.7+
### 4. Check your public IP and that Chroma is running

Get the public IP of your instance

```bash
terraform output instance_public_ip
```

Check that chroma is running (It should take up several minutes for the instance to be ready)

```bash
export instance_public_ip=$(terraform output instance_public_ip | sed 's/"//g')
curl -v http://$instance_public_ip:8000/api/v1/heartbeat
```

#### 4.1 Checking Auth

##### Token

When token auth is enabled you can check the get the credentials from Terraform state by running:

```bash
terraform output chroma_auth_token
```

You should see something of the form:

```bash
PVcQ4qUUnmahXwUgAf3UuYZoMlos6MnF
```

You can then export these credentials:

```bash
export CHROMA_AUTH=$(terraform output chroma_auth_token | sed 's/"//g')
```

Using the credentials:

```bash
curl -v http://$instance_public_ip:8000/api/v1/collections -H "Authorization: Bearer ${CHROMA_AUTH}"
```

##### Basic

When basic auth is enabled you can check the get the credentials from Terraform state by running:

```bash
terraform output chroma_auth_basic
```

You should see something of the form:

```bash
chroma:VuA8I}QyNrm0@QLq
```

You can then export these credentials:

```bash
export CHROMA_AUTH=$(terraform output chroma_auth_basic | sed 's/"//g')
```

Using the credentials:

```bash
curl -v http://$instance_public_ip:8000/api/v1/collections -u "${CHROMA_AUTH}"
```

> Note: Without `-u` you should be getting 401 Unauthorized response
#### 4.2 SSH to your instance

To SSH to your instance:

```bash
ssh -i ./chroma-do root@$instance_public_ip
```

### 5. Destroy your Chroma instance

```bash
terraform destroy -auto-approve
```

## Extras

You can visualize your infrastructure with:

```bash
terraform graph | dot -Tsvg > graph.svg
```

> Note: You will need graphviz installed for this to work
### Digital Ocean Resource Types

Refs: https://slugs.do-api.dev/
133 changes: 133 additions & 0 deletions examples/deployments/do-terraform/chroma.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}

# Define provider
variable "do_token" {}

# Configure the DigitalOcean Provider
provider "digitalocean" {
token = var.do_token
}


resource "digitalocean_firewall" "chroma_firewall" {
name = "chroma-firewall"

droplet_ids = [digitalocean_droplet.chroma_instance.id]

inbound_rule {
protocol = "tcp"
port_range = "22"
source_addresses = var.mgmt_source_ranges
}

dynamic "inbound_rule" {
for_each = var.public_access ? [1] : []
content {
protocol = "tcp"
port_range = var.chroma_port
source_addresses = var.source_ranges
}
}

outbound_rule {
protocol = "tcp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}

outbound_rule {
protocol = "icmp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}

outbound_rule {
protocol = "udp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}

tags = local.tags

}

resource "digitalocean_ssh_key" "chroma_keypair" {
name = "chroma_keypair"
public_key = file(var.ssh_public_key)
}


#Create Droplet
resource "digitalocean_droplet" "chroma_instance" {
image = var.instance_image
name = "chroma"
region = var.region
size = var.instance_type
ssh_keys = [digitalocean_ssh_key.chroma_keypair.fingerprint]

user_data = data.template_file.user_data.rendered

tags = local.tags
}


resource "digitalocean_volume" "chroma_volume" {
region = digitalocean_droplet.chroma_instance.region
name = "chroma-volume"
size = var.chroma_data_volume_size
description = "Chroma data volume"
tags = local.tags
}

resource "digitalocean_volume_attachment" "chroma_data_volume_attachment" {
droplet_id = digitalocean_droplet.chroma_instance.id
volume_id = digitalocean_volume.chroma_volume.id

provisioner "remote-exec" {
inline = [
"export VOLUME_ID=${digitalocean_volume.chroma_volume.name} && sudo mkfs -t ext4 /dev/$(lsblk -o +SERIAL | grep $VOLUME_ID | awk '{print $1}')",
"sudo mkdir /chroma-data",
"export VOLUME_ID=${digitalocean_volume.chroma_volume.name} && sudo mount /dev/$(lsblk -o +SERIAL | grep $VOLUME_ID | awk '{print $1}') /chroma-data",
"cat <<EOF | sudo tee /etc/fstab >> /dev/null",
"/dev/disk/by-id/scsi-0DO_Volume_${digitalocean_volume.chroma_volume.name} /chroma-data ext4 defaults,nofail,discard 0 0",
"EOF",
]

connection {
host = digitalocean_droplet.chroma_instance.ipv4_address
type = "ssh"
user = "root"
private_key = file(var.ssh_private_key)
}
}
}


output "instance_public_ip" {
value = digitalocean_droplet.chroma_instance.ipv4_address
description = "The public IP address of the Chroma instance"
}

output "instance_private_ip" {
value = digitalocean_droplet.chroma_instance.ipv4_address_private
description = "The private IP address of the Chroma instance"
}

output "chroma_auth_token" {
description = "The Chroma static auth token"
value = random_password.chroma_token.result
sensitive = true
}

output "chroma_auth_basic" {
description = "The Chroma basic auth credentials"
value = "${local.basic_auth_credentials.username}:${local.basic_auth_credentials.password}"
sensitive = true
}
Loading

0 comments on commit 0d67509

Please sign in to comment.