Added an example of a Nginx reverse proxy cluster using RMIGs.

This commit is contained in:
Taneli Leppä 2022-10-04 13:39:10 +02:00
parent 981b4ca5cb
commit 4feb3514fd
19 changed files with 774 additions and 18 deletions

View File

@ -0,0 +1,28 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM marketplace.gcr.io/google/debian11
RUN apt-get update && apt-get dist-upgrade -y && apt-get install -y curl gnupg2
RUN curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh
RUN bash add-google-cloud-ops-agent-repo.sh --also-install
RUN rm -f add-google-cloud-ops-agent-repo.sh
RUN echo '#!/bin/bash' > /entrypoint.sh
RUN echo 'cd /tmp' >> /entrypoint.sh
RUN echo '/opt/google-cloud-ops-agent/libexec/google_cloud_ops_agent_engine -service=otel -in /etc/google-cloud-ops-agent/config.yaml' >> /entrypoint.sh
RUN echo '/opt/google-cloud-ops-agent/subagents/opentelemetry-collector/otelopscol --config=/tmp/otel.yaml --feature-gates=exporter.googlecloud.OTLPDirect' >> /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT /entrypoint.sh
CMD []

View File

@ -0,0 +1,45 @@
# Nginx-based reverse proxy cluster
This blueprint shows how to deploy an autoscaling reverse proxy cluster using Nginx, based on regional
Managed Instance Groups.
![High-level diagram](reverse-proxy.png "High-level diagram")
The autoscaling is driven by Nginx current connections metric, sent by Cloud Ops Agent.
The example is for Nginx, but it could be easily adapted to any other reverse proxy software (eg.
Squid, Varnish, etc).
## Ops Agent image
There is a simple [`Dockerfile`](Dockerfile) available for building Ops Agent to be run
inside the ContainerOS instance. Build the container, push it to your Container/Artifact
Repository and set the `ops_agent_image` to point to the image you built.
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [autoscaling_metric](variables.tf#L31) | | <code title="object&#40;&#123;&#10; name &#61; string&#10; single_instance_assignment &#61; number&#10; target &#61; number&#10; type &#61; string &#35; GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE&#10; filter &#61; string&#10;&#125;&#41;&#10;&#10;&#10;default &#61; &#123;&#10; name &#61; &#34;workload.googleapis.com&#47;nginx.connections_current&#34;&#10; single_instance_assignment &#61; null&#10; target &#61; 10 &#35; Target 10 connections per instance, just for demonstration purposes&#10; type &#61; &#34;GAUGE&#34;&#10; filter &#61; null&#10;&#125;">object&#40;&#123;&#8230;&#125;</code> | ✓ | |
| [project_name](variables.tf#L106) | Name of an existing project or of the new project | <code>string</code> | ✓ | |
| [autoscaling](variables.tf#L17) | Autoscaling configuration for the instance group. | <code title="object&#40;&#123;&#10; min_replicas &#61; number&#10; max_replicas &#61; number&#10; cooldown_period &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; min_replicas &#61; 1&#10; max_replicas &#61; 10&#10; cooldown_period &#61; 30&#10;&#125;">&#123;&#8230;&#125;</code> |
| [backends](variables.tf#L49) | Nginx locations configurations to proxy traffic to. | <code>string</code> | | <code title="&#34;&#60;&#60;-EOT&#10; location &#47; &#123;&#10; proxy_pass http:&#47;&#47;10.0.16.13:80;&#10; &#125;&#10;EOT&#34;">&#34;&#60;&#60;-EOT&#8230;EOT&#34;</code> |
| [cidrs](variables.tf#L59) | Subnet IP CIDR ranges. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gce &#61; &#34;10.0.16.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [network](variables.tf#L67) | Network name. | <code>string</code> | | <code>&#34;reverse-proxy-vpc&#34;</code> |
| [network_create](variables.tf#L73) | Create network or use existing one. | <code>bool</code> | | <code>true</code> |
| [nginx_image](variables.tf#L79) | Nginx container image to use. | <code>string</code> | | <code>&#34;gcr.io&#47;cloud-marketplace&#47;google&#47;nginx1:latest&#34;</code> |
| [ops_agent_image](variables.tf#L85) | Google Cloud Ops Agent container image to use. | <code>string</code> | | <code>&#34;gcr.io&#47;sfans-hub-project-d647&#47;ops-agent:latest&#34;</code> |
| [prefix](variables.tf#L91) | Prefix used for resources that need unique names. | <code>string</code> | | <code>&#34;&#34;</code> |
| [project_create](variables.tf#L97) | Parameters for the creation of the new project | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [region](variables.tf#L111) | Default region for resources. | <code>string</code> | | <code>&#34;europe-west4&#34;</code> |
| [subnetwork](variables.tf#L117) | Subnetwork name. | <code>string</code> | | <code>&#34;gce&#34;</code> |
| [tls](variables.tf#L123) | Also offer reverse proxying with TLS (self-signed certificate). | <code>bool</code> | | <code>false</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [load_balancer_url](outputs.tf#L17) | Load balancer for the reverse proxy instance group. | |
<!-- END TFDOC -->

View File

@ -0,0 +1,403 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
locals {
monitoring_agent_unit = <<-EOT
[Unit]
Description=Start monitoring agent container
After=gcr-online.target docker.socket
Wants=gcr-online.target docker.socket docker-events-collector.service
[Service]
Environment="HOME=/home/opsagent"
ExecStartPre=/usr/bin/docker-credential-gcr configure-docker
ExecStart=/usr/bin/docker run --rm --name=monitoring-agent \
--log-driver=gcplogs \
--network host \
-v /etc/google-cloud-ops-agent/config.yaml:/etc/google-cloud-ops-agent/config.yaml \
${var.ops_agent_image}
ExecStop=/usr/bin/docker stop monitoring-agent
EOT
monitoring_agent_config = <<-EOT
logging:
service:
pipelines:
default_pipeline:
receivers: []
metrics:
receivers:
hostmetrics:
type: hostmetrics
nginx:
type: nginx
collection_interval: 10s
stub_status_url: http://localhost/healthz
service:
pipelines:
default_pipeline:
receivers:
- hostmetrics
- nginx
EOT
nginx_config = <<-EOT
server {
listen 80;
server_name HOSTNAME localhost;
%{if var.tls}
listen 443 ssl;
ssl_certificate /etc/ssl/self-signed.crt;
ssl_certificate_key /etc/ssl/self-signed.key;
%{endif}
keepalive_timeout 650s;
keepalive_requests 10000;
proxy_connect_timeout 60s;
proxy_read_timeout 5m;
proxy_send_timeout 5m;
error_log stderr;
access_log /dev/stdout combined;
set_real_ip_from ${module.xlb.ip_address}/32;
set_real_ip_from 35.191.0.0/16;
set_real_ip_from 130.211.0.0/22;
real_ip_header X-Forwarded-For;
real_ip_recursive off;
location /healthz {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 35.191.0.0/16;
allow 130.211.0.0/22;
deny all;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
${var.backends}
}
EOT
nginx_files = {
"/etc/systemd/system/monitoring-agent.service" = {
content = local.monitoring_agent_unit
owner = "root"
permissions = "0644"
}
"/etc/nginx/conf.d/default.conf" = {
content = local.nginx_config
owner = "root"
permissions = "0644"
}
"/etc/google-cloud-ops-agent/config.yaml" = {
content = local.monitoring_agent_config
owner = "root"
permissions = "0644"
}
}
users = [
{
username = "opsagent"
uid = 2001
}
]
}
module "project" {
source = "../../../modules/project"
billing_account = (var.project_create != null
? var.project_create.billing_account_id
: null
)
name = var.project_name
parent = (var.project_create != null
? var.project_create.parent
: null
)
services = [
"cloudresourcemanager.googleapis.com",
"compute.googleapis.com",
"iam.googleapis.com",
"logging.googleapis.com",
"monitoring.googleapis.com",
]
project_create = var.project_create != null
}
module "vpc" {
source = "../../../modules/net-vpc"
project_id = module.project.project_id
name = var.network
subnets = [
{
name = var.subnetwork
ip_cidr_range = var.cidrs[var.subnetwork]
region = var.region
secondary_ip_range = null
},
]
vpc_create = var.network_create
}
module "firewall" {
source = "../../../modules/net-vpc-firewall"
project_id = module.project.project_id
network = module.vpc.name
custom_rules = {
format("%sallow-http-to-proxy-cluster", var.prefix) = {
description = "Allow Nginx HTTP(S) ingress traffic"
direction = "INGRESS"
action = "allow"
sources = []
ranges = [var.cidrs[var.subnetwork], "35.191.0.0/16", "130.211.0.0/22"]
targets = [module.service-account-proxy.email]
use_service_accounts = true
rules = [{ protocol = "tcp", ports = [80, 443] }]
extra_attributes = {}
}
format("%sallow-iap-ssh", var.prefix) = {
description = "Allow Nginx SSH traffic from IAP"
direction = "INGRESS"
action = "allow"
sources = []
ranges = ["35.235.240.0/20"]
targets = [module.service-account-proxy.email]
use_service_accounts = true
rules = [{ protocol = "tcp", ports = [22] }]
extra_attributes = {}
}
}
}
module "nat" {
source = "../../../modules/net-cloudnat"
project_id = module.project.project_id
region = var.region
name = format("%snat", var.prefix)
router_network = module.vpc.name
config_source_subnets = "LIST_OF_SUBNETWORKS"
logging_filter = "ALL"
config_min_ports_per_vm = 4000
subnetworks = [
{
self_link = module.vpc.subnet_self_links[format("%s/%s", var.region, var.subnetwork)]
config_source_ranges = ["ALL_IP_RANGES"]
secondary_ranges = null
}
]
}
###############################################################################
# Proxy resources #
###############################################################################
module "service-account-proxy" {
source = "../../../modules/iam-service-account"
project_id = module.project.project_id
name = format("%sreverse-proxy", var.prefix)
iam_project_roles = {
(module.project.project_id) = [
"roles/logging.logWriter",
"roles/monitoring.metricWriter",
"roles/storage.objectViewer", // For pulling the Ops Agent image
]
}
}
module "cos-nginx" {
count = !var.tls ? 1 : 0
source = "../../../modules/cloud-config-container/nginx"
image = var.nginx_image
files = local.nginx_files
users = local.users
runcmd_pre = ["sed -i \"s/HOSTNAME/$${HOSTNAME}/\" /etc/nginx/conf.d/default.conf"]
runcmd_post = ["systemctl start monitoring-agent"]
}
module "cos-nginx-tls" {
count = var.tls ? 1 : 0
source = "../../../modules/cloud-config-container/nginx-tls"
nginx_image = var.nginx_image
files = local.nginx_files
users = local.users
runcmd_post = ["systemctl start monitoring-agent"]
}
module "mig-proxy" {
source = "../../../modules/compute-mig"
project_id = module.project.project_id
location = var.region
regional = true
name = format("%sproxy-cluster", var.prefix)
named_ports = {
http = "80"
https = "443"
}
autoscaler_config = var.autoscaling == null ? null : {
min_replicas = var.autoscaling.min_replicas
max_replicas = var.autoscaling.max_replicas
cooldown_period = var.autoscaling.cooldown_period
cpu_utilization_target = null
load_balancing_utilization_target = null
metric = var.autoscaling_metric
}
update_policy = {
type = "PROACTIVE"
minimal_action = "REPLACE"
min_ready_sec = 60
max_surge_type = "fixed"
max_surge = 3
max_unavailable_type = null
max_unavailable = null
}
default_version = {
instance_template = module.proxy-vm.template.self_link
name = "proxy-vm"
}
health_check_config = {
type = "http"
check = {
port = 80
request_path = "/healthz"
}
config = {
check_interval_sec = 10
healthy_threshold = 1
unhealthy_threshold = 1
timeout_sec = 10
}
logging = true
}
auto_healing_policies = {
health_check = module.mig-proxy.health_check.self_link
initial_delay_sec = 60
}
}
module "proxy-vm" {
source = "../../../modules/compute-vm"
project_id = module.project.project_id
zone = format("%s-c", var.region)
name = "nginx-test-vm"
instance_type = "e2-standard-2"
tags = ["proxy-cluster"]
network_interfaces = [{
network = module.vpc.self_link
subnetwork = module.vpc.subnet_self_links[format("%s/%s", var.region, var.subnetwork)]
nat = false
addresses = null
}]
boot_disk = {
image = "projects/cos-cloud/global/images/family/cos-stable"
type = "pd-ssd"
size = 10
}
create_template = true
metadata = {
user-data = !var.tls ? module.cos-nginx.0.cloud_config : module.cos-nginx-tls.0.cloud_config
}
service_account = module.service-account-proxy.email
service_account_create = false
}
module "xlb" {
source = "../../../modules/net-glb"
name = format("%sreverse-proxy-xlb", var.prefix)
project_id = module.project.project_id
reserve_ip_address = true
health_checks_config = {
format("%sreverse-proxy-hc", var.prefix) = {
type = "http"
logging = false
options = {
check_interval_sec = 10
healthy_threshold = 1
unhealthy_threshold = 1
timeout_sec = 10
}
check = {
port_specification = "USE_NAMED_PORT"
port_name = "http"
request_path = "/healthz"
}
}
}
backend_services_config = {
format("%sreverse-proxy-backend", var.prefix) = {
bucket_config = null
enable_cdn = false
cdn_config = null
group_config = {
backends = [
{
group = module.mig-proxy.group_manager.instance_group
options = null
}
]
health_checks = [format("%sreverse-proxy-hc", var.prefix)]
log_config = null
options = {
affinity_cookie_ttl_sec = null
custom_request_headers = null
custom_response_headers = null
connection_draining_timeout_sec = null
load_balancing_scheme = null
locality_lb_policy = null
port_name = !var.tls ? "http" : "https"
protocol = !var.tls ? "HTTP" : "HTTPS"
security_policy = null
session_affinity = null
timeout_sec = null
circuits_breakers = null
consistent_hash = null
iap = null
}
}
}
}
}

View File

@ -0,0 +1,20 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
output "load_balancer_url" {
description = "Load balancer for the reverse proxy instance group."
value = !var.tls ? format("http://%s/", module.xlb.ip_address) : format("https://%s/", module.xlb.ip_address)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

View File

@ -0,0 +1,130 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "autoscaling" {
description = "Autoscaling configuration for the instance group."
type = object({
min_replicas = number
max_replicas = number
cooldown_period = number
})
default = {
min_replicas = 1
max_replicas = 10
cooldown_period = 30
}
}
variable "autoscaling_metric" {
type = object({
name = string
single_instance_assignment = number
target = number
type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE
filter = string
})
default = {
name = "workload.googleapis.com/nginx.connections_current"
single_instance_assignment = null
target = 10 # Target 10 connections per instance, just for demonstration purposes
type = "GAUGE"
filter = null
}
}
variable "backends" {
description = "Nginx locations configurations to proxy traffic to."
type = string
default = <<-EOT
location / {
proxy_pass http://10.0.16.58:80;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
EOT
}
variable "cidrs" {
description = "Subnet IP CIDR ranges."
type = map(string)
default = {
gce = "10.0.16.0/24"
}
}
variable "network" {
description = "Network name."
type = string
default = "reverse-proxy-vpc"
}
variable "network_create" {
description = "Create network or use existing one."
type = bool
default = true
}
variable "nginx_image" {
description = "Nginx container image to use."
type = string
default = "gcr.io/cloud-marketplace/google/nginx1:latest"
}
variable "ops_agent_image" {
description = "Google Cloud Ops Agent container image to use."
type = string
default = "gcr.io/sfans-hub-project-d647/ops-agent:latest"
}
variable "prefix" {
description = "Prefix used for resources that need unique names."
type = string
default = ""
}
variable "project_create" {
description = "Parameters for the creation of the new project"
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "project_name" {
description = "Name of an existing project or of the new project"
type = string
}
variable "region" {
description = "Default region for resources."
type = string
default = "europe-west4"
}
variable "subnetwork" {
description = "Subnetwork name."
type = string
default = "gce"
}
variable "tls" {
description = "Also offer reverse proxying with TLS (self-signed certificate)."
type = bool
default = false
}

View File

@ -0,0 +1,29 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
terraform {
required_version = ">= 1.3.0"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 4.32.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 4.32.0" # tftest
}
}
}

View File

@ -64,7 +64,7 @@ module "cos-envoy" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [container_image](variables.tf#L42) | Container image. | <code>string</code> | ✓ | |
| [authenticate_gcr](variables.tf#L118) | Setup docker to pull images from private GCR. Requires at least one user since the token is stored in the home of the first user defined. | <code>bool</code> | | <code>false</code> |
| [authenticate_gcr](variables.tf#L124) | Setup docker to pull images from private GCR. Requires at least one user since the token is stored in the home of the first user defined. | <code>bool</code> | | <code>false</code> |
| [boot_commands](variables.tf#L17) | List of cloud-init `bootcmd`s. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [cloud_config](variables.tf#L23) | Cloud config template path. If provided, takes precedence over all other arguments. | <code>string</code> | | <code>null</code> |
| [config_variables](variables.tf#L29) | Additional variables used to render the template passed via `cloud_config`. | <code>map&#40;any&#41;</code> | | <code>&#123;&#125;</code> |
@ -76,6 +76,7 @@ module "cos-envoy" {
| [file_defaults](variables.tf#L74) | Default owner and permissions for files. | <code title="object&#40;&#123;&#10; owner &#61; string&#10; permissions &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; owner &#61; &#34;root&#34;&#10; permissions &#61; &#34;0644&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [files](variables.tf#L86) | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | <code title="map&#40;object&#40;&#123;&#10; content &#61; string&#10; owner &#61; string&#10; permissions &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [gcp_logging](variables.tf#L96) | Should container logs be sent to Google Cloud Logging. | <code>bool</code> | | <code>true</code> |
| [run_as_first_user](variables.tf#L118) | Run as the first user if users are specified. | <code>bool</code> | | <code>true</code> |
| [run_commands](variables.tf#L102) | List of cloud-init `runcmd`s. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [users](variables.tf#L108) | List of usernames to be created. If provided, first user will be used to run the container. | <code title="list&#40;object&#40;&#123;&#10; username &#61; string,&#10; uid &#61; number,&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#91;&#10;&#93;">&#91;&#8230;&#93;</code> |

View File

@ -49,7 +49,7 @@ write_files:
ExecStartPre=/usr/bin/docker-credential-gcr configure-docker
%{~ endif ~}
ExecStart=/usr/bin/docker run --rm --name=${container_name} \
%{~ if length(users) > 0 ~}
%{~ if length(users) > 0 && run_as_first_user ~}
--user=${users[0].uid} \
%{~ endif ~}
%{~ if docker_logging ~}

View File

@ -28,6 +28,7 @@ locals {
run_commands = var.run_commands
users = var.users
authenticate_gcr = var.authenticate_gcr
run_as_first_user = var.run_as_first_user
}))
files = {
for path, attrs in var.files : path => {

View File

@ -115,6 +115,12 @@ variable "users" {
]
}
variable "run_as_first_user" {
description = "Run as the first user if users are specified."
type = bool
default = true
}
variable "authenticate_gcr" {
description = "Setup docker to pull images from private GCR. Requires at least one user since the token is stored in the home of the first user defined."
type = bool

View File

@ -50,7 +50,11 @@ module "vm-nginx-tls" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [docker_logging](variables.tf#L23) | Log via the Docker gcplogs driver. Disable if you use the legacy Logging Agent instead. | <code>bool</code> | | <code>true</code> |
| [files](variables.tf#L41) | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | <code title="map&#40;object&#40;&#123;&#10; content &#61; string&#10; owner &#61; string&#10; permissions &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [nginx_image](variables.tf#L17) | Nginx container image to use. | <code>string</code> | | <code>&#34;nginx:1.23.1&#34;</code> |
| [runcmd_post](variables.tf#L35) | Extra commands to run after starting nginx. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [runcmd_pre](variables.tf#L29) | Extra commands to run before starting nginx. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [users](variables.tf#L51) | Additional list of usernames to be created. | <code title="list&#40;object&#40;&#123;&#10; username &#61; string,&#10; uid &#61; number,&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#91;&#10;&#93;">&#91;&#8230;&#93;</code> |
## Outputs

View File

@ -16,4 +16,5 @@
FQDN=$(curl -s -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/hostname)
HOSTNAME=$(echo $FQDN | cut -d"." -f1)
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj /CN=$HOSTNAME/ -addext "subjectAltName = DNS:$FQDN" -keyout /etc/ssl/self-signed.key -out /etc/ssl/self-signed.crt
chgrp nginx /etc/ssl/self-signed.key -out /etc/ssl/self-signed.crt
sed -i "s/HOSTNAME/${HOSTNAME}/" /etc/nginx/conf.d/default.conf

View File

@ -14,9 +14,34 @@
* limitations under the License.
*/
locals {
default_files = {
"/var/run/nginx/customize.sh" = {
content = file("${path.module}/files/customize.sh")
owner = "root"
permissions = "0744"
}
"/etc/nginx/conf.d/default.conf" = {
content = file("${path.module}/files/default.conf")
owner = "root"
permissions = "0644"
}
}
files = var.files != null ? merge(local.default_files, var.files) : local.default_files
}
module "cos-envoy-td" {
source = "../cos-generic-metadata"
authenticate_gcr = true
users = concat([
{
username = "nginx"
uid = 2000
}
], var.users)
run_as_first_user = false
boot_commands = [
"systemctl start node-problem-detector",
]
@ -32,27 +57,16 @@ module "cos-envoy-td" {
docker_args = "--network host --pid host"
files = {
"/var/run/nginx/customize.sh" = {
content = file("${path.module}/files/customize.sh")
owner = "root"
permissions = "0744"
}
"/etc/nginx/conf.d/default.conf" = {
content = file("${path.module}/files/default.conf")
owner = "root"
permissions = "0644"
}
}
files = local.files
gcp_logging = var.docker_logging
run_commands = [
run_commands = concat(var.runcmd_pre, [
"iptables -I INPUT 1 -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT",
"iptables -I INPUT 1 -p tcp -m tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT",
"/var/run/nginx/customize.sh",
"systemctl daemon-reload",
"systemctl start nginx",
]
], var.runcmd_post)
}

View File

@ -25,3 +25,37 @@ variable "docker_logging" {
type = bool
default = true
}
variable "runcmd_pre" {
description = "Extra commands to run before starting nginx."
type = list(string)
default = []
}
variable "runcmd_post" {
description = "Extra commands to run after starting nginx."
type = list(string)
default = []
}
variable "files" {
description = "Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null."
type = map(object({
content = string
owner = string
permissions = string
}))
default = null
}
variable "users" {
description = "Additional list of usernames to be created."
type = list(object({
username = string,
uid = number,
}))
default = [
]
}

View File

@ -64,8 +64,11 @@ module "cos-nginx" {
| [files](variables.tf#L59) | Map of extra files to create on the instance, path as key. Owner and permissions will use defaults if null. | <code title="map&#40;object&#40;&#123;&#10; content &#61; string&#10; owner &#61; string&#10; permissions &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [image](variables.tf#L35) | Nginx container image. | <code>string</code> | | <code>&#34;nginxdemos&#47;hello:plain-text&#34;</code> |
| [nginx_config](variables.tf#L41) | Nginx configuration path, if null container default will be used. | <code>string</code> | | <code>null</code> |
| [runcmd_post](variables.tf#L75) | Extra commands to run after starting nginx. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [runcmd_pre](variables.tf#L69) | Extra commands to run before starting nginx. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [test_instance](variables-instance.tf#L17) | Test/development instance attributes, leave null to skip creation. | <code title="object&#40;&#123;&#10; project_id &#61; string&#10; zone &#61; string&#10; name &#61; string&#10; type &#61; string&#10; network &#61; string&#10; subnetwork &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [test_instance_defaults](variables-instance.tf#L30) | Test/development instance defaults used for optional configuration. If image is null, COS stable will be used. | <code title="object&#40;&#123;&#10; disks &#61; map&#40;object&#40;&#123;&#10; read_only &#61; bool&#10; size &#61; number&#10; &#125;&#41;&#41;&#10; image &#61; string&#10; metadata &#61; map&#40;string&#41;&#10; nat &#61; bool&#10; service_account_roles &#61; list&#40;string&#41;&#10; tags &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; disks &#61; &#123;&#125;&#10; image &#61; null&#10; metadata &#61; &#123;&#125;&#10; nat &#61; false&#10; service_account_roles &#61; &#91;&#10; &#34;roles&#47;logging.logWriter&#34;,&#10; &#34;roles&#47;monitoring.metricWriter&#34;&#10; &#93;&#10; tags &#61; &#91;&#34;ssh&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [users](variables.tf#L81) | List of additional usernames to be created. | <code title="list&#40;object&#40;&#123;&#10; username &#61; string,&#10; uid &#61; number,&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#91;&#10;&#93;">&#91;&#8230;&#93;</code> |
## Outputs

View File

@ -20,6 +20,10 @@
users:
- name: nginx
uid: 2000
%{ for user in users }
- name: ${user.username}
uid: ${user.uid}
%{ endfor }
write_files:
- path: /var/lib/docker/daemon.json
@ -52,6 +56,8 @@ write_files:
After=gcr-online.target docker.socket
Wants=gcr-online.target docker.socket docker-events-collector.service
[Service]
Environment="HOME=/home/nginx"
ExecStartPre=/usr/bin/docker-credential-gcr configure-docker
ExecStart=/usr/bin/docker run --rm --name=nginx \
%{~ if docker_logging ~}
--log-driver=gcplogs \
@ -68,13 +74,19 @@ write_files:
owner: ${lookup(data, "owner", "root")}
permissions: ${lookup(data, "permissions", "0644")}
content: |
${indent(4, data.content)}
${indent(6, data.content)}
%{ endfor }
bootcmd:
- systemctl start node-problem-detector
runcmd:
%{ for cmd in runcmd_pre ~}
- ${cmd}
%{ endfor ~}
- iptables -I INPUT 1 -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
- systemctl daemon-reload
- systemctl start nginx
%{ for cmd in runcmd_post ~}
- ${cmd}
%{ endfor ~}

View File

@ -21,13 +21,16 @@ locals {
var.nginx_config != null || length([
for name in keys(var.files) :
name if substr(name, 0, 18) == "/etc/nginx/conf.d/"
]) > 1
]) > 0
)
files = local.files
users = var.users
image = var.image
nginx_config = (var.nginx_config == null ? null : templatefile(
var.nginx_config, var.config_variables
))
runcmd_pre = var.runcmd_pre
runcmd_post = var.runcmd_post
}))
files = {
for path, attrs in var.files : path => {

View File

@ -65,3 +65,25 @@ variable "files" {
}))
default = {}
}
variable "runcmd_pre" {
description = "Extra commands to run before starting nginx."
type = list(string)
default = []
}
variable "runcmd_post" {
description = "Extra commands to run after starting nginx."
type = list(string)
default = []
}
variable "users" {
description = "List of additional usernames to be created."
type = list(object({
username = string,
uid = number,
}))
default = [
]
}