Skip to content

Commit

Permalink
feat(otel): creating the otel collector service (#801)
Browse files Browse the repository at this point in the history
* feat(otel): creating the otel collector service

* fix(otel): ensuring service will start

* fix(otel): update image name
  • Loading branch information
bassrock authored Oct 8, 2024
1 parent c33dd2e commit 9d140ee
Show file tree
Hide file tree
Showing 14 changed files with 648 additions and 84 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/otel-collector.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: OTEL Collector
on:
pull_request:
paths:
- 'infrastructure/otel-collector/**'
- 'packages/**'
- 'docker-compose.yml'
- 'servers/otel-collector/**'
- '.github/actions/**'
- '.github/workflows/otel-collector.yml'
- '.github/workflows/reuse-*.yml'
push:
branches:
- main
- dev
paths:
- 'infrastructure/otel-collector/**'
- 'packages/**'
- 'servers/otel-collector/**'
- '.github/actions/**'
- '.github/workflows/otel-collector.yml'
- '.github/workflows/reuse-*.yml'
jobs:

infrastructure:
uses: ./.github/workflows/reuse-infrastructure.yml
with:
scope: otel-collector-cdk
stack-output-path: infrastructure/otel-collector/cdktf.out/stacks/otel-collector
secrets: inherit

api:
uses: ./.github/workflows/reuse-build-and-push-image.yml
needs: [infrastructure]
with:
scope: otel-collector
docker-repo-name-pattern: otelcollector-{0}-app
context: servers/otel-collector
app-path: servers/otel-collector
terraform-output: ${{needs.infrastructure.outputs.terraform-output}}
secrets: inherit
4 changes: 4 additions & 0 deletions infrastructure/otel-collector/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.terraform/
node_modules/
cdktf.out/
.gen/
1 change: 1 addition & 0 deletions infrastructure/otel-collector/.terraform-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.8.3
5 changes: 5 additions & 0 deletions infrastructure/otel-collector/cdktf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"language": "typescript",
"app": "npm run --silent compile && node dist/main.js",
"projectId": "7e0b548c-601b-4fd5-9387-cd9ad7f5007c"
}
3 changes: 3 additions & 0 deletions infrastructure/otel-collector/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import cdktf from '@pocket-tools/eslint-config/cdktf';
import tseslint from 'typescript-eslint';
export default tseslint.config(...cdktf);
32 changes: 32 additions & 0 deletions infrastructure/otel-collector/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "otel-collector-cdk",
"version": "1.0.0",
"private": true,
"main": "dist/main.js",
"types": "src/main.ts",
"scripts": {
"build": "rm -rf dist && tsc",
"compile": "tsc --pretty",
"format": "eslint --fix",
"lint": "eslint --fix-dry-run",
"synth": "cdktf synth",
"watch": "tsc -w"
},
"dependencies": {
"@cdktf/provider-aws": "19.33.0",
"@cdktf/provider-local": "10.1.0",
"@cdktf/provider-null": "10.0.0",
"@cdktf/provider-pagerduty": "13.11.6",
"@pocket-tools/terraform-modules": "workspace:*",
"cdktf": "0.20.8",
"cdktf-cli": "0.20.8",
"constructs": "10.3.0"
},
"devDependencies": {
"@pocket-tools/eslint-config": "workspace:*",
"@types/node": "^22.5.2",
"ts-node": "10.9.2",
"tsconfig": "workspace:*",
"typescript": "5.5.4"
}
}
36 changes: 36 additions & 0 deletions infrastructure/otel-collector/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const name = 'OtelCollector';
const domainPrefix = 'otel-collector';
const isDev = process.env.NODE_ENV === 'development';
const environment = isDev ? 'Dev' : 'Prod';
const domain = isDev
? `${domainPrefix}.getpocket.dev`
: `${domainPrefix}.readitlater.com`;
const isProd = process.env.NODE_ENV === 'production';
const releaseSha = process.env.CIRCLE_SHA1;

export const config = {
name,
isProd,
prefix: `${name}-${environment}`,
shortName: 'OTEL',
environment,
domain,
isDev,
releaseSha,
tags: {
service: name,
environment,
owner: 'Pocket',
costCenter: 'Shared',
app_code: 'pocket',
component_code: `pocket-${name.toLowerCase()}`,
env_code: isDev ? 'dev' : 'prod',
},
healthCheck: {
command: ['CMD-SHELL', 'curl -f http://localhost:3000/status || exit 1'],
interval: 15,
retries: 3,
timeout: 5,
startPeriod: 0,
},
};
183 changes: 183 additions & 0 deletions infrastructure/otel-collector/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import { config } from './config';

import {
provider as awsProvider,
cloudwatchLogGroup,
dataAwsCallerIdentity,
dataAwsRegion,
dataAwsKmsAlias,
dataAwsSnsTopic,
} from '@cdktf/provider-aws';
import { provider as localProvider } from '@cdktf/provider-local';
import { provider as nullProvider } from '@cdktf/provider-null';
import { PocketALBApplication } from '@pocket-tools/terraform-modules';

import { App, S3Backend, TerraformStack } from 'cdktf';
import { Construct } from 'constructs';
import fs from 'fs';

class OTELCollector extends TerraformStack {
constructor(scope: Construct, name: string) {
super(scope, name);

new awsProvider.AwsProvider(this, 'aws', {
region: 'us-east-1',
defaultTags: [{ tags: config.tags }],
});
new localProvider.LocalProvider(this, 'local_provider');
new nullProvider.NullProvider(this, 'null_provider');
new S3Backend(this, {
bucket: `mozilla-pocket-team-${config.environment.toLowerCase()}-terraform-state`,
dynamodbTable: `mozilla-pocket-team-${config.environment.toLowerCase()}-terraform-state`,
key: config.name,
region: 'us-east-1',
});

const caller = new dataAwsCallerIdentity.DataAwsCallerIdentity(
this,
'caller',
);
const region = new dataAwsRegion.DataAwsRegion(this, 'region');

const alarmTopic = this.getCodeDeploySnsTopic();

this.createPocketAlbApplication({
secretsManagerKmsAlias: this.getSecretsManagerKmsAlias(),
snsTopic: alarmTopic,
region,
caller,
});
}

/**
* Get the sns topic for code deploy
* @private
*/
private getCodeDeploySnsTopic() {
return new dataAwsSnsTopic.DataAwsSnsTopic(this, 'backend_notifications', {
name: `Backend-${config.environment}-ChatBot`,
});
}

/**
* Get secrets manager kms alias
* @private
*/
private getSecretsManagerKmsAlias() {
return new dataAwsKmsAlias.DataAwsKmsAlias(this, 'kms_alias', {
name: 'alias/aws/secretsmanager',
});
}

private createPocketAlbApplication(dependencies: {
region: dataAwsRegion.DataAwsRegion;
caller: dataAwsCallerIdentity.DataAwsCallerIdentity;
secretsManagerKmsAlias: dataAwsKmsAlias.DataAwsKmsAlias;
snsTopic: dataAwsSnsTopic.DataAwsSnsTopic;
}): PocketALBApplication {
const { region, caller, secretsManagerKmsAlias, snsTopic } = dependencies;

return new PocketALBApplication(this, 'application', {
internal: true,
prefix: config.prefix,
alb6CharacterPrefix: config.shortName,
tags: config.tags,
cdn: false,
domain: config.domain,
taskSize: {
cpu: 2048,
memory: 4096,
},
containerConfigs: [
{
healthCheck: config.healthCheck,
name: 'app',
essential: true,
portMappings: [{ containerPort: 3000, hostPort: 3000 }],
logMultilinePattern: '^\\S.+',
logGroup: this.createCustomLogGroup('otel-collector'),
envVars: [
{
name: 'DEPLOYMENT_ENVIRONMENT_NAME',
value: config.tags.env_code,
},
],
secretEnvVars: [
{
name: 'GOOGLE_APPLICATION_CREDENTIALS_JSON',
valueFrom: `arn:aws:secretsmanager:${region.name}:${caller.accountId}:secret:Shared/GCP_SA_TRACES:::`,
},
],
},
],
codeDeploy: {
useCodeDeploy: true,
useCodePipeline: false,
useTerraformBasedCodeDeploy: false,
generateAppSpec: false,
snsNotificationTopicArn: snsTopic.arn,
successTerminationWaitTimeInMinutes: 5,
notifications: {
//only notify on failed deploys
notifyOnFailed: true,
notifyOnStarted: false,
notifyOnSucceeded: false,
},
},
exposedContainer: {
name: 'app',
port: 3000,
healthCheckPath: '/status',
},
ecsIamConfig: {
prefix: config.prefix,
taskExecutionRolePolicyStatements: [
//This policy could probably go in the shared module in the future.
{
actions: ['secretsmanager:GetSecretValue', 'kms:Decrypt'],
resources: [
`arn:aws:secretsmanager:${region.name}:${caller.accountId}:secret:Shared`,
`arn:aws:secretsmanager:${region.name}:${caller.accountId}:secret:Shared/*`,
secretsManagerKmsAlias.targetKeyArn,
],
effect: 'Allow',
},
],
taskRolePolicyStatements: [],
taskExecutionDefaultAttachmentArn:
'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy',
},
autoscalingConfig: {
targetMinCapacity: config.isProd ? 4 : 1,
targetMaxCapacity: config.isProd ? 20 : 10,
},
alarms: {},
});
}

/**
* Create Custom log group for ECS to share across task revisions
* @param containerName
* @private
*/
private createCustomLogGroup(containerName: string) {
const logGroup = new cloudwatchLogGroup.CloudwatchLogGroup(
this,
`${containerName}-log-group`,
{
name: `/Backend/${config.prefix}/ecs/${containerName}`,
retentionInDays: 90,
skipDestroy: true,
tags: config.tags,
},
);

return logGroup.name;
}
}

const app = new App();
const stack = new OTELCollector(app, 'otel-collector');
const tfEnvVersion = fs.readFileSync('.terraform-version', 'utf8');
stack.addOverride('terraform.required_version', tfEnvVersion);
app.synth();
14 changes: 14 additions & 0 deletions infrastructure/otel-collector/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "tsconfig/cdktf.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"exclude": [
"node_modules/",
"dist/"
],
"include": [
"src/**/*.ts",
]
}
Loading

0 comments on commit 9d140ee

Please sign in to comment.