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 에 붙여 리뷰한다. 사람이 리뷰하는 대상이 코드가 아니라 코드가 만들어 낼 변경 목록이라는 점이 핵심이다.

상태 파일이 진짜 무거운 자산이다

선언형 도구에서 가장 자주 사고가 나는 자리는 코드가 아니라 상태 파일이다. 앞의 3축 모델에서 "Last Known" 을 담당하는 그것이다. 이 파일이 없거나 망가지면 도구는 지금 있는 자원을 자기가 만든 것으로 인식하지 못하고, 그대로 apply 하면 이미 있는 것을 또 만들거나 남의 것을 지우려 든다.

그래서 몇 가지가 기본이다.

공유 저장소에 두고 잠근다. 상태 파일이 각자의 노트북에 있으면 두 사람이 동시에 apply 할 때 서로의 기록을 덮어쓴다. 원격 저장소에 두고 apply 동안 잠그는 것이 표준이고, 잠금이 없는 구성은 사람이 적을 때만 우연히 안전하다.

비밀이 그 안에 들어간다. 데이터베이스 비밀번호처럼 자원을 만들 때 준 값이 상태 파일에 평문으로 남는 경우가 흔하다. 그래서 상태 파일은 코드 저장소에 커밋하지 않고, 암호화되는 저장소에 두며, 접근 권한을 코드와 다르게 관리한다.

손으로 고치지 않는다. 도구가 제공하는 명령으로만 옮기고 지운다. 텍스트로 열어 편집하면 형식은 맞는데 내부 참조가 어긋나서, 몇 번의 apply 뒤에 원인 모를 재생성이 일어난다.

그리고 범위를 나누는 것이 커질수록 중요해진다. 전체 인프라를 하나의 상태로 관리하면 plan 이 몇 분씩 걸리고, 작은 변경 하나에 전체가 잠긴다. 수명과 담당이 다른 것끼리 나누는 것이 원칙이다. 네트워크처럼 거의 안 바뀌는 것, 클러스터처럼 가끔 바뀌는 것, 애플리케이션처럼 매일 바뀌는 것을 한 덩어리에 두면 매일 바뀌는 것 때문에 거의 안 바뀌는 것까지 매번 위험에 노출됩니다.

다음 실습에서 할 것

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