Skip to content

Commit f01d36e

Browse files
committed
Add a Terraform configuration to deploy lnt.llvm.org
This patch adds a Terraform configuration file that should allow deploying to an EC2 instance. It requires a few secrets to be made available to Github Actions.
1 parent 7995809 commit f01d36e

File tree

7 files changed

+281
-0
lines changed

7 files changed

+281
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Deploy lnt.llvm.org
2+
3+
on:
4+
push:
5+
branches: ['main']
6+
paths:
7+
- '.github/workflows/deploy-lnt.llvm.org.yaml'
8+
- 'deployment/*'
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
deploy:
15+
runs-on: ubuntu-24.04
16+
17+
steps:
18+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
19+
20+
- name: Setup Terraform
21+
uses: hashicorp/setup-terraform@v3
22+
23+
- name: Configure AWS Credentials
24+
uses: aws-actions/configure-aws-credentials@v4
25+
with:
26+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
27+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
28+
29+
- name: Initialize Terraform
30+
run: terraform -chdir=deployment init
31+
32+
- name: Apply Terraform changes
33+
run: terraform -chdir=deployment apply -auto-approve
34+
env:
35+
TF_VAR_lnt_db_password: ${{ secrets.LNT_DB_PASSWORD }}
36+
TF_VAR_lnt_auth_token: ${{ secrets.LNT_AUTH_TOKEN }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
.tox/
44
/llvm_lnt.egg-info
55
build
6+
deployment/.terraform
67
dist
78
docs/_build
89
lnt/server/ui/static/docs

deployment/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This directory contains configuration files to deploy lnt.llvm.org.
2+
3+
The https://lnt.llvm.org instance gets re-deployed automatically whenever changes
4+
are made to the configuration files under `deployment/` on the `main` branch via
5+
a Github Action. Manually deploying the instance is also possible by directly using
6+
Terraform:
7+
8+
```bash
9+
aws configure # provide appropriate access keys
10+
terraform -chdir=deployment init
11+
terraform -chdir=deployment plan # to see what will be done
12+
terraform -chdir=deployment apply
13+
```

deployment/compose.env.tpl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
LNT_DB_PASSWORD=${__db_password__}
2+
LNT_AUTH_TOKEN=${__auth_token__}
3+
LNT_IMAGE=${__lnt_image__}
4+
LNT_HOST_PORT=${__lnt_host_port__}

deployment/ec2-startup.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/bin/bash
2+
3+
#
4+
# This is the startup script that gets executed when the EC2 instance running lnt.llvm.org
5+
# is brought up. This script references some files under /etc/lnt that are put into place
6+
# by cloud-init, which is specified in the Terraform configuration file.
7+
#
8+
9+
set -e
10+
11+
echo "Installing docker"
12+
sudo yum update -y
13+
sudo yum install -y docker
14+
docker --version
15+
16+
echo "Installing docker compose"
17+
sudo mkdir -p /usr/local/lib/docker/cli-plugins
18+
sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m) \
19+
-o /usr/local/lib/docker/cli-plugins/docker-compose
20+
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
21+
docker compose version
22+
23+
echo "Starting the Docker service"
24+
sudo service docker start
25+
sudo systemctl enable docker # also ensure the Docker service starts on reboot
26+
27+
if ! lsblk --output FSTYPE -f /dev/sdh | grep --quiet ext4; then
28+
echo "Formatting /dev/sdh -- this is a new EBS volume"
29+
sudo mkfs -t ext4 /dev/sdh
30+
else
31+
echo "/dev/sdh already contains a filesystem -- reusing previous EBS volume"
32+
fi
33+
34+
echo "Mounting EBS volume with persistent information at /persistent-state"
35+
sudo mkdir /persistent-state
36+
sudo mount /dev/sdh /persistent-state
37+
38+
echo "Creating folders to map volumes in the Docker container to locations on the EC2 instance"
39+
sudo mkdir -p /persistent-state/var/lib/lnt
40+
(cd /var/lib && ln -s /persistent-state/var/lib/lnt lnt)
41+
sudo mkdir -p /persistent-state/var/lib/postgresql
42+
(cd /var/lib && ln -s /persistent-state/var/lib/postgresql postgresql)
43+
sudo mkdir -p /var/log/lnt # logs are not persisted
44+
45+
echo "Starting LNT service with Docker compose"
46+
sudo docker compose --file /etc/lnt/compose.yaml \
47+
--file /etc/lnt/ec2-volume-mapping.yaml \
48+
--env-file /etc/lnt/compose.env \
49+
up --detach

deployment/ec2-volume-mapping.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#
2+
# This file maps volumes in the Docker container to actual devices on the EC2 instance.
3+
# We basically bind the volumes inside the Docker image to the corresponding location
4+
# on the EC2 instance for ease of access.
5+
#
6+
7+
volumes:
8+
instance:
9+
driver: local
10+
driver_opts:
11+
o: bind
12+
type: none
13+
device: /var/lib/lnt
14+
15+
logs:
16+
driver: local
17+
driver_opts:
18+
o: bind
19+
type: none
20+
device: /var/log/lnt
21+
22+
database:
23+
driver: local
24+
driver_opts:
25+
o: bind
26+
type: none
27+
device: /var/lib/postgresql

deployment/main.tf

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#
2+
# Terraform file for deploying lnt.llvm.org.
3+
#
4+
5+
variable "lnt_db_password" {
6+
type = string
7+
description = "The database password for the lnt.llvm.org database."
8+
sensitive = true
9+
}
10+
11+
variable "lnt_auth_token" {
12+
type = string
13+
description = "The authentication token to perform destructive operations on lnt.llvm.org."
14+
sensitive = true
15+
}
16+
17+
locals {
18+
lnt_image = "d9ffa5317a9a42a1d2fa337cba97ec51d931f391"
19+
lnt_host_port = "80"
20+
}
21+
22+
terraform {
23+
backend "s3" {
24+
bucket = "lnt.llvm.org-test-bucket" # TODO: Adjust this for the real LLVM Foundation account
25+
key = "terraform.tfstate"
26+
region = "us-west-2"
27+
encrypt = true
28+
}
29+
}
30+
31+
locals {
32+
availability_zone = "us-west-2a"
33+
}
34+
35+
provider "aws" {
36+
region = "us-west-2"
37+
}
38+
39+
#
40+
# Setup the EC2 instance
41+
#
42+
data "aws_ami" "amazon_linux_2023" {
43+
most_recent = true
44+
owners = ["amazon"]
45+
46+
filter {
47+
name = "name"
48+
values = ["al2023-ami-ecs-hvm-*-kernel-*-x86_64"]
49+
}
50+
}
51+
52+
data "cloudinit_config" "startup_scripts" {
53+
base64_encode = true
54+
55+
part {
56+
filename = "ec2-startup.sh"
57+
content_type = "text/x-shellscript"
58+
content = file("${path.module}/ec2-startup.sh")
59+
}
60+
61+
part {
62+
content_type = "text/cloud-config"
63+
content = yamlencode({
64+
write_files = [
65+
{
66+
path = "/etc/lnt/compose.yaml"
67+
permissions = "0400" # read-only for owner
68+
content = file("${path.module}/../docker/compose.yaml")
69+
},
70+
{
71+
path = "/etc/lnt/ec2-volume-mapping.yaml"
72+
permissions = "0400" # read-only for owner
73+
content = file("${path.module}/ec2-volume-mapping.yaml")
74+
},
75+
{
76+
path = "/etc/lnt/compose.env"
77+
permissions = "0400" # read-only for owner
78+
content = templatefile("${path.module}/compose.env.tpl", {
79+
__db_password__ = var.lnt_db_password,
80+
__auth_token__ = var.lnt_auth_token,
81+
__lnt_image__ = local.lnt_image,
82+
__lnt_host_port__ = local.lnt_host_port,
83+
})
84+
}
85+
]
86+
})
87+
}
88+
}
89+
90+
resource "aws_security_group" "server" {
91+
name = "lnt.llvm.org/server-security-group"
92+
description = "Allow SSH and HTTP traffic"
93+
94+
ingress {
95+
description = "Allow incoming SSH traffic from anywhere"
96+
from_port = 22
97+
to_port = 22
98+
protocol = "tcp"
99+
cidr_blocks = ["0.0.0.0/0"]
100+
}
101+
102+
ingress {
103+
description = "Allow incoming HTTP traffic from anywhere"
104+
from_port = 80
105+
to_port = 80
106+
protocol = "tcp"
107+
cidr_blocks = ["0.0.0.0/0"]
108+
}
109+
110+
egress {
111+
description = "Allow outgoing traffic to anywhere"
112+
from_port = 0
113+
to_port = 0
114+
protocol = "-1"
115+
cidr_blocks = ["0.0.0.0/0"]
116+
}
117+
}
118+
119+
resource "aws_instance" "server" {
120+
ami = data.aws_ami.amazon_linux_2023.id
121+
availability_zone = local.availability_zone
122+
instance_type = "t2.micro" # TODO: Adjust the size of the real instance
123+
associate_public_ip_address = true
124+
security_groups = [aws_security_group.server.name]
125+
tags = {
126+
Name = "lnt.llvm.org/server"
127+
}
128+
129+
user_data_base64 = data.cloudinit_config.startup_scripts.rendered
130+
}
131+
132+
#
133+
# Setup the EBS volume attached to the instance that stores the DB
134+
# and other instance-related configuration (e.g. the schema files,
135+
# profiles and anything else that should persist).
136+
#
137+
resource "aws_ebs_volume" "persistent_state" {
138+
availability_zone = local.availability_zone
139+
# TODO: Put a real size once we're ready to go to production
140+
size = 20 # GiB
141+
type = "gp2"
142+
tags = {
143+
Name = "lnt.llvm.org/persistent-state"
144+
}
145+
}
146+
147+
resource "aws_volume_attachment" "persistent_state_attachment" {
148+
instance_id = aws_instance.server.id
149+
volume_id = aws_ebs_volume.persistent_state.id
150+
device_name = "/dev/sdh"
151+
}

0 commit comments

Comments
 (0)