Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions reference-architectures/nat-gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Reference Architecture: NAT Gateway for DOKS and Droplet

This example provisions:
- A **VPC** and **NAT Gateway**
- A **DOKS** cluster with the **Routing Agent** enabled
- A **Droplet** that routes egress via the NAT Gateway
- A **Route** CRD that overrides `0.0.0.0/0` to the NAT Gateway **gateway IP** (private VPC IP)

## Prerequisites
- terraform, doctl, kubectl installed
- `export DIGITALOCEAN_ACCESS_TOKEN="YOUR_DO_PAT"`
- `doctl auth init --access-token "$DIGITALOCEAN_ACCESS_TOKEN"`

## Apply (two modules)
```bash
# 1) Infra
terraform -chdir=terraform/1-infra init
terraform -chdir=terraform/1-infra apply -auto-approve

# 2) Route CRD
terraform -chdir=terraform/2-routes init
terraform -chdir=terraform/2-routes apply \
-var="cluster_id=$(terraform -chdir=../1-infra output -raw cluster_id)" \
-var="nat_gateway_gateway_ip=$(terraform -chdir=../1-infra output -raw nat_gateway_gateway_ip)" \
-auto-approve
69 changes: 69 additions & 0 deletions reference-architectures/nat-gateway/terraform/1-infra/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
terraform {
required_version = ">= 1.6.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.43"
}
}
}

provider "digitalocean" {
token = var.do_token != null ? var.do_token : ""
}

resource "digitalocean_vpc" "this" {
name = var.vpc_name
region = var.region
ip_range = "10.20.0.0/16"
}

resource "digitalocean_vpc_nat_gateway" "this" {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @CODEBRAKERBOYY thanks for contribution.

This resource config doesn't match the digitalocean_vpc_nat_gateway resource schema. The schema looks like this:

resource "digitalocean_vpc_nat_gateway" "my-vpc-nat-gateway" {
  name   = "terraform-example"
  type   = "PUBLIC"
  region = "nyc3"
  size   = "1"
  vpcs {
    vpc_uuid = digitalocean_vpc.my-vpc.id
  }
  udp_timeout_seconds  = 30
  icmp_timeout_seconds = 30
  tcp_timeout_seconds  = 30
}

https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/vpc_nat_gateway

I'm curious if found the schema you used in other docs or where the one you used came from.

region = var.region
vpc_id = digitalocean_vpc.this.id
name = var.nat_gateway_name
ip_reserve = true
}

resource "digitalocean_kubernetes_cluster" "this" {
name = var.cluster_name
region = var.region
version = var.k8s_version
vpc_uuid = digitalocean_vpc.this.id

routing_agent {
enabled = true
}

node_pool {
name = "np-default"
size = var.node_size
node_count = var.node_count
}
}

locals {
cloud_init = <<-CLOUD
#cloud-config
package_update: false
package_upgrade: false
runcmd:
- original_gw=$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/gateway || true)
- if [ -n "$original_gw" ]; then ip route add 169.254.169.254 via $${original_gw} dev eth0 || true; fi
- ip route replace default via ${digitalocean_vpc_nat_gateway.this.gateway_address}
- netplan apply || true
CLOUD
}

resource "digitalocean_droplet" "this" {
name = var.droplet_name
region = var.region
size = var.droplet_size
image = var.droplet_image
vpc_uuid = digitalocean_vpc.this.id
user_data = local.cloud_init
backups = false
ipv6 = false
monitoring = true
tags = ["ra-nat-example"]
}
17 changes: 17 additions & 0 deletions reference-architectures/nat-gateway/terraform/1-infra/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
output "vpc_id" {
value = digitalocean_vpc.this.id
}

output "nat_gateway_gateway_ip" {
description = "Private VPC gateway IP; use in Route and Droplet default route."
value = digitalocean_vpc_nat_gateway.this.gateway_address
}

output "nat_gateway_public_ip" {
description = "Static public egress IP for verification."
value = digitalocean_vpc_nat_gateway.this.ip_address
}

output "cluster_id" { value = digitalocean_kubernetes_cluster.this.id }
output "cluster_name" { value = digitalocean_kubernetes_cluster.this.name }
output "droplet_private_ip" { value = digitalocean_droplet.this.ipv4_address_private }
11 changes: 11 additions & 0 deletions reference-architectures/nat-gateway/terraform/1-infra/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "do_token" { type = string, sensitive = true, default = null }
variable "region" { type = string default = "sfo3" }
variable "vpc_name" { type = string default = "ra-nat-vpc" }
variable "nat_gateway_name"{ type = string default = "ra-nat-gw" }
variable "cluster_name" { type = string default = "ra-nat-doks" }
variable "k8s_version" { type = string default = "1.30.4-do.0" }
variable "node_size" { type = string default = "s-2vcpu-4gb" }
variable "node_count" { type = number default = 1 }
variable "droplet_name" { type = string default = "ra-nat-droplet" }
variable "droplet_size" { type = string default = "s-1vcpu-1gb" }
variable "droplet_image" { type = string default = "ubuntu-22-04-x64" }
37 changes: 37 additions & 0 deletions reference-architectures/nat-gateway/terraform/2-routes/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
terraform {
required_version = ">= 1.6.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.43"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.29"
}
}
}

provider "digitalocean" {}

data "digitalocean_kubernetes_cluster" "this" {
id = var.cluster_id
}

provider "kubernetes" {
host = data.digitalocean_kubernetes_cluster.this.endpoint
cluster_ca_certificate = base64decode(data.digitalocean_kubernetes_cluster.this.kube_config[0].cluster_ca_certificate)
token = data.digitalocean_kubernetes_cluster.this.kube_config[0].token
}

resource "kubernetes_manifest" "default_egress_via_nat" {
manifest = {
apiVersion = "networking.doks.digitalocean.com/v1alpha1"
kind = "Route"
metadata = { name = "default-egress-via-nat" }
spec = {
destinations = ["0.0.0.0/0"]
gateways = [var.nat_gateway_gateway_ip]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "route_name" {
value = kubernetes_manifest.default_egress_via_nat.object.metadata.name
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
variable "cluster_id" { type = string }
variable "nat_gateway_gateway_ip" { type = string }