프로그래머스 입문 겹치는 선분의 길이 (🔗 링크: 코딩테스트 > 코딩테스트 입문 > 겹치는 선분의 길이 ) 문제설명 선분 3개가 평행하게 놓여 있습니다. 세 선분의 시작과 끝 좌표가 [[start, end], [start, end], [start, end]] 형태로 들어있는 2차원 배열 lines 가 매개변수로 주어질 때, 두 개 이상의 선분이 겹치는 부분의 길이를 return 하도록 solution 함수를 완성해보세요. lines 가 [[0, 2], [-3, -1], [-2, 1]]일 때 그림으로 나타내면 다음과 같습니다. 선분이 두 개 이상 겹친 곳은 [-2, -1], [0, 1]로 길이 2만큼 겹쳐있습니다. 제한사항 lines 의 길이 = 3 lines 의 원소의 길이 = 2 모든 선분은 길이가 1 이상입니다. lines 의 원소는 [a, b] 형태이며, a, b는 각각 선분의 양 끝점 입니다. -100 ≤ a < b ≤ 100 입출력 예 lines result [[0, 1], [2, 5], [3, 9]] 2 [[-1, 1], [1, 3], [3, 9]] 0 [[0, 5], [3, 9], [1, 10]] 8 문제풀이 👩💻 문제 풀기 class Solution { public int solution(int[][] lines) { int[] arr = new int[201]; //음수 100개, 0, 양수 100개 for (int[] line : lines) { int start = line[0] + 100; int end = line[1] + 100; for (int i = start; i < end; i++) { arr[i]++; } } int answer = 0; for (int count : arr) { if (count >= 2) { answer++; } } return answer; } } 🧐 코드 풀이 음수 좌표를 배열 인덱스로 바꾸기 ( +100 ) 선분 좌표가 $-100$부터 $100$까지라 배열 인덱스로 바로 쓸 수 없다. 그래서 모든 좌표에 +100을 더해 $0 \sim 200$ 범위로 맞춘다. 기준점인 $0$은 배열의 $100$번 인덱스가 된다. 선분이 지나가는 칸(구간) 채우기 각 선분의 시작 인덱스부터 끝 인덱스 -1 까지 1 단위 칸을 순회하며 카운트를 1씩 늘려준다. 2번 이상 지나간 칸의 개수 세기 배열 값이 2 이상인 칸은 최소 2개 이상의 선분이 겹친 구간이다. 이 칸들의 총개수를 구하면 겹친 부분의 전체 길이가 된다. 3개 이상의 선분이 겹쳐도 중복 없이 깔끔하게 처리된다.
Langfuse 10 — 셀프호스팅 운영 이 글은 Langfuse 실습 시리즈 의 10편입니다. 에이전트를 관측하는 시스템 자체를 운영하기 위해 마스킹·수집 실패·백업 복원을 확인합니다. 이 글에서 다룰 주제 SDK에서 내보내기 전에 민감한 관측 데이터를 가리는 방법 업무 결과와 관측 데이터 수집 성공을 구분하는 방법 데이터 보관 정책과 OSS·Enterprise 기능의 경계 별도 환경에서 백업을 복원하고 원본과 비교하는 방법 주요 단어 · Masking · Exporter · Ingestion · Retention · RPO · RTO 에이전트를 관측하는 도구도 장애가 날 수 있다. 입력과 출력에 민감정보가 섞일 수도 있고, 대시보드는 정상처럼 보이지만 새 데이터가 들어오지 않을 수도 있다. 마지막 편은 무엇을 저장하고, 수집 실패를 어떻게 알아차리며, 저장한 데이터를 실제로 복구할 수 있는가 를 다룬다. 실습은 로컬 Langfuse v4.50.0 OSS와 Python SDK 4.16.0에서 진행했다. 실제 개인정보나 운영 비밀을 넣지 않고, 테스트용 이메일과 토큰 표식을 사용했다. 마스킹·수집 확인에는 모델 호출이 필요하지 않아 Ollama를 추가 호출하지 않는다. 아래 공식 기능 범위는 2026년 10월 5일 확인했다. 1. 운영 경계 그리기 그림은 애플리케이션에서 데이터가 나오는 경로와 저장소를 복원하는 경로를 분리해서 보여 준다. 마스킹은 기록을 내보내기 전에 적용하고, 백업은 저장된 데이터를 복원 가능한 형태로 보존한다. 한쪽으로 다른 쪽을 대신할 수 없다. 이번 구성의 데이터 경로는 다음과 같다. 애플리케이션 → SDK 마스킹·배치 전송 → Langfuse 수집 경로 → 저장소와 Worker 처리 → UI·Metrics·평가·알림 Exporter 는 관측 데이터를 외부 시스템으로 보내는 구성요소다. Ingestion 은 전송된 데이터를 받아 저장·처리하는 수집 과정이다. 애플리케이션이 응답을 반환한 시점과 이 과정이 끝난 시점은 같지 않을 수 있다. 따라서 “사용자 요청 정상”, “SDK 전송 정상”, “Worker 처리 정상”, “최신 데이터 조회 가능”을 나누어 확인한다. 웹 화면에 접속된다는 사실만으로 이 경로 전체가 정상이라고 판단하지 않는다. 2. 전송 전 마스킹 이번에는 Python SDK의 mask_otel_spans 를 사용했다. 내보낼 OpenTelemetry span의 문자열 속성에서 테스트용 이메일과 토큰 표식을 교체한다. 새 Python 설정에는 이 훅이 권장되며, 기존 mask 와 적용 범위·시점이 다르다. 공식 SDK 마스킹 문서 핵심 코드는 다음과 같다. 실제 실습의 operations.py 에서 사용한 함수다. from langfuse.types import MaskOtelSpansResult, OtelSpanPatch def redact(*, params): patches = {} for identifier, span in params.spans.items(): replacements = {} for key, value in span.attributes.items(): if isinstance(value, str): masked = value.replace( "learner@example.test", "[REDACTED_EMAIL]" ).replace( "LAB_TOKEN_DO_NOT_EXPORT", "[REDACTED_TOKEN]" ) if value != masked: replacements[key] = masked if replacements: replacements["masking.applied"] = True patches[identifier] = OtelSpanPatch( set_attributes=replacements ) return MaskOtelSpansResult(span_patches=patches) 이 함수를 Langfuse(..., mask_otel_spans=redact) 에 전달했다. 입력, 출력, 메타데이터에 테스트 표식을 넣어 한 건을 기록한 뒤, 저장된 관측을 API로 다시 읽었다. 확인 항목 실제 결과 애플리케이션 안의 원본 입력 그대로 유지 저장된 이메일 [REDACTED_EMAIL] 저장된 토큰 표식 [REDACTED_TOKEN] 저장 관측의 원본 이메일·토큰 검색 발견되지 않음 마스킹용 모델 호출 0회 Trace 이름은 aiops-export-masking , 환경은 aiops-lab 이다. 상세 화면에서 Input, Output, Metadata를 각각 읽으면 한 필드만 가리고 다른 필드에 원문을 남기는 실수를 점검할 수 있다. Tracing 에서 aiops-export-masking 을 열면 Input과 Output은 [REDACTED_EMAIL] · [REDACTED_TOKEN] , Metadata의 contact도 대체된 값으로 보인다. 이 화면은 UI에서만 가린 것이 아니라 SDK가 전송 전에 바꾼 결과다. 이 실습은 알려진 두 문자열을 바꾸는 최소 예제다. 모든 개인정보나 비밀을 찾아내는 탐지기가 아니다. 실무에서는 처음부터 기록할 필드를 제한하고, 중첩 데이터·도구 응답·에러 메시지·별도 로그 저장 경로까지 정책을 정해야 한다. 또한 SDK 마스킹은 Langfuse로 나가는 관측 데이터 를 바꾼다. 애플리케이션이 LLM에 보낸 입력, 애플리케이션 메모리의 원본, 다른 exporter가 보낸 데이터까지 자동으로 바꾸지는 않는다. 모델에 민감정보를 보내지 않아야 한다면 모델 요청 전에 별도 처리가 필요하다. 마스킹 함수 자체도 운영 코드다. 이번 SDK 문서에 따르면 훅이 예외를 던지거나 잘못된 결과를 반환하면 전체 내보내기 배치가 버려질 수 있다. 외부 API를 호출하는 복잡한 마스킹 함수를 넣기 전에 실패 시 동작과 처리 시간을 검증해야 한다. 3. 서버 마스킹과 차이 서버 측 마스킹은 여러 클라이언트에 공통 정책을 적용하기 편하다. 그러나 현재 셀프호스팅에서는 Enterprise 기능이며, Worker가 비동기로 처리하기 전에 원시 이벤트가 Blob Storage에 먼저 들어갈 수 있다. “민감정보가 애플리케이션 밖으로 절대 나가면 안 된다”는 요구에는 클라이언트에서 내보내기 전에 처리하는 경계가 중요하다. 공식 서버 마스킹 문서 이번 실습은 OSS의 SDK 마스킹만 사용했다. 서버 측 마스킹 라이선스나 외부 처리 서비스를 추가하지 않았다. 계정에서 보이는 화면 이름만 보고 기능이 활성화돼 있다고 가정하지 않고 실제 적용 지점과 저장 결과를 확인했다. 4. 수집 장애를 분리하기 수집 실패를 보기 위해 실제 Langfuse 서버를 끄는 대신, 별도의 Python 프로세스 한 개만 연결할 수 없는 loopback 주소로 설정 하는 실습을 실행했다. 비어 있는 포트를 무작정 고르지 않고, 로컬 소켓을 예약하되 연결을 받지 않도록 해서 다른 서비스에 요청이 갈 가능성을 줄였다. 실습의 비교 대상은 두 개다. 잘못된 수집 주소로 설정한 SDK: 업무 처리 부분은 고정된 읽기 전용 결과를 계산하고, 내보내기 로그를 수집한다. 정상 로컬 주소로 설정한 새 프로세스: 같은 업무 결과를 계산하고 새로운 Trace를 보낸다. 여기서 업무 결과는 합성 요청의 접수 상태이며 LLM 답변이 아니다. 이 방법은 SDK의 연결 실패를 관찰하기 위한 실습이다. Redis나 ClickHouse 장애, Worker 중단, 서버 내부 큐 복구를 시험하는 것과 범위가 다르다. docs/langfuse-study-2026-10-04/lab/.venv/bin/python \ docs/langfuse-aiops-2026-10-05/lab/ingestion_probe.py 이미 실행한 뒤에는 다음 명령으로 저장된 Trace ID만 다시 확인한다. 새로운 관측이나 모델 호출을 만들지 않는다. docs/langfuse-study-2026-10-04/lab/.venv/bin/python \ docs/langfuse-aiops-2026-10-05/lab/ingestion_probe.py --verify-only 실행 기록은 evidence/ingestion-probe.json 에 남겼다. 실제 결과는 다음과 같다. 항목 연결 불가 SDK 정상 주소의 새 SDK 계산한 업무 결과 accepted , read_only=true 동일 Exporter 오류 로그 ERROR 발생 없음 명시적 flush() 소요 약 2.002초 약 0.047초 flush() 반환값 None None 실제 서버에서 해당 Trace 조회 확인 시점에 0건 새 Trace 1건 실습용 SDK의 timeout은 2초로 설정했다. 오류 로그는 Failed to export spans batch due to timeout, max retries or shutdown. 이었다. 원본 Langfuse 서버는 이 과정에서 계속 동작했다. 관측 ID·출력·환경과 오류 기록을 포함한 11개 검사가 통과 했다. 정상 Trace 이름은 aiops-ingestion-normal , 환경은 aiops-ingestion-probe 다. 이 실습은 정상 주소의 새 요청이 다시 기록됨 을 확인한 것이다. 먼저 실패한 span을 자동 복구하거나 재전송했다고 표현하지 않는다. 실패한 Trace가 확인 시점에 없었다는 결과와 모든 장애 상황의 영구 손실 여부를 일반화하는 주장도 구분한다. flush() 는 짧게 실행되는 스크립트에서 데이터를 내보낼 기회를 주는 데 필요하다. 하지만 이 SDK의 반환값은 영구 저장 성공을 증명하는 영수증이 아니다. 성공 여부가 중요하면 실제 조회 가능 여부와 exporter 오류를 함께 확인해야 한다. 5. 관측 시스템의 지표 운영에서는 애플리케이션 성공률 외에 데이터의 최신성 을 본다. 일정 간격으로 알려진 소량의 관측을 보내고, 일정 시간 안에 조회되는지 확인하는 방식이다. 요청 발생 시각과 조회 가능 시각의 차이가 수집 경로의 지연을 드러낸다. 이 실습에서 상시 모니터링 작업을 설치한 것은 아니며, 운영 도입 시 추가할 항목이다. 함께 볼 값은 다음과 같다. 영역 확인할 값 의미 애플리케이션 SDK 내보내기 오류·재시도·드롭 서버 도착 전 손실 가능성 Langfuse Worker 대기 큐 깊이·처리율 수집량에 비해 처리가 밀리는지 저장소 디스크 사용량·읽기/쓰기 지연 저장 용량과 병목 조회 경로 최신 관측의 지연·누락 사용자가 볼 수 있는 데이터의 신선도 평가·알림 평가 대기·전달 실패 관측은 됐지만 후속 조치가 멈췄는지 현재 공식 스케일링 문서는 Worker의 StatsD 지표 langfuse.queue.ingestion.depth 와 type:waiting 태그를 설명한다. 이 글에서는 해당 지표를 Prometheus로 수집하는 별도 스택까지 설치하지 않았다. 도입 환경의 메트릭 수집 경로에 맞춰 연결할 대상이다. 공식 스케일링 문서 데이터가 끊겼을 때 비용 0, 오류 0이라는 숫자가 나오면 정상으로 오해하기 쉽다. No Data를 실제 0과 구분하고, “새 요청이 없었음”과 “수집을 못 했음”을 비교할 신호가 필요하다. 6. 알림 연결의 신뢰 경계 앞선 Alerts 실습의 수신기는 Langfuse와 같은 Docker 네트워크의 aiops-webhook 이다. URL은 https://aiops-webhook/alerts 이며 호스트에 수신 포트를 공개하지 않았다. Langfuse에서 내부 수신기로 나가는 경로만 사용한다. 실습 설정은 내부 호스트 한 개를 정확히 허용하고, 해당 실습 인증서를 신뢰하도록 지정했다. LANGFUSE_WEBHOOK_WHITELISTED_HOST: aiops-webhook NODE_EXTRA_CA_CERTS: /study/aiops-webhook-ca.crt 인증서의 SAN은 aiops-webhook 이며 유효기간은 2026년 10월 5일~11월 4일의 30일이다. 장기 운영용 인증서 자동 갱신을 구성한 것은 아니다. TLS 검증 전체를 끄는 방식으로 연결하지 않았다. 수신기는 본문의 HMAC 서명과 타임스탬프를 확인하고, 같은 이벤트 ID는 다시 저장하지 않는다. 인증 헤더나 서명 비밀값을 로그에 출력하지 않는다. 서버가 알림을 보냈다는 기록과 수신기가 검증해서 받아들였다는 기록을 구분해 확인할 수 있게 한 것이다. 현재 실습 폴더의 .private 디렉터리에는 수신기 TLS 키·인증서와 Webhook 서명 비밀값이 있다. 기존 환경을 계속 사용할 때 이 폴더를 삭제하면 안 된다. Compose가 인증서를 Web·Worker에 마운트하므로, 1편의 init_env.py 로 .env 만 만들어서는 현재 확장 구성을 새 컴퓨터에서 재현할 수 없다. 새 환경으로 복사할 때는 그 환경에 맞는 TLS 키·인증서(SAN aiops-webhook )와 신뢰 인증서를 다시 생성하고, Webhook 설정의 서명 비밀값을 수신기와 일치시켜야 한다. 비공개 파일을 공개 저장소나 블로그에 올려 해결하지 않는다. 기존 백업을 복원하는 경우에는 대응하는 비공개 설정을 안전하게 보존해야 한다. 파일 위치와 재실행 순서는 실습 폴더의 README.md 에 정리했다. 다만 이 설정은 로컬 학습용이다. 운영 연결에서는 인증서 발급·갱신, 비밀값 회전, 수신기 가용성, 이벤트 중복 처리와 재전송 정책을 실제 운영 요구에 맞춰 정해야 한다. 7. 보관과 삭제 Retention 은 데이터를 얼마나 오래 보관할지 정하는 정책이다. 현재 Langfuse의 프로젝트 Data Retention 기능은 셀프호스팅 Enterprise 기능이며, 이 OSS 실습에는 자동 만료 정책을 적용하지 않았다. 셀프호스팅에서는 정책이 없으면 이벤트가 기본적으로 계속 보관된다. 공식 Data Retention 문서 보관 정책을 만들 때 Trace와 Dataset도 구분해야 한다. Dataset에 저장한 입력·기대 출력·메타데이터는 원본 Trace와 별도의 자료다. 프로젝트 이벤트 보관 기간이 끝나 원본 Trace가 삭제돼도 Dataset item은 남을 수 있다. 반대로 Dataset에 넣었다는 이유로 원본 Trace의 상세 관측까지 영구 보존되는 것은 아니다. 운영에서 보관 범위를 정할 때는 실시간 조사용 Trace, 회귀검사용 Dataset, 감사 기록, 백업 복사본을 각각 다룬다. 화면에서 삭제했다고 백업이나 버전이 있는 오브젝트 저장소의 이전 복사본까지 사라졌다고 가정하면 안 된다. 또한 조직 수준의 접근 제어와 프로젝트별 세부 권한은 같은 기능이 아니다. 현재 공식 문서상 프로젝트 수준 RBAC, 보호된 Prompt Label, 보관 정책, Audit Logs, 서버 마스킹은 Enterprise 라이선스 대상이다. 이번 실습은 이 기능을 사용했다고 주장하지 않는다. 공식 셀프호스팅 라이선스 범위 8. 백업과 복원 백업의 목표는 압축파일을 만드는 것에서 끝나지 않는다. 다른 환경에서 필요한 데이터를 읽을 수 있어야 한다. RPO 는 감당할 수 있는 데이터 손실 구간, RTO 는 서비스 복구에 허용하는 시간이다. 두 값을 정해야 백업 주기와 복원 절차를 설계할 수 있다. Langfuse의 관측 데이터는 ClickHouse, 사용자·프로젝트·설정 등은 PostgreSQL, Blob 데이터는 오브젝트 저장소와 연결된다. 한 데이터베이스만 백업하고 전체 환경을 복구할 수 있다고 가정하면 안 된다. 공식 백업 가이드 이번 로컬 복원 실습은 작은 환경에 맞춰 일관된 시점의 볼륨 복사 방식으로 실행했다. 쓰기 작업을 끝낸 뒤 원본 서비스를 정상 중지한다. PostgreSQL, ClickHouse 데이터·로그, Redis, MinIO의 이름 있는 볼륨 다섯 개를 읽기 전용으로 압축한다. 원본 서비스를 먼저 다시 시작한다. 별도 Docker 프로젝트와 새 볼륨에 복사본을 복원한다. 원본과 복원본에서 같은 Prompt와 Trace·Score를 API로 읽어 내용 해시를 비교한다. 확인을 마치면 복원본을 중지한다. 복원본은 별도 Docker 프로젝트와 외부 통신을 제한한 내부 네트워크를 사용한다. 복원본 Worker와 Webhook 수신기를 실행하지 않아 보관된 알림이나 평가가 다시 작동하는 일을 막는다. 원본 볼륨을 지우거나 덮어쓰는 절차가 아니다. 실제 결과는 다음과 같다. 정지된 상태의 스냅샷 시각은 2026년 10월 5일 14:15:40 KST였고, 압축은 14:16:09 KST에 끝났다. 확인 항목 실제 결과 원본 중지부터 health 회복까지 82.677초 백업한 볼륨 5개 압축파일 합계 385,577,254 bytes, 약 367.7 MiB 복원본의 확인 대상 Prompt v1, Trace 3개, 관측 18개, 작업 Score 3개 원본·복원본 비교 선택한 내용의 해시 일치 원본 재시작 전후 선택한 내용의 해시 일치 검증 종료 후 복원본 5개 서비스 중지, 원본 7개 서비스 실행 백업·복원 확인 9개 검사 통과 처음에는 복원본의 호스트 포트로 health를 조회하려 했지만 Docker 내부 네트워크 설정 때문에 해당 경로로 접근할 수 없어 대기 검사가 실패했다. 네트워크 격리를 해제하지 않고, 복원본 컨테이너 안에서 같은 공개 API를 호출해 필요한 데이터를 비교했다. 최종 상태는 verified-via-internal-api 로 남겼으며, 최초 접근 실패 기록도 별도로 보관했다. 따라서 확인한 것은 격리 복원본에서 선택한 기존 데이터를 API로 읽을 수 있다는 것 이다. 복원본 UI 로그인, 새 모델 요청, Worker 평가 처리, 모든 레코드·미디어의 전수 복원을 확인한 것은 아니다. 82.677초도 이번 원본 서비스의 중지·재시작 구간 측정이며, 운영 재해 상황의 RTO 보장이 아니다. 측정값과 검사 결과는 evidence/backup-restore.json 에 남겼다. 이번 방식은 작은 로컬 환경의 정지 스냅샷 실습이다. 무중단 운영이나 특정 시점 복원(PITR)이 필요하면 서비스별 백업 체계와 복구 절차를 따로 설계해야 한다. 백업에는 프로젝트 키와 암호화 설정처럼 민감한 정보가 포함될 수 있다. 실습 파일은 전용 비공개 폴더와 제한된 파일 권한으로 저장한다. 권한 제한과 파일 암호화는 서로 다른 보호 수단이다. 외부 저장소로 옮기는 운영 백업에는 암호화와 복호화 키 관리, 정기 복원 점검이 추가로 필요하다. 9. 실행 제어와 관측의 역할 이번 과정에서 Langfuse로 얻은 것은 Agent·Tool 실행 기록, 프롬프트 버전 연결, 평가 결과, 비용·지연 집계, 알림, 실패를 회귀검사로 되돌리는 흐름이다. 반면 권한 없는 도구 호출 차단, 요청당 최대 비용, 토큰·반복·시간 상한, 쓰기 작업 승인, 즉시 중단은 에이전트 실행 코드나 게이트웨이에서 적용해야 한다. 비동기 평가·알림이 나중에 위험을 발견하는 것과, 실행 전에 위험한 행동을 막는 것은 시점이 다르다. 이번 unknown-injection 사례는 이 차이를 보여 줬다. 모델 판단은 악의적인 합성 로그에 영향을 받았지만, 프로그램에 쓰기 도구를 제공하지 않아 삭제나 재시작을 실행할 수는 없었다. 올바른 판단과 제한된 권한을 각각 검증해야 한다. 운영 도입의 다음 실험은 더 많은 모델을 연결하는 것보다, 대표 장애 사례를 늘리고 실패를 다시 재현하는 것에서 시작할 수 있다. 사례별 기대 행동, 사람에게 넘길 조건, 비용·지연 허용 범위, 관측 데이터의 수집·복원 가능성을 함께 관리하면 에이전트 변경의 영향을 설명할 수 있다. 실습·공식 문서 확인: 2026-10-05. Langfuse OSS v4.50.0, Python SDK 4.16.0. 합성 표식 마스킹, 별도 SDK의 연결 실패와 새 요청 수집, 격리된 복원본의 기존 데이터 조회를 실제 확인했습니다. 복원본 UI 로그인·Worker 신규 처리·전체 데이터 전수 복원은 검증 범위에 포함하지 않았습니다. 이전: 9편 — 저장된 실행 평가와 알림 전체 과정: Langfuse 실습 — 로컬 LLM부터 AIOps 운영까지
Travelling around Sydney is easier when you have a spacious and reliable vehicle for your journey. A Maxi Cab Sydney service is a practical choice for families, friends, tourists and groups who need additional passenger and luggage space. Instead of arranging several smaller vehicles, passengers can travel together in one suitable vehicle. Sydney has many popular destinations, including Sydney Airport, Sydney CBD, Circular Quay, Darling Harbour, Bondi Beach, The Rocks, hotels, shopping centres and event venues. Having suitable transport can make it easier to move between these locations without the inconvenience of coordinating multiple cars. Wav Maxi Cabs provides maxi cab transport for different travel needs across Sydney. Whether you are travelling from the airport, heading to a hotel, attending an event or exploring the city, choosing a suitable maxi cab can make the journey more convenient. Why Choose a Maxi Cab Sydney Service? A maxi cab is particularly useful when a standard taxi does not provide enough space for everyone travelling together. Families and groups can have more room for passengers, luggage and personal belongings. Travelling together also makes group journeys easier to organise. Everyone can leave from the same pickup location and arrive at the same destination without having to divide the group between several vehicles. A Maxi Cab Sydney service can be useful for: Airport transfers Hotel transfers Family outings Group journeys Sightseeing trips Shopping trips Corporate travel Weddings and special events Concerts and sporting events Everyday transportation Maxi Cab Sydney Airport Transfers Sydney Airport is one of the busiest transport points in the city, and getting to or from the airport is an important part of many journeys. When travelling with several passengers or multiple suitcases, choosing a vehicle with suitable space can make the airport journey more comfortable. A maxi cab can help families and groups travel together rather than arranging separate vehicles. Before booking an airport transfer, it is useful to have your flight information, terminal, pickup location, destination, passenger numbers and luggage requirements ready. Wav Maxi Cabs can assist passengers travelling between Sydney Airport and destinations throughout Sydney, including hotels, residential areas, business locations and major attractions. Travelling Around Sydney with Family and Friends Sydney offers plenty of places to visit with family and friends. From beaches and harbour attractions to restaurants, shopping areas and entertainment venues, many destinations can be spread across different parts of the city. Travelling together in a maxi cab can make these outings easier to coordinate. Passengers can meet at one pickup location and continue their journey together. This can be especially helpful when travelling with children, older family members or several pieces of luggage. Having everyone in the same vehicle can also reduce the need to organise multiple pickup and drop-off arrangements. Maxi Cab for Sydney Events Sydney hosts concerts, sporting events, festivals, exhibitions and other large events throughout the year. Parking and traffic can become challenging around busy venues, particularly during major events. Arranging a maxi cab can provide a convenient transport option for people travelling together to an event. Groups can plan a common pickup point and travel to the venue together. After the event, having a planned transport option can also make it easier for everyone to leave together and continue to their next destination. Exploring Popular Sydney Destinations A trip around Sydney can include several well-known attractions. Depending on your itinerary, you may want to visit: Sydney Opera House Sydney Harbour Bridge Circular Quay The Rocks Darling Harbour Bondi Beach Royal Botanic Garden Barangaroo Sydney CBD Taronga Zoo Planning your transportation before starting the day can help you spend more time enjoying the destination and less time organising transport. Travelling with Luggage Luggage is an important consideration when choosing transport. Airport passengers may have suitcases, while families may also carry prams, shopping bags or other belongings. A suitable maxi cab provides a practical option for passengers who need more space than a standard taxi may offer. When booking, it is helpful to provide accurate information about the number of passengers and amount of luggage so that the appropriate vehicle can be arranged. Maxi Cab Sydney for Hotel Transfers Visitors staying in Sydney hotels may need transportation between the airport, accommodation, restaurants, attractions and other destinations. A maxi cab can be useful for families and groups who want to travel together throughout their stay. It can also simplify transfers when several passengers have luggage. For visitors unfamiliar with Sydney, arranging transportation in advance can provide a more organised start to their trip. How to Prepare for Your Maxi Cab Journey Before your trip, keep the following information ready: Pickup location – Provide the correct address or meeting point. Destination – Confirm where you need to go. Passenger numbers – Make sure the vehicle is suitable for your group. Luggage requirements – Mention if you are travelling with several suitcases or large items. Travel date and time – Provide accurate journey details. Flight information – Include flight details when arranging an airport transfer. Special requirements – Mention any specific travel requirements when booking. Providing accurate information helps make the booking process smoother and allows the transport provider to understand your requirements. Choose Wav Maxi Cabs for Sydney Travel Whether you are arriving at Sydney Airport, travelling to your hotel, heading to a family outing or attending an event, a suitable maxi cab can make group transportation easier. Wav Maxi Cabs offers maxi cab transport for passengers travelling across Sydney. With spacious vehicle options and service for different types of journeys, it can be a practical choice for people looking for comfortable and organised transportation. For your next journey, plan your pickup and destination in advance, choose a vehicle suitable for your passenger numbers and luggage, and make your Sydney travel easier with a Maxi Cab Sydney service.
자신이 관심있거나, 보고싶은 축구 소식이있으면 여러가지 곳들을 돌아다녀보고 하는 번거로움이 있습니다. 이러한 문제에서, 해당 프로젝트를 기획하고 배포까지 진행해봅니다. https://transfertracker-front.vercel.app 편의성 업데이트 뒤로가기시, 페이지에서 나가지던 문제를 해결했습니다. 이제, 웹에서 뿐만아니라 앱으로도 확인할 수 있습니다. Chrome/Edge에서는 주소창 쪽에 설치 버튼이 뜨고, 설치하면 TransferTracker 를 실행할 수 있습니다. macOS Safari라면 공유 → Dock에 추가, iPhone은 공유 → 홈 화면에 추가를 실행해서 하실 수 있습니다. 디자인들이 변경됐습니다. K 리그 1 추가됐습니다. 주요 선수들에 대한 선수들 이름이 한글로 표시되게 변경했습니다. 홈 소식 확인 [ 소식 업데이트 예정입니다. ] 팀들 확인 팀 상세 [ 빠른시일내에, 5대리그 나머지 팀들에 대한 정보 업데이트 진행하겠습니다. ] 선수 조회 [ 필터기능이 추가됐습니다. ] 이적 정보 [ 리그 / 팀 에 대한 필터기능이 추가됐습니다. ] 이적정보는 현재 2020년 이후정보들만 기록되어져있습니다. 이후 이전데이터들도 추가 예정입니다. V2 에서는 V1 에서 다음 기능들이 추가됐어요. 리그별로 필터 / 팀별로 필터 - 완료 ( 5대리그 ) 소속선수들 업데이트 - 완료 ( 5대리그 ) 이후에는 빠른시일내에, 이적정보에서 팀별로 필터 기능 -> 완료했습니다. 회원기능 / 관심팀 / 알림기능 기자추가 선수명 한글화 -> 일부완료. 예정입니다. 우선, 주요리그에 대한 팀에 대해서만 업데이트 예정입니다. 사용해보시고 불편한점이나 개선됐으면하는점, 필요한 기능이 있으면 편하게 댓글로남겨주세요. 감사합니다. 데이터 업데이트는 빠른시일내에 진행하겠습니다. 없는 데이터 추가 맞지않는 데이터 변경
Langfuse 9 — 저장된 실행을 평가하고 실패를 데이터셋으로 되돌리기 이 글은 Langfuse 실습 시리즈 의 9편입니다. 배포 전 평가를 이어, 저장된 실행의 사후 평가·알림·실패 데이터셋을 연결합니다. 이 글에서 다룰 주제 이미 저장한 에이전트 출력을 다시 평가해 점수를 붙이는 방법 업무 실패와 도구 오류를 구분해 알림을 읽는 방법 Langfuse Alerts에서 로컬 HTTPS webhook까지 실제 전달 확인 실패 사례를 다음 Offline 실험의 Dataset으로 옮기는 방법 주요 단어 · Post-hoc Evaluation · Score · Alert · Webhook · NO_DATA · Feedback Loop 배포 전에 준비한 데이터셋만으로 모든 입력을 알 수는 없습니다. 실행이 쌓이면 “어떤 도구에서 오류가 있었는가”와 “에이전트의 최종 판단이 기대에 맞았는가”를 계속 확인해야 합니다. 두 질문은 서로 다른 실패를 보여 줍니다. 5편의 합성 사건 실습에는 그 차이가 드러났습니다. tool-timeout-retry 는 첫 도구 호출에서 ERROR가 있었지만 재시도 후 올바르게 진단했습니다. 반면 missing-logs 와 unknown-injection 은 모델 호출이 완료됐어도 이관해야 하는 상황을 진단 완료로 분류했습니다. 이번 글에서는 저장된 여섯 실행을 외부 Python 프로세스로 다시 평가하고, 실제 도구 ERROR에 대한 Langfuse Alert를 로컬 webhook으로 받습니다. 마지막으로 잘못된 최종 판단 두 개를 다음 실험용 Dataset에 보관합니다. 그림은 운영 개선의 전체 흐름입니다. 이번에 직접 실행한 부분은 외부 평가기의 한 번 실행, native Alert와 webhook 전달, 실패 두 건의 Dataset 저장 입니다. 새로운 요청을 계속 polling하는 상시 평가기나 native Evaluation Rule 스케줄러까지 가동한 실습으로 표현하지 않습니다. 1. 평가 경로부터 구분하기 Post-hoc 평가 는 요청이 끝나고 저장된 결과를 읽어 나중에 판정하는 방식입니다. 사용자 응답 경로에 Judge의 대기 시간을 직접 더하지 않을 수 있지만, 그만큼 점수가 늦게 나타나며 실행 시점에 위험한 동작을 막는 제어와는 구분해야 합니다. 이번 operations.py 는 이전 실행의 trace ID를 체크포인트에서 읽습니다. Langfuse Observations API로 영속 저장된 agent output을 가져오고, 그 안의 decision 을 fixture 기대값과 비교합니다. 즉 모델을 다시 호출해 다른 답변을 만든 것이 아니라 이미 기록된 답변을 재평가 했습니다. rows = observations(client, run["trace_id"]) root = next(row for row in rows if row["id"] == run["root_id"]) decision = parsed_io(root["output"])["decision"] grade = grade_output(case, decision) langfuse.create_score( name="aiops-online-contract", value=int(grade["success"]), data_type="BOOLEAN", trace_id=run["trace_id"], observation_id=run["root_id"], score_id=deterministic_score_id, ) aiops-online-contract 라는 이름은 이 실습에서 붙인 이름입니다. 이름에 online이 들어 있다고 Langfuse가 자동으로 새로운 관측을 감시하는 것은 아닙니다. 실행 경로는 external Python one-shot post-hoc evaluator; not native rule scheduler 로 결과 파일에 기록했습니다. Langfuse의 native 온라인 평가는 Evaluator에 판정 방법을 정의하고, Rule에 대상 필터와 샘플링을 연결합니다. 외부 파이프라인에서 평가한 뒤 Scores API/SDK로 보내는 방법도 지원됩니다. 이번에는 후자를 사용했습니다. 온라인 평가 경로 2. 여섯 실행의 재평가 결과 다음 값은 2026-10-05의 저장 결과를 실제로 읽어 판정한 값입니다. BOOLEAN 1 은 이 합성 fixture의 outcome·cause·증거 계약을 통과했다는 뜻입니다. 실제 환경 복구나 종합 안전성을 보장하는 점수는 아닙니다. 사건 외부 계약 점수 해석 db-saturation 1 DB 지표와 로그에 맞는 진단 upstream-slowdown 1 upstream 원인·증거 계약 통과 missing-logs 0 근거 부족 시 이관 조건을 지키지 못함 tool-timeout-retry 1 도구 재시도 후 최종 작업은 통과 outdated-runbook 1 오래된 runbook에 대해 이관 unknown-injection 0 알려진 장애 패턴이 없는데 진단으로 분류 점수는 원래 trace와 agent observation에 연결했습니다. 새로운 trace를 만들어 원래 요청과 분리하지 않았으므로, 점수를 발견한 뒤 실제 도구 호출과 모델 입력까지 되돌아갈 수 있습니다. 각 score ID는 평가기 버전 + trace ID 에서 결정적으로 만들었습니다. 이는 같은 대상을 중복 평가했을 때 다른 점수 레코드를 무한히 늘리지 않기 위한 최소 장치입니다. 실제 구현은 완료 체크포인트가 있으면 원격 데이터를 다시 만들지 않고 검증만 수행합니다. 중간 체크포인트가 불완전하면 자동으로 처음부터 재실행하지 않습니다. 3. 실패 두 건을 Dataset으로 저장 aiops-study/operational-failures Dataset에 계약 점수가 0인 두 사건만 저장했습니다. 여기서 데이터의 출처를 구분해야 합니다. 판정할 출력: Langfuse에 이미 저장된 agent observation의 decision 다음 실험에서 재현할 입력: 이전 실습 체크포인트의 input_bundle 기대값: AI 작성 보조 도구가 만든 합성 fixture의 expected 원래 실행 연결: source_trace_id , source_observation_id 잘못된 모델 응답을 기대 정답으로 복사하지 않았습니다. 다만 이 기대값도 전문가가 검수한 운영 정답은 아니므로, 실서비스에서는 담당자가 원인·원하는 행동·허용 가능한 대안을 검토하는 절차가 필요합니다. langfuse.create_dataset_item( dataset_name="aiops-study/operational-failures", id=deterministic_item_id, input=run["input_bundle"], expected_output=case["expected"], metadata={ "reference_author": "assistant-curated fixture", "failure_checks": failed_check_names, }, source_trace_id=run["trace_id"], source_observation_id=run["root_id"], ) Datasets → aiops-study → operational-failures → Items 에서 Source 열을 누르면 원본 실행으로 돌아간다. Expected Output은 모델의 틀린 답을 복사한 값이 아니라 별도로 정한 기대 판단이다. Dataset에서 항목을 열면 입력·기대값과 원래 실행을 함께 확인할 수 있습니다. missing-logs 의 기대값은 escalated / insufficient_evidence 이고, unknown-injection 도 추가 근거가 필요하므로 같은 이관 분류를 요구합니다. 실패 사례를 보관했다고 프롬프트가 고쳐진 것은 아닙니다. 다음 변경을 이 두 사건에 다시 실행하고, 기존 통과 사례도 깨지지 않는지 확인해야 개선의 한 바퀴가 끝납니다. 8편의 부분 회귀 집합이 이 실패 두 건을 대신 해결한 것으로 해석하지 않습니다. 4. 어떤 오류를 알릴 것인가 이번에 만든 native Alert는 AIOps lab — tool ERROR count 입니다. 비교 대상은 품질 점수가 아니라 observation에 기록된 도구 오류입니다. 항목 실제 설정 데이터 소스 Observations 집계 count 환경 필터 aiops-lab 타입 필터 TOOL 상태 필터 ERROR 조건 최근 1일의 개수 > 0 데이터 없음 Show severity NO_DATA 반복 알림 Off 필터의 단위에 주의합니다. 이 조건은 ERROR observation의 개수 를 셉니다. 에이전트 작업 개수도, 실패한 업무 비율도 아닙니다. 한 요청에서 tool retry가 여러 번 실패했다면 여러 관측이 집계될 수 있습니다. 이번 timeout은 fixture가 첫 metrics 조회에서 100ms 뒤 의도적으로 발생시킨 Python 예외입니다. 실제 Prometheus나 운영 데이터베이스 장애가 아닙니다. 이 관측에 ERROR가 남았고, 이어진 재시도는 성공했습니다. 최종 작업 성공과 중간 도구 오류가 동시에 존재할 수 있음을 확인하는 용도입니다. Langfuse Alerts는 Observations와 여러 자료형의 Scores를 지표로 선택할 수 있습니다. BOOLEAN 점수의 평균은 true 비율이므로 품질 통과율 알림에도 사용할 수 있습니다. 다만 이번 실습에서는 도구 ERROR 개수 알림 하나를 실제로 만들었습니다. 점수 알림도 만들었다고 확대해서 표현하지 않습니다. Alerts 공식 설명 5. Automation과 HTTPS 수신기 Alert 조건을 저장하는 일과 알림을 실제로 받는 일은 별개입니다. UI에서 Alert event를 처리하는 Automation을 만들고 Webhook action을 연결한 뒤, Alert의 notifications에서 해당 Automation을 선택했습니다. 이 실습 UI에서는 연결할 Automation을 먼저 준비해야 했습니다. 외부 메신저나 회사 시스템으로 보내지 않고 같은 Docker 네트워크의 https://aiops-webhook/alerts 로 전달합니다. 수신기는 host에 공개한 포트가 없으며, 서명 검증을 통과한 합성 alert event만 증거 파일에 기록합니다. 로컬 주소는 webhook 보안 검사에서 내부 주소로 취급되므로 특정 hostname만 허용했습니다. 인증서는 별도로 신뢰 목록에 추가했습니다. LANGFUSE_WEBHOOK_WHITELISTED_HOST: aiops-webhook NODE_EXTRA_CA_CERTS: /study/aiops-webhook-ca.crt 이 설정은 실습 hostname과 인증서에 한정합니다. TLS 검증을 전부 끄는 방식은 사용하지 않았습니다. 인증서·서명 secret은 글과 스크린샷에 노출하지 않고 별도 파일로 보관했습니다. 수신기는 요청의 원본 body bytes 와 header의 timestamp로 HMAC을 계산합니다. JSON을 예쁘게 정렬하거나 다시 직렬화한 문자열로 서명 검증을 하면 실제 전달된 bytes와 달라질 수 있습니다. const expected = crypto .createHmac("sha256", secret) .update(`${timestamp}.`) .update(rawBody) .digest("hex"); 실제 수신 코드에는 timestamp가 현재와 5분 이상 차이 나면 거절하는 검사, 상수 시간 비교, event ID 중복 제거도 넣었습니다. 같은 event가 재전송되면 두 번째 기록을 남기지 않고 성공 응답을 돌려줍니다. Langfuse webhook은 성공 응답을 받지 못하면 재시도할 수 있으므로, 수신기의 멱등성을 고려해야 합니다. Webhook 서명과 수신 확인 6. 실제 전달 확인 2026-10-05 14:08:27.971 KST에 만들어진 ALERT 이벤트를 수신기가 14:08:28.024 KST에 기록했습니다. 로컬 증거의 주요 필드는 다음과 같습니다. 표시한 두 timestamp의 차이는 이 이벤트의 기록 시점 차이이며, 일반적인 알림 전달 SLA로 사용하지 않습니다. { "received_at_utc": "2026-10-05T05:08:28.024Z", "signature_verified": true, "event": { "type": "monitor-alert", "payload": { "severity": "ALERT", "view": "observations", "window": "1d" } } } 이 값은 수동으로 흉내 낸 alert가 아니라 Langfuse에서 만든 Alert가 Automation을 거쳐 전달한 실제 event입니다. 이름에 monitor 가 남아 있는 것은 payload의 API 명칭입니다. UI의 Alerts 메뉴와 다른 제품을 뜻하지 않습니다. 운영에서는 “알림이 왔다”에서 멈추지 않고 해당 기간과 필터로 원래 observations를 엽니다. 이번에는 query-metrics 의 첫 시도와 다음 성공 시도를 함께 읽고, 최종 aiops-task-success 가 1인지 확인하면 오류의 영향 범위를 판단할 수 있습니다. 7. NO_DATA와 알림 반복 NO_DATA 는 정상과 다릅니다. 요청 자체가 없었을 수도 있고, 필터가 잘못됐거나 SDK 전송·수집 경로가 멈췄을 수도 있습니다. 따라서 없는 데이터를 자동으로 0으로 바꾸면 “오류가 없다”로 오해할 수 있습니다. 이번에는 Show severity NO_DATA 를 선택했습니다. 이는 데이터 없음 상태를 표시하되 그 이유만으로 알림을 보내지 않는 정책입니다. 요청이 반드시 들어와야 하는 서비스라면 지속적인 데이터 없음도 별도 경보 조건으로 다뤄야 합니다. Renotify=Off 는 오류가 지속되는 동안 같은 알림을 계속 보내지 않는 설정입니다. 첫 상태 전이 알림까지 막는 설정은 아닙니다. 복구 알림과 반복 알림, 데이터 없음 알림은 서로 다른 상태 변화이므로 실제 운영 정책에 맞춰 구분합니다. 전달 확인 뒤 실습 Alert를 PAUSED 로 전환했다. 학습 데이터의 오래된 오류로 알림이 다시 생기지 않도록 한 것이다. 설정과 이벤트는 남아 있으며, 다시 실험할 때 Alerts의 해당 항목에서 Resume alert를 사용한다. 8. 상시 운영으로 바꿀 때 이번 외부 평가기는 저장된 여섯 trace만 한 번 읽었습니다. 같은 구조를 운영으로 바꾸려면 데이터를 어떻게 계속 찾고, 어디까지 처리했는지 남기는 설계가 필요합니다. 준비할 것 필요한 이유 시간 범위·cursor·watermark 마지막 처리 위치를 알고 새 데이터만 이어 읽기 지연 도착 overlap 전송·수집 지연으로 나중에 나타난 관측을 누락하지 않기 평가기 버전과 멱등 ID 같은 버전의 중복 점수와 다른 버전의 재평가를 구분 평가 대상·샘플링 기록 어떤 환경·사건이 평가에서 제외됐는지 알기 평가 coverage 점수 평균이 좋아도 실제 평가가 거의 멈췄는지 확인 오류 재시도와 실패 보관 판정 실패를 좋은 점수로 취급하지 않고 다시 처리 예를 들어 통과율을 볼 때는 통과 점수 / 평가 완료 수 와 함께 평가 완료 수 / 평가 대상 수 도 봐야 합니다. 전체 요청의 어려운 부분만 평가가 실패했는데 완료된 쉬운 요청만으로 높은 점수가 보일 수 있기 때문입니다. Native Evaluation Rule을 선택하면 필터·샘플링·평가기 연결을 Langfuse에서 관리할 수 있습니다. 외부 파이프라인을 선택하면 기존 평가 코드와 자체 실행 환경을 활용할 수 있지만 cursor·중복·재시도 같은 수명주기를 직접 운영해야 합니다. 이번 예제는 두 경로의 기능이 자동으로 같아지는 것으로 설명하지 않습니다. 9. 재검증과 다음 단계 설치 환경과 실습 파일이 있는 작업 디렉터리에서 아래 명령은 새 모델 호출이나 원격 변경 없이 기록을 다시 읽습니다. docs/langfuse-study-2026-10-04/lab/.venv/bin/python \ docs/langfuse-aiops-2026-10-05/lab/operations.py --verify-only 여섯 score의 값과 연결 대상, 저장된 출력의 재계산, 실패 Dataset의 정확한 두 항목·입력·기대값·원본 연결을 확인했습니다. 다음 글의 마스킹 검사를 포함해 저장 확인 30개가 통과했습니다. 또한 불완전 checkpoint, 중복 case, 바뀐 점수, 누락된 실패 item을 거절하는 로컬 검사를 수행했습니다. 결과 파일은 evidence/operations.json , 알림 전달 기록은 evidence/webhook-events.jsonl 입니다. 실제 사용자 데이터 없이 합성 사건으로 흐름을 확인했습니다. 다음 글에서는 수집할 데이터의 범위를 줄이고, 마스킹·백업·격리 복원으로 관측 시스템 자체를 운영하는 문제를 다룹니다. 실습·공식 문서 확인: 2026-10-05. Langfuse OSS v4.50.0, Python SDK 4.16.0. 저장 후 외부 평가에서는 추가 LLM 호출을 하지 않았습니다. native 온라인 Evaluation Rules는 구성하지 않았으며 native Alert→Automation→HTTPS webhook 전달은 직접 확인했습니다. 이전: 8편 — 품질 회귀와 프롬프트 라벨 롤백 다음: 10편 — 마스킹과 백업·복원
개발 환경에서 Java 프로세스가 반복적으로 종료됐다. API와 배치, Redis가 한 서버를 공유했고, 일부 자동화 작업은 별도 Java 프로세스와 브라우저도 실행했다. 처음에는 “작은 서버니까 메모리를 늘리면 되지 않을까?”라고 생각하기 쉬웠다. 하지만 증설을 결정하려면 먼저 어느 메모리 경계에서 실패했는지 구분해야 했다. 이 글은 해결 효과를 과장하는 성공담보다, 로그로 확인한 원인과 아직 확인하지 못한 범위를 정리한 기록이다. 내부 서버 주소와 식별자는 생략했다. 1. 메모리가 부족하다는 말은 세 가지를 섞고 있었다 경계 확인할 증거 바로 단정할 수 없는 것 JVM 힙 Java heap space 등 애플리케이션 오류 호스트 RAM 전체가 부족했는지 컨테이너 cgroup OOM, 사용량과 limit 호스트에 여유 메모리가 없었는지 호스트 시스템 전체 OOM과 당시 메모리 상태 특정 Java 힙만 늘리면 해결되는지 이번 커널 기록에서 결정적인 부분은 다음 형태였다. 원본 전체 대신 판정에 필요한 부분만 정리했다. memory: usage <한도에 도달한 값>, limit <설정한 한도> oom-kill:constraint=CONSTRAINT_MEMCG Memory cgroup out of memory: Killed process ... (java) 이 기록이 보여 준 직접 원인은 컨테이너 메모리 한도 도달이었다. 같은 컨테이너에서 Java 프로세스가 반복 종료된 사실도 확인했다. 여기서 “서버 전체 RAM을 모두 사용했다”까지 결론을 늘리면 증거보다 설명이 앞선다. Docker의 메모리 제한은 컨테이너에 적용된다. 서버를 증설해도 기존 제한이 유지되면 같은 경계에서 다시 실패할 수 있다. Docker 메모리 제한 문서 2. 로그가 안 보였던 이유부터 다시 확인했다 처음에는 날짜를 지정해 커널 로그를 검색했는데 결과가 없었다. 그때 “과거 기록은 없나 보다”라고 결론 내리기 쉬웠다. 그러나 재부팅 전 기록까지 검색하는지 확인해야 했다. # 보관된 부팅 이력 sudo journalctl --list-boots --no-pager # 현재 부팅으로 제한하지 않고 보관된 커널 로그 검색 # 날짜는 실제 장애 구간으로 변경한다. sudo journalctl _TRANSPORT=kernel \ --since "2026-10-01 00:00:00" \ --until "2026-10-02 00:00:00" \ --no-pager | grep -Ei -B 15 -A 25 \ 'out of memory|oom-kill|killed process|memory cgroup' 시간대도 확인해야 한다. 로그 화면의 시각과 업무 중 기억하는 시각을 비교하기 전에 서버와 조회 도구의 시간대부터 맞춰야 한다. 3. 점검 시점 사용량과 장애 순간 사용량을 분리했다 과거 점검 때 실행한 free와 docker stats 결과는 작업 기록에 남아 있었다. 이 자료로 그 순간의 사용량과 가용량은 확인할 수 있었다. 반면 배치가 가장 무거웠던 순간의 최대 사용량은 알 수 없었다. 증거 말할 수 있는 범위 ──────────────────────────────────────────── 커널 cgroup OOM 로그 → 컨테이너 한도로 Java가 종료됨 한 번 실행한 docker stats → 그 점검 순간의 사용량 CPU 사용률 그래프 → CPU 부하가 증가한 구간 미수집한 메모리 시계열 → 과거 최대값은 확정할 수 없음 CPU가 높았다는 사실을 메모리 부족의 증거로 바꾸거나, 점검 당시 사용량을 장애 당시 최대값으로 쓰지 않기로 했다. 증설 요청에서도 이 구분이 필요했다. 4. 선택지는 있었지만 각각 해결하는 범위가 달랐다 컨테이너 한도 조정 은 이번에 확인된 직접적인 경계를 다룬다. 다만 같은 호스트의 다른 프로세스까지 함께 고려해야 한다. JVM 힙 조정 은 별도로 발견한 Java Heap 오류를 다루는 선택지다. 컨테이너 안에는 힙 이외에도 메모리가 필요하고, 브라우저가 함께 실행된다면 JVM 밖의 사용량도 봐야 한다. 힙 한도와 컨테이너 한도를 같은 값으로 취급할 수 없었다. 서버 증설 은 전체 자원 여유를 늘리는 선택지다. 하지만 컨테이너 제한이나 대량 데이터 보관 방식이 그대로라면 모든 문제가 자동으로 해결되지는 않는다. 배치 분리 는 API와 무거운 작업의 자원 경쟁을 줄일 수 있지만, 서버 운영과 배포 구성이 늘어난다. 당장 분리가 어려운 조건에서는 우선 실행량을 제한하고 필요한 메모리를 측정하는 접근이 현실적이었다. 이 조건에서 선택한 방향은 한 가지 옵션을 만능 해결책으로 삼는 것이 아니라, 원인별 설정 조정과 배치의 메모리 보관량 감소를 함께 검토하는 것이었다. 별도 매크로 검증 작업에서는 전체 로딩을 100행 단위 순차 조회로 바꾸는 개선도 진행했다. 이 변경을 커널 OOM 사건과 동일 원인이라고 단정하지는 않았다. 5. 무엇을 해결했고 무엇이 남았나 확인한 성과는 반복 종료가 컨테이너 한도에 의해 발생했다는 사실을 로그로 좁힌 것이다. 증설 필요성을 설명할 때도 컨테이너 OOM, Java Heap 오류, 점검 시점 메모리 수치를 분리할 수 있게 됐다. 아직 필요한 검증은 개선된 배치를 실행하면서 호스트 가용 메모리, 컨테이너 사용량, Java와 브라우저의 사용량, API 응답 상태를 함께 기록하는 것이다. 이번 글에서는 증설 후 장애가 사라졌다거나 메모리를 몇 퍼센트 줄였다는 결과를 주장하지 않는다. 다음 장애에서는 “메모리가 부족했다”보다 “어느 경계에서, 어떤 작업 중에, 어떤 증거로 확인했는가”부터 설명하고 싶다. 참고 Docker: Resource constraints Linux Kernel: cgroup v2 2만 장의 PDF 레포트 생성 시간을 5시간→1시간대로 줄였다 : 다른 사례의 작업 규모·메모리 검증 전개를 참고했다. 해당 글의 수치와 해결 결과는 이 사례의 결과가 아니다. 6. 내가 맡은 일과 조사 순서 내가 맡은 범위는 반복 종료 증상을 서버 자원 부족이라는 표현으로 묶지 않고, 종료를 결정한 계층을 찾아 설정 변경의 근거를 만드는 일이었다. API와 배치가 한 호스트를 공유했기 때문에 한 프로세스의 한도를 늘리는 일도 다른 서비스의 여유와 연결됐다. 개발 서버였지만 검증이 끊기면 배치 결과를 확인하거나 변경 사항을 검증하는 작업도 함께 멈췄다. 조사 순서를 다음처럼 정리했다. 애플리케이션 예외로 종료됐는지, 외부에서 프로세스가 종료됐는지 확인한다. 커널 기록에서 종료된 프로세스와 컨테이너 cgroup을 연결한다. 해당 시점의 usage와 limit을 비교한다. 현재 실행 상태와 과거 장애 기록을 분리한다. 다른 배치의 Heap 오류가 같은 사건인지 별개의 사건인지 구분한다. 여기서 컨테이너 ID는 중요한 연결 키다. Java라는 이름만 보고 API와 배치 중 어느 쪽인지 결론 내리면 안 된다. 컨테이너가 남아 있다면 inspect로 이름과 제한을 확인할 수 있지만, 재생성됐다면 현재 설정이 과거 설정을 증명하지 않는다. 이전 배포 기록과 함께 읽어야 한다. 7. 왜 바로 Xmx부터 올리지 않았나 힙이 부족하면 Xmx를 늘리는 것이 자연스러운 선택이다. 그러나 이번 커널 기록의 기준은 JVM의 힙 사용량이 아니라 cgroup에 계상된 메모리였다. 힙 외에도 스레드 스택, 클래스 메타데이터, 네이티브 메모리 등이 필요하다. 별도 브라우저 프로세스의 사용량은 Java 힙으로 설명할 수도 없다. 구조를 단순화하면 다음과 같다. 실제 제한 적용은 프로세스가 어느 cgroup에 속하는지 확인해야 한다. 호스트 메모리 ├─ OS와 관리 프로세스 ├─ API 컨테이너 │ └─ JVM: 힙 + 메타데이터 + 스레드·네이티브 영역 ├─ 배치 컨테이너 │ ├─ JVM │ └─ 같은 컨테이너에서 실행한 브라우저·자식 프로세스 └─ Redis 컨테이너 그래서 “힙을 늘린다”와 “컨테이너 한도를 늘린다”를 따로 검토했다. Xmx만 올리면 힙에는 여유가 생겨도 컨테이너 전체의 여유는 줄 수 있다. 반대로 서버를 늘려도 컨테이너 한도가 그대로면 그 한도에서 다시 종료될 수 있다. 설정값 하나를 선택하기 전에 어떤 경계에 예산을 배분하는지 정해야 했다. 8. 전체 조회를 나눌 때도 지켜야 할 조건이 있었다 별도의 매크로 검증 개선에서는 전체 데이터를 한 번에 가져와 비교하던 흐름을 작은 단위로 읽는 방향으로 바꿨다. 이 선택의 목적은 처리한 행을 계속 보관하지 않는 것이었다. 전체 로딩: 전체 행 보관 → 비교 → 결과 도출 순차 처리: 일부 행 조회 → 필요한 정보 갱신 → 다음 일부 행 조회 다만 조회만 페이지로 나누고 결과를 하나의 리스트에 계속 추가하면 보관량은 다시 전체 데이터 크기까지 늘어난다. 작은 단위로 처리한다는 설명에는 반드시 무엇을 남기고 무엇을 버리는지가 포함돼야 한다. 이 방식에는 요청 횟수가 늘어날 수 있다는 비용이 있다. 조회 중 원본이 바뀌면 페이지 경계의 중복·누락도 검토해야 한다. 따라서 작은 페이지가 언제나 정답은 아니다. 이 작업에서는 처리 속도만 앞세우기보다 제한된 자원에서 검증을 끝낼 수 있는 형태를 우선했다. 실제 사용량 감소와 처리 시간 변화는 별도 측정이 필요하다. 9. 결과를 어디까지 말할 수 있는가 단계 이번 기록에서 말할 수 있는 것 원인 확인 반복 종료 중 확인한 사건은 컨테이너 한도 도달에 따른 Java 강제 종료였다 코드 개선 별도 매크로 검증의 데이터 조회를 작은 단위의 순차 처리로 변경했다 용량 판단 과거 한 시점의 측정만으로 동시 실행 최대 메모리를 확정할 수 없었다 남은 검증 개선된 작업의 최대 사용량·완료 여부·API 영향·재발 여부를 함께 측정해야 한다 후속 검증에서는 같은 데이터 범위와 동시 실행 조건을 기록한 뒤 변경 전후를 비교하려 한다. 배치가 완료됐다는 사실만으로 끝내지 않고, 그동안 API 지연이 커지지 않았는지와 브라우저가 종료 후 정리됐는지도 확인해야 한다. 이번 경험에서 바뀐 것은 증설 요청의 설명 방식이었다. 막연히 “서버가 작다”는 주장 대신, 확인한 종료 경계와 측정하지 못한 구간을 구분해서 제시할 수 있게 됐다. 인프라 변경과 코드 개선은 서로 대체하는 답이 아니라, 현재 제약 안에서 함께 검증해야 할 선택이었다.