Docker 29에서 사라진 inspect 최상위 IPAddress 필드
컨테이너 생성이 매번 실패하기 시작했습니다. 컨테이너 자체는 만들어지는데 2~3초 뒤에 자동으로 사라집니다.
사용자가 환경을 하나 만들면 서버에서 컨테이너가 하나 뜨고, 사용자는 IP 가 아니라 이름으로 접속하니 컨테이너가 받은 IP 로 DNS A레코드를 하나 잡아 둡니다. 그래서 컨테이너가 떴다고 생성이 끝나는 게 아니라 DNS 등록까지 성공해야 완료로 치고, 등록이 세 번 다 실패하면 반쯤 만들어진 환경을 남기지 않으려고 컨테이너를 지우고 롤백합니다. 2~3초 뒤에 사라지는 건 그 롤백이 도는 겁니다.
보안 인증 요건 때문에 이 환경을 다른 클라우드에 새로 구축하면서 Docker 를 29 로 올린 직후였습니다.
증상 — 빈 IP로 DNS 등록 실패
롤백까지 갔다는 건 DNS 등록이 세 번 다 실패했다는 뜻이라 등록 쪽 로그부터 봤습니다. 등록 API 가 IP 가 빈 문자열이라며 거절하고 있었으니, 컨테이너에 IP 가 실제로 안 붙은 건지부터 확인해야 합니다.
$ docker inspect <container> --format '{{.NetworkSettings.IPAddress}}'
template parsing error: map has no entry for key "IPAddress"
값이 비어 있는 게 아니라 키 자체가 없어서, 컨테이너에 IP 가 안 붙은 게 아니라 IP 를 꺼내 오던 자리가 없어진 것이었습니다.
원인 — 29부터 제거된 최상위 IPAddress
Docker 29(API 1.52)부터 컨테이너 inspect 응답에서 레거시 최상위 필드인
NetworkSettings.IPAddress / Gateway / MacAddress 가 완전히 제거됐습니다. 원래 이 값들은
네트워크별 맵에도 같이 들어 있고 최상위에도 편의로 복사돼 있었지만, 이제 맵 안에만 남습니다.
NetworkSettings.Networks["bridge"].IPAddress ← 여기에만 있음
문제를 키운 건 역직렬화 쪽입니다. Go 에서 JSON 을 구조체로 풀면 응답에 없는 키는 에러가 아니라
제로값, 문자열이면 빈 문자열이 됩니다. 코드는 아무 문제 없이 돌면서 빈 IP 를 그대로 DNS
등록에 넘겼고, 등록 단계에서만 실패한 겁니다. 필드가 사라졌다는 신호는 어디에도 안 뜨고, 앞에서
--format 으로 직접 찔러 봤을 때만 키가 없다는 게 드러납니다.
해결 — 데몬 버전 보고 backfill
없어진 최상위 필드를 네트워크 맵에서 끌어와 채워 넣었는데, 처음에는 비어 있으면 채운다는 조건만 뒀는데, 그렇게 두면 어느 버전부터 이 처리가 필요한지가 코드에 남지 않아서 나중에 보는 사람은 이 backfill 이 왜 있는지 알 수 없습니다. 데몬의 메이저 버전을 직접 조회해서 분기하는 쪽으로 바꿨습니다.
const DOCKER_MAJOR_VERSION_CHANGED = 29
// 데몬 버전은 최초 1회만 조회해서 캐시(sync.Once).
// 조회·파싱 실패 시 안전한 쪽(29 이상으로 취급 → backfill 수행)으로 폴백.
func (c *Container) Inspect(id string) (*docker.Container, error) {
// ... docker inspect ...
if c.dockerMajorVersion() >= DOCKER_MAJOR_VERSION_CHANGED {
if bridge, ok := container.NetworkSettings.Networks["bridge"]; ok {
if container.NetworkSettings.IPAddress == "" {
container.NetworkSettings.IPAddress = bridge.IPAddress
}
}
}
return container, nil
}
client.Version() 으로 데몬에 물어본 버전 문자열(“29.6.1” 등)에서 메이저 숫자만 떼어
비교합니다. 29 미만에서는 이 분기를 아예 안 타서 기존 동작이 그대로 유지되고, 29 이상에서만
backfill 이 돕니다.
값이 비었다와 키가 없다는 원인이 다름
메이저 버전 업그레이드는 API 응답에서 필드를 조용히 빼기도 하고, 느슨하게 역직렬화하는 오래된 클라이언트는 사라진 필드를 에러가 아니라 빈 값으로 받으니, 문제는 필드를 읽는 자리가 아니라 그 값을 실제로 쓰는 단계에서 터집니다. 빈 문자열이 된 IP 를 그대로 DNS 에 등록하려다 실패한 게 그 자리였습니다.
값이 비었다와 키가 없다는 원인이 완전히 다르니, 값이 비어서 실패하는 것 같으면 --format 이든
raw JSON 이든 그 키가 응답에 있기는 한지부터 확인하는 게 빠릅니다. 버전에 따라 응답 구조가
바뀌는 의존성이라면 필드가 비었는지로 분기하지 말고 데몬 버전을 직접 물어보고 분기해야, 어느
버전부터 필요한 처리인지가 코드에 남습니다.
댓글 0