테마
04 — 일상 운용과 문제해결
매일 아침 하는 일 세 줄과 화별로 켤 컨테이너, 그리고 증상 → 원인 → 조치 표를 모았다. 📌 조치 표의 항목은 모두 실제로 난 오류다. 추정으로 적은 것은 없다.
1. 매일 하는 세 줄
bash
docker start ora-hanorder # 오늘 볼 고객사만 켠다 (볼륨이 있으니 데이터는 그대로다)
docker stop ora-burger # 안 쓰는 건 끈다 (메모리 1~1.5GB 회수)
docker ps --format "{{.Names}}|{{.Status}}|{{.Ports}}"정상 출력 예
ora-hanorder|Up 2 minutes (healthy)|0.0.0.0:1521->1521/tcp재기동한 뒤
(healthy)가 붙기까지는 수십 초가 걸린다. 그 전에 접속하면ORA-12514가 나는데, 고장이 아니다. Docker Desktop 자체가 꺼져 있으면docker명령이failed to connect to the docker API …로 실패한다. 이때는 고래 아이콘부터 띄운다.
2. 화별로 어느 컨테이너를 켜나
| 화 | 켤 것 | 왜 |
|---|---|---|
| 01·02 | ora-hanorder | 베이스라인, 인덱스 무력화 |
| 03 | ora-hanorder + ora-dist | 문자열 날짜는 pup에만 있다 |
| 04 | ora-hanorder + ora-burger | CF 대조군 |
| 05~11, 13, 15, 15b | ora-hanorder | 본편 |
| 12 | ora-hanorder + ora-newshop | 무인덱스 FK는 ORA_SQL_TEST에만 있다 |
| 14 | ora-burger | 파티션 Pruning 실측. 병렬은 Free에서 직렬로 강등된다. 병렬 부분은 판독으로 다룬다 |
| 16·17 | ora-hanorder | AWR·ASH·ADDM 모두 Free에서 된다. 부하는 15화에서 만든 PL/SQL 드라이버로 건다 |
| 18 | ora-newshop | 졸업 과제 (ECOMMERCE) |
동시에 2대를 켜는 화(03·04·12)는 메모리가 8GB 이상이면 편하다. 부족하면 한 대씩 번갈아 켜도 실습은 된다. 두 DB를 한 쿼리에서 조인하는 화는 없기 때문이다.
3. 로그 — 정상인데 놀랄 수 있는 메시지
bash
docker logs --tail 50 ora-hanorderCheckpoint not complete
Thread 1 advanced to log sequence 686 (LGWR switch)고장이 아니다. 리두 로그가 작아 로그 스위치가 잦다는 신호이고, 13화에서
log file sync와 함께 다룰 실제 소재다. 지금은 무시한다.
4. 볼륨과 컨테이너 — 무엇이 어디에 있나
docker volume ora-hanorder-data ← 데이터파일 (/opt/oracle/oradata). 이게 진짜다
docker container ora-hanorder ← 껍데기. 지워도 볼륨은 남는다
docker image gvenzl/oracle-free ← 소프트웨어. 4대가 공유bash
docker inspect ora-hanorder --format '{{json .HostConfig.Binds}}'| 출력 | 뜻 |
|---|---|
["ora-hanorder-data:/opt/oracle/oradata"] | ✅ 볼륨에 있다. 안전 |
null | 🔴 위험. 데이터가 컨테이너 쓰기 레이어에 있어서 docker rm 한 번이면 사라진다. 이 저장소의 launch_ora는 항상 볼륨을 붙이므로, 여기에 해당한다면 다른 방법으로 만든 컨테이너다. 부록/L01_컨테이너_기동과_영속화.md §5의 무손실 이관 절차로 옮긴다 |
포트를 바꾸거나 컨테이너를 다시 만들고 싶을 때: 볼륨은 그대로 두고 껍데기만 갈아 끼운다.
bash
docker stop ora-hanorder && docker rm ora-hanorder
docker run -d --name ora-hanorder -p 1531:1521 --shm-size=1g \
-v ora-hanorder-data:/opt/oracle/oradata \
-e ORACLE_PASSWORD=oracle -e APP_USER=sqlp -e APP_USER_PASSWORD=sqlp \
gvenzl/oracle-free:23.26.2-slim데이터도, 계정도, 권한도 그대로다(기존 볼륨을 붙이면 APP_USER는 새로 만들지 않는다).
5. 증상 → 원인 → 조치
| 증상 | 원인 | 조치 |
|---|---|---|
failed to connect to the docker API at npipe://… | Docker Desktop 꺼짐 | 실행한 뒤 고래 아이콘이 멈출 때까지 1~2분 기다린다 |
docker: Error response from daemon: … port is already allocated | 포트 충돌 | docker ps -a로 포트를 차지한 쪽을 확인한다. 다른 컨테이너면 stop하고, 다른 프로그램이면 -p 1531:1521처럼 포트를 바꾼다 |
ORA-12514: TNS:listener does not currently know of service | 기동 미완료 | wait_ora로 DATABASE IS READY TO USE!를 확인한다(최초 1~5분, 재기동 수십 초) |
ORA-01031: insufficient privileges (DISPLAY_CURSOR) | 앱 계정 권한 부족 | 00_측정권한_부여.sql을 실행한 뒤 다시 접속 |
DISPLAY_CURSOR가 DBMS_OUTPUT.GET_LINES를 보여 줌 | SERVEROUTPUT ON | 측정 전에 SET SERVEROUTPUT OFF |
T_ORD_DET가 0행 | ; 뒤 같은 줄 주석(일괄 실행할 때) | 02_신시스템_스키마B.sql을 처음부터 다시 돌린다(약 20초). 스크립트는 이미 고쳐 두었으므로 다시 생긴다면 파일이 바뀐 것이다. ⚠️ 0행인 테이블만 손으로 채우면 안 된다. 난수 순서가 달라져 금액이 교재와 어긋난다(03_환경검증.sql [1b]에서 FAIL이 난다) |
적재가 끝났는데 SQL>에서 멈춤 | @파일 방식 + 스크립트에 EXIT 없음 | EXIT를 입력해 준다. 앞으로는 stdin 방식으로 돌린다(02 §4) |
한글 주석·데이터가 ??? | PowerShell에서 ① Get-Content에 -Encoding UTF8 누락, 또는 ② 파이프 인코딩($OutputEncoding 기본값 ASCII) | 둘 다 고친다. $OutputEncoding = New-Object System.Text.UTF8Encoding $false를 먼저 입력하고 -Encoding UTF8을 붙인다(02 §4). 이미 ?로 들어간 데이터는 그 스크립트를 다시 돌려야 고쳐진다. 이미지 자체가 AL32UTF8로 뜨므로 DB 쪽 설정은 필요 없다 |
PowerShell에서 <<'SQL'이 오류 | heredoc은 bash 문법 | here-string @" … "@ |을 쓴다(02 §4) |
ORA-12954: exceeds the maximum allowed database size of 12 GB | 한 컨테이너에 과적재 | 컨테이너를 나눈다(이미 나눠져 있다면 ora-burger의 ORD_TEMP 류를 DROP). 대부분 4.sql을 원본 그대로 1,500만 건으로 돌린 경우다 |
ORA-00845: MEMORY_TARGET not supported | /dev/shm 부족(Docker 기본 64MB) | --shm-size=1g 이상으로 다시 만든다(§4 방식, 볼륨은 유지) |
ORA-00439: feature not enabled: Dump File Data Compression | Free는 Data Pump 압축 불가 | expdp의 compression= 옵션을 뺀다 |
SQL*Loader-500: Unable to open file / KUP-04… | docker cp로 넣은 파일의 소유자가 root | docker exec -u 0 <컨테이너> chown -R oracle:oinstall <경로> |
Could not find the file /home/oracle | gvenzl 이미지의 홈은 /opt/oracle | 경로를 /opt/oracle/…로 바꾼다 |
| 컨테이너를 지웠더니 데이터가 사라짐 | 볼륨 없이 기동 | 스크립트로 다시 만든다(L1은 3분). 앞으로는 launch_ora로만 만든다 |
| 지웠는데 디스크가 줄지 않음 (Windows) | WSL2 VHDX 미축소 | Docker Desktop → Settings → Resources → Reclaim space |
E-Rows가 333K가 아니라 20000 (01화의 오추정이 사라짐) | 밤마다 도는 자동 통계수집이 ORD_ST에 히스토그램을 만들었다. SIZE AUTO는 "WHERE에 쓰인 컬럼"인지를 보고 판단한다 | 04_상태복구.sql을 실행한다. 10화의 현상이 미리 온 것일 뿐 고장이 아니다 |
used_mb가 95에서 조금씩 늘어남 | 스키마 스크립트를 다시 돌릴 때마다 DROP한 테이블이 휴지통에 쌓인다 | PURGE RECYCLEBIN; (04_상태복구.sql이 함께 처리한다) |
docker start 직후 첫 실행에서 Buffers가 교재보다 몇~수십 블록 적다 (Reads가 0이 아니다) | 캐시가 비어 있어 디스크에서 곧장 읽은 블록이 섞였다. 전체 스캔과 BATCHED 접근은 그런 블록을 조금 다르게 센다(실측: 한빛유통 Q1 5,827 → 5,822, 버거원 41,825 → 41,72x) | 한 번 더 돌린다. 버거원 04화 스크립트는 맨 앞에서 캐시를 데우므로 이런 차이가 나지 않는다 |
스크립트를 다시 돌렸더니 실행계획 자리에 ORA-01403: no data found · NOTE: cannot fetch plan | 앞선 실행이나 다른 스크립트의 DDL, 통계수집으로 무효화된 커서가 다시 올라오는 순간에는 DISPLAY_CURSOR가 계획을 집지 못한다(간헐적) | 그 스크립트를 한 번 더 돌린다. 계획만 못 가져왔을 뿐 측정은 됐다(Statistics 블록이 함께 찍히는 스크립트라면 그 consistent gets가 이미 정상 값이다) |
힌트를 줬는데 병렬 실행이 안 됨 (V$PQ_TQSTAT 0행) | Free는 병렬 실행을 지원하지 않는다. 에러 없이 직렬로 강등된다 | 고장이 아니다. 05_Free와_EE_트랙G.md 참고 |
6. 전부 지우고 다시 시작하려면
bash
docker stop ora-hanorder ora-burger ora-dist ora-newshop 2>/dev/null
docker rm ora-hanorder ora-burger ora-dist ora-newshop 2>/dev/null
docker volume rm ora-hanorder-data ora-burger-data ora-dist-data ora-newshop-data 2>/dev/null
# 이미지는 남겨 둔다 (다시 받을 필요 없음)그다음 01_첫_고객사_ora-hanorder.md §3부터 다시 한다. L1은 3분이면 돌아온다.
한 고객사만 초기화하려면 그 이름만 넣는다. 데이터만 다시 만들 거라면 컨테이너는 두고 적재 스크립트를 다시 돌리면 된다. 스크립트마다 맨 앞에서 자기 테이블을 지우기 때문이다.
7. 화가 끝났을 때 — 상태를 남겨 둔다
각 화 4_개선_사다리의 STEP5에 나오는 작업이다. 연쇄가 끊겼을 때 돌아올 지점이 된다.
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 <<'SQL' > 상태/after_NN.txt
SET PAGESIZE 100
SELECT table_name, num_rows FROM user_tables ORDER BY 1;
SELECT index_name, table_name FROM user_indexes ORDER BY 2, 1;
SQL더 강한 스냅샷(통째로 되돌리기)이 필요하면
docker stop으로 멈춘 뒤docker commit ora-hanorder hanorder:after_NN을 실행한다. 기동 중에 커밋하면 크래시 상태의 스냅샷이 되므로 반드시 정지한 뒤에 한다.