Skip to content

operations image snapshot

JJong-03 edited this page Jun 18, 2026 · 3 revisions

Image Snapshot 운영

Factory A snapshot uploader, presigner, S3 metadata pipeline의 배포 확인과 장애 대응 절차다. 명령 예시에는 실제 token, certificate 내용, secret endpoint를 출력하지 않는다.


운영 기준

namespace:            ai-apps
snapshot source:      worker2:/var/lib/safe-edge/snapshots
snapshot-uploader:    worker2 단일 Deployment
edge-iot-publisher:   worker2 배치
outbox:               Longhorn RWO PVC /var/lib/aegis/outbox
presigner route:      POST /image-snapshot/presign
presigner Lambda:     AEGIS-Lambda-SnapshotPresigner
data processor:       AEGIS-Lambda-DataProcessor

이미지 binary는 presigned PUT으로 S3 image_snapshot/에 직접 들어간다. IoT Core에는 image_snapshot metadata JSON만 전송된다.

현재 배포 이미지 기준:

edge-iot-publisher: <account-id>.dkr.ecr.ap-south-1.amazonaws.com/aegis/edge-iot-publisher:sha-36eae8c
snapshot-uploader: <account-id>.dkr.ecr.ap-south-1.amazonaws.com/aegis/snapshot-uploader:sha-6d30ef2

edge-iot-publisher는 persistent MQTT 연결과 QoS 1 ack 기준으로 동작한다. AWS IoT Fleet Indexing connectivity와 client ID 기준은 IoT Fleet 연결성을 따른다.

최근 검증 기준(2026-06-08): snapshot-uploader rollout, S3 original image 업로드, raw/processed metadata 적재, DynamoDB latest_image_snapshot 갱신, SnapshotPresigner/DataProcessor Lambda error check가 정상으로 확인됐다. Dashboard /image-snapshots API와 화면은 구현·배포됐지만 실제 저장 이미지 기준 수기 조회 검증은 별도 확인 항목으로 남아 있다.


Presigner Secret

K3s Secret 기본 이름과 key:

ai-apps/snapshot-uploader-presign
  endpoint
  token

등록 스크립트:

cd merge
PRESIGN_ENDPOINT='<presigner-endpoint>' \
PRESIGN_TOKEN='<shared-token-or-empty>' \
scripts/ops/register-snapshot-presigner-secret.sh

Secret 존재와 key 이름만 확인한다. 값은 출력하지 않는다.

kubectl -n ai-apps get secret snapshot-uploader-presign \
  -o go-template='{{range $k, $v := .data}}{{$k}}{{"\n"}}{{end}}'

Secret 변경 후 Deployment를 재시작한다.

kubectl -n ai-apps rollout restart deploy/aegis-spoke-snapshot-uploader
kubectl -n ai-apps rollout status deploy/aegis-spoke-snapshot-uploader

현재 코드와 Terraform 기본값에서 PRESIGN_SHARED_TOKEN=""이면 Lambda 인증이 생략된다. 현재 Factory A MVP 운영 문서도 빈 값으로 기록되어 있어 token auth가 비활성화된 상태다. Token을 활성화할 때는 다음 두 값을 같은 maintenance window에 맞춘다.

Lambda environment PRESIGN_SHARED_TOKEN
K3s Secret snapshot-uploader-presign/token

한쪽만 먼저 바꾸면 presign 요청이 401 unauthorized로 실패한다.


Deployment와 Node 확인

kubectl -n ai-apps get deploy \
  aegis-spoke-snapshot-uploader \
  aegis-spoke-edge-iot-publisher

kubectl -n ai-apps get pod -o wide \
  -l app.kubernetes.io/instance=aegis-spoke

확인 기준:

  • snapshot-uploader replica가 1이다.
  • uploader Pod의 node가 worker2다.
  • edge-iot-publisher도 outbox RWO PVC를 사용할 수 있는 worker2에 있다.
  • uploader의 snapshot volume은 /var/lib/safe-edge/snapshots hostPath이며 read-only다.
  • outbox volume은 aegis-spoke-outbox PVC다.
kubectl -n ai-apps describe deploy aegis-spoke-snapshot-uploader
kubectl -n ai-apps get pvc aegis-spoke-outbox

Worker1 snapshot upload failover는 현재 지원하지 않는다. Worker2 장애 시 Pod를 worker1로 옮기는 것만으로 worker2의 node-local snapshot backlog에 접근할 수 없다.


Uploader와 Publisher 로그

Uploader 최근 로그:

kubectl -n ai-apps logs deploy/aegis-spoke-snapshot-uploader \
  --since=30m

주요 메시지:

로그 의미
uploaded ... -> s3://... S3 PUT, outbox metadata, state 기록 완료
snapshot upload failed presign, PUT, 파일 읽기, JSON/state 처리 실패
skip oversized snapshot 5 MiB 등 설정 상한 초과
failed to read upload state state 손상, .bad 격리 후 빈 state로 진행
recovered upload state 기존 outbox metadata로 state 복구

Publisher 최근 로그:

kubectl -n ai-apps logs deploy/aegis-spoke-edge-iot-publisher \
  --since=30m
로그 의미
published ... -> aegis/factory-a/image_snapshot MQTT publish 성공, outbox 파일 삭제
publish failed MQTT/TLS/네트워크 실패, 파일 유지 후 재시도
schema/JSON 오류 해당 파일을 quarantine/으로 이동

Uploader 로그에 성공이 있고 publisher 로그가 없다면 outbox와 publisher 상태를 먼저 확인한다. Publisher 성공 뒤 outbox 파일이 삭제되는 것은 정상이다.


Lambda와 API 확인

실제 secret 값은 조회하지 않고 Lambda 상태와 환경 변수 key만 확인한다.

aws lambda get-function-configuration \
  --region ap-south-1 \
  --function-name AEGIS-Lambda-SnapshotPresigner \
  --query '{State:State,LastUpdateStatus:LastUpdateStatus,EnvironmentKeys:keys(Environment.Variables)}'

API route:

aws apigatewayv2 get-apis \
  --region ap-south-1 \
  --query 'Items[?contains(Name, `SnapshotPresigner`)].{ApiId:ApiId,Name:Name}'

aws apigatewayv2 get-routes \
  --region ap-south-1 \
  --api-id <api-id>

Lambda 로그:

aws logs tail /aws/lambda/AEGIS-Lambda-SnapshotPresigner \
  --region ap-south-1 \
  --since 30m

aws logs tail /aws/lambda/AEGIS-Lambda-DataProcessor \
  --region ap-south-1 \
  --since 30m

CloudWatch metric:

aws cloudwatch get-metric-statistics \
  --region ap-south-1 \
  --namespace AWS/Lambda \
  --metric-name Errors \
  --dimensions Name=FunctionName,Value=AEGIS-Lambda-SnapshotPresigner \
  --statistics Sum \
  --period 300 \
  --start-time <utc-start> \
  --end-time <utc-end>

400은 factory, filename, content type, size, SHA-256, timestamp validation을 확인한다. 401은 Lambda shared token과 K3s Secret token 불일치다. 5xx 또는 timeout은 Lambda 로그, IAM, API integration을 확인한다.


S3와 DynamoDB 검증

Original image

aws s3api head-object \
  --bucket aegis-bucket-data \
  --key 'image_snapshot/factory_id=factory-a/yyyy=<YYYY>/mm=<MM>/dd=<DD>/hh=<HH>/<filename>'

ContentTypeimage/jpeg 또는 image/png인지 확인한다.

Public ACL이 없는지 확인:

aws s3api get-object-acl \
  --bucket aegis-bucket-data \
  --key 'image_snapshot/factory_id=factory-a/yyyy=<YYYY>/mm=<MM>/dd=<DD>/hh=<HH>/<filename>'

정상 기준은 owner FULL_CONTROL 외 public AllUsers/AuthenticatedUsers grant가 없는 것이다.

Raw metadata

aws s3api list-objects-v2 \
  --bucket aegis-bucket-data \
  --prefix 'raw/factory-a/image_snapshot/yyyy=<YYYY>/mm=<MM>/dd=<DD>/' \
  --max-items 10

Raw 경로에는 현재 hh partition이 없다.

Processed metadata

aws s3api list-objects-v2 \
  --bucket aegis-bucket-data \
  --prefix 'processed/factory-a/image_snapshot/yyyy=<YYYY>/mm=<MM>/dd=<DD>/hh=<HH>/' \
  --max-items 10

DynamoDB latest reference

aws dynamodb get-item \
  --region ap-south-1 \
  --table-name AEGIS-DynamoDB-FactoryStatus \
  --key '{"pk":{"S":"FACTORY#factory-a"},"sk":{"S":"LATEST"}}' \
  --projection-expression 'last_image_snapshot_at,latest_image_snapshot'

latest_image_snapshot.s3_key, source_timestamp, event_type, sha256, message_id가 processed metadata와 연결되는지 확인한다.

Dashboard 조회 경로

Dashboard Web의 /image-snapshots 화면과 Backend /image-snapshots/range, /image-snapshots API는 S3 image_snapshot/factory_id=.../ 객체를 list하고 presigned GET URL을 발급한다. 이 경로는 System 권한 사용자에게만 노출되며, 비인증 호출은 401이어야 한다.

Backend ECS task role에는 다음 범위가 필요하다.

s3:ListBucket on image_snapshot/*
s3:GetObject on image_snapshot/*

운영 배포 이력상 API와 화면은 구현되어 있으나, 실제 저장된 이미지 데이터에 대한 수기 확인은 별도 검증 항목으로 남긴다.


Backlog와 State File

State 파일:

/var/lib/aegis/outbox/.snapshot-uploader-state.json

State가 없는 최초 배포에서는 snapshot directory의 지원 이미지 전체가 backlog가 되어 오래된 파일부터 업로드된다. 파일 수와 S3 PUT/API 호출량을 먼저 확인하고 배포한다.

상태와 backlog를 확인하되 token이나 certificate를 출력하지 않는다.

kubectl -n ai-apps exec deploy/aegis-spoke-snapshot-uploader -- \
  sh -c 'find /var/lib/safe-edge/snapshots -maxdepth 1 -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" \) | wc -l'

kubectl -n ai-apps exec deploy/aegis-spoke-snapshot-uploader -- \
  sh -c 'test -f /var/lib/aegis/outbox/.snapshot-uploader-state.json && wc -c /var/lib/aegis/outbox/.snapshot-uploader-state.json'

kubectl -n ai-apps exec deploy/aegis-spoke-snapshot-uploader -- \
  sh -c 'find /var/lib/aegis/outbox -maxdepth 1 -type f -name "*.json" ! -name ".*" | wc -l'

State를 수동 삭제하면 로컬 backlog가 다시 업로드될 수 있다. Outbox metadata가 아직 남은 항목은 matching SHA-256/size로 state를 복구할 수 있지만, 이미 publisher가 metadata를 삭제한 항목은 다시 PUT될 수 있다. State 초기화는 중복 object/metadata 영향을 검토한 뒤 수행한다.

Uploader는 원본 파일을 삭제하지 않는다. Snapshot directory 정리 문제는 Safe-Edge cleanup/purge 설정에서 해결한다.


장애 분리

관측 우선 확인
새 이미지가 scan되지 않음 worker2 hostPath, 확장자, 파일 권한, uploader node
Presign 400 allowed factory, basename, content type, size, SHA-256, timestamp
Presign 401 Lambda token과 K3s Secret token 일치 여부
S3 PUT 실패 URL 만료, 필수 Content-Type, 네트워크, object prefix IAM
S3 original만 있고 raw 없음 outbox metadata와 publisher MQTT 로그
Raw만 있고 processed/DynamoDB 없음 Data Processor invocation/error 로그
Outbox 증가 IoT endpoint/certificate/TLS, publisher 배치와 PVC
같은 파일 재업로드 state path, size/mtime 변경, state 손상 또는 초기화

Token Auth 강화

현재 shared token은 선택 기능이며 빈 문자열이면 인증이 없다. 운영 강화 순서:

  1. 충분히 긴 무작위 token을 secret 관리 절차로 생성한다.
  2. Token을 repo, shell history, 위키, Terraform example에 평문으로 남기지 않는다.
  3. Terraform의 snapshot_presigner_shared_token을 안전한 입력 경로로 설정한다.
  4. snapshot-uploader-presign/token에 같은 값을 등록한다.
  5. Lambda 적용과 uploader rollout을 같은 maintenance window에 수행한다.
  6. Token 없는 요청이 401, uploader 요청이 200인지 확인한다.
  7. Token rotation 절차와 주기를 운영 문서에 관리한다.

Shared bearer token은 MVP 보강책일 뿐이다. 장기적으로 API Gateway authorizer, mTLS, source network 제한, WAF/rate limit, token의 Secrets Manager 기반 주입을 검토한다. API Gateway에는 현재 throttle이 있지만 token이 비어 있으면 caller identity 인증을 대신하지 못한다.


보안 원칙

  • Raspberry Pi에 장기 AWS access key를 저장하지 않는다.
  • Presigned URL과 bearer token을 로그나 문서에 남기지 않는다.
  • IoT certificate와 private key 내용을 출력하지 않는다.
  • S3 object에 public-read ACL을 설정하지 않는다.
  • Presigned URL은 업로드 대상 key와 짧은 만료 시간으로 제한한다.

관련 문서

Aegis-Pi Wiki

· 대표 문서 목록은 홈의 문서 탐색 표 참조

시작하기

요구사항

핵심 개념

아키텍처

컴포넌트 (Edge → Cloud → Dashboard)

Dashboard & 운영

시나리오 · 사례 · 참조

Clone this wiki locally