1"use strict";(globalThis.webpackChunkdocs=globalThis.webpackChunkdocs||[]).push([[3482],{5191(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>r,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"integration/ecs","title":"Deploying on ECS","description":"Deploy the NeverBlink Agent on AWS Elastic Container Service (ECS) using Fargate, configured through environment variables backed by Secrets Manager.","source":"@site/docs/02-integration/ecs.md","sourceDirName":"02-integration","slug":"/integration/ecs","permalink":"/docs/integration/ecs","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":5,"frontMatter":{"title":"Deploying on ECS","sidebar_position":5,"slug":"/integration/ecs","description":"Deploy the NeverBlink Agent on AWS Elastic Container Service (ECS) using Fargate, configured through environment variables backed by Secrets Manager."},"sidebar":"docsSidebar","previous":{"title":"Deploying with Helm","permalink":"/docs/integration/helm"},"next":{"title":"Deploying on Nomad","permalink":"/docs/integration/nomad"}}');var i=t(4848),o=t(8453);const r={title:"Deploying on ECS",sidebar_position:5,slug:"/integration/ecs",description:"Deploy the NeverBlink Agent on AWS Elastic Container Service (ECS) using Fargate, configured through environment variables backed by Secrets Manager."},a=void 0,l={},c=[{value:"Store credentials in Secrets Manager",id:"store-credentials-in-secrets-manager",level:2},{value:"Task definition",id:"task-definition",level:2},{value:"Service",id:"service",level:2},{value:"Troubleshooting",id:"troubleshooting",level:2},{value:"Grant permission to the task execution role",id:"grant-permission-to-the-task-execution-role",level:3},{value:"Modify the task definition",id:"modify-the-task-definition",level:3}
1,{value:"Deploy the new version",id:"deploy-the-new-version",level:3},{value:"Inspect the logs",id:"inspect-the-logs",level:3}];function d(e){const n={a:"a",code:"code",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.p,{children:"Follow the instructions below to deploy the agent on your AWS Elastic Container Service (ECS) cluster. The basic procedure:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsx)(n.li,{children:"Store the agent's credentials (Ingest Key, cluster password) in Secrets Manager."}),"\n",(0,i.jsx)(n.li,{children:"Register a task definition in your AWS account."}),"\n",(0,i.jsx)(n.li,{children:"Register the service in your ECS cluster."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["This guide uses two credentials: the per-cluster ",(0,i.jsx)(n.strong,{children:"Ingest Key"})," (",(0,i.jsx)(n.code,{children:"NB_INGEST_KEY"}),", from ",(0,i.jsx)(n.strong,{children:"Preferences \u2192 Ingest Keys"})," or the Integration Wizard) and the organization-wide ",(0,i.jsx)(n.strong,{children:"Container Pull Token"})," (embedded in the ",(0,i.jsx)(n.code,{children:"image:"})," field of the task definition below, from ",(0,i.jsx)(n.strong,{children:"Preferences"})," or the Integration Wizard). See ",(0,i.jsx)(n.a,{href:"/docs/get-started/connecting-a-cluster#ingest-keys-and-container-pull-token",children:"Ingest Keys and Container Pull Token"})," for details on both."]}),"\n",(0,i.jsx)(n.h2,{id:"store-credentials-in-secrets-manager",children:"Store credentials in Secrets Manager"}),"\n",(0,i.jsx)(n.p,{children:"Create individual secrets for each sensitive value the task will need. Minimal example for an Amazon OpenSearch Service domain using an IAM task role (only the Ingest Key is sensitive \u2014 the host and region are passed as plain env vars in the task definition):"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"aws secretsmanager create-secret --name neverblink-agent/NB_INGEST_KEY --secret-string '<INGEST_KEY>'\n"})}),"\n",(0,i.jsxs)(n.p,{children:["For basic-auth clusters, also create a secret for the password (e.g. ",(0,i.jsx)(n.code,{children:"neverblink-agent/ELASTICSEARCH_PASSWORD"})," or ",(0,i.jsx)(n.code,{children:"neverblink-agent/OPENSEARCH_PASSWORD"}),")."]}),"\n",(0,i.jsxs)(n.p,{children:["Note the ARN of each secret, and update the task execution role to allow ",(0,i.jsx)(n.code,{children:"secretsmanager:GetSecretValue"})," on those resources. Assuming that your cluster uses the default task execution role (",(0,i.jsx)(n.code,{children:"ecsTaskExecutionRole"}),"), follow these directions:"]}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["Open the IAM console at ",(0,i.jsx)(n.a,{href:"https://console.aws.amazon.com/iam/",children:"https://console.aws.amazon.com/iam/"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["In the navigation pane, choose ",(0,i.jsx)(n.strong,{children:"Roles"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["Search the list of roles for ",(0,i.jsx)(n.code,{children:"ecsTaskExecutionRole"})," (or whatever task execution role you will use) and select it."]}),"\n",(0,i.jsxs)(n.li,{children:["Choose ",(0,i.jsx)(n.strong,{children:"Permissions"}),", ",(0,i.jsx)(n.strong,{children:"Add inline policy"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["Choose the ",(0,i.jsx)(n.strong,{children:"JSON"})," tab and specify the following JSON text, listing the full ARN of each secret you created."]}),"\n",(0,i.jsxs)(n.li,{children:["Choose ",(0,i.jsx)(n.strong,{children:"Review policy"}),". For ",(0,i.jsx)(n.strong,{children:"Name"})," specify ",(0,i.jsx)(n.code,{children:"NeverBlinkAgentSecrets"}),", then choose ",(0,i.jsx)(n.strong,{children:"Create policy"}),"."]}),"\n"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "Version": "2012-10-17",\n "Statement": [\n {\n "Effect": "Allow",\n "Action": ["secretsmanager:GetSecretValue"],\n "Resource": [\n "arn:aws:secretsmanager:REGION:AWS_ACCOUNT_ID:secret:neverblink-agent/NB_INGEST_KEY-HASH"\n ]\n }\n ]\n}\n'})}),"\n",(0,i.jsx)(n.h2,{id:"task-definition",children:"Task definition"}),"\n",(0,i.jsxs)(n.p,{children:["The task specification must be registered with your AW
1S account. It is not specific to a single ECS cluster. Create a file called ",(0,i.jsx)(n.code,{children:"neverblink-agent.json"}),", replacing the placeholders with values from your environment. Set ",(0,i.jsx)(n.code,{children:"AWS_ELASTICSEARCH_HOST"})," for an Elasticsearch-engine domain, or ",(0,i.jsx)(n.code,{children:"AWS_OPENSEARCH_HOST"})," for an OpenSearch-engine domain \u2014 see the matching ",(0,i.jsx)(n.a,{href:"/docs/supported-technologies",children:"Supported Technologies"})," page for the full set of env vars per cluster type."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "executionRoleArn": "arn:aws:iam::AWS_ACCOUNT_ID:role/ecsTaskExecutionRole",\n "family": "neverblink-agent",\n "containerDefinitions": [\n {\n "name": "neverblink-agent",\n "image": "r.neverbl.ink/<CONTAINER_PULL_TOKEN>/agent",\n "cpu": 1024,\n "memory": 16384,\n "portMappings": [],\n "essential": true,\n "environment": [\n { "name": "NB_AGENT_HEAP_SIZE", "value": "2G" },\n { "name": "AWS_OPENSEARCH_HOST", "value": "https://<DOMAIN_ENDPOINT>" },\n { "name": "AWS_REGION", "value": "<AWS_REGION>" }\n ],\n "secrets": [\n {\n "name": "NB_INGEST_KEY",\n "valueFrom": "arn:aws:secretsmanager:REGION:AWS_ACCOUNT_ID:secret:neverblink-agent/NB_INGEST_KEY-HASH"\n }\n ],\n "healthCheck": {\n "command": ["CMD-SHELL", "curl -fsSL http://localhost:8080/healthz"],\n "interval": 30,\n "timeout": 5,\n "retries": 3,\n "startPeriod": 30\n }\n }\n ],\n "networkMode": "awsvpc",\n "requiresCompatibilities": ["FARGATE"],\n "cpu": "2048",\n "memory": "16384",\n "runtimePlatform": {\n "cpuArchitecture": "X86_64",\n "operatingSystemFamily": "LINUX"\n }\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["For basic-auth clusters, add the username under ",(0,i.jsx)(n.code,{children:"environment"})," and the password under ",(0,i.jsx)(n.code,{children:"secrets"})," with the matching variable name (e.g. ",(0,i.jsx)(n.code,{children:"OPENSEARCH_USER"})," / ",(0,i.jsx)(n.code,{children:"OPENSEARCH_PASSWORD"}),")."]}),"\n",(0,i.jsx)(n.p,{children:"Then register the task definition:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"aws ecs register-task-definition --cli-input-json file://neverblink-agent.json\n"})}),"\n",(0,i.jsx)(n.h2,{id:"service",children:"Service"}),"\n",(0,i.jsxs)(n.p,{children:["After registering the task definition, create a service in the desired cluster. The NeverBlink Agent needs to be able to connect to both the monitored services and to ",(0,i.jsx)(n.a,{href:"https://neverblink.ai/",children:"https://neverblink.ai/"}),". Ensure that you select a subnet and security group that meets these requirements."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'aws ecs create-service --cluster my-cluster-name \\\n --service-name neverblink-agent \\\n --task-definition neverblink-agent \\\n --desired-count 1 \\\n --launch-type "FARGATE" \\\n --network-configuration "awsvpcConfiguration={subnets=[subnet-abcd1234],securityGroups=[sg-abcd1234]}"\n'})}),"\n",(0,i.jsx)(n.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,i.jsx)(n.p,{children:"If you encounter difficulties with your deployment, the first step is to enable logging to CloudWatch Logs for the NeverBlink Agent. The steps to do this are:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsx)(n.li,{children:"Grant permission to write logs to the ECS task execution role."}),"\n",(0,i.jsx)(n.li,{children:"Modify the task definition to enable logging."}),"\n",(0,i.jsx)(n.li,{children:"Deploy the new version of the task definition."}),"\n",(0,i.jsx)(n.li,{children:"Inspect the logs in the ECS console."}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"grant-permission-to-the-task-execution-role",children:"Grant permission to the task execution role"}),"\n",(0,i.jsxs)(n.p,{children:["Attach an inline policy to the task execution role in use (if you followed our recommended setup, the task execution role is ",(0,i.jsx)(n.code,{children:"ecsTaskExecutionRole"}),"):"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "Version": "2012-10-17",\n "Statement": [\n {\n "Effect": "Allow",\n "Action": [\n "logs:CreateLogStream",\n "logs:PutLogEvents"\n ],\n "Resource": "*"\n }\n ]\n}\n'})}),"\n",(0,i.jsx)(n.h3,{id:"modify-the-task-definition",children:"Modify the task definition"}),"\n",(0,i.jsxs)(n.p,{children:["Add a ",(0,i.jsx)(n.code,{children:"logConfiguration"})," section to the task definition and upload a revision. The relevant section looks like this:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "containerDefinitions": [\n {\n "name": "neverblink-agent",\n "logConfiguration": {\n "logDriver": "awslogs",\n "options": {\n "awslogs-create-group": "true",\n "awslogs-group": "neverblink-agent",\n "awslogs-region": "MY_AWS_REGION",\n "awslogs-stream-prefix": "neverblink-agent"\n }\n }\n }\n ]\n}\n'})}),"\n",(0,i.jsx)(n.h3,{id:"deploy-the-new-version",children:"Deploy the new version"}),"\n",(0,i.jsxs)(n.p,{children:["Upload the modified task definition using ",(0,i.jsx)(n.code,{children:"aws ecs register-task-definition"}),". Then use ",(0,i.jsx)(n.code,{children:"aws ecs update-service"})," to deploy the new revision."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"aws ecs register-task-definition --cli-input-json file://neverblink-agent.json\naws ecs update-service --cluster my-cluster-name --service neverblink-agent --task-definition neverblink-agent\n"})}),"\n",(0,i.jsx)(n.h3,{id:"inspect-the-logs",children:"Inspect the logs"}),"\n",(0,i.jsx)(n.p,{children:'From the AWS ECS console, select your cluster, then the neverblink-agent service, and then click the logs tab. This provides easy access to the logs, and you can additionally click the button in the console to go to the full AWS CloudWatch Logs page for the logs. From the AWS CloudWatch Logs console, select "Actions" -> "Download Search results (CSV)" to create a CSV file that you can send to NeverBlink support.'}),"\n",(0,i.jsxs)(n.p,{children:["Any messages with ",(0,i.jsx)(n.code,{children:'"level":"ERROR"'})," or ",(0,i.jsx)(n.code,{children:'"level":"WARN"'})," are worth looking into."]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,n,t){t.d(n,{R:()=>r,x:()=>a});var s=t(6540);const i={},o=s.createContext(i);function r(e){const n=s.useContext(o);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),s.createElement(o.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.