Skip to content

Commit

Permalink
feat: update deployment with terraform
Browse files Browse the repository at this point in the history
  • Loading branch information
iqbalpa committed Jul 31, 2024
1 parent 38bf840 commit 158698e
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 23 deletions.
120 changes: 97 additions & 23 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: Deploy to Google Cloud VM with Docker
name: Deploy Dockerized Nest.js App to GCP with Terraform Provisioning

on:
push:
branches:
- feat/docker
- feat/deployment
paths-ignore:
- README.md
- .gitignore
Expand All @@ -15,13 +15,14 @@ on:

jobs:
publish:
name: 'Publish'
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Docker Hub Authentication
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Expand All @@ -38,42 +39,115 @@ jobs:
- name: Push Docker Image to Docker Hub
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ vars.DOCKER_IMAGE_NAME }}:latest


terraform:
name: 'Terraform'
runs-on: ubuntu-latest
outputs:
external_ip: ${{ steps.store.outputs.vm_external_ip }}
defaults:
run:
shell: bash

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Google Auth
uses: 'google-github-actions/auth@v2'
with:
credentials_json: '${{ secrets.GOOGLE_CREDENTIALS }}'

- name: Set up Cloud SDK
uses: 'google-github-actions/setup-gcloud@v2'

- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}

- name: Create Terraform Variables
id: vars
run: |
cat > terraform.tfvars <<EOF
project_id="${{ secrets.GCP_PROJECT_ID }}"
ssh_pub_key="${{ secrets.SSH_PUBLIC_KEY }}"
ssh_username="${{ secrets.GCP_USERNAME }}"
domain_name="${{ secrets.DOMAIN_NAME }}"
dns_record_name="${{ secrets.DNS_RECORD_NAME }}"
managed_zone_name="${{ secrets.MANAGED_ZONE_NAME }}"
EOF
- name: Terraform Init
run: terraform init

- name: Terraform Format
run: terraform fmt

- name: Terraform Plan
run: terraform plan -out=plan

- name: Terraform Apply
run: terraform apply "plan"

- name: Store Terraform Output
run: |
EXTERNAL_IP=$(terraform-bin output -raw vm_external_ip)
echo "VM_EXTERNAL_IP=$EXTERNAL_IP" >> $GITHUB_ENV
echo "not env: $EXTERNAL_IP"
echo "env: ${{ env.VM_EXTERNAL_IP }}"
- name: Echo Terraform Output
id: store
env:
EXTERNAL_IP: ${{ env.VM_EXTERNAL_IP }}
run: |
echo "env directly: ${{ env.VM_EXTERNAL_IP }}"
echo "env variable: $EXTERNAL_IP"
echo "vm_external_ip=$EXTERNAL_IP" >> "$GITHUB_OUTPUT"
- name: Terraform State
run: terraform show

# - name: Terraform Destroy
# if: ${{ always() }}
# run: terraform destroy -auto-approve


deploy:
name: 'Deploy'
runs-on: ubuntu-latest
needs: publish
needs: [publish, terraform]
steps:
- name: Set up Cloud CLI
uses: google-github-actions/setup-gcloud@v0.3.0
- name: Google Auth
uses: 'google-github-actions/auth@v2'
with:
project_id: ${{ secrets.GCE_PROJECT_ID }}
service_account_key: ${{ secrets.GCP_CREDENTIALS }}
export_default_credentials: true
credentials_json: '${{ secrets.GOOGLE_CREDENTIALS }}'

- name: Access gcloud CLI
run: |
gcloud auth configure-docker
- name: Set up Cloud SDK
uses: 'google-github-actions/setup-gcloud@v2'

- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Copy docker-compose.yml into GCP Instance
uses: appleboy/scp-action@master
with:
host: ${{ secrets.GCP_VM_IP }}
username: ${{ secrets.GCP_USER }}
key: ${{ secrets.GCP_SSH_PRIVATE_KEY }}
host: ${{ needs.terraform.outputs.external_ip }}
username: ${{ secrets.GCP_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: 'docker-compose.yml'
target: '/home/${{ secrets.GCP_USER }}/'
target: '/home/${{ secrets.GCP_USERNAME }}/'

- name: SSH into GCP and Run Docker Container
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.GCP_VM_IP }}
username: ${{ secrets.GCP_USER }}
key: ${{ secrets.GCP_SSH_PRIVATE_KEY }}
host: ${{ needs.terraform.outputs.external_ip }}
username: ${{ secrets.GCP_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
echo "DATABASE_URL=postgresql://${{ vars.DATABASE_USERNAME }}:${{ vars.DATABASE_PASSWORD }}@${{ vars.DATABASE_HOST }}:5432/${{ vars.DATABASE_NAME }}?schema=public" >> .env
sed -i -e 's/iqbalpa\/backend-movies/${{ secrets.DOCKERHUB_USERNAME }}\/${{ vars.DOCKER_IMAGE_NAME }}/g' docker-compose.yml
docker compose -f /home/${{ secrets.GCP_USER }}/docker-compose.yml down
docker compose -f /home/${{ secrets.GCP_USER }}/docker-compose.yml up --pull always -d
docker compose -f /home/${{ secrets.GCP_USERNAME }}/docker-compose.yml down
docker compose -f /home/${{ secrets.GCP_USERNAME }}/docker-compose.yml up --pull always -d
docker image prune -f -a
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,19 @@ pids

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Terraform
**/.terraform/*
*.tfstate
*.tfstate.*
crash.log
crash.*.log
*.tfvars
*.tfvars.json
override.tf
override.tf.json
*_override.tf
*_override.tf.json
.terraform.tfstate.lock.info
.terraformrc
terraform.rc
109 changes: 109 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "4.51.0"
}
}
}

provider "google" {
project = var.project_id
region = var.region
zone = var.zone
}

# Create VPC Network
resource "google_compute_network" "vpc_network" {
name = var.vpc_network
}

# Create VM Instance
resource "google_compute_instance" "vm_instance" {
name = var.vm_instance
machine_type = var.machine_type
tags = [
"allow-ssh",
"allow-http",
"allow-https",
"allow-3000"
]

# Add ssh keys
metadata = {
ssh-keys = "${var.ssh_username}:${var.ssh_pub_key}"
}

# Operating System
boot_disk {
initialize_params {
image = var.boot_disk
}
}

# Network setting
network_interface {
network = google_compute_network.vpc_network.name
access_config {
nat_ip = google_compute_address.static.address
}
}
}

# Create Firewall Rules
resource "google_compute_firewall" "ssh-rule" {
name = "allow-ssh"
network = google_compute_network.vpc_network.name
allow {
protocol = "tcp"
ports = ["22"]
}
target_tags = ["allow-ssh"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "http-rule" {
name = "allow-http"
network = google_compute_network.vpc_network.name
allow {
protocol = "tcp"
ports = ["80"]
}
target_tags = ["allow-http"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "https-rule" {
name = "allow-https"
network = google_compute_network.vpc_network.name
allow {
protocol = "tcp"
ports = ["443"]
}
target_tags = ["allow-https"]
source_ranges = ["0.0.0.0/0"]
}
resource "google_compute_firewall" "port-3000-rule" {
name = "allow-3000"
network = google_compute_network.vpc_network.name
allow {
protocol = "tcp"
ports = ["3000"]
}
target_tags = ["allow-3000"]
source_ranges = ["0.0.0.0/0"]
}

# Reserve Static IP Address
resource "google_compute_address" "static" {
name = "ipv4-address"
}

# Create DNS Record
resource "google_dns_record_set" "terraform-learn" {
name = "${var.dns_record_name}.${var.domain_name}"
type = "A"
ttl = 300

managed_zone = var.managed_zone_name

rrdatas = [google_compute_instance.vm_instance.network_interface[0].access_config[0].nat_ip]
}
7 changes: 7 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "vm_internal_ip" {
value = google_compute_instance.vm_instance.network_interface.0.network_ip
}

output "vm_external_ip" {
value = google_compute_instance.vm_instance.network_interface.0.access_config.0.nat_ip
}
53 changes: 53 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
variable "project_id" {
type = string
}

variable "region" {
type = string
default = "us-west1"
}

variable "zone" {
type = string
default = "us-west1-a"
}

variable "vpc_network" {
type = string
default = "movies-catalog-network"
}

variable "vm_instance" {
type = string
default = "movies-catalog-instance"
}

variable "machine_type" {
type = string
default = "f1-micro"
}

variable "ssh_username" {
type = string
}

variable "ssh_pub_key" {
type = string
}

variable "boot_disk" {
type = string
default = "cos-cloud/cos-113-lts"
}

variable "domain_name" {
type = string
}

variable "dns_record_name" {
type = string
}

variable "managed_zone_name" {
type = string
}

0 comments on commit 158698e

Please sign in to comment.