ES /docs

AwsS3Manager::downloadParallel | begin - "Missing credentials in config, if using AWS_CONFIG_FILE, s

RCA: AwsS3Manager::downloadParallel | Missing credentials in config

Error Log#

Datadog Logs

text
AwsS3Manager::downloadParallel | begin - "Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1"

Impact#

  • Service: cupixworks-pano-postprocessor-instance
  • Team: built
  • 발생 횟수: 30
  • 최초 발생: 2026-04-16T12:20:00.960Z
  • 최근 발생: 2026-04-16T12:20:00.961Z

Root Cause Summary#

AwsS3Manager.download() 메서드가 new AWS.S3({ region }) 으로 S3 클라이언트를 생성할 때 명시적 credentials를 전달하지 않으며, AWS SDK default credential provider chain(ECS task role)에 의존한다. Terraform에서 ECS task role에 s3:ListBucket/s3:GetObject 권한이 *-source-* 패턴으로 부여되어 있으나(pano_postprocessor/main.tf:84-95), AWS SDK v2가 ECS task role credentials를 resolve하지 못해 "Missing credentials in config" 에러가 발생했다. ap-southeast-2 리전의 capture 68852에 속한 pano들은 bucket_endpoint가 없다 — 이는 AWS S3에 직접 저장된 데이터를 의미하며(MinIO가 아닌 경우), ECS task role을 통한 직접 S3 다운로드가 설계된 동작이다. 그러나 credential provider chain이 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 환경변수를 통해 ECS task role을 해석하는 과정이 실패하여 다운로드가 모두 실패했다. 에러가 downloadParallel의 catch 블록에서 개별적으로 잡혀 다운로드 단계는 "완료"되었지만, 실제 이미지 파일이 없어 후속 mask inference 단계(MaskWork::maskPanos)에서 Python 스크립트가 실패하며 전체 job이 실패했다.

Technical Analysis#

Code Path#

  • Entry point: pano-postprocessor-service.ts:43init() 호출
  • Authentication: pano-postprocessor-service.ts:62-77 — Cupix API 인증 (성공)
  • Run pipeline: pano-postprocessor-service.ts:79run() 시작
  • Pano fetch: pano-postprocessor-service.ts:96 — Tesla API에서 capture 68852의 pano 목록 조회
  • Download dispatch: pano-postprocessor-service.ts:117awsS3Manager.downloadParallel(cpPanos) 호출

downloadParallel 내부에서 각 pano에 대해 panoBucketEndpoint 존재 여부를 확인한다:

typescript
// aws-s3.manager.ts:60-77
const tasks = cpPano.map(cpPano => {
    return limit(async () => {
        try {
            logger.debug('AwsS3Manager::downloadParallel | buket region, name, key, endpoint, download path %s %s %s %s %s', cpPano.panoBucketRegion, cpPano.panoBucketName, cpPano.panoBucketKey, cpPano.panoBucketEndpoint, cpPano.downloadImagePath);

            if (cpPano.panoBucketEndpoint) {
                await this.downloadByPanoApi(cpPano);
            } else {
                await this.download(
                    cpPano.panoBucketRegion!,
                    cpPano.panoBucketName!,
                    cpPano.panoBucketKey!,
                    cpPano.downloadImagePath!
                );
            }
        } catch (err) {
            logger.error('AwsS3Manager::downloadParallel | download failed - pano: %s, error: %s', cpPano.downloadImagePath, (err as Error).message);
        }
    });
});

panoBucketEndpoint가 undefined이므로 else 분기로 진입하여 download() 메서드 호출:

typescript
// aws-s3.manager.ts:18-21
download(bucketRegion: string, bucketName: string, bucketKey: string, downloadPath: string): Promise<void> {
    if (!this._s3) {
        this._s3 = new AWS.S3({ region: bucketRegion });
    }
  • Failure point: aws-s3.manager.ts:20new AWS.S3({ region: bucketRegion }) 생성 시 명시적 credentials를 전달하지 않음. 이는 설계상 ECS task role credentials를 AWS SDK v2 credential provider chain을 통해 자동으로 resolve하도록 의도된 것이나, credential chain이 정상 동작하지 못해 "Missing credentials in config" 에러 발생.

AWS SDK v2 Credential Provider Chain 순서 (aws-sdk v2.1599.0):

AWS SDK v2의 CredentialProviderChain.defaultProviders는 다음 순서로 credentials를 시도한다:

  1. EnvironmentCredentials('AWS')AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY 환경변수 (미설정)
  2. EnvironmentCredentials('AMAZON')AMAZON_ACCESS_KEY_ID 환경변수 (미설정)
  3. SsoCredentials — SSO config (컨테이너 내 없음)
  4. SharedIniFileCredentials~/.aws/credentials 파일 (컨테이너 내 없음)
  5. ECSCredentialsAWS_CONTAINER_CREDENTIALS_RELATIVE_URI 환경변수가 설정된 경우 http://169.254.170.2{uri} 호출
  6. ProcessCredentialscredential_process (미설정)
  7. TokenFileWebIdentityCredentials — Web Identity Token (미설정)
  8. EC2MetadataCredentialshttp://169.254.169.254 EC2 메타데이터 서비스

pano-postprocessor 컨테이너는 network_mode = "bridge" (main.tf:171)로 실행되며, ECS-optimized GPU AMI (amzn2-ami-ecs-gpu-hvm-*, main.tf:551)를 사용한다. ECS-optimized AMI는 ECS_ENABLE_TASK_IAM_ROLE=true169.254.170.2 iptables 라우팅 규칙을 자동 포함하므로, ECS agent가 컨테이너에 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 환경변수를 주입하여 5번(ECSCredentials)에서 task role credentials를 resolve해야 한다.

"Missing credentials in config" 에러 발생 조건 (AWS SDK v2 소스 분석):

이 에러는 aws-sdk/lib/event_listeners.jsVALIDATE_CREDENTIALS 이벤트에서 발생한다. getCredentials() 호출 시 위 8개 provider가 모두 실패하면, 마지막 provider의 에러가 generic 메시지 "Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1"로 래핑된다. 실제 원인(underlying error)은 err.originalError에만 보존되며, 대부분의 로깅 프레임워크에서 이를 출력하지 않아 실제 실패 원인이 마스킹된다 (aws-sdk-js issue #4417).

ECS 환경에서 이 에러가 발생하는 주요 시나리오:

  • AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 미주입 — ECS agent 버전 < 1.11.0이거나, ECS_ENABLE_TASK_IAM_ROLE=true 미설정, 또는 bridge mode에서 iptables 라우팅 미구성
  • Credential endpoint 타임아웃169.254.170.2 endpoint가 고부하 상태에서 1초(기본값) 내 응답 실패 (aws-sdk-js issue #3284)
  • IAM 권한 부족 — task role이 sts:AssumeRole 등 필요 권한 미보유 시, 실제 에러는 permission denial이지만 generic 메시지로 마스킹 (aws-sdk-js issue #3304)
  • AWS_SDK_LOAD_CONFIG=1 미설정~/.aws/config 파일에 credential_process나 profile이 정의된 경우에만 해당. ECS 환경에서는 일반적으로 무관

본 케이스에서 AWS_SDK_LOAD_CONFIG=1 설정은 해결책이 아니다. ECS 컨테이너에는 ~/.aws/config 파일이 없으므로 이 환경변수는 효과가 없다. 에러 메시지에 포함된 "if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1" 문구는 AWS SDK v2가 모든 credential 실패에 대해 출력하는 generic 안내이며, 개발자 워크스테이션 환경을 대상으로 한 것이다.

panoBucketEndpoint 분기의 설계 의도:

bucket_endpoint 필드는 MinIO 등 S3 호환 스토리지를 지원하기 위해 존재한다. uploadDirectoryByCredential 메서드에서 이를 확인할 수 있다:

typescript
// aws-s3.manager.ts:108-117
async uploadDirectoryByCredential({ credential, directory }: { credential: TESLA.UploadCredentials, directory: string }): Promise<void> {
    const s3 = new AWS.S3({
        apiVersion: '2006-03-01',
        signatureVersion: 'v4',
        accessKeyId: credential['aws_access_key_id'],
        secretAccessKey: credential['aws_secret_access_key'],
        sessionToken: credential['aws_session_token'],
        endpoint: credential['endpoint'],      // MinIO endpoint
        s3ForcePathStyle: true,                // MinIO 필수 설정
    });
  • bucket_endpoint 있음 (MinIO): MinIO에 직접 접근할 수 없으므로 downloadByPanoApi를 통해 Cupix API HTTP 프록시 경유로 다운로드 (aws-s3.manager.ts:153-176)
  • bucket_endpoint 없음 (AWS S3): ECS task role의 IAM credentials를 사용하여 AWS S3에 직접 다운로드하도록 설계 (aws-s3.manager.ts:18-49)

CPPano 모델에서 panoBucketEndpoint는 pano resource의 bucket_endpoint 필드에서 가져온다:

typescript
// cppano.ts:35
this._panoBucketEndpoint = pano.resources[0]['bucket_endpoint'];

ECS Task Role IAM 권한 (정상 구성됨):

Terraform에서 pano_postprocessor ECS task role에 S3 권한이 명시적으로 부여되어 있다:

hcl
# cupix-infrastructure: cupix-service/ecs/tasks/pano_postprocessor/main.tf:77-98
resource "aws_iam_role_policy" "ecs_task_policy" {
  name = "ecs-${local.name}-policy-${random_string.random.result}"
  role = aws_iam_role.ecs_task_role.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Sid    = "HandleS3",
        Effect = "Allow",
        Action = [
          "s3:ListBucket",
          "s3:GetObject"
        ],
        Resource = [
          "arn:aws:s3:::*-source-*",
          "arn:aws:s3:::*-source-*/*"
        ]
      }
    ]
  })
}

Task definition에도 task role이 할당되어 있다:

hcl
# cupix-infrastructure: cupix-service/ecs/tasks/pano_postprocessor/main.tf:169-170
task_role_arn      = aws_iam_role.ecs_task_role.arn
execution_role_arn = aws_iam_role.ecs_task_role.arn

따라서 IAM 정책 자체는 정상이며, 문제는 AWS SDK v2가 ECS task role credentials를 resolve하지 못하는 것이다. network_mode = "bridge" 환경에서 ECS agent가 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 환경변수를 컨테이너에 주입해야 SDK가 task role을 사용할 수 있는데, 이 과정이 실패하고 있을 가능성이 높다.

참고: tesla-compute-agent 서비스는 명시적 AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY 환경변수를 주입하는 방식(tesla-compute-agent/main.tf:33-39)을 사용하므로 이 문제가 발생하지 않는다. pano-postprocessor는 이와 다르게 ECS task role 방식을 사용한다.

Log Evidence#

Job 178071 (capture 68852, ap-southeast-2 리전) 전체 타임라인:

text
Datadog query: service:cupixworks-pano-postprocessor-instance (status:error OR status:info) @CPX_JOB_ID:178071
text
12:19:40.174Z [INFO]  PanoPostprocessorService::init                          (4 hosts)
12:19:40.175Z [INFO]  PanoPostprocessorService::authenticate | begin          (4 hosts)
12:19:40.249Z [INFO]  CupixAuth::setSession | session_id: 69f95a00...        (4 hosts)
12:19:40.250Z [INFO]  PanoPostprocessorService::run | begin                   (4 hosts)
12:20:00.960Z [ERROR] AwsS3Manager::downloadParallel | begin - "Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1"
                      (4 hosts x ~10 = 40 errors, within 1ms)
12:20:02.617Z [INFO]  PanoPostprocessorService Elapsed Time - dd_step:download_panos&dd_elapsed_time:11466ms  (4 hosts)
12:20:18.327Z [ERROR] MaskWork::maskPanos | script ...UserWarning: No PYTORCH_KERNEL_CACHE_PATH or HOME...   (4 hosts)
12:23:54.125Z [ERROR] MaskWork::maskPanos | script Traceback ... cupix_inference.py:334, in detect            (4 hosts)
12:23:54.875Z [ERROR] MaskWork::maskPanos | error code:1                     (4 hosts)
12:23:55.151Z [ERROR] PanoPostprocessorService::run | end - false             (4 hosts)
12:23:57.933Z [INFO]  PanoPostprocessorService::terminateService | force shutdown after 10 seconds  (4 hosts)

주요 관찰:

  • 4개 컨테이너 호스트(e9c89f97b7a4, acfb71f39266, eb1c7e3537d8, 820881526b62)에서 동시에 동일 에러 발생
  • 에러가 downloadParallel의 catch 블록에서 잡혀 download step은 11.4초 만에 "완료"로 기록됨
  • 이미지 파일 없이 후속 mask inference(MaskWork::maskPanos) 실행 → Python 스크립트 실패 (exit code 1)
  • 최종적으로 PanoPostprocessorService::run | end - false로 job 실패

동일 시간대 다른 postprocessor 서비스 에러: 없음 (이 이슈는 cupixworks-pano-postprocessor-instance에 국한됨)

text
Datadog query: service:cupixworks-pano-postprocessor* status:error -service:cupixworks-pano-postprocessor-instance
Result: 0 logs

Fix Recommendation#

즉시 조치 (Critical)#

  • ECS 컨테이너 내 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 확인ap-southeast-2 리전의 pano-postprocessor ECS 컨테이너에 exec으로 접속하여 환경변수 존재 여부 확인. 이 변수가 없으면 ECSCredentials provider가 skip되어 credential chain 전체가 실패함. ECS agent 버전(≥ 1.11.0 필요), ECS_ENABLE_TASK_IAM_ROLE=true 설정, 169.254.170.2 iptables 라우팅 규칙 확인 필요.
  • err.originalError 로깅 추가 — AWS SDK v2는 실제 credential 실패 원인을 err.originalError에만 보존하며 generic 메시지로 마스킹 (aws-sdk-js issue #4417). aws-s3.manager.ts:65의 catch 블록에서 (err as any).originalError도 함께 로깅하면 실제 실패 원인(타임아웃, 권한 부족, endpoint 미접근 등) 특정 가능.
  • S3 bucket 이름 패턴 확인 — IAM policy의 resource pattern이 *-source-* (commit 5d62c05d에서 변경됨)인데, 실제 ap-southeast-2 리전의 source bucket 이름이 이 패턴과 매치하는지 확인 필요. 예: cupix-source-au는 매치되지만 cupix-source는 매치 안 됨.

단기 개선 (1주 이내)#

  • Credential provider 타임아웃 증가 — ECSCredentials의 기본 HTTP 타임아웃이 1초로, 고부하 시 실패 가능 (aws-sdk-js issue #3284). aws-s3.manager.ts:20에서 S3 클라이언트 생성 전에 AWS.config.credentials = new AWS.ECSCredentials({ httpOptions: { timeout: 5000 }, maxRetries: 10 })로 명시적 설정하거나, S3 생성 시 credential provider를 지정하는 방안 검토.
  • aws-s3.manager.ts:64-66downloadParallel의 catch 블록에서 개별 pano 다운로드 실패 시 에러를 로깅만 하고 계속 진행하는 현재 로직은 위험. 다운로드 실패 pano를 추적하고, 전체 다운로드 실패율이 임계값을 초과하면 후속 단계(mask, infer, resize) 진입 전에 job을 조기 중단하는 로직 추가 필요.
  • ECS agent 설정 및 credential endpoint 접근이 모든 리전에서 일관되게 동작하는지 검증. 특히 ap-southeast-2의 subnet filtering(filtered_subnets, main.tf:14-16)이 credential endpoint 라우팅에 영향을 주는지 확인.

장기 개선 (재발 방지)#

  • AWS SDK v3 전체 마이그레이션 + Root-level 버전 관리 — aws-sdk v2 (2.1599.0)는 AWS에서 공식 지원 종료(end-of-support). v3는 credential provider chain에서 실패 시 구체적인 CredentialsProviderError를 반환하여 "Missing credentials in config"과 같은 generic 메시지로 마스킹되지 않음. 또한 fromContainerMetadata({ timeout: 5000, maxRetries: 5 })로 ECS credential endpoint 타임아웃을 간편하게 설정 가능. 다만 v3 자체가 credential resolution 실패를 해결하지는 않음 — 근본 원인이 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 미주입이거나 credential endpoint 타임아웃이면 v3에서도 동일하게 실패하나, 에러 메시지가 구체적이므로 원인 파악이 즉시 가능해짐.

    Step 1: Root-level 버전 관리 전환

    현재 aws-sdk는 3개 패키지에서 각각 독립적으로 버전을 관리하며 불일치가 존재한다:

    • base/package.json: "aws-sdk": "2.1599.0" (pinned)
    • cupix-pano-postprocessor/package.json: "aws-sdk": "2.1599.0" (pinned)
    • cupix-tesla-compute-agent/package.json: "aws-sdk": "^2.1407.0" (range — 192 버전 뒤처짐)

    pnpm workspace의 catalog 기능(pnpm-workspace.yaml)이 이미 async, request, download, three, tail 등의 공유 의존성 버전 관리에 사용되고 있다. v3 마이그레이션 시 @aws-sdk/client-* 패키지들을 catalog에 등록하여 모든 agent가 동일 버전을 사용하도록 한다:

    yaml
    # pnpm-workspace.yaml catalog 추가
    catalog:
      "@aws-sdk/client-s3": ^3.x.x
      "@aws-sdk/lib-storage": ^3.x.x
      "@aws-sdk/client-sqs": ^3.x.x
      "@aws-sdk/client-ecs": ^3.x.x
      "@aws-sdk/client-auto-scaling": ^3.x.x
      "@aws-sdk/credential-providers": ^3.x.x
    

    각 패키지의 package.json에서는 catalog:로 참조:

    json
    \{ "dependencies": \{ "@aws-sdk/client-s3": "catalog:" \} \}
    

    Step 2: 전체 agents monorepo 마이그레이션 (권장 접근)

    pano-postprocessor만 단독 마이그레이션하는 것이 아니라, 모든 agent를 일괄 마이그레이션한다. 마이그레이션 완료 후 root에서 aws-sdk v2를 완전 제거한다.

    변경 대상 — 직접 의존 패키지 (3개, 13개 소스 파일):

    패키지 파일 AWS 서비스 주요 변경
    base aws-s3.manager.ts S3 AWS.S3()S3Client + Upload
    base aws-ecs.manager.ts ECS AWS.ECS()ECSClient + Command 패턴
    base aws-queue.manager.ts SQS AWS.SQS()SQSClient + Command 패턴
    base base-service.ts (type only) AWS.SQS.MessageMessage from @aws-sdk/client-sqs
    base aws-s3.manager.spec.ts S3 (test) mock 패턴 변경
    base aws-ecs.manager.spec.ts ECS (test) mock 패턴 변경
    base base-service.spec.ts (test) type import 변경
    pano-postprocessor aws-s3.manager.ts S3 AWS.S3()S3Client, getObject().createReadStream()GetObjectCommand + stream, upload().promise()Upload 클래스
    tesla-compute-agent qmAws.ts SQS, ECS, AutoScaling 3개 서비스 클라이언트 전환, callback → Command 패턴
    tesla-compute-agent qmJob.ts (type only) AWS.SQS.MessageMessage
    tesla-compute-agent job.manager.ts (type only) type import 변경
    tesla-compute-agent qmJob.spec.ts (test) type import 변경

    변경 대상 — 간접 의존 패키지 (type 전파, 2개):

    패키지 파일 변경
    cupix-tesla-forge-agent forge-service.ts AWS.SQS.Message (7곳) → Message from @aws-sdk/client-sqs
    cupix-tesla-potree-agent potree-service.ts AWS.SQS.Message (7곳) → Message from @aws-sdk/client-sqs

    v2 → v3 API 매핑 (4개 서비스):

    • S3: new AWS.S3({ region })new S3Client({ region }), s3.getObject(params).createReadStream()s3.send(new GetObjectCommand(params)) + resp.Body, s3.upload({...}).promise()new Upload({ client: s3, params: {...} }), s3ForcePathStyleforcePathStyle
    • SQS: new AWS.SQS()new SQSClient(), sqs.receiveMessage(params, callback)sqs.send(new ReceiveMessageCommand(params)), AWS.SQS.MessageMessage from @aws-sdk/client-sqs
    • ECS: new AWS.ECS()new ECSClient(), ecs.runTask(params, callback)ecs.send(new RunTaskCommand(params)), ecs.stopTaskStopTaskCommand, ecs.listTasksListTasksCommand, ecs.startTaskStartTaskCommand
    • AutoScaling: new AWS.AutoScaling()new AutoScalingClient(), autoScaling.describeAutoScalingGroupsDescribeAutoScalingGroupsCommand, autoScaling.updateAutoScalingGroupUpdateAutoScalingGroupCommand

    마이그레이션 순서 (권장):

    1. pnpm-workspace.yaml catalog에 @aws-sdk/client-* 버전 등록
    2. base 패키지 마이그레이션 (core managers — S3, SQS, ECS) + base-service.ts type 변경
    3. cupix-pano-postprocessor 마이그레이션 (독립 S3 manager)
    4. cupix-tesla-compute-agent 마이그레이션 (SQS, ECS, AutoScaling)
    5. downstream type 전파: forge-agent, potree-agentAWS.SQS.MessageMessage
    6. 3개 패키지에서 aws-sdk v2 dependency 제거
    7. pnpm-workspace.yamlonlyBuiltDependencies에서 aws-sdk 제거
  • download()uploadDirectoryByCredential() 간 credential 처리 방식 통일 검토. 현재 upload는 명시적 credentials + MinIO endpoint, download는 ECS task role implicit credentials로 비대칭적. 다만 이 비대칭은 의도된 설계(MinIO vs AWS S3)이므로, download 경로의 ECS task role credential chain이 안정적으로 동작하도록 보장하는 것이 우선.

Monitoring#

  • 다운로드 실패율 모니터링 추가:
text
service:cupixworks-pano-postprocessor-instance "AwsS3Manager::downloadParallel | download failed" status:error
  • Credential 관련 에러 전용 알림:
text
service:cupixworks-pano-postprocessor-instance "Missing credentials in config" status:error
  • Job 성공/실패율 대시보드 (PanoPostprocessorService::run | end - false 비율 추적)

Risk Assessment#

  • Risk level: medium — AWS S3에 직접 저장된 pano(MinIO endpoint 없음)를 처리하는 ap-southeast-2 리전 job이 모두 실패함. MinIO endpoint가 있는 pano는 API 프록시 경유로 정상 동작.
  • 예상 복잡도: investigation-needed — IAM 정책 자체는 정상 구성되어 있으므로 ECS agent → credential endpoint → SDK credential chain의 어느 단계에서 실패하는지 특정이 필요. 인프라(ECS agent 설정, 네트워크 라우팅) 조사가 선행되어야 함.

Revision History#

Revision 1#

Feedback:

  1. endpoint 있는건 aws 말고 minio download 지원하기 위해서 그런거임
  2. endpoint 없는건 ecs container 에 권한을 주고 있는데 이거 권한이 빠져서 그런건지 한번 확인해바

판정:

피드백 항목 판정 근거
bucket_endpoint는 MinIO 지원용 수용 aws-s3.manager.ts:108-117uploadDirectoryByCredential에서 endpoint: credential['endpoint'] + s3ForcePathStyle: true 설정은 MinIO 필수 패턴. downloadByPanoApi(aws-s3.manager.ts:153-176)는 endpoint가 있을 때 Cupix API HTTP 프록시를 사용하여 MinIO에 간접 접근. 기존 RCA는 이를 단순히 "Cupix Pano API 경유"로만 기술했으나, 실제 설계 의도는 MinIO 스토리지 지원.
ECS container 권한 확인 수용 cupix-infrastructure: cupix-service/ecs/tasks/pano_postprocessor/main.tf:77-98에서 ECS task role에 s3:ListBucket/s3:GetObject 권한이 *-source-* 패턴으로 부여됨. main.tf:169-170에서 task_role_arn이 task definition에 할당됨. IAM 정책 자체는 빠지지 않았다. 기존 RCA는 "IAM user credentials에 S3 권한이 없다"고 잘못 분석했으나, 실제로는 ECS task role 방식을 사용하며 권한도 부여되어 있음. 문제는 AWS SDK v2가 credential provider chain을 통해 task role credentials를 resolve하지 못하는 것. 비교: tesla-compute-agent/main.tf:33-39는 명시적 AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY 주입 방식을 사용하여 이 문제 없음.

변경 사항:

  • Root Cause Summary: "IAM user credentials에 S3 권한이 없다" → "IAM 정책은 정상이나 AWS SDK v2가 ECS task role credentials를 resolve하지 못함"으로 수정
  • Code Path: panoBucketEndpoint 분기 설명에 MinIO 지원 목적 명시, Terraform IAM policy 코드 증거 추가, tesla-compute-agent와의 credential 방식 차이 설명 추가
  • Fix Recommendation: "Terraform IAM policy에 S3 권한 추가" → "ECS task role credential chain 점검 (ECS agent 설정, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, credential endpoint 접근)" + "S3 bucket 이름 패턴 매치 확인"으로 수정
  • Risk Assessment: 복잡도를 "standard"에서 "investigation-needed"로 변경 (인프라 레벨 조사 필요)

추가 조사 내용:

  • cupix-infrastructure 레포 탐색: cupix-service/ecs/tasks/pano_postprocessor/main.tf — ECS task role + IAM policy 확인
  • cupix-infrastructure 레포 탐색: cupix-service/ecs/services/tesla-compute-agent/main.tf:33-39 — 명시적 credentials 주입 방식 비교
  • cupix-infrastructure 레포 탐색: cupix-service/ecs/instance-profile/main.tf — EC2 instance profile에는 S3 권한 없음 확인
  • cupix-infrastructure 레포 탐색: cupix-service/ecs/main.tf:311-329 — pano_postprocessor 모듈 호출에 secrets/추가 environment 미전달 확인
  • cupixworks 레포 탐색: aws-s3.manager.ts 전체 파일 — MinIO endpoint + s3ForcePathStyle 패턴 확인
  • Terraform git history: commit 5d62c05d에서 S3 ARN 패턴이 *-source*-source-*로 변경됨 확인
  • Datadog 로그: 7일간 "Missing credentials" 에러가 ap-southeast-2 리전에서만 발생 확인

Revision 3#

Feedback: "Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1" — 이 에러가 언제 발생하는건지 인터넷에서도 한번 찾아봐 그리고 해결방법도

판정:

피드백 항목 판정 근거
에러 발생 조건 상세화 수용 AWS SDK v2 소스(lib/event_listeners.jsVALIDATE_CREDENTIALS, lib/credentials/credential_provider_chain.jsdefaultProviders)를 분석한 결과, 이 에러는 8개 credential provider가 모두 실패할 때 마지막 에러를 "Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1"로 래핑하여 발생. 실제 원인은 err.originalError에만 보존됨 (aws-sdk-js issue #4417). ECS bridge mode 환경에서는 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 미주입, credential endpoint 타임아웃(1초 기본값, issue #3284), IAM 권한 부족(issue #3304)이 주요 트리거.
인터넷 조사 + 해결방법 수용 AWS 공식 문서(docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html), ECS task IAM roles 문서(docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html), aws-sdk-js GitHub issues(#4417, #3284, #3304)를 조사. 해결방법: (1) ECS 컨테이너 내 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 존재 확인, (2) err.originalError 로깅으로 실제 원인 파악, (3) ECSCredentials 타임아웃 증가(httpOptions.timeout: 5000, maxRetries: 10), (4) ECS agent ≥ 1.11.0 + ECS_ENABLE_TASK_IAM_ROLE=true 확인(ECS-optimized AMI 사용 시 자동 포함), (5) bridge mode에서 169.254.170.2 iptables 라우팅 규칙 확인(ECS-optimized AMI 자동 포함), (6) 장기적으로 AWS SDK v3 마이그레이션. AWS_SDK_LOAD_CONFIG=1 자체는 ECS 환경에서 무의미 — ~/.aws/config 파일이 없으므로 효과 없음.

변경 사항:

  • Technical Analysis > Code Path: AWS SDK v2 credential provider chain 8단계 순서 추가, ECS-optimized AMI가 자동으로 ECS_ENABLE_TASK_IAM_ROLE=true + iptables 설정 포함하는 점 설명, "Missing credentials" 에러 발생 조건 4가지 시나리오 상세 기술, AWS_SDK_LOAD_CONFIG=1이 ECS 환경에서 무관한 이유 설명
  • Fix Recommendation > 즉시 조치: err.originalError 로깅 추가 권장 신규 항목 추가
  • Fix Recommendation > 단기 개선: ECSCredentials 타임아웃 증가 방안 추가 (httpOptions.timeout: 5000, maxRetries: 10)
  • Fix Recommendation > 장기 개선: AWS SDK v3 마이그레이션 검토 항목 추가 (v2 end-of-support)

추가 조사 내용:

  • AWS SDK v2 공식 문서: credential provider chain 순서 및 ECS container credential provider 동작 확인 (docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html)
  • AWS ECS task IAM roles 공식 문서: bridge mode에서 iptables 규칙 + ECS_ENABLE_TASK_IAM_ROLE=true 필요, ECS-optimized AMI 사용 시 자동 포함 확인 (docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html)
  • aws-sdk-js GitHub issue #4417: generic error message가 실제 원인을 마스킹하며, originalError에만 보존됨 확인
  • aws-sdk-js GitHub issue #3284: Fargate 환경에서 credential endpoint 타임아웃 (1초 기본값) 이슈 확인
  • aws-sdk-js GitHub issue #3304: IAM 권한 부족 시에도 동일한 generic 메시지 출력 확인
  • cupix-infrastructure 레포: init_gpu.sh — user data에 ECS_ENABLE_TASK_IAM_ROLE 명시 없음 (ECS-optimized AMI가 자동 처리)
  • cupix-infrastructure 레포: main.tf:551amzn2-ami-ecs-gpu-hvm-* AMI 사용 확인 (ECS-optimized, task IAM role 자동 지원)
  • cupixworks 레포: package.json — aws-sdk v2.1599.0 사용 확인 (end-of-support 버전)

Revision 4#

Feedback: AWS SDK v3 마이그레이션 했을때 해당문제가 해결되는지, agent 코드내 변경사항이 어느정도 될지 확인좀

판정:

피드백 항목 판정 근거
SDK v3 마이그레이션 시 문제 해결 여부 부분 수용 v3 자체가 credential resolution 실패를 직접 해결하지는 않음. 근본 원인이 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 미주입이거나 credential endpoint 타임아웃이면 v3에서도 동일하게 실패한다. 단, v3는 credential provider 실패 시 CredentialsProviderError에 구체적 원인을 포함하여 반환하므로(v2의 generic "Missing credentials in config" 메시지와 달리), 디버깅이 즉시 가능해짐. 또한 fromContainerMetadata({ timeout: 5000, maxRetries: 5 })로 ECS credential endpoint 타임아웃을 간편하게 늘릴 수 있어, 타임아웃이 원인인 경우 실질적 해결이 됨. 결론: 진단 용이성 + 타임아웃 시나리오에서는 해결 가능하나, 환경변수 미주입 등 인프라 레벨 문제는 v3로도 해결 불가.
agent 코드 변경 범위 확인 수용 cupixworks 레포 실제 코드 탐색 결과: (1) pano-postprocessor만 마이그레이션 시aws-s3.manager.ts 1파일, ~40줄 변경. new AWS.S3()new S3Client(), .getObject().createReadStream()GetObjectCommand + stream, .upload().promise()@aws-sdk/lib-storageUpload 클래스. pano-postprocessor는 base 패키지의 AwsS3Manager를 상속하지 않고 자체 구현이므로 독립적으로 마이그레이션 가능. (2) 전체 agents monorepo 마이그레이션 시 — 3개 패키지(cupix-pano-postprocessor, base, cupix-tesla-compute-agent)의 10개 파일, 4개 AWS 서비스(S3, SQS, ECS, AutoScaling) 클라이언트 전환 필요. base 패키지의 AwsQueueManager(SQS), AwsEcsManager(ECS)를 사용하는 forge-agent, potree-agent 등 다수 downstream 패키지에도 type 변경 전파.

변경 사항:

  • Fix Recommendation > 장기 개선: "AWS SDK v3 마이그레이션 검토"를 구체화 — v3가 해결하는 것과 해결하지 못하는 것 명시, pano-postprocessor 단독 마이그레이션 변경 범위(1파일 ~40줄) 상세 기술, 전체 monorepo 마이그레이션 범위(3패키지, 10파일, 4 AWS 서비스) 추가, v2→v3 API 매핑(S3Client, GetObjectCommand, Upload, ReceiveMessageCommand 등) 구체적 코드 수준으로 기술

추가 조사 내용:

  • cupixworks 레포: applications/agents/packages/cupix-pano-postprocessor/src/manager/aws-s3.manager.tsimport * as AWS from 'aws-sdk', new AWS.S3({ region }) (line 20), s3.getObject(params).createReadStream() (line 29), s3.upload({...}).promise() (line 130-134), new AWS.S3({ accessKeyId, secretAccessKey, endpoint, s3ForcePathStyle }) (line 109-117). base 패키지의 AwsS3Manager를 상속하지 않는 독립 구현 확인.
  • cupixworks 레포: applications/agents/packages/base/src/manager/aws-s3.manager.tsAWS.config.update() (line 19), new AWS.S3() with explicit credentials (line 47), .upload().promise() (line 151). setCredentials()로 명시적 credentials 설정.
  • cupixworks 레포: applications/agents/packages/base/src/manager/aws-ecs.manager.tsnew AWS.ECS() (line 21), callback 패턴의 stopTask, runTask, listTasks, describeContainerInstances 사용 (7개 메서드).
  • cupixworks 레포: applications/agents/packages/base/src/manager/aws-queue.manager.tsnew AWS.SQS() (line 17), callback 패턴의 receiveMessage, deleteMessage, getQueueAttributes, changeMessageVisibility 사용 (5개 메서드).
  • cupixworks 레포: applications/agents/packages/cupix-tesla-compute-agent/src/model/qmAws.tsnew AWS.SQS(), new AWS.ECS(), new AWS.AutoScaling() (line 20-22). startTask, stopTask, listContainerInstances, describeContainerInstances, describeAutoScalingGroups, updateAutoScalingGroup 등 12개 메서드.
  • cupixworks 레포: applications/agents/packages/cupix-pano-postprocessor/package.json"aws-sdk": "2.1599.0" 직접 의존성. base/package.json"aws-sdk": "2.1599.0". cupix-tesla-compute-agent/package.json"aws-sdk": "^2.1407.0".
  • cupixworks 레포: forge-agent, potree-agentaws-sdk 직접 import 없음, base 패키지의 AWS.SQS.Message 타입만 사용. v3 전환 시 Message from @aws-sdk/client-sqs로 type import 변경 필요.

Revision 5#

Feedback: pano-postprocessor 말고, aws-sdk 를 root 에서 버전관리하고, 모든 agent 전부 마이그레이션 진행

판정:

피드백 항목 판정 근거
aws-sdk를 root에서 버전관리 수용 pnpm-workspace.yamlcatalog 기능이 이미 async, request, download, three, tail 등 공유 의존성 버전 관리에 사용되고 있으나, aws-sdk는 catalog에 미등록 상태. 현재 3개 패키지가 각각 독립적으로 버전을 관리하며 불일치 존재: base/package.json"aws-sdk": "2.1599.0" (pinned), cupix-pano-postprocessor/package.json"aws-sdk": "2.1599.0" (pinned), cupix-tesla-compute-agent/package.json"aws-sdk": "^2.1407.0" (range, 192 버전 뒤처짐). v3 마이그레이션 시 @aws-sdk/client-s3, @aws-sdk/client-sqs, @aws-sdk/client-ecs, @aws-sdk/client-auto-scaling, @aws-sdk/lib-storage, @aws-sdk/credential-providers를 catalog에 등록하여 단일 버전으로 관리 가능.
모든 agent 전부 마이그레이션 수용 기존 RCA는 "pano-postprocessor만 단독 마이그레이션 가능"을 강조했으나, 실제로 aws-sdk v2는 monorepo 전체에 걸쳐 사용됨: 직접 의존 3개 패키지 (baseaws-s3.manager.ts, aws-ecs.manager.ts, aws-queue.manager.ts; cupix-pano-postprocessoraws-s3.manager.ts; cupix-tesla-compute-agentqmAws.ts, qmJob.ts, job.manager.ts), 간접 type 참조 2개 패키지 (cupix-tesla-forge-agent/forge-service.tsAWS.SQS.Message 7곳, cupix-tesla-potree-agent/potree-service.tsAWS.SQS.Message 7곳), 테스트 3개 파일 (aws-s3.manager.spec.ts, aws-ecs.manager.spec.ts, qmJob.spec.ts). 총 5개 패키지, 13개 소스 파일, 4개 AWS 서비스(S3, SQS, ECS, AutoScaling). 단독 마이그레이션은 v2/v3 혼재 상태를 만들어 유지보수 복잡도를 높이므로, 전체 일괄 마이그레이션이 합리적. base 패키지가 core manager를 제공하므로 base 먼저 전환 후 downstream 패키지를 순차 전환하는 접근이 적절.

변경 사항:

  • Fix Recommendation > 장기 개선: 제목을 "AWS SDK v3 마이그레이션"에서 "AWS SDK v3 전체 마이그레이션 + Root-level 버전 관리"로 변경
  • Fix Recommendation > 장기 개선: "Step 1: Root-level 버전 관리 전환" 섹션 신규 추가 — pnpm workspace catalog을 활용한 @aws-sdk/client-* 중앙 버전 관리 방안, 현재 버전 불일치 현황(base/pano-postprocessor: pinned 2.1599.0 vs tesla-compute-agent: ^2.1407.0) 기술
  • Fix Recommendation > 장기 개선: "Step 2: 전체 agents monorepo 마이그레이션 (권장 접근)" 섹션으로 재구성 — pano-postprocessor 단독이 아닌 전체 마이그레이션을 권장 접근으로 변경, 직접 의존 3패키지 12파일 + 간접 type 참조 2패키지 2파일을 테이블로 정리, 마이그레이션 순서 7단계 제시(catalog 등록 → base → pano-postprocessor → tesla-compute-agent → downstream type 전파 → v2 제거 → onlyBuiltDependencies 정리)
  • Fix Recommendation > 장기 개선: 기존 "pano-postprocessor만 단독 마이그레이션 가능" 문구 제거

추가 조사 내용:

  • cupixworks 레포: applications/agents/pnpm-workspace.yamlcatalog 섹션에 async, request, download, three, tail 5개 패키지만 등록, aws-sdk 미등록 확인
  • cupixworks 레포: applications/agents/package.json — root pnpm.onlyBuiltDependenciesaws-sdk 포함 확인, monorepo 설정: pnpm@10.30.3 + Nx@21.4.1
  • cupixworks 레포: applications/agents/pnpm-lock.yaml — aws-sdk 실제 resolved 버전 2.1599.0 (deprecated 경고 포함) 확인
  • cupixworks 레포: packages/base/src/base-service.ts:18-19,37-38,123,153,225AWS.SQS.Message type 7곳 사용 확인
  • cupixworks 레포: packages/cupix-tesla-forge-agent/src/forge-service.ts:25-26,52-53,64,106,162,329AWS.SQS.Message type 8곳 사용 (aws-sdk 직접 dependency 없음, base 경유)
  • cupixworks 레포: packages/cupix-tesla-potree-agent/src/potree-service.ts:23-24,44-45,90,180,444AWS.SQS.Message type 7곳 사용 (aws-sdk 직접 dependency 없음, base 경유)
  • cupixworks 레포: packages/cupix-tesla-forge-agent/package.json, packages/cupix-tesla-potree-agent/package.json — aws-sdk 직접 dependency 없음 확인 (base 패키지 통해 간접 참조)