LabHub
배우기 러닝패스 코스

OTCA — OpenTelemetry Certified Associate

Another order’s tag came along

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

실제 Python 함수 일곱 개를 고쳐 실행 시점의 문맥을 전달하고, 정상·예외·취소 뒤 원래 문맥을 복구합니다. 이어서 외부 baggage의 추출과 목적지별 전파 정책을 분리합니다.

왜 중요한가

요청 값이 틀린 작업에 붙으면 데이터가 수집돼도 원인을 잘못 해석하게 됩니다. 이 실습은 Python 3.12와 OpenTelemetry SDK 1.44.0이 설치된 전용 환경에서 실제 asyncio·ThreadPoolExecutor·W3C baggage 전파기를 실행합니다. 의존성은 이미지에 미리 설치되므로 인터넷 다운로드나 API 키가 필요하지 않습니다.

외부 HTTP·DNS·Collector·인증 서버는 실행하지 않습니다. warehouse.internal은 함수에 전달하는 논리적 목적지 문자열입니다. 과제 통과가 실제 네트워크 목적지 검증, 인증·인가, 외부 저장소 적재나 포괄적인 개인정보 보호의 검증을 뜻하지 않습니다. 8단계 중 baggage의 스팬 속성 자동 기록 여부는 앞 이론의 SDK 실험과 공식 설명을 근거로 판정합니다. 나머지 코드 검사는 Context 함수의 실행 관측입니다.

준비와 실행

작업 디렉터리는 /root/otca-context입니다. starters 안에는 문법은 맞지만 동작이 틀린 시작 파일 8개만 준비됩니다. 해당 파일을 작업 디렉터리에 복사해서 고치세요.

cd /root/otca-context
cp starters/scope.py scope.py
/opt/otel-lab/bin/python /opt/app/otca_sdk/runner.py --track context run 1

run 뒤의 숫자를 바꾸면 해당 단계의 observations와 checks가 나옵니다. 현재 관측과 기대값을 비교하세요. SDK나 채점기를 수정하는 과제가 아니며, 동일한 의미의 다른 코드도 실제 동작이 맞으면 통과합니다. 답안 사본을 제한된 자식 프로세스에서 실행하고 원본 파일은 바꾸지 않습니다. 개별 코드는 최대 8초, 종합 실행은 전체 50초 예산이며 무한 루프·출력 폭주는 실패입니다. 이 실행 제한을 악성 코드에 대한 별도의 보안 격리라고 해석하지 않습니다. 격리는 실습 파드가 담당합니다.

단계

  1. /root/otca-context/scope.py — scope.py의 with_context(value, callback)를 고칩니다. 콜백을 한 번 실행하는 동안 baggage request가 value여야 합니다. 반환값과 원래 예외를 그대로 전달하고, 정상·예외 모두 호출자의 이전 문맥을 복구하세요.
  2. /root/otca-context/tasks.py — tasks.py의 start_task(coro)는 현재 요청 문맥으로 코루틴을 예약하고 호출자가 await할 객체를 반환합니다. 호출자가 나중에 문맥을 바꿔도 작업은 예약 시점의 값을 읽어야 하며, 호출자의 새 값은 보존하세요.
  3. /root/otca-context/threads.py — threads.py의 start_thread(function)은 동기 함수를 스레드에서 실행하도록 예약하고 await할 객체를 반환합니다. 나중 await 시점이 아니라 이 함수를 호출한 시점의 요청 값을 워커가 읽도록 하세요.
  4. /root/otca-context/executor.py — executor.py의 submit(executor, function)은 주어진 ThreadPoolExecutor에 함수를 제출하고 Future를 반환합니다. 제출 시점의 요청 값이 전달되고, 같은 워커의 다음 일반 작업에는 남지 않아야 합니다. 호출자 문맥도 보존하세요.
  5. /root/otca-context/requests.py — requests.py의 async handle(value, callback)은 baggage request를 value로 적용해 비동기 콜백을 한 번 await합니다. 두 요청이 겹쳐도 값을 분리하고, 정상 반환·예외·Task.cancel 모두 이전 문맥을 복구하세요. 반환값·예외·취소를 호출자에게 그대로 전달합니다.
  6. /root/otca-context/incoming.py — incoming.py의 inbound(carrier)는 W3C baggage 헤더 dict를 빈 OTel Context 기준으로 추출해 OTel Context를 반환합니다. 기존 로컬 baggage를 섞지 않고 원본 dict와 호출자 문맥을 보존합니다. role=admin도 이 단계에서는 추출하되 인증 성공으로 해석하지 않습니다.
  7. /root/otca-context/outgoing.py — outgoing.py의 outbound(source, destination)은 destination이 warehouse.internal과 정확히 같을 때만 region의 test-east/test-west, channel의 web/batch 문자열을 허용합니다. 새 Context에 선택해 W3C baggage 헤더 dict를 반환하세요. 다른 키·값·목적지는 제외하고 원본을 보존합니다. 남은 값이 없으면 빈 dict입니다.
  8. /root/otca-context/report.json — report.json의 여덟 가설을 JSON 불리언으로 판정합니다. 앞 일곱 코드도 모두 동작해야 합니다. Task 생성 시점, 지연 to_thread, 일반 executor, 취소 복구, baggage와 스팬 속성, 빈 inbound, 인가, 목적지의 영향을 구별하세요.

참고

콜백 뒤 원래 요청으로 돌아오기

/root/otca-context/scope.py: scope.py의 with_context(value, callback)를 고칩니다. 콜백을 한 번 실행하는 동안 baggage request가 value여야 합니다. 반환값과 원래 예외를 그대로 전달하고, 정상·예외 모두 호출자의 이전 문맥을 복구하세요.

set_baggage의 반환값과 현재 문맥을 붙이는 동작은 다릅니다. 콜백 뒤 복구가 예외 경로에서도 실행되는지 보세요.

Task 예약 시점의 요청 보존하기

/root/otca-context/tasks.py: tasks.py의 start_task(coro)는 현재 요청 문맥으로 코루틴을 예약하고 호출자가 await할 객체를 반환합니다. 호출자가 나중에 문맥을 바꿔도 작업은 예약 시점의 값을 읽어야 하며, 호출자의 새 값은 보존하세요.

코루틴 객체를 돌려주는 것과 현재 Context를 가진 Task를 만드는 것의 차이를 확인하세요.

지연된 스레드 작업의 시점 고치기

/root/otca-context/threads.py: threads.py의 start_thread(function)은 동기 함수를 스레드에서 실행하도록 예약하고 await할 객체를 반환합니다. 나중 await 시점이 아니라 이 함수를 호출한 시점의 요청 값을 워커가 읽도록 하세요.

to_thread가 반환한 코루틴은 언제 실행되나요? 현재 문맥에서 실행을 예약하는 단계를 생각해 보세요.

재사용 워커에 문맥을 남기지 않기

/root/otca-context/executor.py: executor.py의 submit(executor, function)은 주어진 ThreadPoolExecutor에 함수를 제출하고 Future를 반환합니다. 제출 시점의 요청 값이 전달되고, 같은 워커의 다음 일반 작업에는 남지 않아야 합니다. 호출자 문맥도 보존하세요.

복사를 워커 안에서 하면 이미 늦습니다. 제출마다 별도 복사본을 만들고 그 안에서 실행하는 방법을 찾아보세요.

겹친 요청과 실제 취소를 정리하기

/root/otca-context/requests.py: requests.py의 async handle(value, callback)은 baggage request를 value로 적용해 비동기 콜백을 한 번 await합니다. 두 요청이 겹쳐도 값을 분리하고, 정상 반환·예외·Task.cancel 모두 이전 문맥을 복구하세요. 반환값·예외·취소를 호출자에게 그대로 전달합니다.

정상 반환 뒤에만 detach가 있으면 취소가 그 줄을 건너뜁니다. 복구와 취소 은폐를 구별하세요.

외부 헤더와 로컬 요청 분리하기

/root/otca-context/incoming.py: incoming.py의 inbound(carrier)는 W3C baggage 헤더 dict를 빈 OTel Context 기준으로 추출해 OTel Context를 반환합니다. 기존 로컬 baggage를 섞지 않고 원본 dict와 호출자 문맥을 보존합니다. role=admin도 이 단계에서는 추출하되 인증 성공으로 해석하지 않습니다.

기본 extract와 명시적으로 빈 Context를 지정한 extract를 비교하세요. contextvars.Context와 OTel Context를 구별합니다.

목적지·키·값을 함께 제한하기

/root/otca-context/outgoing.py: outgoing.py의 outbound(source, destination)은 destination이 warehouse.internal과 정확히 같을 때만 region의 test-east/test-west, channel의 web/batch 문자열을 허용합니다. 새 Context에 선택해 W3C baggage 헤더 dict를 반환하세요. 다른 키·값·목적지는 제외하고 원본을 보존합니다. 남은 값이 없으면 빈 dict입니다.

키만 허용하면 값에 다른 정보가 들어갈 수 있습니다. 목적지를 접두어로 비교하면 유사 호스트를 허용하지 않는지도 확인하세요.

동작과 가설을 함께 검증하기

/root/otca-context/report.json: report.json의 여덟 가설을 JSON 불리언으로 판정합니다. 앞 일곱 코드도 모두 동작해야 합니다. Task 생성 시점, 지연 to_thread, 일반 executor, 취소 복구, baggage와 스팬 속성, 빈 inbound, 인가, 목적지의 영향을 구별하세요.

보고서만 맞으면 충분하지 않습니다. 실제 관측과 공식 계약을 구분해 읽고 각 함수의 실패 원인도 함께 고치세요.