LabHub

Infrastructure as Code · 선언형 사고 · 이론

명령형에서 선언형으로

LabHub 에서 이어서 보기

한 줄 요약

선언적 접근은 원하는 최종 상태를 정의하면 도구가 현재 상태와의 차이를 계산해 변경을 수행하는 방식이고, 명령형은 실행할 단계를 순서대로 기술하는 방식이다. 한 문장으로, 인프라의 무엇(What)을 정의하면 어떻게(How)는 도구가 처리한다.

왜 이게 필요했나

수동 관리의 문제는 네 가지로 요약된다. 같은 환경을 다시 만들 수 없고(재현 불가능), 누가 무엇을 언제 바꿨는지 남지 않고(변경 추적 불가), 대수가 늘면 손이 못 따라가고, 환경끼리 조금씩 달라진다. 특히 세 번째가 결정적이다. 서버 10대까지는 수동으로 관리할 수 있지만 100대 이상은 사실상 불가능하다.

그렇게 손으로 조금씩 손댄 서버들은 눈송이처럼 전부 제각각이 된다. Snowflake Server 라는 은유가 여기서 나온다. 문제는 그 서버가 죽었을 때다. 아무도 그 서버를 똑같이 다시 만들 수 없다. 설정이 코드가 아니라 그 기계의 디스크에만 있었기 때문이다.

명령형 스크립트도 이 문제를 완전히 풀지는 못한다. "패키지를 설치하고 설정 파일을 복사하고 서비스를 재시작한다"는 순서는 처음 실행할 때는 맞지만 두 번째 실행에서 무슨 일이 벌어질지는 스크립트를 다 읽어 봐야 안다. 선언형은 이 질문을 아예 없앤다. 목표 상태만 적고, 지금 상태와의 차이는 도구가 계산한다.

어떻게 동작하나

선언형 도구는 항상 세 덩어리로 나뉜다. 원하는 상태(코드), 현재 상태(실제 인프라를 읽어 온 것), 그리고 둘의 차이(plan). 차이를 사람이 읽을 수 있게 기호로 표시하는데 표준 기호는 이렇다. + create 는 새로 만들 것, - destroy 는 지울 것, ~ update 는 제자리에서 속성만 고칠 것, -/+ replace 는 삭제 후 재생성, <= read 는 읽기만 하는 조회다. 리뷰에서 가장 눈여겨봐야 할 기호는 -/+ 다. 속성 하나 바꾸는 줄 알았는데 리소스가 통째로 재생성되는 경우가 여기서 드러난다.

자동화에서는 plan 의 결과를 종료코드로 받는다. plan -detailed-exitcode 는 0 이면 변경 없음, 1 이면 오류, 2 면 적용할 변경이 존재한다는 뜻이다. 이 세 값이 있어야 "변경이 있으면 승인 단계로, 없으면 그냥 통과" 같은 파이프라인을 짤 수 있다. plan 결과를 파일로 저장해 그 파일로 apply 하면, plan 시점과 apply 시점 사이에 코드가 변경되어도 plan 시점의 변경만 적용된다. 리뷰한 것과 적용된 것이 같다는 보장이 여기서 나온다.

드리프트를 이야기할 때는 3축 모델이 편하다. 코드는 Desired, 상태 파일은 Last Known, 실제 인프라는 Actual 이다. 셋이 모두 같아야 정상이고, 둘 중 하나라도 어긋나면 드리프트다. GitOps 도구는 이 비교를 계속 돌려 자가 치유를 한다. 누군가 kubectl 로 직접 replicas 를 변경하면 컨트롤러가 감지하고 Git 에 정의된 값으로 되돌린다.

현장에서 만나는 모습

신입이 가장 자주 하는 실수는 콘솔에서 급하게 고친 뒤 코드에 반영하지 않는 것이다. 그 순간 코드는 거짓말이 되고, 다음 사람의 apply 가 그 변경을 조용히 되돌린다. 반대로 잘 굴러가는 팀은 plan 출력을 PR 에 붙여 리뷰한다. 사람이 리뷰하는 대상이 코드가 아니라 코드가 만들어 낼 변경 목록이라는 점이 핵심이다.

다음 실습에서 할 것

이 파드에는 terraform 바이너리가 없다. 그래서 YAML 로 원하는 상태를 선언하고, 실제 컨테이너 목록을 같은 형식의 JSON 으로 읽어 오고, 둘을 비교해 + create / - destroy / ~ update 를 출력하는 plan 을 직접 만든다. 그다음 apply 로 수렴시키고, 밖에서 컨테이너를 지워 드리프트를 만들어 plan 이 그걸 잡아내는지 확인한다. 도구가 화면에 뿌리는 그 기호들이 어떻게 만들어지는지 알고 나면 남의 plan 출력도 다르게 보인다.