4편 끝에서 솔직하게 인정한 게 있다. mincloud의 credstore는 인메모리 맵 하나라, IAM이 발급한 키를 STS가 즉시 검증할 수 있는 건 “나눌 데이터가 애초에 한 벌뿐이라서"라고. 그 공짜에는 유효기간이 있었다. 프로세스를 내리면 발급한 키가 전부 증발한다.
$ aws iam create-access-key --user-name friend01 ... # 키 발급
$ docker restart mincloud # 재시작
$ aws sts get-caller-identity ... # 방금 그 키로
An error occurred (InvalidClientTokenId) ... # 없는 사람 취급
이번 편은 이걸 고친다. credstore를 프로세스 밖으로 빼서, docker compose down && up 을 해도 발급한 모든 자격증명이 살아있게 만든다.
AWS는 이걸 무엇에 담나
파일 하나에 JSON으로 적어도 “재시작 후 유지"는 된다. 그런데 이왕이면 AWS가 실제로 하는 방식을 따라가고 싶었다. 그러려면 먼저 물어야 한다. AWS는 IAM 같은 서비스의 자격증명을 무엇에 담을까?
관계형 데이터베이스가 아니다. 근거는 IAM의 성격 그 자체에 있다.
- 조회가 키 하나로 끝난다. 우리가 하는 일은
accessKeyID → credential딱 하나다. 조인이 아니라 단일 키 조회다. 이건 관계형의 강점을 하나도 안 쓰고, 오히려 key-value 스토어의 정확히 그 모양이다. - 글로벌 + 최종 일관성(eventual consistency). 4편에서 “IAM 변경이 전파되는 데 몇 초 걸린다"고 했다. 그 몇 초가 단일 관계형 DB의 지문이 아니다. 여러 리전에 복제되는 분산 key-value 스토어의 지문이다. 트랜잭션 하나로 끝나는 DB라면 그런 지연이 날 이유가 없다.
그 모양에 정확히 맞는 AWS 서비스가 DynamoDB다. 실제로 AWS의 여러 컨트롤플레인이 그 위에 올라가 있다. 그리고 고맙게도 AWS는 DynamoDB Local — 공식 amazon/dynamodb-local 컨테이너 — 을 배포한다. 로컬 docker-compose에 그대로 얹어, mincloud가 진짜 DynamoDB처럼 쓸 수 있다.
우리가 손으로 짜는 건 여전히 AWS의 표면(SigV4, STS/IAM 프로토콜)이다. 스토어는 “데이터베이스를 밑바닥부터"가 아니라 — 그건 이 프로젝트의 주제가 아니니까 — AWS가 실제로 쓰는 그 스토어를 그대로 쓴다.
인터페이스는 그대로, 구현만 밖으로
여기서 4편에 깔아 둔 게 값을 한다. credstore는 처음부터 인터페이스였다.
| |
이 인터페이스가 작게 유지된 덕에, 뒤를 통째로 갈아도 어떤 핸들러도 눈치채지 못한다. STS도 IAM도 여전히 Lookup과 Put만 부른다. 바뀌는 건 그 두 메서드 안쪽뿐이다.
DynamoDB 구현은 accessKeyID를 파티션 키로 삼는 단일 테이블이다. Put은 PutItem, Lookup은 GetItem.
| |
관계형이었다면 스키마와 마이그레이션이 따라왔을 것이다. key-value라 그런 의식(儀式)이 없다. 테이블 하나, 파티션 키 하나. IAM이 이 스토어에 어울리는 이유가 코드에서도 그대로 드러난다.
바꿔 말하면, main은 이제 둘 중 하나를 고른다. DynamoDB 엔드포인트가 주어지면 그걸로 영속화하고, 없으면 예전처럼 인메모리로 돈다. 나머지 코드는 자기가 어느 쪽을 받았는지 모른다.
| |
두 서비스, 한 compose
이제 mincloud는 자기 스토어를 프로세스 안에 두지 않는다. 그래서 실행 단위가 둘로 늘어난다 — mincloud, 그리고 그 자격증명을 담는 DynamoDB. docker-compose가 이 둘을 묶는다.
| |
핵심은 dynamodb-data 이름 볼륨이다. DynamoDB Local은 -sharedDb -dbPath로 상태를 파일에 쓰고, 그 디렉토리를 이름 볼륨에 얹어 뒀다. 컨테이너는 down으로 사라져도 볼륨은 남는다. 그래서 다음 up에서 DynamoDB가 예전 데이터를 그대로 들고 올라온다.
작은 정직함 하나. mincloud가 DynamoDB보다 먼저 떠서 첫 연결이 거부당하는 순간이 있다. 그래서 restart: on-failure를 걸어, 스토어가 응답할 때까지 프로세스가 죽고 다시 뜨게 뒀다. 로그에 그 찰나가 그대로 찍힌다.
mincloud-1 | open credstore: ... dial tcp 172.18.0.2:8000: connect: connection refused
mincloud-1 | persisting credentials in DynamoDB at http://dynamodb:8000 (table "mincloud-credentials")
mincloud-1 | mincloud sts listening on :9900, iam listening on :9910
죽었다 살아나도 기억한다
이제 증명. 먼저 friend01에게 키를 발급한다(IAM :9910).
$ aws iam create-access-key --user-name friend01 \
--endpoint-url http://localhost:9910
{
"AccessKey": {
"UserName": "friend01",
"AccessKeyId": "AKIA8KM1NAU3S7PFZQHM",
"Status": "Active",
"SecretAccessKey": "HfhFEYv1KiUrMAcs3jCDUSCYmUk6d1UPkcQxpSR4",
"CreateDate": "2026-07-06T11:34:10+00:00"
}
}
(여기 값은 로컬에서 방금 만든 가짜다. 어디에도 통하지 않는다.)
그 키로 STS(:9900)에 신원을 물으면 friend01로 돌아온다.
$ AWS_ACCESS_KEY_ID=AKIA8KM1NAU3S7PFZQHM \
AWS_SECRET_ACCESS_KEY=HfhFEYv1KiUrMAcs3jCDUSCYmUk6d1UPkcQxpSR4 \
aws sts get-caller-identity --endpoint-url http://localhost:9900
{
"UserId": "AIDABJDQJDVSDYOKZX2L",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/friend01"
}
여기까진 4편과 같다. 이제 스택을 통째로 내렸다 다시 올린다.
$ docker compose down
$ docker compose up -d
컨테이너는 새로 만들어졌다. mincloud의 인메모리 맵은 당연히 비었다. 그런데 아까 그 키를 그대로 다시 쓰면:
$ AWS_ACCESS_KEY_ID=AKIA8KM1NAU3S7PFZQHM \
AWS_SECRET_ACCESS_KEY=HfhFEYv1KiUrMAcs3jCDUSCYmUk6d1UPkcQxpSR4 \
aws sts get-caller-identity --endpoint-url http://localhost:9900
{
"UserId": "AIDABJDQJDVSDYOKZX2L",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/friend01"
}
똑같이 friend01이 돌아온다. 프로세스가 죽었다 살아났는데도 기억한다. friend01의 자격증명은 mincloud의 메모리가 아니라 DynamoDB의 볼륨에 있었기 때문이다. 4편에서 “곧바로 검증 가능한 자격증명"이라던 그 키가, 이제 재시작을 건너뛰고도 검증된다.
공짜였던 게 값을 치르기 시작한다
4편에서 “글로벌이 공짜"라고 했다. 프로세스가 하나고 맵이 하나라, 복제도 지연도 불일치도 없다고. credstore를 밖으로 빼는 순간, 그 공짜가 값을 치르기 시작한다. 이제 자격증명은 네트워크 건너 다른 프로세스에 있다. 그래서 스토어가 안 뜨면 인증이 실패할 수 있고(그래서 restart: on-failure), 조회 하나에 왕복이 든다.
물론 DynamoDB Local은 단일 노드라, 진짜 AWS의 리전 간 복제 지연은 아직 겪지 않는다. 하지만 모양은 이제 맞다. 스토어가 프로세스 밖 독립 서비스가 됐고, 인메모리 맵이라면 못 했을 “여러 mincloud 인스턴스가 한 스토어를 공유"가 이제 가능한 그림이 됐다. 4편이 예고한 그 세계로 한 발 들어선 것이다.
정직하게 덧붙이면, 지금 DynamoDB에는 시크릿이 평문으로 들어간다. mincloud가 던지는 시크릿이 어차피 버려도 되는 가짜라 넘어가는 것이지, 진짜 컨트롤플레인이라면 자격증명을 이렇게 담지 않는다. 이것도 언젠가 갚아야 할 빚으로 적어 둔다.
인증 골격이 이제 재시작을 견딘다. 다음 편에서는, 이 단단해진 골격 위에 지금까지와 완전히 다른 종류의 서비스를 얹어 본다 — 신원이 아니라 컴퓨트, EC2다. 골격이 정말 값을 하는지 거기서 드러난다.