Commit 32ac998e authored by Nicky White's avatar Nicky White

Initial

parents
*.retry
*.ignore
.DS_Store
.terraform/*
terraform.*
# Signalfx Terraform Module
## Docker image
Signalfx Daemon and Signalfx sidecar work slightly differently in that the SignalFx agent collects metrics from the underlying instance and metric endpoint, Daemon works in the standard way by slurping metrics directly from the Docker socket. It does this by giving itself read only access to the socket by mounting the socket as a read only volume.
To do this root permissions are required for the Daemon but not for the Sidecar, to build for ecs pass RUNUSER=root and Fargate pass RUNUSER=signalfx-agent.
```
git@git.steamhaus.co.uk:terraform-signalfx/steamhaus-signalfx-agent.git
https://github.com/steamhaus/tf_mod_signalfx_daemon.git
```
## Variable Injection
I've configured the runtime to set user by variable injection , untested so in the interim you can build for the specific ECS type
To build the image for ECS Daemon set user as RUNUSER=root
### Untested variable injection
```
VARIABLE RUNUSER=[signalfx-agent , root]
```
### Fargate Sidecar agent
To build the image for Fargate pass in the sigalfx-agent user
```
docker build -t steamhaus-signalfx:latest -a RUNUSER=signalfx-agent
```
### Deploy
Deploy the Signalfx agent then deploy the docker image to the new repo.
#### To Do
In module create CodeBuild pipeline for SignalFx image deployment
# Terraform Module
Example resource block
### Repository
Before the service is deployed an ECR repository needs to be created.
```
module "signalfx_daemon" {
source = "git@git.steamhaus.co.uk:terraform-signalfx/signalfx_terraform_module.git"
client = "right-said"
envname = "fred"
region = "eu-west-1"
cluster = aws_ecs_cluster.ecs.id
tag = "latest"
signalfx_memory = "512"
signalfx_cpu = "256"
signalfx_switch = "enabled/disabled"
signalfx_api_token = "123456789abcdefghijklmnopqrstuvwxyz"
}
```
###########################################################################
# ECR
###########################################################################
resource "aws_ecr_repository" "ecr" {
name = "${var.client}/${var.envname}-signalfx-agent"
}
data "aws_iam_policy_document" "ecr_policy" {
statement {
actions = [
"ecr:arn:aws:ecr:region:${data.aws_caller_identity.current.account_id}:repository/${var.client}/${var.envname}-signalfx-agent",
]
principals {
type = "AWS"
identifiers = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root",
]
}
}
}
resource "aws_ecr_repository_policy" "allow_prod" {
repository = aws_ecr_repository.ecr.name
policy = data.aws_iam_policy_document.ecr_policy.json
}
############################################################################
## SignalFx ECR (cross account sharing policy)
############################################################################
#
#resource "aws_ecr_repository_policy" "ecr" {
# repository = aws_ecr_repository.ecr.name
#
# policy = <<EOF
#{
# "Version": "2008-10-17",
# "Statement": [
# {
# "Sid": "new policy",
# "Effect": "Allow",
# "Principal": "*",
# "Action": [
# "ecr:GetDownloadUrlForLayer",
# "ecr:BatchGetImage",
# "ecr:BatchCheckLayerAvailability",
# "ecr:PutImage",
# "ecr:InitiateLayerUpload",
# "ecr:UploadLayerPart",
# "ecr:CompleteLayerUpload",
# "ecr:DescribeRepositories",
# "ecr:GetRepositoryPolicy",
# "ecr:ListImages",
# "ecr:DeleteRepository",
# "ecr:BatchDeleteImage",
# "ecr:SetRepositoryPolicy",
# "ecr:DeleteRepositoryPolicy"
# ]
# }
# ]
#}
#EOF
#}
#
#
#
#
#
#resource "aws_ecr_repository_policy" "ecs_policy" {
# repository = "${var.envname}/${var.client}/${var.envname}-signalfx"
# policy = data.aws_iam_policy_document.ecr_policy.json
#}
#
\ No newline at end of file
# This agent config file is designed for use within an ECS cluster. The agent
# should be run as a Daemon service with the host EC2 instance's filesystem
# mounted in at /hostfs and the docker socket mounted in at
# /var/run/docker.sock.
signalFxAccessToken: {"#from": "env:ACCESS_TOKEN"}
ingestUrl: {"#from": "env:INGEST_URL", optional: true}
apiUrl: {"#from": "env:API_URL", optional: true}
traceEndpointUrl: {"#from": "env:TRACE_ENDPOINT_URL", optional: true}
intervalSeconds: {"#from": "env:INTERVAL_SECONDS", default: 10}
logging:
level: {"#from": "env:LOG_LEVEL", default: "info"}
etcPath: /hostfs/etc
procPath: /hostfs/proc
runPath: /hostfs/run
varPath: /hostfs/var
sysPath: /hostfs/sys
# observers are what discover running services in the environment
observers:
# Enable the docker observer to discover other ECS tasks running on the
# same EC2 instance.
- type: docker
# ECS tasks running the awsvpc network mode don't actually expose and IP
# address on the docker inspect API so we need to use the hostname instead.
useHostnameIfPresent: true
labelsToDimensions: &labelMap
# We map the cluster label to ClusterName to match what our AWS
# CloudWatch integration uses.
com.amazonaws.ecs.cluster: ClusterName
com.amazonaws.ecs.container-name: container_spec_name
com.amazonaws.ecs.task-arn: ecs_task_arn
com.amazonaws.ecs.task-definition-family: ecs_task_group
com.amazonaws.ecs.task-definition-version: ecs_task_version
monitors:
- type: cpu
- type: filesystems
hostFSPath: /hostfs
- type: disk-io
- type: net-io
- type: load
- type: memory
- type: host-metadata
- type: processlist
- type: vmem
- type: docker-container-stats
labelsToDimensions:
<<: *labelMap
# You can set this envvar in your task definition to provide extra monitor
# configuration to monitor applications running as ECS tasks. You should
# probably use discovery rules so that you don't have to have a specific
# config for each EC2 instance.
- {"#from": "env:EXTRA_MONITOR_CONFIG", default: [], flatten: true}
# Enables the Smart Agent to receive traces.
- type: signalfx-forwarder
# Specify the hostname:port where the Smart Agent receives traces.
listenAddress: "0.0.0.0:9080"
# Optionally, add span tags to each span in a trace the Smart Agent receives.
# defaultSpanTags:
# SPAN_TAG_KEY: "SPAN_TAG_VALUE"
# Exports traces in the specified format to a SignalFx ingest endpoint or
# OpenTelemetry Collector according to the traceEndpointUrl you set.
writer:
traceExportFormat: sapm
metricsToExclude:
- {"#from": "env:METRICS_TO_EXCLUDE", default: [], flatten: true}
# This allows any config above to be completely overridden. CONFIG_OVERRIDE
# must be given as a YAML/JSON object.
_: {"#from": "env:CONFIG_OVERRIDE", optional: true, flatten: true}
\ No newline at end of file
PLACEHOLDER
\ No newline at end of file
---
# *Required* The access token for the org that you wish to send metrics to.
signalFxAccessToken: {"#from": '\ProgramData\SignalFxAgent\token'}
ingestUrl: {"#from": '\ProgramData\SignalFxAgent\ingest_url', default: "https://ingest.signalfx.com"}
apiUrl: {"#from": '\ProgramData\SignalFxAgent\api_url', default: "https://api.signalfx.com"}
intervalSeconds: 10
logging:
# Valid values are 'debug', 'info', 'warning', and 'error'
level: info
# observers are what discover running services in the environment
observers:
- type: host
monitors:
- {"#from": '\ProgramData\SignalFxAgent\monitors\*.yaml', flatten: true, optional: true}
- type: host-metadata
- type: processlist
- type: cpu
- type: disk-io
- type: filesystems
- type: memory
- type: net-io
- type: vmem
# This agent config file is designed for use within an ECS cluster. The agent
# should be run as a Daemon service with the host EC2 instance's filesystem
# mounted in at /hostfs and the docker socket mounted in at
# /var/run/docker.sock.
signalFxAccessToken: {"#from": "env:ACCESS_TOKEN"}
ingestUrl: {"#from": "env:INGEST_URL", optional: true}
apiUrl: {"#from": "env:API_URL", optional: true}
hostname: {"#from": "env:SERVICE_NAME", optional: true}
intervalSeconds: {"#from": "env:INTERVAL_SECONDS", default: 10}
logging:
level: {"#from": "env:LOG_LEVEL", default: "info"}
etcPath: /hostfs/etc
procPath: /hostfs/proc
runPath: /hostfs/run
varPath: /hostfs/var
sysPath: /hostfs/sys
# observers are what discover running services in the environment
observers:
# Enable the docker observer to discover other ECS tasks running on the
# same EC2 instance.
- type: docker
# ECS tasks running the awsvpc network mode don't actually expose and IP
# address on the docker inspect API so we need to use the hostname instead.
useHostnameIfPresent: true
labelsToDimensions: &labelMap
# We map the cluster label to ClusterName to match what our AWS
# CloudWatch integration uses.
com.amazonaws.ecs.cluster: ClusterName
com.amazonaws.ecs.container-name: container_spec_name
com.amazonaws.ecs.task-arn: ecs_task_arn
com.amazonaws.ecs.task-definition-family: ecs_task_group
com.amazonaws.ecs.task-definition-version: ecs_task_version
monitors:
- type: cpu
- type: filesystems
hostFSPath: /hostfs
- type: disk-io
- type: net-io
- type: load
- type: memory
- type: host-metadata
- type: processlist
- type: vmem
- type: collectd/disk
- type: docker-container-stats
labelsToDimensions:
<<: *labelMap
# You can set this envvar in your task definition to provide extra monitor
# configuration to monitor applications running as ECS tasks. You should
# probably use discovery rules so that you don't have to have a specific
# config for each EC2 instance.
- {"#from": "env:EXTRA_MONITOR_CONFIG", default: [], flatten: true}
metricsToExclude:
- {"#from": "env:METRICS_TO_EXCLUDE", default: [], flatten: true}
# This allows any config above to be completely overridden. CONFIG_OVERRIDE
# must be given as a YAML/JSON object.
_: {"#from": "env:CONFIG_OVERRIDE", optional: true, flatten: true}
# This agent config file is designed for use within an ECS task. The agent
# should be as a Fargate service within an ECS task.
signalFxAccessToken: {"#from": "env:ACCESS_TOKEN"}
ingestUrl: {"#from": "env:INGEST_URL", optional: true}
apiUrl: {"#from": "env:API_URL", optional: true}
hostname: {"#from": "env:SERVICE_NAME", optional: true}
intervalSeconds: {"#from": "env:INTERVAL_SECONDS", default: 10}
logging:
level: {"#from": "env:LOG_LEVEL", default: "info"}
# observers are what discover running services in the environment
observers:
# Enable the ecs observer to discover other ECS containers running on the
# same ECS task.
- type: ecs
labelsToDimensions: &labelMap
com.amazonaws.ecs.container-name: container_spec_name
monitors:
- type: cpu
- type: disk-io
- type: net-io
- type: load
- type: memory
- type: collectd/protocols
- type: vmem
- type: ecs-metadata
excludedImages:
- signalfx-agent
labelsToDimensions:
<<: *labelMap
# You can set this envvar in your task definition to provide extra monitor
# configuration to monitor applications running as ECS tasks. You should
# probably use discovery rules so that you don't have to have a specific
# config for each EC2 instance.
- {"#from": "env:EXTRA_MONITOR_CONFIG", default: [], flatten: true}
metricsToExclude:
- {"#from": "env:METRICS_TO_EXCLUDE", default: [], flatten: true}
# This allows any config above to be completely overridden. CONFIG_OVERRIDE
# must be given as a YAML/JSON object.
_: {"#from": "env:CONFIG_OVERRIDE", optional: true, flatten: true}
data "aws_region" "current" {
}
data "aws_caller_identity" "current" {
}
data "aws_iam_policy_document" "ecs_assume" {
statement {
actions = [
"sts:AssumeRole",
]
principals {
type = "Service"
identifiers = [
"ecs.amazonaws.com",
"ecs-tasks.amazonaws.com",
"logs.amazonaws.com",
]
}
}
}
resource "aws_iam_role" "signalfx_agent" {
name = "${var.envname}-signalfx_agent"
path = "/"
assume_role_policy = data.aws_iam_policy_document.ecs_assume.json
}
resource "aws_iam_instance_profile" "signalfx_agent" {
name = "${var.envname}-signalfx_agent"
role = aws_iam_role.signalfx_agent.name
}
data "aws_iam_policy_document" "signalfx_agent" {
statement {
actions = [
"s3:*",
]
resources = [
"arn:aws:s3:::${var.client}-${var.envname}-signalfx-store",
"arn:aws:s3:::${var.client}-${var.envname}-signalfx-store/*"
]
}
}
resource "aws_iam_policy" "signalfx_agent" {
name = "${var.envname}-signalfx_agent"
path = "/"
policy = data.aws_iam_policy_document.signalfx_agent.json
}
resource "aws_iam_role_policy_attachment" "signalfx_agent" {
role = aws_iam_role.signalfx_agent.name
policy_arn = aws_iam_policy.signalfx_agent.arn
}
###########################################################################
# Container Definition
###########################################################################
locals {
signalfx_agent_daemon_container_definitions = [
{
name = "signalfx-agent"
image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com/${var.client}/${var.envname}-signalfx-agent:${var.tag}"
essential = "true"
cpu = var.signalfx_cpu
memory = var.signalfx_memory
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = aws_cloudwatch_log_group.signalfx_agent_daemon.name
awslogs-region = data.aws_region.current.name
awslogs-stream-prefix = "${var.envname}-signalfx-agent"
}
}
networkMode = "host"
portMappings = []
environment = [
{
name = "ACCESS_TOKEN"
value = var.signalfx_api_token
},
{
name = "INGEST_URL"
value = "https://ingest.${var.signal_fx_realm}.signalfx.com"
},
{
name = "API_URL"
value = "https://api.${var.signal_fx_realm}.signalfx.com"
},
{
name = "SIGNALFX_NODE"
value = "${var.envname}-agent-daemon"
},
{
name = "SIGNALFX_MODE"
value = var.signalfx_switch
},
{
name = "SIGNALFX_S3_CONFIG_STORE"
value = "${var.client}-${var.envname}-signalfx-store/ecs/ecsdaemon"
},
]
mountPoints = [
{
readOnly = "true"
containerPath = "/hostfs"
sourceVolume = "hostfs"
},
{
readOnly = "true"
containerPath = "/var/run/docker.sock"
sourceVolume = "docker-socket"
},
{
readOnly = "true"
containerPath = "/etc/passwd"
sourceVolume = "etc-passwd"
},
]
},
]
}
###########################################################################
# TASK Definition
###########################################################################
resource "aws_ecs_task_definition" "signalfx_agent_daemon" {
family = "${var.envname}-signalfx-agent-daemon"
network_mode = "bridge"
task_role_arn = aws_iam_role.signalfx_agent.arn
# Nasty regex to workaround https://github.com/hashicorp/terraform/issues/17033
container_definitions = replace(
replace(
jsonencode(local.signalfx_agent_daemon_container_definitions),
"/\"([0-9]+\\.?[0-9]*|true|false)\"/",
"$1",
),
"/\"value\":([0-9]+\\.?[0-9]*|true|false)/",
"\"value\":\"$1\"",
)
volume {
name = "hostfs"
host_path = "/"
}
volume {
name = "docker-socket"
host_path = "/var/run/docker.sock"
}
volume {
name = "etc-passwd"
host_path = "/etc/passwd"
}
}
###########################################################################
# ECS Service
###########################################################################
resource "aws_ecs_service" "signalfx_agent_daemon" {
name = "${var.envname}-signalfx-agent-daemon"
cluster = var.cluster
task_definition = aws_ecs_task_definition.signalfx_agent_daemon.arn
scheduling_strategy = "DAEMON"
}
###########################################################################
# Cloudwatch
###########################################################################
resource "aws_cloudwatch_log_group" "signalfx_agent_daemon" {
name = "${var.envname}-signalfx-agent-daemon"
retention_in_days = var.log_retention
}
resource "aws_cloudwatch_log_stream" "signalfx_agent_daemon" {
name = "${var.envname}-singalfx-daemon"
log_group_name = aws_cloudwatch_log_group.signalfx_agent_daemon.name
}
###########################################################################
# S3 Object Store (Config)
###########################################################################
resource "aws_s3_bucket" "signalfx_config_store" {
bucket = "${var.client}-${var.envname}-signalfx-store"
acl = "private"
}
###########################################################################
# S3 Object Store (Object)
###########################################################################
resource "null_resource" "signalfx_object_push" {
provisioner "local-exec" {
command = "aws s3 sync ${path.module}/files/signalfx/config s3://${aws_s3_bucket.signalfx_config_store.id}"
}
}
variable "environment_name" {
}
variable "region" {}
variable "signal_fx_realm" {
default = "eu0"
}
variable "envtype" {
default = "prod"
}
variable "envname" {
}
variable "signalfx_api_token" {
}
variable "cluster" {
}
variable "signalfx_memory" {
}
variable "signalfx_cpu" {
}
variable "signalfx_switch" {
}
variable "tag" {}
variable "log_retention" {
default = 14
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment