Infrastructure as Code · 선언형 사고 · 실습
desired / actual / plan 3분할 만들기
이 실습은 진짜 VM 에서 돕니다
이 상자는 파드가 아니라 KubeVirt 가 띄운 가상머신입니다. 리눅스 커널이
따로 돌고 systemd 가 실제로 서비스를 관리하며, docker 는 흉내가 아니라
진짜 도커 엔진입니다. docker run 으로 띄운 컨테이너는 실제로 프로세스가
되고 docker exec 도 docker logs 도 그대로 동작합니다.
예전에는 이 실습이 파드 안에서 돌았습니다. 커널 권한을 전부 내려놓은 상자라
컨테이너를 띄우는 단계가 막혀 있었고, 그래서 이미지 아카이브를 직접 풀어
보는 우회로 배웠습니다. 이제 우회가 필요 없습니다.
알아 둘 것이 둘 있습니다.
- 처음 뜨는 데 1분 남짓 걸립니다. VM 이 부팅하고 도커를 설치하기
- 브라우저 미리보기가 없습니다. VM 으로 들어오는 연결은 채점 포트
때문입니다. 파드 실습(보통 40초)보다 느립니다.
하나만 열려 있습니다. 웹 서버를 띄웠다면 VM 안에서 curl 로 확인하세요.
목표
원하는 상태(desired), 현재 상태(actual), 둘의 차이(plan)라는 3분할 구조를 직접 만들고, 드리프트가 어떻게 감지되고 수렴되는지 확인한다.
왜 중요한가
IaC 도구가 화면에 뿌리는 + create, - destroy, ~ update 는 마법이 아니라 두 상태를 비교한 결과다. 이 구조를 손으로 한 번 만들어 보면 plan 을 읽는 눈이 달라진다. 특히 자동화에서는 결과를 종료코드로 받아야 하므로 0 은 변경 없음, 1 은 오류, 2 는 변경 있음이라는 관례를 그대로 따른다. 3축 모델로 말하면 코드는 Desired, 상태 파일은 Last Known, 실제 인프라는 Actual 이고, 셋 중 둘이라도 어긋나면 드리프트다. GitOps 의 자가 치유도 결국 이 비교를 반복해서 돌리는 일이다.
단계
1. /root/iac1/desired.yaml 에 원하는 상태를 선언한다. 최상위에 containers: 한 줄을 두고 그 아래 항목 2개를 둔다. 이름은 각각 iac-web 과 iac-cache 이고, 항목마다 image: 를 한 줄씩 적는다(파일 전체에서 image: 는 정확히 2줄). 이미지는 파드에 미리 있는 alpine:3.20, busybox:1.36, python:3.12-alpine, nginx:1.27-alpine 중에서만 고른다.
2. /root/iac1/parse.sh 를 만들어 그 YAML 을 /root/iac1/desired.json 으로 변환한다. JSON 모양은 {"containers":[{"name":"...","image":"..."}, ...]} 이고 원소는 2개, image 값이 비어 있으면 안 된다.
3. /root/iac1/actual.sh 를 만든다. 지금 실제로 떠 있는 관리 대상 컨테이너를 같은 모양의 JSON 으로 표준출력에 낸다. 각 원소에는 name 과 image 가 모두 있어야 한다. 관리 대상은 이름이 iac- 로 시작하는 컨테이너만이며, 이미지 문자열은 localhost/ 나 docker.io/library/ 접두를 벗겨 desired 와 같은 표기로 맞춘다.
4. /root/iac1/plan.sh <desired.json> <actual.json> 을 만든다. desired 에만 있으면 + create <이름>, actual 에만 있으면 - destroy <이름>, 양쪽에 있는데 image 가 다르면 ~ update <이름> 을 한 줄씩 출력한다. 차이가 하나도 없으면 no changes 를 출력하고 종료코드 0 을 낸다. 차이가 있으면 종료코드 2 를 낸다(0=변경 없음, 1=오류, 2=변경 있음).
5. /root/iac1/apply.sh 를 만들어 실제 상태를 선언에 맞춘다. 끝나면 desired 에 적힌 두 컨테이너가 모두 running 이어야 한다.
6. 아무것도 바꾸지 않은 채 plan 을 다시 돌려 /root/iac1/plan2.txt 에 저장한다. 이 파일에는 no changes 가 있고 +, -, ~ 로 시작하는 줄이 하나도 없어야 한다.
7. 코드 밖에서 상태를 흔든다. docker rm -f iac-cache 로 컨테이너 하나를 지운 뒤 plan 을 돌려 /root/iac1/drift.txt 에 저장한다. 변경 줄은 정확히 하나여야 하고 그 줄은 + create iac-cache 여야 한다.
8. 다시 apply.sh 를 돌려 수렴시키고, 그 뒤의 plan 을 /root/iac1/plan3.txt 에 저장한다. no changes 가 나오고 두 컨테이너가 모두 running 이어야 한다.
참고
- 이 실습의 핵심은 파서 품질이 아니라 desired/actual/plan 의 3분할이다. YAML 파싱은
grep/sed수준으로 충분하다. nginx:1.27-alpine은 그대로 두면 계속 떠 있지만alpine:3.20은 바로 끝난다.docker run -d --name iac-cache alpine:3.20 tail -f /dev/null처럼 붙잡아 둔다.- 채점 6번은 desired.json 과 actual.sh 출력을
{name,image}만 남겨 정렬한 뒤 그대로 비교한다. 이미지 표기가 한 글자라도 다르면 실패한다. - 흔한 실수: actual.sh 가
iac-가 아닌 다른 실습 컨테이너까지 긁어 오기, plan 출력에 기호와 낱말 사이 공백을 빼기, 07 단계에서 두 개를 지워 변경 줄이 두 개가 되기.
단계 8개
- 원하는 상태 선언하기
- 선언을 기계가 읽는 형식으로
- 현재 상태 읽어 오기
- 차이 계산하기
- 선언한 상태 만들기
- 다시 적용해도 변화 없음
- 밖에서 생긴 변경 잡아내기
- 자동 수렴