# 2026-W33 Worklog
AI Summary
Purpose:
- Record cross-repository development and review work for ISO week 33 of 2026.
Key points:
- 2026-08-10: Added
human/reports/2026-08-10-remember-interview-2day-cram.md,
a D-2 compressed study sheet for the Remember & Company Data Engineer first interview on 2026-08-12.
- 2026-08-10: Added
human/career/remember-interview/assignment-schema.html
(published at llm-wiki.hwlabs.dev/career/remember-interview/assignment-schema.html): all six take-home tables with full column lists, real example rows verified against both source CSVs (five named workplaces covering unchanged / address-move UPDATE / NULL-overwrite UPDATE / INSERT / ABSENT), a write-order sequence diagram (transaction boxes, parallel staging+quarantine writes) and a per-row branching flowchart. Diagrams are mermaid sources under diagrams/schema-*.mmd rendered to SVG with mermaid CLI (headless chrome, WSL). Linked from the interview hub index card grid and the nav of assignment.html.
- 2026-08-10 (second pass): Extended the schema page with a code-structure
section grounded in the working copy (2,427 lines total; every module except the DAG under 250 lines): five entry points, the per-run call chain (start_run → land_raw → stage → apply_master → finish_run), a module/function map for all 19 include/pipeline/ modules, and 18 collapsible interview Q&As on code structure (facade split, is_valid vs duplicate keys, rules-in-code vs rules-table, IS DISTINCT FROM vs hash, transaction boundaries, SQL-set merge vs Python loop, crash/rerun behavior, test-path fidelity, DuckDB concurrency, empty-string vs NULL semantics, PK placement vs rollback scope, failed-run retry behavior, master-vs-source ABSENT stamping, why-DuckDB and the 8 reconciliation checks in plain terms, the 17 date-regression rows with named examples).
- Reason: the user judged the full prep guide (755 lines) unlearnable in two
days while working, and asked for a short-horizon sheet that explains the take-home assignment and its underlying technologies simply enough for a child to follow.
- Structure: a four-hour study timetable, six signals a team lead checks in a
five-year engineer, the assignment in three depths (10s / child version / interview version), seven design decisions each paired with a rebuttal line, a memorized-arithmetic number block, three limits to concede first, and keyword-only anchors for the eight portfolio items (the user already lived that work and does not need to re-read it).
- Every technical term is written as `plain-language line / precise definition
/ my concrete link`, so the answer can start at the analogy and descend only when the interviewer pushes.
- Four Mermaid diagrams, all render-verified with
@mermaid-js/mermaid-cli:
assignment data flow (RAW/STG/MASTER + OPS), Docker runtime topology, Remember's publicly documented AWS architecture, and a Full Load ↔ CDC handoff sequence.
- Facts are sourced only from existing repo documents; no new claims were
introduced. The AWS tag error (submitted material implies RDS/S3/EFS; actual production scope is EC2 only) and the three document-versus-implementation gaps are carried forward as things to correct proactively in the interview.
scripts/check_human_voice.py: BAN 0, WARN 0. Four STRUCT arrow findings
remain and were accepted — they are table cells carrying before/after measurements and two deliberate memorization sequences, not narrative prose.
- 2026-08-10 (third pass): Added section 2-10 to the cram sheet — a two-part
code-review script: (A) self-review of the take-home in severity order (stage() memory ceiling, the 17 unadjudicated date regressions, hardcoded rules) closing with the test-path defect anecdote, and (B) a live-review procedure (context questions first, declared reading order, 6-item pipeline checklist, severity-labeled question phrasing, summary close). Checker after the edit: BAN 0 / WARN 0 / STRUCT 5 — the fifth arrow finding is the deliberate checklist reading order in 2-10, same accepted category.
- 2026-08-11: Filled the 2026 second-half 업무고과 평가서 (goal-setting half only)
for the company HR cycle, due 2026-08-14. Source of the work items is the AI분석엔진팀 업무분장 sheet (2026-08-04) linked from Confluence DT page 4192436234; the form rules come from Confluence 2026-space page 4191388148. Five 주요업무 rows at weights 20/25/25/15/15 (=100), difficulty A/A/S/A/B. AI 기반 CI/CD 자동화·품질 평가 sits in the 팀 KPI block, not in a personal row — user-confirmed 2026-08-11. Self-evaluation, score, and evaluator columns were left empty because the guide assigns them to the evaluation period, not to goal setting.
- 2026-08-11 (second pass): The user asked why nothing from 2026-01~06 was in
the form. The scope was wrong — see the Details section. Two variants now exist (연간 1~12월, 하반기 7~12월), and the 2026 H1 work inventory was pulled from Jira rather than from this wiki, whose worklog only starts at W22 (late May 2026).
- 2026-08-12: labgit — the IDC ACL blocker is gone (labgit1 now answers on
50080/50443/53000 from the office network) and TLS/API were verified end to end. Created Confluence incident-response page 4202364930 under the build-result page. While collecting facts for it, found labgit2's Forgejo in an OOM crash loop (400 restarts, ~40h). See the section below.
- 2026-08-13: BT26-707 (신한투자증권 정보보호팀 취약점 점검) — shipped BTS v4.1.1,
which replaces the base image's el9 curl/libcurl 7.76.1 (18 CVEs) with an upstream 8.21.0 build. Established by measurement first that mysqld does not link libcurl at all — the only consumers are microdnf/librepo/libdnf and mysql-shell — so removal was viable, but the upgrade path was chosen to keep in-container mysqlsh access.
- 2026-08-13: the same release exposed three traps worth remembering: deleting
the microdnf install curl line does not remove curl (it comes from the base image); a native library rebuilt for this image must be compiled on the same base as the final stage (OpenSSL symbol versions); and db-infra/up.sh runs the container as whoever invokes it, which took lab-master-db down for ~5 minutes. All three are recorded in ai/repo-notes/binlog-transfer-system.md under Known Pitfalls.
- 2026-08-13: rolled out on
labM(DB running 4.1.1, updater compose bumped for
its next batch) and published to nas1:/volume1/Public/cdbvdb/cdbvdbInstaller/4.1.1/ with all three release files and checksums verified on both ends. See the section below.
Relevant when:
- Patching curl/libcurl or any base-image package in the BTS unified image.
- Preparing or revising Remember interview material.
- Looking for the short-form counterpart to the full prep guide.
- Writing or revising the company 업무고과 평가서 (half-year goal setting).
- Following up on the labgit OOM crash loop or the incident-response page.
- Needing the 2026-01~06 work inventory, which this wiki does not otherwise
hold (worklog coverage starts 2026-W22).
Do not read full document unless:
- Week-level detail beyond the summary is needed.
Linked documents:
ai/repo-notes/binlog-transfer-system.mdbinlog-transfer-systemRELEASE/v4.1.1.md(release note, in the target repo)human/reports/2026-08-10-remember-interview-2day-cram.mdhuman/reports/2026-08-05-remember-data-engineer-interview-prep.mdhuman/reports/2026-08-07-remember-interview-study-map.mdai/repo-notes/remember-data-engineer-assignment-claude.mdai/wiki/projects/forgejo-git-mirror.mddocs/superpowers/specs/2026-08-10-labgit-ssl-ha-dr-design.md
Open Questions
- Whether the second-round interview format differs enough to need its own
sheet. Needs confirmation.
- labgit
check_repo_statsfix is written into the localdata-infra
checkout but NOT applied to either server; labgit2 stays in the crash loop and labgit1 runs the same cron nightly until someone applies it.
- Whether to give the Forgejo container a memory limit so a runaway task is
contained instead of taking the host to a global OOM. Not proposed as a decision yet — it would trade a host-wide outage for container kills during legitimately heavy migrations.
- BT26-707: the Jira reply has not been sent. It needs to say that
CVE-2026-43503 (Linux Kernel) is the host OS's item because a container image ships no kernel, and that the fix is in v4.1.1 only.
- BT26-707: 신한투자증권 is still on BTS v3 (image 2.0.2) and the fix ships in v4,
so applying it depends on the v3→v4 upgrade landing before the 8월 말 due date. Whether a 2.0.2 patch is needed as a fallback is undecided. Open question.
- BT26-707: whether the customer's scanner is satisfied by a clean rpm DB and a
clean merged filesystem. The 7.76.1 bytes still sit in the mysql:8.4.0 base layers under a whiteout, which only a layer-by-layer scanner would see. Removing that would mean changing the base image. Open question.
labM/tmpstill holds the rollout working files (the 207MB image tar.gz,
lmi-src.tar.gz, lab-master-installer.tar.gz, lmi-work/). Not cleaned up.
- Two references outside the release path still point at the old image:
db-infra/instances/lab-master/.env.template (DOCKER_IMAGE=…:4.0.0) and labM:/product/Infra/lab-master-installer/updater/db.connect.sh (…:4.0.0, the deployed copy, not the repo). Left as-is pending a decision.
Details
2026-08-10 — Remember interview two-day cram sheet
The full prep guide holds 35 numbered questions with written answer scripts. That format assumes rehearsal time the user does not have this week, and memorized scripts break when a question arrives slightly rotated. The cram sheet therefore drops the scripts and keeps structure, numbers, and boundary statements — the parts that survive rephrasing.
The organizing idea came from the user: an expert is someone who can make anyone understand. So each technology entry leads with an everyday analogy (three columns of a notebook for RAW/STG/MASTER, an elevator button for idempotency, a bank transfer for transactions, a restaurant turning guests away for backpressure), then gives the precise definition, then attaches the user's own verified case. The instruction is to speak layer one first and go deeper only on follow-up.
The portfolio section is intentionally the thinnest part of the document. The user performed all eight items and does not need to study them; each row carries one angle to lead with and one number, plus a short block on the three figures whose units are easy to overstate (the 1.3M→6,000 plan-row estimate, the 2일→5분 design estimate, and the row-count-only reconciliation evidence from the IDC migration).
2026-08-11 — 2026 second-half 업무고과 평가서 (goal setting)
The HR notice (박승연, 2026-08-03) splits the cycle: the work items and targets must be agreed with the team lead by 2026-08-14, and the scoring happens at year end. So only the goal-setting half of the form was filled — 인적사항, 주요업무 5 rows (업무내용 / 난이도 / 가중치) and 역량향상. 자기평가, 자기점수, 1차·2차·대표이사 점수, 피평가자 의견, 1차평가자 의견 and 평가 종합의견 stay empty by design.
Work items were taken from the 업무분장 sheet rather than invented. Rows marked 27년도 (취약점 통합 관리 시스템, 컴포넌트 수집·저장 체계, 출처별 취약점 통합 매핑, DB 스키마 개선, V1scan 기술이전) were excluded from the second half, and mysql 대체 구성 (AIA) was excluded because the sheet records it as dropped from the schedule on 2026-08-04 in favour of the ServerCarePlus OS-package work.
| 항목 | 난이도 | 가중치 | 근거 |
|---|---|---|---|
| 팀 KPI | A | 20 | 인사위원회 확정, 팀 공통 할당 |
| OS 패키지 취약점 수집 확대 | A | 25 | 서버케어플러스 p1a, 9월말 기한 |
| 데이터 수집/배포 플랫폼 구축 | S | 25 | 미래아카데미 과제 (크롤러·수집/배포DB·BTS) |
| 오브젝트 스토리지·Git 미러 구축 | A | 15 | labgit 샤딩 미러 97.8% + 스토리지 대기 |
| 데이터 수집 플랫폼 운영 및 검증 | B | 15 | 데이터파트 공통 유지보수 |
Two corrections came from the user while filling the form, both worth keeping:
- The 업무분장 sheet also assigns
데이터수집/배포 플랫폼 전체 구축 (크롤러, 수집/배포DB, BTS) (미래아카데미) to the user. It was not in the CSV export read earlier — re-check the live sheet rather than a cached export next cycle.
AI 기반 CI/CD 자동화 / 품질 개선 평가 구축is a 팀 KPI, not a personal
주요업무 row. It moved into the 팀 KPI block (C13) instead of holding one of the four personal slots, which is what freed the slot for the mirror item.
The template holds only five 주요업무 blocks (팀 KPI + four personal rows), so personal items compete for four slots; check the KPI-versus-personal split before merging items to make them fit.
Layout note for future edits: the C:F merged 업무내용 cells hold roughly 32 half-width units at 8pt, so bullet lines must stay short — the template's own sample line (- IBK기업은행 IVAS 2.0 마이그레이션 지원) is already clipped in print. The sample rows also use a gray-italic placeholder font (fontId 10/11/ 14/15/16); real content should be re-pointed at the black equivalents (7/12/0/4) or it prints as if it were still example text.
Output: ~/Downloads/[래브라도랩스] 인사평가서_김현욱.xlsx — the company template filename with the name appended, which is how the team lead collects the forms. The blank template stays untouched at [래브라도랩스] 인사평가서.xlsx.
2026-08-11 (2nd) — evaluation scope was wrong: annual, not second-half
The user asked why none of their 2026-01~06 work appeared. It should have. Evidence for annual scope:
- The HR notice says 평가 시기 = 2026년 연말, and asks for
"올 한 해 진행할 본인의 업무 내용" / "2026년도 본인 업무 내용(목표)".
- The Confluence
2026 인사평가space (key2026) holds exactly **two pages,
both created 2026-08-03**. There is no first-half evaluation artifact, so 1~6월 work is otherwise recorded nowhere.
- The 업무분장 sheet that the first draft was built from is titled
"2026 하반기" with 7~12월 columns — that was the source of the error.
Structural point: the guide puts 실적 in the 자기평가 column (written at the evaluation point) and 항목/난이도/가중치 in 주요업무 (fixed now). If the items only cover 하반기, there is no row left to write the 상반기 실적 into at year end. Coverage has to be decided at the item stage.
2026 H1 inventory (from Jira, assignee = currentUser(), created 2026-01-01~07-01, ~300 issues across DAT / PKB / BUG / INFR). This wiki could not supply it: ai/worklog/2026/ starts at W22 (2026-05-25).
| 기간 | 주요 작업 | Jira |
|---|---|---|
| 1~2월 | BTS v4 개선 (설계 문서화 → download server v4 → updater v4 → 테스트 → 가이드) | DAT-2739~2745, PKB-307, PKB-473~475 |
| 1~2월 | Database 통합 운영·계정 체계 방안/적용, DIST instance name, 월 백업 | DAT-2710~2715, 2712, 2735~2738 |
| 1~2월 | License 크롤러 scraper 마이그레이션 + DAG, OSV 수집 개선(미래 날짜 방어) | DAT-2668~2670, PKB-483/484 |
| 2월 | Synchronizer 개선·전체 sync·File Comp sync, K8s CoreDNS/Airflow·inode 장애 분석 | PKB-480~482, PKB-568, PKB-615/616 |
| 3월 | npm·pypi 크롤러 RESYNC 모듈 개선(뉴로엑스티 1차) | DAT-2754~2768 |
| 3월 | 8개 생태계 누락 데이터 검증(Yokoten) | DAT-2799~2858 |
| 3월 | ETL 수집 플로우 구성도·재정립, Gathering DB replica 1·2차 튜닝 + In-House 재구축 | DAT-2879/2881, 2885~2895 |
| 3~4월 | BTS 고객사 정합성 이슈 + 보고서, 4.0.0 모듈 테스트·리뷰 | DAT-2905~2909, 3036 |
| 3~4월 | 고객사 Binlog 다운로드 모니터링 사이트 v2 재구축 + 문서화 | DAT-2915~2917, 2998 |
| 4월 | 데이터 플랫폼 모듈 개발: DML-Broker, raw data scraper, transformer | DAT-3077/3079/3081 |
| 4월 | Malicious 패키지 수집, RHEL 취약점 수집/탐지 점검 | DAT-2973, 2989/2991, BUG-224 |
| 4~5월 | 삼성화재 lab-master DB CPU 이상 분석, IBK STANDARD_LICENSE 에러 | DAT-3114~3120, 3157/3158 |
| 5월 | 문자열/JSON 컬럼 전수조사 + 보고서, 패키지매니저 API rate limit 조사 | DAT-3160/3162, 3164 |
| 5월 | 오브젝트 스토리지 구축 방안 연구·문서화·회의 | DAT-3173~3178 |
| 6월 | 9개 생태계 누락 크롤러 개선 + 누락 건수 수집, 수집량 로그 11종 | DAT-3185~3199, 3264, 3214~3233 |
| 6월 | 악성 패키지 수집 개발, 미래아카데미 BMT 지원·정탐 소명 | DAT-3241, 3205, 3260 |
팀 KPI item names came from Jira, not guesswork. KPI work carries a [KPI|*] prefix: [KPI|DEV] AI 기반 CI/CD 자동화 프로세스 구축 (DAT-2919 설계 → 2984 구축 → 3153 사용 리뷰), [KPI|EVAL] 컨테이너(OS 패키지) 분석 품질 테스트 자동화 (DAT-2935), [KPI|COV] 라이선스/취약점 핵심 수집 대상 선정 (DAT-2952/2954), [KPI|MAL] 악성 패키지 수집 개발 (DAT-3241). This confirms the user's correction that the CI/CD item is a 팀 KPI, and those four now fill the 팀 KPI block in both variants.
연간 variant 주요업무 (weights 20/25/25/15/15, difficulty A/A/S/A/B): 팀 KPI · 라이브러리 수집 누락 개선 · 데이터 수집/배포 플랫폼 구축 · 오브젝트 스토리지·Git 미러 구축 · DB·수집 플랫폼 운영·장애 대응.
The user then flagged that the BTS monitoring rebuild was missing — correct, [ENHC|BTS] 고객사의 Binlog 다운로드 모니터링 사이트 v2 구축 (DAT-2915/2916/2917, 완료 2026-03-30; 문서화 DAT-2998, 04-09) is a separate build from the BTS 4.0.0 work, and v2 means rebuild, not first build. The 데이터 수집/배포 플랫폼 구축 item now reads ETL 재정립·모듈 개발 / BTS v4 개선·모니터링 v2 재구축 / 미래아카데미 구축. Lesson for the next pass: read BTS as three tracks (전송 모듈, 모니터링, 고객사 이슈 대응), not one.
Outputs: ~/Downloads/[래브라도랩스] 인사평가서_김현욱_연간.xlsx and …_하반기.xlsx. The earlier …_김현욱.xlsx is superseded by the 하반기 file (deletion was blocked by the tool permission prompt, so it is still on disk).
Gap worth closing: ai/wiki/people/career-timeline.md still ends at 2025. The table above is the raw material for a 2026 section.
2026-08-11 (3rd) — 자기평가 + 피평가자 의견 filled
The user asked to fill the 자기평가 column and the 피평가자 의견 block from Jira and Confluence. Both were previously left blank on the grounds that the guide assigns them to the evaluation period. Filled as drafts, with one constraint held: only work confirmed complete as of 2026-08-11 goes into 자기평가 — writing the rest of the year in advance would be fabricating results.
Second-half confirmed work (Jira, created >= 2026-07-01, 44 issues): DAT-3295 라이브러리 누락 개선 전체 문서화 · DAT-3314 MySQL→MariaDB 이전 검토 · DAT-3334 오브젝트 스토리지 구축 방안 검토 · INFR-393 git mirror 서버 HDD 증설 · DAT-3418 신한라이프 오탐 전수 검토 AI 자동화 · DAT-3452 행안부 BTS POST 테스트·배포 · DAT-3477 go 라이브러리 뷰테이블/인덱스 수정 · INFR-399 labgit1,2 내부망 포트포워딩 · PKB-1200 삼성증권 no release 149건 정오탐 판단. So the 하반기 variant's 자기평가 is deliberately sparse for the OS-package and 미래아카데미 items — those started in August.
피평가자 의견 is a DRAFT: 소속부서 만족도 A / 담당업무 만족도 A / 희망 이동부서 "없음 (현 데이터파트 유지 희망)" and the four free-text rows are written in the user's likely voice, not from evidence. These are personal-intent fields and the user must confirm them before submitting.
Rendering constraint discovered this round: the 자기평가 cells (M:R) carry wrapText=1, so overflow WRAPS into the next row instead of clipping like the C:F cells — a long line silently pushes the block out of alignment. Budgets are now enforced in the generator (budget() / over report): C:F 35 units, M:R 36, M:V 45, Korean glyph = 2. Both files report over 0.
Note on verification: LibreOffice on this Mac has no 맑은 고딕 and substitutes a different face on each run (one pass rendered the whole sheet in a serif, another in a script face), so PDF width checks are only indicative. The numeric budget check is the reliable gate; the PDF is for layout/one-page confirmation.
2026-08-12 — labgit: ACL/TLS verification, incident-response page, OOM crash loop found
Two things were asked for and a third turned up on the way.
ACL blocker resolved. The 2026-08-10 note recorded that from the office network labgit1 answered only on port 22 while labgit2 answered on everything, which meant HA was nominal — if labgit2 died, consumers could not reach labgit1's gateway. Re-measured 2026-08-12: labgit1 now answers on 22/50080/50443/53000, matching labgit2. Nobody told us it changed; it was found by re-running the check.
TLS was verified against that: the internal CA at /labgit-ca.crt validates (ssl_verify_result=0) on both servers, certs carry the right IP SANs (CN=labgit1 / IP:211.115.125.166, and the labgit2 equivalent), and every API surface answers over https://…:50443 — broker /x/resolve, Forgejo /api/v1/repos/{org}/{repo}, /api/v1/version, and /user/login (the dot-free path that the 2026-08-10 routing fix addressed). git ls-remote was run for two gateways x two shards: all four succeeded with identical HEADs, so cross-shard routing works in both directions.
One gap: the broker returns clone_url over plain HTTP even when the request arrived on TLS, because broker.py:65 builds it from the static PUBLIC_BASE (docker-compose.yml:85 = http://${LABGIT_HOST}:50080). Not an outage — a consumer that follows clone_url after a TLS call is silently downgraded to plaintext. Left undecided between honoring X-Forwarded-Proto per request (keeps CA-less consumers working) and pinning PUBLIC_BASE to https (simpler, breaks anyone without the CA).
Confluence incident-response page (4202364930, child of build-result 4197876226). Centered on the scenario the user asked for: labgit1 dies, and until it is back, its repos are re-fetched from the real upstream. Documents that nginx converts a dead peer into 404 + X-Labgit-Degraded: 1 rather than 502, that broker._pick_server() falls back to the surviving server after a 5s probe, and that /x/ensure then clones from upstream onto the survivor — only for repos actually requested, because a full re-seed is ~3.5 days at 102k repos. Also carries the diagnosis sequence, per-failure-mode procedures, reconcile.py cleanup, and a replication-requirements section: same disk capacity is mandatory (a replica holds the same bytes), CPU can be lower, RAM can be lower only while standby — the promotion target still has to survive Forgejo's real memory profile, which the same day's incident put above 50GiB. Three options (replicate / stay as-is / duplicate only the manifest) are laid out without a recommendation; the decision is a meeting item.
Incident found while collecting facts: labgit2 Forgejo OOM crash loop. RestartCount=400, roughly a 6-minute cycle, running ~40h; labgit1 at 0. Kernel log: Out of memory: Killed process (gitea) anon-rss:126393920kB, constraint=CONSTRAINT_NONE — a global OOM, since the container has no memory limit and took nearly the whole 125GiB host. The pre-death log is identical every cycle: Forgejo's stock [cron.check_repo_stats] recomputes num_forks/num_watches with correlated subqueries over all 51k repos, measured at 51s and 2m43s. app.ini has no [cron] section, so the upstream defaults apply — including RUN_AT_START, which is what makes it a loop rather than a nightly problem: each OOM restart re-runs the task that caused the OOM.
labgit1 is on the same path, not exempt from it — same cron, nightly at 00:00 (log confirms 08-11 and 08-12), currently 50.86GiB RSS.
Fork and watch counts are meaningless on a mirror-only instance, so the fix is to disable the task: FORGEJO__cron_0X2E_check_repo_stats__ENABLED: "false" (same _0X2E_ section encoding already proven by the git.timeout setting). Written into the local data-infra checkout, uncommitted. Not applied to either server — the in-place server edit was blocked by the Claude Code permission classifier, and /data/product/data-infra is a copy deployment rather than a git checkout, so a repo commit would not reach the servers anyway.
Also confirmed along the way, closing two long-standing open questions: /data is mounted noatime on labgit1, and the server deploy path is /data/product/data-infra/labgit. A new gap opened in their place — /data sits on a Broadcom MegaRAID SAS39xx hardware RAID10 presented as a single sdb, so mdadm shows nothing, and neither server has storcli64 or any controller tool. Today there is no way to tell from the OS which disk slot failed.
2026-08-13 — BT26-707: curl/libcurl 8.21.0 in the BTS image (v4.1.1)
What shipped:
| Where | State |
|---|---|
binlog-transfer-system main | 1360350; release commit 68de5ef tagged v4.1.1; v4.1.0 tagged retroactively at 3aca7f1 |
| Image | labradorlabs/master-bts:4.1.1, amd64, built by the user on labM |
labM lab-master-db | running 4.1.1 as uid 1000:1000 |
labM lab-master-updater | compose bumped to 4.1.1; applies on its next batch run |
nas1:…/cdbvdbInstaller/4.1.1/ | image tar.gz + lab-master-installer.tar.gz + bts.config.tar.gz |
| Not done | Jira reply; 신한투자증권 is still on v3 (image 2.0.2) |
신한투자증권's security team scans the DMZ proxy host and flagged curl/libcurl 7.76.1 against 18 CVEs (CVE-2026-8286, -8458, -8924~8927, -8932, -9079/9080, -9545~9547, -10536, -11352, -11564, -11586, -11856, -12064), fix version 8.21.0. The same report lists CVE-2026-43503 (Linux Kernel), which is out of scope for a container image — no kernel ships in it; that is the host OS's item.
Deployment shape (settles the blast radius). lab-master-installer/ shows lab-master-db, download-proxy-server and updater all running the same image labradorlabs/master-bts:<ver>, differing only by the command that entrypoint.sh dispatches on. One image fix covers all three containers. No healthcheck uses curl.
Who actually needs libcurl (measured inside master-bts:4.0.0). A full ELF scan found exactly six consumers of libcurl.so.4: /usr/bin/microdnf, /usr/lib64/librepo.so.0, /usr/lib64/libdnf.so.2, /usr/bin/mysqlsh, /usr/bin/mysql-secret-store-login-path, /usr/bin/curl. mysqld does not link it, there are no dlopen string references in /usr/sbin or /usr/lib64/mysql/plugin, and the repo has zero references to curl, mysqlsh or mysql-shell outside the Dockerfile install line. rpm's "libcurl is needed by" answer is about mysql-shell and librepo, not the database server.
Removal was proven viable before choosing: rpm -e --nodeps curl libcurl mysql-shell left only microdnf/librepo/libdnf broken, and mysqld booted with log_bin=1/binlog_format=ROW, DDL/DML passed, and the server and updater roles produced startup logs identical to the original image. The user chose the upgrade path over removal so mysqlsh stays usable for in-container DB access.
Trap: deleting the install line does not remove curl. rpm -q --last shows curl/libcurl carrying the same install timestamp as tar/procps-ng (our microdnf install layer) while mysql-shell/rpm/librepo carry the base image's 2024 date. Since the base's mysql-shell already requires libcurl.so.4, curl was present in the base and our line merely upgraded it — pristine mysql:8.4.0 indeed ships curl/libcurl 7.76.1. Removal has to be explicit in the final stage.
Trap: the curl builder must use the same base as the final stage. The first attempt built on oraclelinux:9, whose OpenSSL is 3.5.5 while the target image had 3.5.1, and the final image failed with libssl.so.3: version 'OPENSSL_3.2.0' not found (required by libcurl.so.4). The build-time assertion added in the same change is what caught it. Fixed by building FROM mysql:8.4.0, having both stages run microdnf update -y openssl-libs zlib, and linking libcurl against OpenSSL/zlib only (--without-nghttp2 --without-libidn2 --without-libpsl --without-brotli --without-libssh --disable-ldap) so the DT_NEEDED set stays at libssl, libcrypto, libz, libc.
Verified on the built image: curl 8.21.0 / libcurl 8.21.0 / OpenSSL 3.5.5, no curl entries in the rpm DB, HTTPS to curl.se returns 200 with ssl_verify_result=0, mysqlsh --version and microdnf makecache both work (librepo ABI intact), mysqld boots and serves DDL/DML and a TLS_AES_128_GCM_SHA256 connection, and both the server and updater roles match the previous image's startup output. openssl-libs also moves 3.5.1 → 3.5.5, which pre-empts the next scan.
Sizing note for any similar patch: rpm -e in a derived layer does not reclaim space — the files stay in the lower layers and the rewritten rpmdb is added on top, so an in-place removal patch of 4.0.0 measured 1.37GB → 1.39GB. Reclaiming mysql-shell's 341MB would mean replacing the base with oraclelinux:9-slim + mysql-community-server-minimal, which is a separate decision.
Repo changes (binlog-transfer-system): UnifiedDockerfile (+89), VERSION → 4.1.1, new RELEASE/v4.1.1.md. Committed as 68de5ef on branch fix/curl-8.21.0-bt26-707, tagged v4.1.1 (lightweight, matching the existing v1.1.0/v2.0.0/v4.0.0 tags), then fast-forwarded into main and pushed — main, the branch and the tag all point at 68de5ef, so history stays linear and the tag is valid on main. Note that v4.1.0 was never tagged.
The validation build used a throwaway key via a direct docker build, deliberately not build.sh — build.sh writes generated RSA keys into ./config and a later production build would silently reuse them. build.sh also passes the key as a command-line argument and has no --platform, so on an Apple Silicon machine it silently produces an arm64 image. The release build was therefore done by the user on the labM Ubuntu (amd64) server, with the recommended form being a direct docker build --secret id=secret_key,src=<file> so the key never lands in argv, the environment, or an image layer.
Release-build provenance that was independently checked on labM: Architecture=amd64/linux, curl 8.21.0 / libcurl 8.21.0, no curl entries in the rpm DB, and updater --version printing BTS Updater v4.1.1 (commit=68de5ef, built=2026-08-13T00:34:52Z) — so the shipped binary carries the tagged commit. Whether the embedded SECRET_KEY is the production one could not be verified from outside; that rests on the build having been run with the correct key.
Rollout on labM — and the operator-account trap it exposed. labM runs lab-master-db (was 4.0.0) and lab-master-updater (was 4.1.0, batch). The DB image comes from DOCKER_IMAGE in /product/Infra/data-infra/db-infra/instances/lab-master/.env, not from the compose file; the updater pins the tag directly in /product/Infra/lab-master-installer/updater/docker-compose.yml. Both were moved to 4.1.1 (backups kept as *.bak-20260813).
db-infra/up.sh lab-m then brought the DB up in a crash loop: Could not open file '/var/log/mysql/error.log' for error logging: Permission denied. Cause is not the image — scripts/lib/common.sh:119 injects UID_GID=$(id -u):$(id -g), so the container runs as whoever invokes the script. Everything under /data/lab-master and /data/logs/lab-master is owned by 1000:1000 (labrador, the account the instance was originally deployed with), and the run was made as hyunwook711 (1003). Recovered by recreating the container with UID_GID=1000:1000 passed in the environment (shell env outranks --env-file in Compose v2); mysqld reached ready for connections and curl 8.21.0 was confirmed inside the running container. Downtime was roughly 09:47–09:52 KST.
Two things worth carrying forward: up.sh/down.sh for these instances must be run as labrador, and up.sh reports STARTUP-MYSQL DONE plus a Slack "인스턴스 시작" notice purely from the compose exit code — it never checks that mysqld stayed alive, so a crash loop reads as a successful start.
NAS distribution. docker save | gzip on labM produced labradorlabs_master-bts:4.1.1.tar.gz (217,282,767 B, sha256 0838ffe4ee…d25e88), uploaded to nas1:/volume1/Public/cdbvdb/cdbvdbInstaller/4.1.1/ with the checksum verified on both ends. labM cannot reach the NAS itself — the key in the instance .env (/home/labrador/.ssh/lab-master_id) belongs to labrador — so the transfer was streamed labM → local → nas1. The release-directory convention for 4.x is three files: labradorlabs_master-bts:<ver>.tar.gz (note the colon), lab-master-installer.tar.gz (the repo's lab-master-installer/ tree), and bts.config.tar.gz (customer config including private.pem — key material, not something to regenerate casually).
The 4.1.1 directory was completed the same day: bts.config.tar.gz copied byte-identical from 4.1.0 (it holds private.pem and customer config, so it is carried forward, not regenerated), and lab-master-installer.tar.gz rebuilt from the 4.1.0 bundle with every 4.1.0 string replaced by 4.1.1 — 11 lines across the three docker-compose.yml files, updater/db.connect.sh (IMAGE=), and README.md. The README matters more than it looks: it carries the NAS path, the scp commands and the docker load filename, so a compose-only edit would have shipped a 4.1.1 bundle that tells the customer to download the 4.1.0 image. File list (26 entries) and executable bits were verified identical to the source bundle, and every transfer was checksum-verified on both ends.
The repo was then brought in line in 1360350 (same 11 lines, fast-forwarded into main and pushed), and the repo tree was verified byte-identical to the published bundle — all 20 files match by sha256, so rebuilding from main reproduces it.
v4.1.1 was first tagged at 68de5ef and then, on the user's instruction, force-moved to 1360350 so the tag covers the complete release including the installer bundle. Consequence to remember: the tag no longer matches the commit hash embedded in the shipped image. updater --version in labradorlabs/master-bts:4.1.1 prints commit=68de5ef, which is v4.1.1~1. When tracing a running container back to source, resolve the printed hash directly rather than assuming it equals the tag. Anyone who fetched the tag before the move keeps the old target until they prune (git fetch --tags --force).