二度回しても同じでなければならない
한국어 원문으로 표시합니다.
한 줄 요약
멱등성은 같은 작업을 여러 번 실행해도 결과가 동일한 성질이고, 이것이 없으면 자동화는 돌릴 때마다 결과가 달라지는 도박이 된다.
왜 이게 필요했나
자동화는 한 번만 실행되지 않는다. 러너가 타임아웃으로 죽어 재실행되고, 다른 사람이 같은 플레이북을 또 돌리고, 스케줄러가 매시간 같은 것을 적용한다. 이때 "이미 되어 있으면 아무 일도 하지 않는다"가 보장되지 않으면 재실행 자체가 위험해진다. 결국 사람들은 자동화를 무서워하게 되고, 무서우니까 손으로 하게 되고, 손으로 하니까 다시 눈송이 서버가 된다.
증명 방법은 의외로 단순하다. apply 를 두 번 연속 돌리고, 두 번째 plan 이 변경 없음(종료코드 0)인지 보면 된다. 이 한 줄짜리 검사가 통과하지 않는 자동화는 아직 신뢰할 수 없다.
어떻게 동작하나
멱등성은 "실행 전에 현재 상태를 확인한다"에서 나온다. 디렉터리를 만들기 전에 있는지 보고, 파일을 쓰기 전에 내용이 같은지 보고, 권한을 바꾸기 전에 현재 권한을 읽는다. 그래서 잘 만든 모듈은 항상 두 갈래다. 바꿨으면 changed, 이미 맞으면 unchanged.
멱등성이 깨지는 가장 흔한 자리는 전용 모듈 대신 shell 이나 command 로 명령을 직접 실행할 때다. 도구는 그 명령이 무엇을 하는지 모르므로 매번 실행한다. 그래서 creates= 나 removes= 같은 조건을 붙여 이미 끝난 작업은 건너뛰게 만든다. 대표적인 버그는 설정 파일에 한 줄을 추가한다면서 >> 로 덧붙이는 것이다. 실행할 때마다 같은 줄이 하나씩 쌓여 열 번 돌리면 열 줄이 된다. 올바른 구현은 그 줄이 이미 있는지 먼저 확인하고, 같은 키의 다른 값이 있으면 그 자리를 교체하는 것이다.
적용 전에 무엇이 바뀔지 미리 보려면 check mode 와 diff 를 함께 쓴다(--check --diff). 실제로 바꾸지 않고 바뀔 내용만 보여 주므로 운영 서버에 처음 적용할 때 필수다. 그리고 결과는 반드시 세어서 보여 줘야 한다. changed 와 unchanged 를 집계한 리포트가 있어야 두 번째 실행에서 changed 가 0 이라는 사실을 사람이 확인할 수 있다.
현장에서 만나는 모습
"플레이북이 성공했는데 왜 설정이 이상하죠"라는 질문의 절반은 멱등하지 않은 태스크에서 나온다. 로그에는 ok 만 찍혔지만 실제로는 같은 줄이 세 번 들어갔거나, 재시작이 매번 일어나 서비스가 주기적으로 끊긴 것이다. 그래서 성숙한 팀은 CI 에서 플레이북을 두 번 돌린다. 두 번째 실행의 changed 가 0 이 아니면 빌드를 깬다. 멱등성을 문서가 아니라 테스트로 강제하는 것이다.
또 하나, 상태를 기록해 두면 감지 능력이 생긴다. 적용 시점에 각 리소스의 경로와 해시를 남겨 두면 나중에 그 파일이 밖에서 바뀌었는지 비교만으로 알 수 있다. 상태 파일이 있는 도구들이 하는 일이 정확히 이것이다.
멱등성과 수렴은 다르다
두 낱말이 자주 섞여 쓰이지만 보장하는 범위가 다르다. 멱등성은 같은 작업을 여러 번 해도 결과가 같다는 성질이고, 수렴은 지금 상태가 무엇이든 원하는 상태로 끌고 간다는 성질이다. 대부분의 자동화는 앞의 것만 만족하고 뒤의 것은 놓친다.
차이는 "지우는 일" 에서 드러난다. 설정 파일 세 개를 두는 플레이북을 두 번 돌리면 changed 는 0 이다. 여기까지가 멱등성이다. 그런데 누군가 손으로 네 번째 파일을 만들어 두었다면, 이 플레이북은 그것을 영원히 발견하지 못한다. 원하는 상태를 "이 세 개가 있다" 로만 적었지 "이 세 개만 있다" 로 적지 않았기 때문이다. 수렴하려면 디렉터리의 내용 전체를 관리 대상으로 선언하고, 목록에 없는 것은 지워야 한다.
그래서 자동화를 설계할 때 두 가지를 명시적으로 정합니다. 어디까지가 내가 소유한 영역인가, 그리고 그 영역 안에서 낯선 것을 발견하면 지울 것인가 알릴 것인가. 조용히 지우면 남의 작업을 날리고, 아무것도 안 하면 서버는 서서히 서로 달라집니다.
부작용이 있는 작업에서도 같은 구분이 필요하다. 서비스 재시작이 대표적이다. 설정 파일이 바뀌었을 때만 재시작해야 하는데, 이것을 조건 없이 매번 실행하면 플레이북 자체는 멱등해 보여도(파일 내용은 늘 같으니까) 실제로는 돌릴 때마다 서비스가 끊긴다. 알림(handler) 방식이 존재하는 이유가 이것이고, 이때 재시작이 실제로 몇 번 일어났는지를 결과 리포트에 남겨 두면 나중에 "왜 매시간 잠깐씩 끊기죠" 라는 질문을 몇 초 만에 닫을 수 있다.
마지막으로 재시도와의 관계를 짚어 둔다. 멱등한 작업은 실패했을 때 그냥 다시 실행하면 되지만, 멱등하지 않은 작업은 재시도가 곧 중복 실행이다. 네트워크 오류로 응답을 못 받았을 때 그 작업이 실제로 수행됐는지 알 수 없다는 점이 핵심이다. 재시도할 계획이라면 그 작업은 반드시 멱등해야 하고, 멱등하게 만들 수 없다면 작업마다 고유한 표식을 붙여 이미 처리한 것인지 확인하는 수밖에 없다.
다음 실습에서 할 것
디렉터리·파일·줄·권한을 보장하는 작은 스크립트 네 개를 만들고, 각각이 changed 와 unchanged 를 정확히 구분하게 한다. 그다음 이 넷을 묶은 러너를 두 번 연속 실행해 두 번째에는 changed 가 0 이 나오는지로 멱등성을 증명한다. 마지막에는 상태 파일과 드리프트 감지, 그리고 동시 실행을 막는 잠금까지 붙인다.