BlueByte

DevOps·클라우드

Docker, Kubernetes, AWS, CI 파이프라인 등 빌드·컨테이너·클라우드 장애를 다룹니다.

detected dubious ownershipFixed

Git: fatal: detected dubious ownership in repository

Git 2.35.2의 CVE-2022-24765 수정 이후, Git은 작업 트리나 .git 디렉터리의 소유자가 명령을 실행한 사용자와 다르면 저장소를 읽지 않습니다. 컨테이너, CI 작업, sudo 세션, 공유 드라이브에서 주로 나타납니다. 저장소가 내 것이어야 한다면 소유권을 바로잡고, 아니라면 global 설정의 safe.directory에 정확한 경로를 추가하세요 — Git은 이 설정을 저장소 자신의 설정에서는 무시합니다.

Git
failed calling webhookFixed

Kubernetes: Internal error occurred: failed calling webhook

admission webhook이 쓰기 요청 앞에 서 있는데 API server가 응답을 받지 못했고, 기본값인 failurePolicy: Fail이 그 침묵을 거부로 바꾼 것입니다. 메시지 끝부분이 곧 진단입니다 — context deadline exceeded는 호출이 도달하지 못한 것, no endpoints available은 떠 있는 게 없는 것, x509 줄은 API server가 webhook 인증서를 신뢰하지 않는 것입니다. 원인마다 해결이 다르고, 어느 것도 매니페스트 문제가 아닙니다.

Kubernetes
1205Fixed

MySQL: ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

다른 트랜잭션이 쥐고 있는 행 잠금을 innodb_lock_wait_timeout 만큼 기다리다 포기한 것입니다. sys.innodb_lock_waits 가 막고 있는 세션을 지목하고 KILL 문까지 만들어 주며, blocking_query 가 NULL 이면 블로커가 열린 트랜잭션 위에서 놀고 있다는 뜻입니다. 재시도 로직이 가장 자주 틀리는 지점은 따로 있습니다. 기본값에서는 타임아웃된 문장 하나만 롤백되므로, 트랜잭션은 그대로 열린 채 앞서 잡은 잠금을 전부 쥐고 있습니다.

MySQL
MISCONFFixed

Redis: MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk

읽기는 되는데 모든 쓰기가 거부됩니다. 마지막 백그라운드 저장이 실패했고 stop-writes-on-bgsave-error 기본값이 yes 이기 때문입니다. 진짜 원인은 로그에 적혀 있습니다 — 디스크 공간 부족, redis 사용자가 쓸 수 없는 dir, rename 시점의 read-only 마운트, 또는 fork 가 Cannot allocate memory 로 실패하는 경우입니다. 원인을 고치고 BGSAVE 한 번만 돌리면 재시작 없이 쓰기가 돌아옵니다. rdb_last_bgsave_status 가 err 에서 ok 로 바뀝니다. stop-writes-on-bgsave-error no 는 쓰기를 즉시 되살리지만 스냅샷은 여전히 실패한 상태로 두므로, 해결이 아니라 의도한 맞교환으로 다뤄야 합니다.

Redis
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memoryFixed

Node.js: FATAL ERROR: Reached heap limit — JavaScript heap out of memory (종료 코드 134)

V8 힙에는 시스템 메모리와 Node 릴리스에서 유도되는 자기만의 천장이 있고, 가진 RAM 보다 훨씬 낮은 경우가 흔합니다. 빌드나 서버가 거기 닿으면 V8 은 FATAL ERROR: Reached heap limit 과 종료 코드 134 로 스스로 중단합니다. v8.getHeapStatistics().heap_size_limit 으로 실제 한도를 읽은 뒤, 큰 작업은 --max-old-space-size(MiB)나 NODE_OPTIONS 로 올리고, 컨테이너 안에서는 cgroup 한도보다 낮게 잡고, 오래 도는 프로세스의 누수는 --heapsnapshot-near-heap-limit 으로 잡으세요. FATAL ERROR 줄 없이 137 로 끝나면 컨테이너 kill 이지 이 오류가 아닙니다.

Node.js
exec /docker-entrypoint.sh: exec format errorFixed

Docker: 컨테이너 시작 직후 "exec format error" — 플랫폼이 다른 이미지, 에뮬레이터 없음, 셔뱅 없는 스크립트

컨테이너가 첫 명령에서 exec format error 로 끝납니다. 커널의 ENOEXEC, 즉 파일은 있지만 여기서는 실행할 수 없다는 뜻입니다. 실제로는 한 CPU 아키텍처에서 빌드한 이미지(Apple 실리콘 Mac은 linux/arm64 를 만듭니다)를 binfmt_misc 에 QEMU 핸들러가 없는 다른 아키텍처(x86_64 서버)에서 돌리거나, 엔트리포인트 스크립트의 첫 줄이 셔뱅이 아닌 경우입니다. uname -m, docker image inspect, ls /proc/sys/fs/binfmt_misc 로 원인을 가르고, docker buildx build --platform 을 명시(또는 두 플랫폼 매니페스트 목록 발행)하거나, 에뮬레이션이 목적이면 QEMU 등록과 --platform, 스크립트라면 #!/bin/sh 한 줄로 해결합니다.

Docker
curl: (60) SSL certificate problem: unable to get local issuer certificateFixed

curl: (60) SSL certificate problem: unable to get local issuer certificate

curl이 서버가 보낸 인증서 체인을 따라가다가, 자기가 읽는 CA 저장소에 발급자가 없는 인증서에 닿아 종료 코드 60으로 연결을 거부한 것입니다. 발급자가 없는 이유는 넷 중 하나입니다. 서버가 중간 인증서 없이 리프만 보내거나(브라우저는 스스로 받아 와서 가려 줍니다), TLS 검사 프록시가 컨테이너·러너가 신뢰하지 않는 회사 CA로 사이트를 다시 서명했거나, curl이 생각과 다른 CA 번들(CURL_CA_BUNDLE, SSL_CERT_FILE, 벤더 curl)을 읽고 있거나, ca-certificates 패키지가 너무 오래된 경우입니다. curl -v로 어느 저장소를 썼는지, openssl s_client로 서버가 무엇을 보냈는지 보고 고리가 빠진 쪽을 고친 뒤 -w '%{ssl_verify_result}'로 확인합니다.

curl
FATAL: no pg_hba.conf entry for host (28000)Fixed

PostgreSQL: FATAL — no pg_hba.conf entry for host

클라이언트가 서버까지는 닿았지만 pg_hba.conf의 어느 줄도 그 접속 유형·출발지 주소·데이터베이스·사용자와 일치하지 않았고, PostgreSQL은 일치하는 줄이 없으면 거부합니다. 메시지 끝의 no encryption 또는 SSL encryption이 어떤 종류의 접속이 거부됐는지 알려줍니다. pg_hba_file_rules로 서버가 보는 규칙을 읽고, 클라이언트의 실제 주소에 맞는 host 또는 hostssl 줄을 scram-sha-256으로 추가한 뒤 pg_reload_conf()로 다시 읽게 하면 됩니다. 첫 번째로 일치하는 줄이 이기므로 CIDR만큼 순서와 접속 유형도 중요합니다.

PostgreSQL
OOMKilled / exit code 137Fixed

Kubernetes: 컨테이너가 OOMKilled(exit code 137)로 종료됨

OOMKilled는 컨테이너가 자기 cgroup의 메모리 limit을 넘어서, 또는 limit이 없을 때 노드 전체가 메모리 부족에 빠져 oom_killer가 이 컨테이너를 골라서 Linux 커널이 SIGKILL을 보냈다는 뜻입니다. kubectl describe의 Last State에 Reason: OOMKilled, Exit Code: 137이 찍힙니다. kubectl top으로 실제 working set을 재서 그보다 높은 limit을 잡고, JVM·Node 힙이 limit 안에 들어오게 맞추고, 노드의 OOM killer가 이 Pod를 먼저 고르지 않도록 request를 주면 해결됩니다.

Kubernetes
413Fixed

nginx: 업로드에서 413 Request Entity Too Large

nginx는 프록시하기 전에 요청의 Content-Length를 client_max_body_size와 먼저 비교하므로, 기본값 1MB를 넘는 업로드는 413으로 거절되고 백엔드는 파일을 구경도 못 합니다. 실제로 로드된 값과 어느 구간이 거절했는지는 nginx -T가 알려줍니다. 업로드 경로를 덮는 블록에 — Kubernetes라면 Ingress annotation에 — 한도를 설정하고 reload하세요.

nginx
429 toomanyrequestsFixed

Docker Hub: toomanyrequests — pull rate limit에 도달함(429)

출발지 IP(익명) 또는 계정(Personal)의 pull 예산이 소진되면 Docker Hub는 manifest 요청에 429로 답하고, 데몬·kubelet·BuildKit 모두 같은 toomanyrequests 본문을 그대로 전달합니다. ratelimitpreview/test에 HEAD 요청을 보내면 한도·남은 횟수·집계 중인 주소가 보입니다. pull이 실제로 일어나는 곳(러너, imagePullSecrets를 통한 kubelet)에서 로그인하거나 플릿 앞에 pull-through 캐시를 두면 해결됩니다.

Docker Hub
externally-managed-environmentFixed

pip: error: externally-managed-environment

Debian 12·Ubuntu 23.04 이후·Homebrew의 Python에는 EXTERNALLY-MANAGED 마커가 들어 있고, pip 23.0 이상은 가상환경 밖에서는 설치를 거부합니다. sudo와 --user도 PEP 668의 설계대로 막힙니다. 고장이 아닙니다. 프로젝트 의존성은 venv, 명령줄 도구는 pipx, OS가 직접 쓰는 스크립트는 배포판 패키지로 설치하고, --break-system-packages는 일회용 컨테이너에만 남겨 두세요.

pip
53300Fixed

PostgreSQL: 연결 거절 — sorry, too many clients already

max_connections가 허용한 슬롯이 전부 차서 PostgreSQL이 새 연결을 거절합니다. 실제 부하보다는 과하게 큰 클라이언트 풀이나 열린 트랜잭션에 멈춰 선 세션이 원인인 경우가 많습니다. 어느 쪽인지는 pg_stat_activity가 알려줍니다. idle 슬롯을 회수하고 클라이언트 쪽을 먼저 조이세요. max_connections를 올리는 것도 방법이지만 재시작과 추가 공유 메모리가 듭니다.

PostgreSQL
non-fast-forwardFixed

Git: push 거부 — 원격에 없는 커밋이 있어 거절됨 (non-fast-forward)

Git은 원격 브랜치를 이어가는 push만 받아들이므로, 이 거절은 원격이 앞서 나가 당신 브랜치가 그 자손이 아니라는 뜻입니다. 거절 줄이 어느 경우인지 알려줍니다: (fetch first)는 단지 뒤처진 것, (non-fast-forward)는 히스토리가 갈라진 것입니다. 원격 작업을 들여와 push하거나, 히스토리를 일부러 다시 썼다면 force-with-lease로 push하세요.

Git
Cannot connect to the Docker daemonFixed

Docker: docker 데몬 소켓에 연결할 수 없음 (unix:///var/run/docker.sock)

모든 docker 명령은 CLI가 /var/run/docker.sock을 통해 백그라운드 데몬에 넘기는 요청이며, 이 에러는 그 요청이 도착하지 못했다는 뜻입니다 — 데몬이 멈췄거나, 사용자가 docker 그룹에 없거나, CLI가 엉뚱한 context를 향한 것입니다. 어느 변형을 받았는지 읽고 그 원인 하나를 고치면 소켓이 다시 응답합니다.

Docker
YN0018Fixed

Yarn: install이 integrity checksum mismatch로 실패

Yarn이 tarball에서 계산한 checksum이 yarn.lock에 기록된 값과 달라 패키지 설치를 거부합니다 — 보통 손상된 캐시, 파일을 다시 포장한 레지스트리, 손으로 고친 아카이브가 원인입니다. 해결은 Yarn 계열마다 다릅니다: Berry는 YARN_CHECKSUM_BEHAVIOR=reset으로 비우고 다시 받고, Classic은 캐시를 지우고 재설치합니다. lockfile을 지우지는 마세요.

Yarn
network not foundFixed

Docker Compose: network not found

docker compose up이 network not found 오류로 멈춥니다 — 파일이 기대하는 external 네트워크가 생성된 적이 없거나, prune·삭제된 네트워크를 가리키는 dangling 참조 때문입니다. docker network ls로 데몬이 가진 것을, docker compose config로 프로젝트가 기대하는 것을 확인한 뒤, 프로젝트를 내렸다 올려 네트워크를 다시 만들거나 external 네트워크를 먼저 생성하면 됩니다.

Docker Compose
x509: certificate signed by unknown authorityFixed

kubectl: x509: certificate signed by unknown authority

kubectl이 API 서버에는 도달했지만, 서버가 제시한 TLS 인증서가 kubeconfig에 기록된 CA로 거슬러 올라가지 않아 신뢰를 거부합니다 — 대개 재구축된 클러스터, 잘못된 context, 사내 프록시 때문이지 실제 공격은 아닙니다. kubectl config view --minify로 서버와 CA를 확인하고, openssl로 서버가 실제로 보내는 인증서를 읽은 뒤, kubeconfig를 갱신하거나 올바른 CA를 임베드하면 됩니다.

Kubernetes
InvalidClientTokenIdFixed

AWS CLI: InvalidClientTokenId — 요청의 security token이 유효하지 않음

AWS CLI가 "The security token included in the request is invalid"로 명령을 거부하는 것은, 보낸 자격 증명이 AWS가 아는 살아 있는 key가 아니기 때문입니다 — 삭제된 key, 만료된 임시 세션, 또는 프로필을 덮어쓴 오래된 환경 변수. CLI가 실제로 쓴 자격 증명을 찾아 그 출처를 고치면 다음 호출은 통과합니다.

AWS CLI
EACCESFixed

npm: 전역 설치에서 EACCES permission denied

전역 npm 설치가 code EACCES로 실패하는 것은 npm이 root 소유의 prefix 디렉터리에 쓰려 하는데 여러분 계정이 그곳에 파일을 만들 수 없기 때문입니다. 해결은 sudo가 아닙니다 — npm이 여러분 소유의 prefix를 가리키게 하거나(또는 Node를 버전 매니저로 다시 설치해) 전역 설치에 다시는 root가 필요 없게 만드세요.

npm
Error acquiring the state lockFixed

Terraform: Error acquiring the state lock

Terraform은 상태를 쓸 수 있는 작업 전에 state를 잠그는데, 이 오류는 이미 다른 누군가가 그 lock을 쥐고 있다고 판단했다는 뜻입니다. Lock Info 블록이 진짜 충돌과 남은 lock을 구분해 줍니다: 실행이 실제로 진행 중이면 기다리고, apply 도중 job이 죽었다면 terraform force-unlock으로 남은 lock을 지우면 됩니다. -lock=false로 우회하지 마세요.

Terraform
GH001Fixed

GitHub: push 거부 — GH001 Large files detected (100 MB 초과)

GitHub은 서버에서 100 MiB를 넘는 파일을 막으므로 push 전체가 GH001로 거부됩니다. 파일을 지우고 다시 커밋해도 소용없습니다 — 큰 blob이 history에 여전히 남아 있기 때문입니다. 최신 커밋에만 있으면 amend로, history 깊이 묻혀 있으면 git lfs migrate로 history를 다시 씁니다. history 재작성은 파괴적입니다: SHA가 바뀌고 force-push가 필요합니다.

GitHub
502 Bad GatewayFixed

nginx: upstream에서 오는 502 Bad Gateway

502는 nginx는 멀쩡하지만 proxy_pass 뒤의 upstream이 응답하지 않았거나 nginx가 쓸 수 없는 응답을 보냈다는 뜻입니다. error log가 정확한 실패를 알려줍니다 — connection refused, upstream 조기 종료, 버퍼보다 큰 헤더. nginx가 아니라 백엔드나 버퍼를 고치세요.

nginx
ImagePullBackOffFixed

Kubernetes: Pod이 ImagePullBackOff에서 멈춤

kubelet이 컨테이너 이미지를 가져오지 못해 Pod가 시작하지 못하고 back-off로 계속 재시도합니다. kubectl describe의 Events가 정확한 원인을 알려줍니다 — 잘못된 이름·태그, pull secret 없는 프라이빗 레지스트리, 레이트 리밋. 참조나 자격 증명을 고치면 다음 재시도에서 성공합니다.

Kubernetes
driver failedFixed

docker: driver failed programming external connectivity (iptables)

방화벽 reload가 Docker의 iptables 체인을 지운 뒤 docker run -p가 "driver failed programming external connectivity"로 실패합니다. DOCKER 체인이 사라졌는지 확인하고, 데몬을 재시작해 다시 만든 뒤, reload가 또 지우지 않게 막습니다.

Docker Engine
CrashLoopBackOffWorkaround

Kubernetes 파드가 CrashLoopBackOff에 멈춤

CrashLoopBackOff는 컨테이너가 시작·종료되고 kubelet이 점점 긴 지연으로 재시작을 반복한다는 뜻입니다. 이전 컨테이너의 로그와 종료 코드를 읽어 상태를 구체적 원인으로 바꾼 뒤 그것을 고칩니다.

Kubernetes
AccessDeniedFixed

AWS S3 PutObject가 허용 정책이 있는데도 AccessDenied로 실패

버킷 정책·SCP·명시적 거부·객체 소유권 설정이 쓰기를 막으면 IAM 허용만으로는 부족합니다. 허용을 더 쌓지 말고 정책 시뮬레이터로 실효 권한을 추적하세요.

Amazon S3
unrelated historiesFixed

git: fatal: refusing to merge unrelated histories

두 브랜치에 공통 커밋이 없어서 git이 기본적으로 병합을 거부합니다. 두 히스토리가 정말 하나로 합쳐질 게 맞는지 확인한 뒤 --allow-unrelated-histories로 병합하세요.

Git
ERESOLVEWorkaround

npm install이 ERESOLVE peer dependency 충돌로 실패

npm 7+는 패키지의 peer dependency를 만족할 수 없으면 설치를 거부합니다. 어떤 peer가 충돌하는지 읽고 가능하면 버전을 맞추세요 — --legacy-peer-deps는 호환 버전이 아직 없을 때만.

npm
no space left on deviceFixed

Docker: no space left on device

Docker의 데이터 디렉터리가 오래된 이미지·멈춘 컨테이너·빌드 캐시·댕글링 볼륨으로 찼습니다. docker system df로 무엇이 쓰는지 보고 필요 없는 것을 prune 하고, 호스트 디스크나 inode가 진짜 한계인지 확인하세요.

Docker Engine
detached HEADFixed

Git: detached HEAD — 돌아가서 커밋 지키기

커밋·태그·remote-tracking 참조를 checkout해서 HEAD가 브랜치가 아니라 커밋을 곧장 가리킵니다. 잃은 것은 없지만, 여기서 만든 커밋은 이름을 붙이기 전까지 어느 브랜치에도 속하지 않습니다. 살펴보기만 했으면 switch로 돌아가고, 작업을 했으면 git switch -c로 지키고, 이미 옮겼으면 git reflog로 되살립니다.

Git
port is already allocatedFixed

Docker: Bind failed — port is already allocated

Docker가 커널에 컨테이너용 호스트 포트를 예약해 달라고 요청했는데 이미 무언가가 쥐고 있어, 컨테이너가 시작되기 전에 실행이 실패합니다. 메시지가 범인을 알려줍니다 — 'port is already allocated'는 다른 컨테이너, 'address already in use'는 평범한 호스트 프로세스입니다. docker ps나 ss로 소유자를 찾아 포트를 비우거나 다른 포트로 옮기면 실행이 됩니다.

Docker Engine