diff --git a/README.md b/README.md index 83c17b2..abe35c8 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The following are the important variables you should configure when using this m - `instance_type`: The instance type for the GDB cluster nodes. This should match your performance and cost requirements. -- `node_count`: The number of instances in the cluster. Recommended is 3, 5 or 7 in order to have consensus according to the [Raft algorithm](https://raft.github.io/). +- `node_count`: The number of instances in the cluster. Recommended is 3, 5 or 7 in order to have consensus according to the [Raft algorithm](https://raft.github.io/). @@ -87,7 +87,7 @@ This Terraform module creates AWS Systems Manager (SSM) parameters for managing This Terraform module creates a private hosted zone in Amazon Route 53 for DNS resolution in your Virtual Private Cloud (VPC). #### [IAM](modules/iam/README.md) -This Terraform module creates an AWS Identity and Access Management (IAM) role and an instance profile that can be used for EC2 instances. It also supports the option to set a permissions boundary on the IAM role. +This Terraform module creates an AWS Identity and Access Management (IAM) role and an instance profile that can be used for EC2 instances. It also supports the option to set a permissions boundary on the IAM role. #### [Load Balancer](modules/load_balancer/README.md) This Terraform module sets up an AWS Elastic Load Balancer (Network Load Balancer) with optional TLS listeners. The module is designed to be flexible and customizable by accepting various input variables to tailor the NLB configuration to your specific requirements. diff --git a/modules/user_data/README.md b/modules/user_data/README.md index 6fcf2be..758bcb3 100644 --- a/modules/user_data/README.md +++ b/modules/user_data/README.md @@ -1,113 +1,112 @@ -# Terraform Module: - -This Terraform module configures an AWS EC2 instance for running GraphDB with various optional parameters and user-supplied userdata. - -## Usage: - -To use this module, include it in your Terraform configuration and provide the required and optional variables: -```hcl -module "graphdb_instance" { - source = "path/to/module" - - # Provide required and optional variables - var.aws_region = "us-east-1" - var.resource_name_prefix = "my-graphdb-instance" - var.device_name = "/dev/sdh" - var.backup_schedule = "0 0 * * *" - var.backup_bucket_name = "my-backup-bucket" - var.ebs_volume_type = "gp2" - var.ebs_volume_size = 100 - var.ebs_volume_throughput = 100 - var.ebs_volume_iops = 100 - var.ebs_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/abcd1234" - var.zone_dns_name = "myprivatedns.local" - var.zone_id = "Z1234567890" - var.instance_type = "m5.large" - - # Optional variables - var.user_supplied_userdata_path = "path/to/userdata_script.sh" - var.backup_retention_count = 7 -} -``` - -## Variables: - -### Required Parameters: - -`var.aws_region` (string): AWS region where GraphDB is being deployed. - -`var.resource_name_prefix` (string): Resource name prefix used for tagging and naming AWS resources. - -`var.device_name` (string): The device to which EBS volumes for the GraphDB data directory will be mapped. - -`var.backup_schedule` (string): Cron expression for the backup job. - -`var.backup_bucket_name` (string): Name of the S3 bucket for storing GraphDB backups. - -`var.ebs_volume_type` (string): Type of the EBS volumes used by the GraphDB nodes. - -`var.ebs_volume_size` (number): The size of the EBS volumes used by the GraphDB nodes. - -`var.ebs_volume_throughput` (number): Throughput for the EBS volumes used by the GraphDB nodes. - -`var.ebs_volume_iops` (number): IOPS for the EBS volumes used by the GraphDB nodes. - -`var.ebs_kms_key_arn` (string): KMS key used for EBS volume encryption. - -`var.zone_dns_name` (string): DNS name for the private hosted zone in Route 53. - -`var.zone_id` (string): Route 53 private hosted zone ID. - -`var.instance_type` (string): EC2 instance type. - -### Optional Parameters: - -`var.user_supplied_userdata_path` (string, default: null): File path to custom userdata script supplied by the user. - -`var.backup_retention_count` (number, default: 7): Number of backups to keep. - -## What the Module Creates - -This Terraform module creates an AWS EC2 instance configured for running GraphDB with the following components and settings: - -An EC2 instance with specifications based on the specified var.instance_type. -EBS volumes for the GraphDB data directory with the specified type, size, throughput, IOPS, and encryption using the provided KMS key. -A user data script to initialize and start GraphDB, which can be customized using the var.user_supplied_userdata_path variable or a default template. -A backup job schedule using the specified var.backup_schedule. -Configuration for backing up GraphDB to the specified S3 bucket (var.backup_bucket_name). -Private hosted zone DNS settings for Route 53 using the specified var.zone_dns_name and var.zone_id. -The module combines these components and settings to create a fully configured AWS EC2 instance ready to run GraphDB, with the flexibility to customize various parameters to suit your requirements. - -## Outputs - -The module provides two output values for reference in your Terraform configuration: -`graphdb_userdata_base64_encoded` (string): Base64-encoded user data for the GraphDB instance. -`graphdb_max_memory` (number): Maximum memory for the JVM in GiB, computed based on the EC2 instance type and adjusted for GraphDB's memory requirements. - -## Example - -Here's a complete example that demonstrates how to use the module: - -```hcl -module "graphdb_instance" { - source = "path/to/module" # Replace with the actual source - - var.aws_region = "us-east-1" - var.resource_name_prefix = "my-graphdb-instance" - var.device_name = "/dev/sdh" - var.backup_schedule = "0 0 * * *" - var.backup_bucket_name = "my-backup-bucket" - var.ebs_volume_type = "gp2" - var.ebs_volume_size = 100 - var.ebs_volume_throughput = 100 - var.ebs_volume_iops = 100 - var.ebs_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/abcd1234" - var.zone_dns_name = "myprivatedns.local" - var.zone_id = "Z1234567890" - var.instance_type = "m5.large" - - var.user_supplied_userdata_path = "path/to/userdata_script.sh" - var.backup_retention_count = 7 -} -``` -This example demonstrates how to use the module to configure an AWS EC2 instance for running GraphDB. Adjust the variables as needed for your specific use case. +# Terraform Module: + +This Terraform module configures an AWS EC2 instance for running GraphDB with various optional parameters and user-supplied userdata. + +## Usage: + +To use this module, include it in your Terraform configuration and provide the required and optional variables: +```hcl +module "graphdb_instance" { + source = "path/to/module" + + # Provide required and optional variables + var.aws_region = "us-east-1" + var.resource_name_prefix = "my-graphdb-instance" + var.device_name = "/dev/sdh" + var.backup_schedule = "0 0 * * *" + var.backup_bucket_name = "my-backup-bucket" + var.ebs_volume_type = "gp3" + var.ebs_volume_size = 100 + var.ebs_volume_throughput = 150 + var.ebs_volume_iops = 3000 + var.ebs_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/abcd1234" + var.zone_dns_name = "myprivatedns.local" + var.zone_id = "Z1234567890" + var.instance_type = "m5.large" + + # Optional variables + var.user_supplied_userdata_path = "path/to/userdata_script.sh" + var.backup_retention_count = 7 +} +``` + +## Variables: + +### Required Parameters: + +`var.aws_region` (string): AWS region where GraphDB is being deployed. + +`var.resource_name_prefix` (string): Resource name prefix used for tagging and naming AWS resources. + +`var.device_name` (string): The device to which EBS volumes for the GraphDB data directory will be mapped. + +`var.backup_schedule` (string): Cron expression for the backup job. + +`var.backup_bucket_name` (string): Name of the S3 bucket for storing GraphDB backups. + +`var.ebs_volume_type` (string): Type of the EBS volumes used by the GraphDB nodes. + +`var.ebs_volume_size` (number): The size of the EBS volumes used by the GraphDB nodes. + +`var.ebs_volume_throughput` (number): Throughput for the EBS volumes used by the GraphDB nodes. + +`var.ebs_volume_iops` (number): IOPS for the EBS volumes used by the GraphDB nodes. + +`var.ebs_kms_key_arn` (string): KMS key used for EBS volume encryption. + +`var.zone_dns_name` (string): DNS name for the private hosted zone in Route 53. + +`var.zone_id` (string): Route 53 private hosted zone ID. + +`var.instance_type` (string): EC2 instance type. + +### Optional Parameters: + +`var.user_supplied_userdata_path` (string, default: null): File path to custom userdata script supplied by the user. + +`var.backup_retention_count` (number, default: 7): Number of backups to keep. + +## What the Module Creates + +This Terraform module creates an AWS EC2 instance configured for running GraphDB with the following components and settings: + +An EC2 instance with specifications based on the specified var.instance_type. +EBS volumes for the GraphDB data directory with the specified type, size, throughput, IOPS, and encryption using the provided KMS key. +A user data script to initialize and start GraphDB, which can be customized using the var.user_supplied_userdata_path variable or a default template. +A backup job schedule using the specified var.backup_schedule. +Configuration for backing up GraphDB to the specified S3 bucket (var.backup_bucket_name). +Private hosted zone DNS settings for Route 53 using the specified var.zone_dns_name and var.zone_id. +The module combines these components and settings to create a fully configured AWS EC2 instance ready to run GraphDB, with the flexibility to customize various parameters to suit your requirements. + +## Outputs + +The module provides two output values for reference in your Terraform configuration: +`graphdb_userdata_base64_encoded` (string): Base64-encoded user data for the GraphDB instance. + +## Example + +Here's a complete example that demonstrates how to use the module: + +```hcl +module "graphdb_instance" { + source = "path/to/module" # Replace with the actual source + + var.aws_region = "us-east-1" + var.resource_name_prefix = "my-graphdb-instance" + var.device_name = "/dev/sdh" + var.backup_schedule = "0 0 * * *" + var.backup_bucket_name = "my-backup-bucket" + var.ebs_volume_type = "gp3" + var.ebs_volume_size = 100 + var.ebs_volume_throughput = 150 + var.ebs_volume_iops = 3000 + var.ebs_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/abcd1234" + var.zone_dns_name = "myprivatedns.local" + var.zone_id = "Z1234567890" + var.instance_type = "m5.large" + + var.user_supplied_userdata_path = "path/to/userdata_script.sh" + var.backup_retention_count = 7 +} +``` +This example demonstrates how to use the module to configure an AWS EC2 instance for running GraphDB. Adjust the variables as needed for your specific use case. diff --git a/modules/user_data/main.tf b/modules/user_data/main.tf index 378daa4..1001e39 100644 --- a/modules/user_data/main.tf +++ b/modules/user_data/main.tf @@ -1,33 +1,83 @@ -data "aws_ec2_instance_type" "graphdb" { - instance_type = var.instance_type -} +data "cloudinit_config" "graphdb_user_data" { + base64_encode = true + gzip = true + + part { + content_type = "text/x-shellscript" + content = <<-EOF + #!/bin/bash + set -euo pipefail + + until ping -c 1 google.com &> /dev/null; do + echo "waiting for outbound connectivity" + sleep 5 + done + + # Stop GraphDB to override configurations + echo "Stopping GraphDB" + systemctl stop graphdb + EOF + } + + part { + content_type = "text/x-shellscript" + content = templatefile("${path.module}/templates/01_disk_management.sh.tpl", { + name : var.resource_name_prefix + ebs_volume_type : var.ebs_volume_type + ebs_volume_size : var.ebs_volume_size + ebs_volume_iops : var.ebs_volume_iops + ebs_volume_throughput : var.ebs_volume_throughput + ebs_kms_key_arn : var.ebs_kms_key_arn + device_name : var.device_name + }) + } + + part { + content_type = "text/x-shellscript" + content = templatefile("${path.module}/templates/02_dns_provisioning.sh.tpl", { + zone_id : var.zone_id + zone_dns_name : var.zone_dns_name + }) + } + + part { + content_type = "text/x-shellscript" + content = templatefile("${path.module}/templates/03_gdb_conf_overrides.sh.tpl", { + name : var.resource_name_prefix + region : var.aws_region + }) + } + + part { + content_type = "text/x-shellscript" + content = templatefile("${path.module}/templates/04_gdb_backup_conf.sh.tpl", { + name : var.resource_name_prefix + region : var.aws_region + backup_schedule : var.backup_schedule + backup_retention_count : var.backup_retention_count + backup_bucket_name : var.backup_bucket_name + }) + } + + part { + content_type = "text/x-shellscript" + content = templatefile("${path.module}/templates/05_linux_overrides.sh.tpl", {}) + } + + part { + content_type = "text/x-shellscript" + content = templatefile("${path.module}/templates/06_cloudwatch_setup.sh.tpl", { + name : var.resource_name_prefix + region : var.aws_region + }) + } -locals { - # MiB to GiB - 10 - jvm_max_memory = ceil(data.aws_ec2_instance_type.graphdb.memory_size * 0.0009765625 - 10) - - graphdb_user_data = templatefile( - var.user_supplied_userdata_path != null ? var.user_supplied_userdata_path : "${path.module}/templates/start_graphdb.sh.tpl", - { - region = var.aws_region - name = var.resource_name_prefix - device_name = var.device_name - - backup_schedule = var.backup_schedule - backup_retention_count = var.backup_retention_count - backup_bucket_name = var.backup_bucket_name - - ebs_volume_type = var.ebs_volume_type - ebs_volume_size = var.ebs_volume_size - ebs_volume_iops = var.ebs_volume_iops - ebs_volume_throughput = var.ebs_volume_throughput - ebs_kms_key_arn = var.ebs_kms_key_arn - - zone_dns_name = var.zone_dns_name - zone_id = var.zone_id - - jvm_max_memory = local.jvm_max_memory - resource_name_prefix = var.resource_name_prefix - } - ) + part { + content_type = "text/x-shellscript" + content = templatefile("${path.module}/templates/07_cluster_setup.sh.tpl", { + name : var.resource_name_prefix + region : var.aws_region + zone_id : var.zone_id + }) + } } diff --git a/modules/user_data/outputs.tf b/modules/user_data/outputs.tf index fcdea3f..11113f1 100644 --- a/modules/user_data/outputs.tf +++ b/modules/user_data/outputs.tf @@ -1,7 +1,5 @@ output "graphdb_userdata_base64_encoded" { - value = base64encode(local.graphdb_user_data) + description = "User data script for GraphDB VM scale set." + value = data.cloudinit_config.graphdb_user_data.rendered } -output "graphdb_max_memory" { - value = local.jvm_max_memory -} diff --git a/modules/user_data/templates/01_disk_management.sh.tpl b/modules/user_data/templates/01_disk_management.sh.tpl new file mode 100644 index 0000000..ad8a82b --- /dev/null +++ b/modules/user_data/templates/01_disk_management.sh.tpl @@ -0,0 +1,138 @@ +#!/usr/bin/env bash + +# This script performs the following actions: +# * Set common variables: Retrieves IMDS token, instance ID, and availability zone. +# * Search for available EBS volume: Iterates up to 6 times to find or create an EBS volume tagged for GraphDB. +# * Attach EBS volume to instance: Connects the volume to the EC2 instance using a specified device name. +# * Store volume ID: Saves the volume ID for use in another script. +# * Handle EBS volume for GraphDB data directory: Manages device mapping complexities and creates a file system if needed. +# * Mount and configure file system: Ensures the file system is mounted, configures automatic mounting, and sets proper ownership. + +set -o errexit +set -o nounset +set -o pipefail + +echo "###########################################" +echo "# Creating/Attaching managed disks #" +echo "###########################################" + +# Set common variables used throughout the script. +IMDS_TOKEN=$( curl -Ss -H "X-aws-ec2-metadata-token-ttl-seconds: 6000" -XPUT 169.254.169.254/latest/api/token ) +INSTANCE_ID=$( curl -Ss -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" 169.254.169.254/latest/meta-data/instance-id ) +AVAILABILITY_ZONE=$( curl -Ss -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" 169.254.169.254/latest/meta-data/placement/availability-zone ) +VOLUME_ID="" + +# Search for an available EBS volume to attach to the instance. Wait one minute for a volume to become available, +# if no volume is found - create new one, attach, format and mount the volume. +for i in $(seq 1 6); do + + VOLUME_ID=$( + aws --cli-connect-timeout 300 ec2 describe-volumes \ + --filters "Name=status,Values=available" "Name=availability-zone,Values=$AVAILABILITY_ZONE" "Name=tag:Name,Values=${name}-graphdb-data" \ + --query "Volumes[*].{ID:VolumeId}" \ + --output text | \ + sed '/^$/d' + ) + + if [ -z "$${VOLUME_ID:-}" ]; then + echo 'EBS volume not yet available' + sleep 10 + else + break + fi +done + +if [ -z "$${VOLUME_ID:-}" ]; then + + VOLUME_ID=$( + aws --cli-connect-timeout 300 ec2 create-volume \ + --availability-zone "$AVAILABILITY_ZONE" \ + --encrypted \ + --kms-key-id "${ebs_kms_key_arn}" \ + --volume-type "${ebs_volume_type}" \ + --size "${ebs_volume_size}" \ + --iops "${ebs_volume_iops}" \ + --throughput "${ebs_volume_throughput}" \ + --tag-specifications "ResourceType=volume,Tags=[{Key=Name,Value=${name}-graphdb-data}]" | \ + jq -r .VolumeId + ) + + aws --cli-connect-timeout 300 ec2 wait volume-available --volume-ids "$VOLUME_ID" +fi + +aws --cli-connect-timeout 300 ec2 attach-volume \ + --volume-id "$VOLUME_ID" \ + --instance-id "$INSTANCE_ID" \ + --device "${device_name}" + +# Storing it to be used in another script +echo $VOLUME_ID > /tmp/volume_id + +# Handle the EBS volume used for the GraphDB data directory. +# beware, here be dragons... +# read these articles: +# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html +# https://github.com/oogali/ebs-automatic-nvme-mapping/blob/master/README.md + +# this variable comes from terraform, and it's what we specified in the launch template as the device mapping for the ebs. +# because this might be attached under a different name, we'll need to search for it. +device_mapping_full="${device_name}" +device_mapping_short="$(echo $device_mapping_full | cut -d'/' -f3)" + +graphdb_device="" + +# The device might not be available immediately, wait a while +for i in $(seq 1 12); do + for volume in $(find /dev | grep -i 'nvme[0-21]n1$'); do + # Extract the specified device from the vendor-specific data. + # Read https://github.com/oogali/ebs-automatic-nvme-mapping/blob/master/README.md, for more information. + real_device=$(nvme id-ctrl --raw-binary $volume | cut -c3073-3104 | tr -s ' ' | sed 's/ $//g') + if [ "$device_mapping_full" = "$real_device" ] || [ "$device_mapping_short" = "$real_device" ]; then + graphdb_device="$volume" + echo "Device found: $graphdb_device" + break + fi + done + + if [ -n "$graphdb_device" ]; then + break + fi + echo "Device not available, retrying ..." + sleep 5 +done + +# Create a file system if there isn't any. +if [ "$graphdb_device: data" = "$(file -s $graphdb_device)" ]; then + echo "Creating file system for $graphdb_device" + mkfs -t ext4 $graphdb_device +fi + +disk_mount_point="/var/opt/graphdb" + +# Check if the disk is already mounted. +if ! mount | grep -q "$graphdb_device"; then + echo "The disk at $graphdb_device is not mounted." + + # Create the mount point if it doesn't exist. + if [ ! -d "$disk_mount_point" ]; then + mkdir -p "$disk_mount_point" + fi + + # Add an entry to the fstab file to automatically mount the disk. + if ! grep -q "$graphdb_device" /etc/fstab; then + echo "$graphdb_device $disk_mount_point ext4 defaults 0 2" >> /etc/fstab + fi + + # Mount the disk. + mount "$disk_mount_point" + echo "The disk at $graphdb_device is now mounted at $disk_mount_point." +else + echo "The disk at $graphdb_device is already mounted." +fi + +echo "Creating data folders" +# Ensure data folders exist. +mkdir -p $disk_mount_point/node $disk_mount_point/cluster-proxy + +# This is required due to ownership being reverted after the disc attachment. +chown -R graphdb:graphdb $disk_mount_point diff --git a/modules/user_data/templates/02_dns_provisioning.sh.tpl b/modules/user_data/templates/02_dns_provisioning.sh.tpl new file mode 100644 index 0000000..ad77a89 --- /dev/null +++ b/modules/user_data/templates/02_dns_provisioning.sh.tpl @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# This script performs the following actions: +# * Retrieve necessary instance metadata using the EC2 metadata service. +# * Obtain the volume ID from a temporary file created by a previous script. +# * Generate a subdomain and construct the full node DNS name using the volume ID and specified DNS zone information. +# * Update the Route 53 hosted zone with an A record for the node's DNS name pointing to its local IPv4 address. +# * Set the hostname of the EC2 instance to the newly created node DNS name. + +set -o errexit +set -o nounset +set -o pipefail + +echo "########################" +echo "# DNS Provisioning #" +echo "########################" + +IMDS_TOKEN=$( curl -Ss -H "X-aws-ec2-metadata-token-ttl-seconds: 6000" -XPUT 169.254.169.254/latest/api/token ) +LOCAL_IPv4=$( curl -Ss -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" 169.254.169.254/latest/meta-data/local-ipv4 ) +VOLUME_ID=$(cat /tmp/volume_id) + +# Subdomain is based on the volume name. +SUBDOMAIN="$( echo -n "$VOLUME_ID" | sed 's/^vol-//' )" +NODE_DNS="$SUBDOMAIN.${zone_dns_name}" + +# Storing it to be used in another script +echo $NODE_DNS > /tmp/node_dns + +# Creates the DNS record +aws --cli-connect-timeout 300 route53 change-resource-record-sets \ + --hosted-zone-id "${zone_id}" \ + --change-batch '{"Changes": [{"Action": "UPSERT","ResourceRecordSet": {"Name": "'"$NODE_DNS"'","Type": "A","TTL": 60,"ResourceRecords": [{"Value": "'"$LOCAL_IPv4"'"}]}}]}' + +echo "DNS record for $NODE_DNS has been created" + +hostnamectl set-hostname "$NODE_DNS" + diff --git a/modules/user_data/templates/03_gdb_conf_overrides.sh.tpl b/modules/user_data/templates/03_gdb_conf_overrides.sh.tpl new file mode 100644 index 0000000..8b7059e --- /dev/null +++ b/modules/user_data/templates/03_gdb_conf_overrides.sh.tpl @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# This script performs the following actions: +# * Retrieve GraphDB configuration parameters from AWS Systems Manager (SSM). +# * Decode and save the GraphDB license file. +# * Set GraphDB cluster token, node DNS, and other properties in the GraphDB configuration file. +# * Get the load balancer (LB) DNS name from AWS Systems Manager. +# * Configure the GraphDB Cluster Proxy properties with LB DNS and node DNS. +# * Create systemd service overrides for GraphDB, setting JVM max memory to 85% of the total memory. +# * Save the calculated JVM max memory to systemd service overrides. + +set -o errexit +set -o nounset +set -o pipefail + +echo "#######################################" +echo "# GraphDB configuration overrides #" +echo "#######################################" + +# Get and store the GraphDB license +aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/license" --with-decryption | \ + jq -r .Parameter.Value | \ + base64 -d > /etc/graphdb/graphdb.license + +# Get the cluster token +GRAPHDB_CLUSTER_TOKEN="$(aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/cluster_token" --with-decryption | jq -r .Parameter.Value)" +# Get the NODE_DNS value from the previous script +NODE_DNS=$(cat /tmp/node_dns) + +cat << EOF > /etc/graphdb/graphdb.properties +graphdb.auth.token.secret=$GRAPHDB_CLUSTER_TOKEN +graphdb.connector.port=7201 +graphdb.external-url=http://$${NODE_DNS}:7201/ +graphdb.rpc.address=$${NODE_DNS}:7301 +EOF + +LB_DNS=$(aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/lb_dns_name" | jq -r .Parameter.Value) + +cat << EOF > /etc/graphdb-cluster-proxy/graphdb.properties +graphdb.auth.token.secret=$GRAPHDB_CLUSTER_TOKEN +graphdb.connector.port=7200 +graphdb.external-url=http://$${LB_DNS} +graphdb.vhosts=http://$${LB_DNS},http://$${NODE_DNS}:7200 +graphdb.rpc.address=$${NODE_DNS}:7300 +graphdb.proxy.hosts=$${NODE_DNS}:7301 +EOF + +mkdir -p /etc/systemd/system/graphdb.service.d/ + +echo "Calculating 85 percent of total memory" +# Get total memory in kilobytes +TOTAL_MEMORY_KB=$(grep -i "MemTotal" /proc/meminfo | awk '{print $2}') +# Convert total memory to gigabytes +TOTAL_MEMORY_GB=$(echo "scale=2; $TOTAL_MEMORY_KB / 1024 / 1024" | bc) +# Calculate 85% of total VM memory +JVM_MAX_MEMORY=$(echo "$TOTAL_MEMORY_GB * 0.85" | bc | cut -d'.' -f1) + +cat << EOF > /etc/systemd/system/graphdb.service.d/overrides.conf +[Service] +Environment="GDB_HEAP_SIZE=$${JVM_MAX_MEMORY}g" +EOF + +echo "Completed applying overrides" diff --git a/modules/user_data/templates/04_gdb_backup_conf.sh.tpl b/modules/user_data/templates/04_gdb_backup_conf.sh.tpl new file mode 100644 index 0000000..cf7e20e --- /dev/null +++ b/modules/user_data/templates/04_gdb_backup_conf.sh.tpl @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# This script performs the following actions: +# * Create a GraphDB backup script at /usr/bin/graphdb_backup. +# * Make the backup script executable. +# * Configure a cron job for GraphDB backup with the specified schedule. + +set -o errexit +set -o nounset +set -o pipefail + +echo "#################################################" +echo "# Configuring the GraphDB backup cron job #" +echo "#################################################" + +cat <<-EOF > /usr/bin/graphdb_backup +#!/bin/bash + +set -euxo pipefail + +GRAPHDB_ADMIN_PASSWORD="\$(aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/admin_password" --with-decryption | jq -r .Parameter.Value)" +NODE_STATE="\$(curl --silent --fail --user "admin:\$GRAPHDB_ADMIN_PASSWORD" localhost:7201/rest/cluster/node/status | jq -r .nodeState)" + +if [ "\$NODE_STATE" != "LEADER" ]; then + echo "current node is not a leader, but \$NODE_STATE" + exit 0 +fi + +function trigger_backup { + local backup_name="\$(date +'%Y-%m-%d_%H-%M-%S').tar" + + curl \ + -vvv --fail \ + --user "admin:\$GRAPHDB_ADMIN_PASSWORD" \ + --url localhost:7201/rest/recovery/cloud-backup \ + --header 'Content-Type: application/json' \ + --header 'Accept: application/json' \ + --data-binary @- <<-DATA + { + "backupOptions": { "backupSystemData": true }, + "bucketUri": "s3:///${backup_bucket_name}/\$backup_name?region=${region}" + } +DATA +} + +function rotate_backups { + all_files="\$(aws --cli-connect-timeout 300 s3api list-objects --bucket ${backup_bucket_name} --query 'Contents' | jq .)" + count="\$(echo \$all_files | jq length)" + delete_count="\$((count - ${backup_retention_count} - 1))" + + for i in \$(seq 0 \$delete_count); do + key="\$(echo \$all_files | jq -r .[\$i].Key)" + + aws --cli-connect-timeout 300 s3 rm s3://${backup_bucket_name}/\$key + done +} + +if ! trigger_backup; then + echo "failed to create backup" + exit 1 +fi + +rotate_backups + +EOF + +chmod +x /usr/bin/graphdb_backup +echo "${backup_schedule} graphdb /usr/bin/graphdb_backup" > /etc/cron.d/graphdb_backup + +echo "Cron job created" diff --git a/modules/user_data/templates/05_linux_overrides.sh.tpl b/modules/user_data/templates/05_linux_overrides.sh.tpl new file mode 100644 index 0000000..676abca --- /dev/null +++ b/modules/user_data/templates/05_linux_overrides.sh.tpl @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# This script performs the following actions: +# * Override Linux settings related to network load balancers. +# * Apply the changes to the system. + +set -o errexit +set -o nounset +set -o pipefail + +echo "###################################" +echo "# Overriding Linux Settings #" +echo "###################################" + +# Read this article: https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#connection-idle-timeout +echo 'net.ipv4.tcp_keepalive_time = 120' | tee -a /etc/sysctl.conf +echo 'fs.file-max = 262144' | tee -a /etc/sysctl.conf + +sysctl -p diff --git a/modules/user_data/templates/06_cloudwatch_setup.sh.tpl b/modules/user_data/templates/06_cloudwatch_setup.sh.tpl new file mode 100644 index 0000000..befcde6 --- /dev/null +++ b/modules/user_data/templates/06_cloudwatch_setup.sh.tpl @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# This script performs the following actions: +# * Set CloudWatch configurations: Retrieves GraphDB admin password from AWS SSM and updates CloudWatch and Prometheus configurations. +# * Start CloudWatch agent: Initiates the CloudWatch agent, fetches configurations, and starts the agent. + +set -o errexit +set -o nounset +set -o pipefail + +echo "#################################" +echo "# Cloudwatch Provisioning #" +echo "#################################" +GRAPHDB_ADMIN_PASSWORD=$(aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/admin_password" --with-decryption --query "Parameter.Value" --output text) + +tmp=$(mktemp) +jq '.logs.metrics_collected.prometheus.log_group_name = "${name}-graphdb"' /etc/graphdb/cloudwatch-agent-config.json > "$tmp" && mv "$tmp" /etc/graphdb/cloudwatch-agent-config.json +jq '.logs.metrics_collected.prometheus.emf_processor.metric_namespace = "${name}-graphdb"' /etc/graphdb/cloudwatch-agent-config.json > "$tmp" && mv "$tmp" /etc/graphdb/cloudwatch-agent-config.json +cat /etc/prometheus/prometheus.yaml | yq '.scrape_configs[].static_configs[].targets = ["localhost:7201"]' > "$tmp" && mv "$tmp" /etc/prometheus/prometheus.yaml +cat /etc/prometheus/prometheus.yaml | yq '.scrape_configs[].basic_auth.username = "admin"' | yq ".scrape_configs[].basic_auth.password = \"$${GRAPHDB_ADMIN_PASSWORD}\"" > "$tmp" && mv "$tmp" /etc/prometheus/prometheus.yaml + +amazon-cloudwatch-agent-ctl -a start +amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/etc/graphdb/cloudwatch-agent-config.json diff --git a/modules/user_data/templates/07_cluster_setup.sh.tpl b/modules/user_data/templates/07_cluster_setup.sh.tpl new file mode 100644 index 0000000..b81f3e6 --- /dev/null +++ b/modules/user_data/templates/07_cluster_setup.sh.tpl @@ -0,0 +1,203 @@ +#!/usr/bin/env bash + +# This script performs the following actions: +# * Retrieve necessary information from AWS and set variables. +# * Start and enable GraphDB and GraphDB cluster proxy services. +# * Check GraphDB availability for all instances and wait for DNS records. +# * Attempt to create a GraphDB cluster. +# * Change the admin user password and enable security if the node is the leader. + +set -o errexit +set -o nounset +set -o pipefail + +NODE_DNS=$(cat /tmp/node_dns) +IMDS_TOKEN=$(curl -Ss -H "X-aws-ec2-metadata-token-ttl-seconds: 6000" -XPUT 169.254.169.254/latest/api/token) +INSTANCE_ID=$(curl -Ss -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" 169.254.169.254/latest/meta-data/instance-id) +GRAPHDB_ADMIN_PASSWORD=$(aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/admin_password" --with-decryption --query "Parameter.Value" --output text) +VPC_ID=$(aws ec2 describe-instances --instance-id "$${INSTANCE_ID}" --query 'Reservations[0].Instances[0].VpcId' --output text) + +RETRY_DELAY=5 +MAX_RETRIES=10 + +echo "###########################" +echo "# Starting GraphDB #" +echo "###########################" + +# the proxy service is set up in the AMI but not enabled, so we enable and start it +systemctl daemon-reload +systemctl start graphdb +systemctl enable graphdb-cluster-proxy.service +systemctl start graphdb-cluster-proxy.service + +##################### +# Cluster setup # +##################### + +# Checks if GraphDB is started, we assume it is when the infrastructure endpoint is reached +check_gdb() { + if [ -z "$1" ]; then + echo "Error: IP address or hostname is not provided." + return 1 + fi + + local gdb_address="$1:7201/rest/monitor/infrastructure" + if curl -s --head -u "admin:$${GRAPHDB_ADMIN_PASSWORD}" --fail "$${gdb_address}" >/dev/null; then + echo "Success, GraphDB node $${gdb_address} is available" + return 0 + else + echo "GraphDB node $${gdb_address} is not available yet" + return 1 + fi +} + +# Waits for 3 DNS records to be available +wait_dns_records() { + ALL_DNS_RECORDS=($(aws ec2 describe-instances --filters "Name=vpc-id,Values=$${VPC_ID}" --query 'Reservations[*].Instances[*].[PrivateDnsName]' --output text)) + ALL_DNS_RECORDS_COUNT="$${#ALL_DNS_RECORDS[@]}" + + if [ "$${ALL_DNS_RECORDS_COUNT}" -ne 3 ]; then + sleep 5 + wait_dns_records + else + echo "Private DNS zone record count is $${ALL_DNS_RECORDS_COUNT}" + fi +} + +wait_dns_records + +# Check all instances are running +for record in "$${ALL_DNS_RECORDS[@]}"; do + echo "Pinging $record" + + if [ -n "$record" ]; then + while ! check_gdb "$record"; do + echo "Waiting for GDB $record to start" + sleep "$RETRY_DELAY" + done + else + echo "Error: address is empty." + fi +done + +echo "All GDB instances are available." + +# Determine the order of GraphDB instances in the cluster by querying Route 53 DNS records. +# This is done because we don't want the 3 nodes to attempt to create the same cluster. +EXISTING_RECORDS=($(aws route53 list-resource-record-sets --hosted-zone-id "${zone_id}" --query "ResourceRecordSets[?contains(Name, '.graphdb.cluster') == \`true\`].Name" --output text | sort -n)) +# Extract individual DNS names for the GraphDB cluster nodes. +NODE1="$${EXISTING_RECORDS[0]%?}" +NODE2="$${EXISTING_RECORDS[1]%?}" +NODE3="$${EXISTING_RECORDS[2]%?}" + +# Check if the current GraphDB node is the first one in the cluster (lowest instance). +if [ $NODE_DNS == $NODE1 ]; then + + echo "##################################" + echo "# Beginning cluster setup #" + echo "##################################" + + # Attempt to create a GraphDB cluster by configuring cluster nodes. + for ((i = 1; i <= $MAX_RETRIES; i++)); do + # /rest/monitor/cluster will return 200 only if a cluster exists, 503 if no cluster is set up. + IS_CLUSTER=$( + curl -s -o /dev/null \ + -u "admin:$${GRAPHDB_ADMIN_PASSWORD}" \ + -w "%%{http_code}" \ + http://localhost:7201/rest/monitor/cluster + ) + + # Check if GraphDB is part of a cluster; 000 indicates no HTTP code was received. + if [[ "$IS_CLUSTER" == 000 ]]; then + echo "Retrying ($i/$MAX_RETRIES) after $RETRY_DELAY seconds..." + sleep $RETRY_DELAY + elif [ "$IS_CLUSTER" == 503 ]; then + # Create the GraphDB cluster configuration if it does not exist. + CLUSTER_CREATE=$( + curl -X POST -s http://localhost:7201/rest/cluster/config \ + -o "/dev/null" \ + -w "%%{http_code}" \ + -H 'Content-type: application/json' \ + -u "admin:$${GRAPHDB_ADMIN_PASSWORD}" \ + -d "{\"nodes\": [\"$${NODE1}:7301\",\"$${NODE2}:7301\",\"$${NODE3}:7301\"]}" + ) + if [[ "$CLUSTER_CREATE" == 201 ]]; then + echo "GraphDB cluster successfully created!" + break + fi + elif [ "$IS_CLUSTER" == 200 ]; then + echo "Cluster exists" + break + else + echo "Something went wrong! Check the log files." + fi + done + + echo "###########################################################" + echo "# Changing admin user password and enable security #" + echo "###########################################################" + + LEADER_NODE="" + # Before enabling security a Leader must be elected. Iterates all nodes and looks for a node with status Leader. + while [ -z "$LEADER_NODE" ]; do + NODES=($NODE1 $NODE2 $NODE3) + for node in "$${NODES[@]}"; do + endpoint="http://$node:7201/rest/cluster/group/status" + echo "Checking leader status for $node" + + # Gets the address of the node if nodeState is LEADER. + LEADER_ADDRESS=$(curl -s "$endpoint" -u "admin:$${GRAPHDB_ADMIN_PASSWORD}" | jq -r '.[] | select(.nodeState == "LEADER") | .address') + if [ -n "$${LEADER_ADDRESS}" ]; then + LEADER_NODE=$LEADER_ADDRESS + echo "Found leader address $LEADER_ADDRESS" + break 2 # Exit both loops + else + echo "No leader found at $node" + fi + done + + echo "No leader found on any node. Retrying..." + sleep 5 + done + + IS_SECURITY_ENABLED=$(curl -s -X GET \ + --header 'Accept: application/json' \ + -u "admin:$${GRAPHDB_ADMIN_PASSWORD}" \ + 'http://localhost:7200/rest/security') + + # Check if GDB security is enabled + if [[ $IS_SECURITY_ENABLED == "true" ]]; then + echo "Security is enabled" + else + # Set the admin password + SET_PASSWORD=$( + curl --location -s -w "%%{http_code}" \ + --request PATCH 'http://localhost:7200/rest/security/users/admin' \ + --header 'Content-Type: application/json' \ + --data "{ \"password\": \"$${GRAPHDB_ADMIN_PASSWORD}\" }" + ) + if [[ "$SET_PASSWORD" == 200 ]]; then + echo "Set GraphDB password successfully" + else + echo "Failed setting GraphDB password. Please check the logs!" + fi + + # Enable the security + ENABLED_SECURITY=$(curl -X POST -s -w "%%{http_code}" \ + --header 'Content-Type: application/json' \ + --header 'Accept: */*' \ + -d 'true' 'http://localhost:7200/rest/security') + + if [[ "$ENABLED_SECURITY" == 200 ]]; then + echo "Enabled GraphDB security successfully" + else + echo "Failed enabling GraphDB security. Please check the logs!" + fi + fi +else + echo "Node $NODE_DNS is not the lowest instance, skipping cluster creation." +fi + +echo "###########################" +echo "# Script completed #" +echo "###########################" diff --git a/modules/user_data/templates/start_graphdb.sh.tpl b/modules/user_data/templates/start_graphdb.sh.tpl deleted file mode 100644 index a3830b6..0000000 --- a/modules/user_data/templates/start_graphdb.sh.tpl +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -until ping -c 1 google.com &> /dev/null; do - echo "waiting for outbound connectivity" - sleep 5 -done - -systemctl stop graphdb - -# Set common variables used throughout the script. -imds_token=$( curl -Ss -H "X-aws-ec2-metadata-token-ttl-seconds: 300" -XPUT 169.254.169.254/latest/api/token ) -local_ipv4=$( curl -Ss -H "X-aws-ec2-metadata-token: $imds_token" 169.254.169.254/latest/meta-data/local-ipv4 ) -instance_id=$( curl -Ss -H "X-aws-ec2-metadata-token: $imds_token" 169.254.169.254/latest/meta-data/instance-id ) -availability_zone=$( curl -Ss -H "X-aws-ec2-metadata-token: $imds_token" 169.254.169.254/latest/meta-data/placement/availability-zone ) -volume_id="" - -# Search for an available EBS volume to attach to the instance. Wait one minute for a volume to become available, -# if no volume is found - create new one, attach, format and mount the volume. - -for i in $(seq 1 6); do - - volume_id=$( - aws --cli-connect-timeout 300 ec2 describe-volumes \ - --filters "Name=status,Values=available" "Name=availability-zone,Values=$availability_zone" "Name=tag:Name,Values=${name}-graphdb-data" \ - --query "Volumes[*].{ID:VolumeId}" \ - --output text | \ - sed '/^$/d' - ) - - if [ -z "$${volume_id:-}" ]; then - echo 'ebs volume not yet available' - sleep 10 - else - break - fi -done - -if [ -z "$${volume_id:-}" ]; then - - volume_id=$( - aws --cli-connect-timeout 300 ec2 create-volume \ - --availability-zone "$availability_zone" \ - --encrypted \ - --kms-key-id "${ebs_kms_key_arn}" \ - --volume-type "${ebs_volume_type}" \ - --size "${ebs_volume_size}" \ - --iops "${ebs_volume_iops}" \ - --throughput "${ebs_volume_throughput}" \ - --tag-specifications "ResourceType=volume,Tags=[{Key=Name,Value=${name}-graphdb-data}]" | \ - jq -r .VolumeId - ) - - aws --cli-connect-timeout 300 ec2 wait volume-available --volume-ids "$volume_id" -fi - -aws --cli-connect-timeout 300 ec2 attach-volume \ - --volume-id "$volume_id" \ - --instance-id "$instance_id" \ - --device "${device_name}" - -# Handle the EBS volume used for the GraphDB data directory -# beware, here be dragons... -# read these articles: -# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html -# https://github.com/oogali/ebs-automatic-nvme-mapping/blob/master/README.md - -# this variable comes from terraform, and it's what we specified in the launch template as the device mapping for the ebs -# because this might be attached under a different name, we'll need to search for it -device_mapping_full="${device_name}" -device_mapping_short="$(echo $device_mapping_full | cut -d'/' -f3)" - -graphdb_device="" - -# the device might not be available immediately, wait a while -for i in $(seq 1 12); do - for volume in $(find /dev | grep -i 'nvme[0-21]n1$'); do - # extract the specified device from the vendor-specific data - # read https://github.com/oogali/ebs-automatic-nvme-mapping/blob/master/README.md, for more information - real_device=$(nvme id-ctrl --raw-binary $volume | cut -c3073-3104 | tr -s ' ' | sed 's/ $//g') - if [ "$device_mapping_full" = "$real_device" ] || [ "$device_mapping_short" = "$real_device" ]; then - graphdb_device="$volume" - break - fi - done - - if [ -n "$graphdb_device" ]; then - break - fi - sleep 5 -done - -# create a file system if there isn't any -if [ "$graphdb_device: data" = "$(file -s $graphdb_device)" ]; then - mkfs -t ext4 $graphdb_device -fi - -disk_mount_point="/var/opt/graphdb" - -# Check if the disk is already mounted -if ! mount | grep -q "$graphdb_device"; then - echo "The disk at $graphdb_device is not mounted." - - # Create the mount point if it doesn't exist - if [ ! -d "$disk_mount_point" ]; then - mkdir -p "$disk_mount_point" - fi - - # Add an entry to the fstab file to automatically mount the disk - if ! grep -q "$graphdb_device" /etc/fstab; then - echo "$graphdb_device $disk_mount_point ext4 defaults 0 2" >> /etc/fstab - fi - - # Mount the disk - mount "$disk_mount_point" - echo "The disk at $graphdb_device is now mounted at $disk_mount_point." -else - echo "The disk at $graphdb_device is already mounted." -fi - -# Ensure data folders exist -mkdir -p $disk_mount_point/node $disk_mount_point/cluster-proxy - -# this is needed because after the disc attachment folder owner is reverted -chown -R graphdb:graphdb $disk_mount_point - -# Register the instance in Route 53, using the volume id for the sub-domain - -subdomain="$( echo -n "$volume_id" | sed 's/^vol-//' )" -node_dns="$subdomain.${zone_dns_name}" - -aws --cli-connect-timeout 300 route53 change-resource-record-sets \ - --hosted-zone-id "${zone_id}" \ - --change-batch '{"Changes": [{"Action": "UPSERT","ResourceRecordSet": {"Name": "'"$node_dns"'","Type": "A","TTL": 60,"ResourceRecords": [{"Value": "'"$local_ipv4"'"}]}}]}' - -hostnamectl set-hostname "$node_dns" - -# Configure GraphDB - -aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/license" --with-decryption | \ - jq -r .Parameter.Value | \ - base64 -d > /etc/graphdb/graphdb.license - -graphdb_cluster_token="$(aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/cluster_token" --with-decryption | jq -r .Parameter.Value)" - -cat << EOF > /etc/graphdb/graphdb.properties -graphdb.auth.token.secret=$graphdb_cluster_token -graphdb.connector.port=7201 -graphdb.external-url=http://$${node_dns}:7201/ -graphdb.rpc.address=$${node_dns}:7301 -EOF - -load_balancer_dns=$(aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/lb_dns_name" | jq -r .Parameter.Value) - -cat << EOF > /etc/graphdb-cluster-proxy/graphdb.properties -graphdb.auth.token.secret=$graphdb_cluster_token -graphdb.connector.port=7200 -graphdb.external-url=http://$${load_balancer_dns} -graphdb.vhosts=http://$${load_balancer_dns},http://$${node_dns}:7200 -graphdb.rpc.address=$${node_dns}:7300 -graphdb.proxy.hosts=$${node_dns}:7301 -EOF - -mkdir -p /etc/systemd/system/graphdb.service.d/ - -cat << EOF > /etc/systemd/system/graphdb.service.d/overrides.conf -[Service] -Environment="GDB_HEAP_SIZE=${jvm_max_memory}g" -EOF - -# Configure the GraphDB backup cron job - -cat <<-EOF > /usr/bin/graphdb_backup -#!/bin/bash - -set -euxo pipefail - -GRAPHDB_ADMIN_PASSWORD="\$(aws --cli-connect-timeout 300 ssm get-parameter --region ${region} --name "/${name}/graphdb/admin_password" --with-decryption | jq -r .Parameter.Value)" -NODE_STATE="\$(curl --silent --fail --user "admin:\$GRAPHDB_ADMIN_PASSWORD" localhost:7201/rest/cluster/node/status | jq -r .nodeState)" - -if [ "\$NODE_STATE" != "LEADER" ]; then - echo "current node is not a leader, but \$NODE_STATE" - exit 0 -fi - -function trigger_backup { - local backup_name="\$(date +'%Y-%m-%d_%H-%M-%S').tar" - - curl \ - -vvv --fail \ - --user "admin:\$GRAPHDB_ADMIN_PASSWORD" \ - --url localhost:7201/rest/recovery/cloud-backup \ - --header 'Content-Type: application/json' \ - --header 'Accept: application/json' \ - --data-binary @- <<-DATA - { - "backupOptions": { "backupSystemData": true }, - "bucketUri": "s3:///${backup_bucket_name}/\$backup_name?region=${region}" - } -DATA -} - -function rotate_backups { - all_files="\$(aws --cli-connect-timeout 300 s3api list-objects --bucket ${backup_bucket_name} --query 'Contents' | jq .)" - count="\$(echo \$all_files | jq length)" - delete_count="\$((count - ${backup_retention_count} - 1))" - - for i in \$(seq 0 \$delete_count); do - key="\$(echo \$all_files | jq -r .[\$i].Key)" - - aws --cli-connect-timeout 300 s3 rm s3://${backup_bucket_name}/\$key - done -} - -if ! trigger_backup; then - echo "failed to create backup" - exit 1 -fi - -rotate_backups - -EOF - -chmod +x /usr/bin/graphdb_backup -echo "${backup_schedule} graphdb /usr/bin/graphdb_backup" > /etc/cron.d/graphdb_backup - -# https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#connection-idle-timeout -echo 'net.ipv4.tcp_keepalive_time = 120' | tee -a /etc/sysctl.conf -echo 'fs.file-max = 262144' | tee -a /etc/sysctl.conf - -sysctl -p - -tmp=$(mktemp) -jq '.logs.metrics_collected.prometheus.log_group_name = "${resource_name_prefix}-graphdb"' /etc/graphdb/cloudwatch-agent-config.json > "$tmp" && mv "$tmp" /etc/graphdb/cloudwatch-agent-config.json -jq '.logs.metrics_collected.prometheus.emf_processor.metric_namespace = "${resource_name_prefix}-graphdb"' /etc/graphdb/cloudwatch-agent-config.json > "$tmp" && mv "$tmp" /etc/graphdb/cloudwatch-agent-config.json -cat /etc/prometheus/prometheus.yaml | yq '.scrape_configs[].static_configs[].targets = ["localhost:7201"]' > "$tmp" && mv "$tmp" /etc/prometheus/prometheus.yaml - -amazon-cloudwatch-agent-ctl -a start -amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/etc/graphdb/cloudwatch-agent-config.json - -# the proxy service is set up in the AMI but not enabled there, so we enable and start it -systemctl daemon-reload -systemctl start graphdb -systemctl enable graphdb-cluster-proxy.service -systemctl start graphdb-cluster-proxy.service diff --git a/modules/vm/iam.tf b/modules/vm/iam.tf index 4480c4b..0e79594 100644 --- a/modules/vm/iam.tf +++ b/modules/vm/iam.tf @@ -22,7 +22,9 @@ data "aws_iam_policy_document" "instance_volume" { actions = [ "ec2:CreateVolume", "ec2:AttachVolume", - "ec2:DescribeVolumes" + "ec2:DescribeVolumes", + "ec2:DescribeInstances", + "route53:ListResourceRecordSets" ] resources = ["*"] diff --git a/outputs.tf b/outputs.tf index d81bd65..6f96c9e 100644 --- a/outputs.tf +++ b/outputs.tf @@ -35,7 +35,3 @@ output "graphdb_sg_id" { description = "Security group ID of GraphDB cluster" value = module.vm.graphdb_sg_id } - -output "graphdb_max_memory" { - value = module.user_data.graphdb_max_memory -}