Skip to content

Commit c591f0b

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 c591f0b

File tree

7 files changed

+284
-0
lines changed

7 files changed

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

0 commit comments

Comments
 (0)