mirror of
https://github.com/Brandon-Rozek/website.git
synced 2024-11-09 18:50:34 -05:00
229 lines
5.4 KiB
Markdown
229 lines
5.4 KiB
Markdown
|
---
|
||
|
title: "Auto-Deploy Docker Applications"
|
||
|
date: 2020-05-09T10:20:34-04:00
|
||
|
draft: false
|
||
|
tags: []
|
||
|
---
|
||
|
|
||
|
This post will combine that last three posts on [Packer](https://brandonrozek.com/blog/snapshotswithpacker/), [Terraform](https://brandonrozek.com/blog/autodeployterraform/), and their [configuration](https://brandonrozek.com/blog/sharedpackerterraformconfig/) to show an entire example of how to deploy a docker-compose application. We will specifically look at deploying a game called [`minetest`](https://www.minetest.net/) on DigitalOcean, but these principles can be adjusted to deploy your application as well. The entire setup is [documented on Github](https://github.com/Brandon-Rozek/minetest-deploy).
|
||
|
|
||
|
## Shared Config
|
||
|
|
||
|
We're going to use a shared configuration between Packer and Terraform. The template goes like this:
|
||
|
|
||
|
```
|
||
|
base_system_image = "ubuntu-20-04-x64"
|
||
|
region = "nyc3"
|
||
|
size = "512mb"
|
||
|
domain = "brandonrozek.com" # Replace
|
||
|
subdomain = "minetest"
|
||
|
|
||
|
# Secrets
|
||
|
do_token = "DO-TOKEN-HERE" # Replace
|
||
|
key_name = "SSH-NAME-ON-DO-HERE" # Replace
|
||
|
```
|
||
|
|
||
|
We'll also need to define the types of these variables in `variables.hcl`
|
||
|
|
||
|
```
|
||
|
variable "do_token" {
|
||
|
type = string
|
||
|
}
|
||
|
|
||
|
variable "base_system_image" {
|
||
|
type = string
|
||
|
}
|
||
|
|
||
|
variable "domain" {
|
||
|
type = string
|
||
|
}
|
||
|
|
||
|
variable "key_name" {
|
||
|
type = string
|
||
|
}
|
||
|
|
||
|
variable "subdomain" {
|
||
|
type = string
|
||
|
}
|
||
|
|
||
|
variable "region" {
|
||
|
type = string
|
||
|
}
|
||
|
|
||
|
variable "size" {
|
||
|
type = string
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Packer
|
||
|
|
||
|
Create a packer directory and setup some symbolic links to the share configuration
|
||
|
|
||
|
```bash
|
||
|
mkdir packer && cd packer
|
||
|
ln -s ../config variables.auto.pkrvars.hcl
|
||
|
ln -s ../variables.hcl variables.pkr.hcl
|
||
|
```
|
||
|
|
||
|
Now let's create a script named `setup.sh` that will run on top of our base image. This will install Docker and setup the firewall to allow SSH and Minetest traffic through.
|
||
|
|
||
|
```bash
|
||
|
#!/bin/bash
|
||
|
apt update
|
||
|
apt upgrade -y
|
||
|
|
||
|
apt install -y docker.io docker-compose
|
||
|
systemctl enable docker-compose
|
||
|
systemctl start docker-compose
|
||
|
|
||
|
ufw allow OpenSSH
|
||
|
# Add any firewall rules you need
|
||
|
# for your application here
|
||
|
ufw allow 30000/udp
|
||
|
ufw --force enable
|
||
|
```
|
||
|
|
||
|
The image that we'll use for Minetest comes from [linuxserverio](https://fleet.linuxserver.io/image?name=linuxserver/minetest). To configure docker-compose we'll need a file named `docker-compose.yml`. Its contents will be highly similar to what is listed on their [Github](https://github.com/linuxserver/docker-minetest/blob/466cde1f2fd38278fe69d33ea3b2f42df50e6b16/README.md).
|
||
|
|
||
|
```yml
|
||
|
version: "2.1"
|
||
|
services:
|
||
|
minetest:
|
||
|
image: linuxserver/minetest
|
||
|
container_name: minetest
|
||
|
environment:
|
||
|
- PUID=1000
|
||
|
- PGID=1000
|
||
|
- TZ=US/Eastern
|
||
|
volumes:
|
||
|
- /volumes/minetest/config/.minetest:/config/.minetest
|
||
|
ports:
|
||
|
- 30000:30000/udp
|
||
|
restart: unless-stopped
|
||
|
```
|
||
|
|
||
|
We'll need to create a `systemd` script called `docker-compose.service` for systemd to [enable docker-compose on startup](https://brandonrozek.com/blog/composesystemd/).
|
||
|
|
||
|
```ini
|
||
|
[Unit]
|
||
|
Description=Docker Compose Application Service
|
||
|
Requires=docker.service
|
||
|
After=docker.service
|
||
|
|
||
|
[Service]
|
||
|
Type=oneshot
|
||
|
RemainAfterExit=yes
|
||
|
WorkingDirectory=/root
|
||
|
ExecStart=/usr/bin/docker-compose up -d
|
||
|
ExecStop=/usr/bin/docker-compose down
|
||
|
TimeoutStartSec=0
|
||
|
|
||
|
[Install]
|
||
|
WantedBy=multi-user.target
|
||
|
```
|
||
|
|
||
|
Finally we'll need to write a packer configuration file `do.pkr.hcl` to create our snapshot.
|
||
|
|
||
|
```
|
||
|
source "digitalocean" "web" {
|
||
|
api_token = var.do_token
|
||
|
image = var.base_system_image
|
||
|
region = var.region
|
||
|
size = var.size
|
||
|
ssh_username = "root"
|
||
|
snapshot_name = "packer-docker"
|
||
|
}
|
||
|
|
||
|
|
||
|
build {
|
||
|
sources = [
|
||
|
"source.digitalocean.web",
|
||
|
]
|
||
|
|
||
|
provisioner "file" {
|
||
|
source = "docker-compose.yml"
|
||
|
destination = "/root/docker-compose.yml"
|
||
|
}
|
||
|
|
||
|
provisioner "file" {
|
||
|
source = "docker-compose.service"
|
||
|
destination = "/etc/systemd/system/docker-compose.service"
|
||
|
}
|
||
|
|
||
|
provisioner "shell" {
|
||
|
scripts = [ "setup.sh" ]
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
To build our image we need to run `packer build .` in the directory with all these files.
|
||
|
|
||
|
## Terraform
|
||
|
|
||
|
Like before, we need to tell terraform where to look for its configuration
|
||
|
|
||
|
```bash
|
||
|
mkdir terraform && cd terraform
|
||
|
ln -s ../config terraform.tfvars
|
||
|
ln -s ../variables.hcl variables.tf
|
||
|
```
|
||
|
|
||
|
To deploy, we only need to create one additional file that will tell digital ocean to create a droplet and assign a subdomain to that droplet.
|
||
|
|
||
|
```
|
||
|
provider "digitalocean" {
|
||
|
token = var.do_token
|
||
|
}
|
||
|
|
||
|
data "digitalocean_ssh_key" laptop {
|
||
|
name = var.key_name
|
||
|
}
|
||
|
|
||
|
data "digitalocean_droplet_snapshot" "packer_snapshot" {
|
||
|
name = "packer-docker"
|
||
|
most_recent = true
|
||
|
}
|
||
|
|
||
|
# Create a droplet
|
||
|
resource "digitalocean_droplet" "web" {
|
||
|
name = "tf-1"
|
||
|
image = data.digitalocean_droplet_snapshot.packer_snapshot.id
|
||
|
region = var.region
|
||
|
size = var.size
|
||
|
ssh_keys = [data.digitalocean_ssh_key.laptop.id]
|
||
|
backups = false
|
||
|
}
|
||
|
|
||
|
# Attach a subdomain
|
||
|
resource "digitalocean_record" "www" {
|
||
|
domain = var.domain
|
||
|
type = "A"
|
||
|
name = var.subdomain
|
||
|
value = digitalocean_droplet.web.ipv4_address
|
||
|
}
|
||
|
|
||
|
output "ip" {
|
||
|
value = digitalocean_droplet.web.ipv4_address
|
||
|
}
|
||
|
|
||
|
output "domain" {
|
||
|
value = "${digitalocean_record.www.name}.${digitalocean_record.www.domain}"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
To deploy run
|
||
|
|
||
|
```bash
|
||
|
terraform apply
|
||
|
```
|
||
|
|
||
|
To later take down the minetest server, run
|
||
|
|
||
|
```bash
|
||
|
terraform destroy
|
||
|
```
|
||
|
|
||
|
## Conclusion
|
||
|
|
||
|
This method can be easily configured to run whichever docker services you'd like. All you have to do is edit the `packer/docker.compose.yml` file and `packer/setup.sh` to setup the firewall rules.
|