728x90

infra 11

[RHEL] Rocky 9 to 10 인플레이스 업그레이드 leapp 7관문과 distro-sync 강행

부제: 공식적으로 안 된다는 Rocky 9→10 인플레이스 업그레이드를 끝까지 밀어붙여 성공시킨 기록Rocky Linux는 메이저 버전 인플레이스 업그레이드를 공식 지원하지 않는다. 팀의 답은 한결같다 — "백업하고 새로 설치하세요." AlmaLinux의 ELevate(leapp 기반) 도구도 Rocky 9→10 경로는 아직 없다.그런데 검증용 VM에 백업이 있었다. 그래서 끝까지 가봤다. "공식 불가"가 코드의 어느 지점에서 나오는지, 그리고 그걸 어떻게든 뚫을 수 있는지.뜻밖의 출발점먼저 놀란 것 하나. Rocky 9.8의 AppStream 저장소에 leapp-upgrade-el9toel10 패키지가 실제로 있었다. RHEL의 공식 업그레이드 도구 leapp이, Rocky에도 el9→el10 버전으로..

명령어/Linux 2026.07.17

[운영] 모니터링을 붙였더니 결함이 나왔다, 그런데 바로 안 고쳤다

부제: 측정이 드러낸 숫자를 곧바로 결함으로 단정하지 않은 이야기모니터링을 붙이는 작업을 했다.목적은 단순했다. 안 보이던 걸 보이게 하는 것.그런데 실측을 시작하자, 모니터링과 무관한 원래부터 있던 것들이 드러났다.PostgreSQL 트랜잭션 롤백율 18.4%정렬·해시가 메모리가 모자라 디스크로 새면서 6일간 임시 파일 19GB, 1,439개로그용 Redis가 메모리 무제한 + 축출 안 함 → 호스트 램이 유일한 한계, 즉 OOM 위험. 게다가 append 로그가 원본 대비 47배로 부풀어 있음숫자만 보면 다 나쁘다.그런데 바로 안 고쳤다.18%는 나쁜 숫자인가롤백율 18%는 언뜻 앱이 에러투성이로 보인다.그런데 롤백은 에러일 때만 생기는 게 아니다.커넥션 풀은 연결을 반납할 때 상태를 리셋하며 롤백한..

Infra 2026.07.16

[백엔드] 성공이라 말하는 실패, 상태코드가 거짓말할 때

부제: 전부 거부됐는데 202를 돌려주던 API, 그리고 끝내 못 고친 이유이미지 업로드가 조용히 실패했다.파일이 전부 거부됐는데 서버는 202를 반환했다.앞단(nginx·백엔드)은 그 상태코드를 보고 "성공"으로 처리했다.정작 실패 목록은 응답 본문의 errors[] 안에 얌전히 담겨 있었다.상태코드만 보는 호출자는 알 길이 없었다.상태코드가 결과를 속인다문제를 이렇게 정의했다.2xx를 반환하지만 실제로는 실패해서, 상태코드만 보는 호출자가 성공으로 오인하는 응답.핵심은 상태코드가 결과를 정직하게 반영하지 않는다는 것이다.배치 처리, 비동기 접수, 예외를 삼키는 핸들러 — 이런 자리에서 잘 생긴다.한 곳에서 났으면 다른 곳에도 있을 거라 봤다.그래서 라우터를 전수조사하기로 했다.무엇이 거짓말이고 무엇이..

개발 2026.07.16

[백엔드] BE 개발자 없이 시작 느슨한 JSONB 계약 컨텍스트

부제: AI↔백엔드 계약을 맺을 사람이 없어서 어떤 구조든 받게 만든 이야기시스템은 백엔드(BE)가 채워 보내는 사용자 정보를 받아 대화 컨텍스트에 넣는다.그 정보를 어떤 형태로 주고받을지가 문제였다.그리고 이 결정은 기술이 아니라 조직 현실이 정했다.계약할 상대가 없었다프로젝트를 시작할 때 CTO도, 백엔드 개발자도 없었다.AI 서버와 백엔드 사이에 "무슨 필드를 어떤 스키마로 주고받자"는 계약을 맺을 상대가 없었다는 뜻이다.엄격한 스키마를 정하려면 양쪽이 합의하고, 백엔드가 그에 맞춰 개발해야 한다.그럴 사람이 없으니 그 길은 애초에 막혀 있었다.어떤 구조든 받는다그래서 방향을 뒤집었다."스키마를 맞춘다"가 아니라 "어떤 구조가 와도 추가 개발 없이 받는다"로.최소한의 뼈대만 정했다.사용자 정보를 세..

개발 2026.07.14

[백엔드] 외부 API 선택 이론적 정확도보다 실사용 제약

부제: 이론상 더 정확한 국내 API 대신 글로벌 API를 고른 이유챗봇에 날씨 기능을 붙이며 어떤 날씨 API를 쓸지 골라야 했다.후보는 둘이었다. 국내 기상청과 글로벌 날씨 API.이론상 더 정확한 쪽한국의 단기 강수 정확도만 놓고 보면, 이론상 국내 기상청이 우위일 수 있다."가장 정확한 걸 쓰면 되지 않나" 하는 게 첫 생각이었다.그런데 종합해 보니 글로벌 API를 골랐다.정확도 하나가 아니라 세 가지를 같이 봤기 때문이다.요청 한도(quota): 실사용에서 얼마나 부를 수 있느냐. 정확해도 한도에 막히면 못 쓴다.코드 복잡도: 붙이고 유지하는 비용.확장성: 한국만이 아니라 해외 사용자까지 커버."가장 정확한 것"이 아니라 "실제로 감당 가능하고 넓게 쓰이는 것"을 택한 것이다.국내 초단기실황을 ..

개발 2026.07.13

[관측성] 트레이스 원본 바이너리 마스킹 base64 PII

부제: 관측 도구에 14MB base64를 통째로 보냈다가 멈춘 이야기LLM 서비스엔 관측 도구를 붙여 둔다.어떤 요청이 들어와 어떤 프롬프트로 무슨 답을 냈는지, 흐름을 트레이스로 남긴다.문제를 디버깅할 때 이 기록이 없으면 깜깜하다.그런데 이 기록에 넣지 말아야 할 게 있었다.원본을 통째로 실었다PDF를 분석하는 요청이 있었다.그 파일을 14MB짜리 base64 문자열로 바꿔 트레이스에 통째로 실어 보냈다."입력이 무엇이었나"를 남긴다는 게, 파일 원본을 그대로 담아 버린 것이다.트레이스 하나가 수십 MB로 부풀었다.이게 트레이스 저장소(ClickHouse)의 다른 버그와 맞물리면서 크게 번졌다.쿼리가 12시간 멈추고, 헬스체크까지 막혀서 컨테이너가 재시작을 반복했다.교훈은 분명했다.관측 도구는 "무..

Infra 2026.07.12

[복구] 서버 재시작 유실 백그라운드 작업 10분 타임스탬프 가드

부제: 재시작하면 사라지는 fire-and-forget 작업을 되살리기서버엔 요청과 별도로 뒤에서 도는 작업이 있다.문서를 업로드하면 바로 "접수됐다"고 응답하고, 분석·텍스트 추출·임베딩은 백그라운드에서 처리한다.사용자를 수십 초씩 기다리게 하지 않으려는 구조다.여기에 구멍이 있었다.재시작하면 증발한다이 백그라운드 작업은 메모리에서만 돈다.서버가 재시작되면(배포, 메모리 부족으로 강제 종료, 컨테이너 재생성) 그 작업이 통째로 사라진다.작업 안에 예외 처리를 해 뒀어도 소용없다.프로세스 자체가 죽으면 예외를 잡을 코드도 같이 죽기 때문이다.결과가 좋지 않았다.문서가 "처리 중" 상태로 영원히 멈춘다.검색도 조회도 안 되는 채로. 실제로 며칠에서 열흘 넘게 고착된 게 여러 건 있었다.켤 때 되살린다방향은..

Infra 2026.07.11

[모니터링] 저트래픽 서비스 4계층 관측 비율 대신 절대건수

부제: 하루 몇십 요청짜리 서비스에서 비율 임계값이 무의미한 이유모니터링을 계층으로 쌓았다.호스트(자원이 한계인가), 컨테이너, 컴포넌트(DB·redis 내부), 그리고 서비스(앱이 요청을 제대로 처리하나).계층을 나눈 데는 이유가 있고, 알림 임계값을 정하는 데는 더 큰 고민이 있었다.계층마다 다른 질문각 계층은 서로 다른 질문에 답한다.호스트는 "서버 자원이 한계인가"를 본다. CPU·메모리·디스크의 사용률과 포화다.컨테이너는 그 자원이 어느 컨테이너로 갔는지를 나눈다.컴포넌트는 DB·redis 같은 내부 부품이 건강한지를 본다.그런데 여기까지로는 정작 중요한 게 안 보인다."앱이 사용자 요청을 실제로 성공시키고 있나."호스트가 멀쩡해도 앱은 500을 뱉고 있을 수 있다.그래서 마지막 계층을 서비스로..

Infra 2026.07.11

[비동기] SSE heartbeat 컨텍스트 누수 OTel 트레이스 깨짐

부제: 하트비트를 넣었더니 트레이스가 깨진 이야기챗봇은 답을 스트리밍으로 흘려보낸다.답이 길어지면 중간에 연결이 끊기지 않게, 주기적으로 빈 신호(하트비트)를 함께 보낸다.문제는 그 하트비트를 어떻게 끼워 넣었느냐였다.하트비트가 필요했다원래 구현은 이랬다.청크가 하나 올 때마다, 제너레이터의 다음 단계를 별도의 비동기 task로 띄웠다.하트비트 타이머와 다음 청크를 동시에 기다리려고 그렇게 쪼갠 것이다.겉보기엔 잘 돌았다.그런데 관측 도구에서 이상한 에러가 쏟아지기 시작했다."다른 컨텍스트에서 만들어진 토큰"트레이스를 남기는 라이브러리(Langfuse, 내부적으로 OpenTelemetry)가계속 "Token was created in a different Context"라는 에러를 냈다.원인은 컨텍스트였..

Infra 2026.07.10

[디버깅] user thread 완전복제 재현 원본 오염 방지

부제: 운영 버그를 원본을 건드리지 않고 재현하기특정 유저, 특정 대화에서만 나는 버그가 있다.재현하려고 그 유저에게 채팅을 한 번 쏘면, 그 순간 메모리·요약·체크포인트가 바뀐다.상태가 오염돼서 두 번째 디버깅은 이미 다른 조건이 된다.원본에 쏘면 오염된다손으로 재현 데이터를 만들 수도 없었다.수백 개의 대화 스레드에 분석 기록까지 누적된 상태를,픽스처 코드로 흉내 내는 건 사실상 불가능하다.그래서 방향은 하나였다.원본과 똑같은 상태의 사본을 만들고, 실험은 사본에서 한다.복제의 정의부터 못 박았다.모든 기본 키(PK)와 외래 키(FK)만 새로 생성하고,나머지 컬럼은 JSONB든 임베딩이든 파일이든 타임스탬프든 바이트 그대로 복사한다.즉 ID만 빼면 원본과 구별이 안 되는 사본이다.복제 범위는 손으로 ..

Infra 2026.07.09
728x90