Ansible은 시작됐다고 하지만 WireGuard는 여전히 옛 설정으로 돈다
state: started는 이미 실행 중인 unit을 건드리지 않으므로 다시 작성한 wg0.conf는 절대 적용되지 않습니다. 변경 handler를 추가한 뒤 실행 중인 장치와 파일을 비교하세요.
playbook 실행은 초록으로 끝나고 WireGuard unit은 활성 상태이며 handshake도 동작하는데, 새 peer는 여전히 아무것도 닿지 못합니다. 서버가 어제 설정으로 돌아가는 동안 playbook이 성공을 보고하는 모습은 출력에 아무 오류가 없기에 더 답답합니다. Ansible은 요청받은 대로 했습니다. unit이 시작됐는지 확인했고, unit은 이미 시작돼 있었습니다. 다시 작성한 설정 파일을 적용하는 것은 다른 작업이며, 무언가가 서비스에 그 파일을 다시 읽으라고 알려야만 일어납니다.
두 번째 실행이 실제로 무엇을 보고하는지 보십시오
변경한 설정을 그대로 두고 playbook을 두 번 실행해 요약을 비교하십시오. 두 번째 실행은 unit 작업을 changed가 아니라 ok로 보여야 합니다. 이미 실행 중인 서비스는 설계상 그대로 두기 때문입니다. 이것이 올바르게 동작하는 멱등성이며, 새 파일이 커널에 도달하지 못하는 이유이기도 합니다.
diff 출력이 있는 점검 실행을 추가해 어떤 작업이 무엇을 건드릴지 확인하십시오.
ansible-playbook -i inventory site.yml --check --diff
systemctl show wg-quick@wg0 -p ActiveEnterTimestamp -p NRestarts
stat -c '%y %n' /etc/wireguard/wg0.conf세 타임스탬프가 로그 한 줄을 읽기 전에 상황을 말해 줍니다. 설정 파일이 unit이 활성화된 시점보다 새롭다면 실행 중인 인터페이스는 더 오래된 파일로 만들어진 것이고, 이후의 모든 증상은 거기서 나옵니다.
이것은 Ansible의 결함이 아닙니다. 「실행 중인지 확인하라」와 「이것으로 실행 중인지 확인하라」를 구분하는 어떤 설정 관리 도구에서도 같은 동작이 나타납니다. 그 구분은 반나절을 잃기 전까지는 지나친 것처럼 보입니다.
시작됨은 상태이고 재로딩이 아니다
state: started은 unit에 대한 조건을 주장할 뿐입니다. 서비스가 입력을 다시 읽는다고 약속하지 않으며, 한 번도 열지 않은 파일이 바뀌었다는 것도 알 수 없습니다. wg-quick 배포에서 입력에는 인터페이스 설정, 주소, 경로, DNS 항목, 모든 post-up hook이 포함됩니다.
그래서 갱신된 파일에 새 peer, 다른 주소, 바뀐 라우팅 지시가 들어 있어도 실행 중인 인터페이스는 옛 집합을 계속 제공합니다. handshake가 여전히 성공하는 것은 로드된 키와 엔드포인트가 아직 유효하기 때문입니다. 바로 그 점이 장애를 보기 어렵게 만들고, 사람들이 타임스탬프를 확인하기 전에 한 시간씩 DNS를 쫓는 이유입니다.
모듈의 다른 두 옵션이 종종 해결책으로 오해됩니다. daemon_reload는 systemd가 unit 파일을 다시 읽게 하지만 WireGuard 설정 파일과는 무관합니다. state: reloaded은 unit에 재로딩 동작을 실행하라고 요청하며, 그것이 필요한 일을 하는지는 전적으로 unit에 달려 있습니다. 둘 다 실행 중인 프로세스가 실제로 어떤 입력을 읽었는지 아는 것을 대신하지 못합니다.
파일을 설치한 다음 handler에 알리십시오
멱등성을 지키면서 변경을 적용하는 방식은 평범합니다. 파일을 쓰는 작업이 handler에 알리고, handler는 파일이 실제로 바뀌었을 때만 실행됩니다. 편집이 없는 두 번째 실행은 아무것도 하지 않으며, 그것이 원하는 동작입니다.
- name: write the interface configuration
ansible.builtin.template:
src: wg0.conf.j2
dest: /etc/wireguard/wg0.conf
owner: root
mode: "0600"
notify: apply wg0
- name: ensure the interface unit is enabled
ansible.builtin.systemd_service:
name: wg-quick@wg0
enabled: true
state: started
- name: apply wg0
listen: apply wg0
ansible.builtin.systemd_service:
name: wg-quick@wg0
state: restartedhandler는 play 끝에서, 자신을 알린 작업 뒤에 실행되므로 같은 play의 뒤쪽 작업은 아직 옛 인터페이스를 볼 수 있습니다. 순서가 중요하면 새 설정에 의존하는 작업 전에 meta: flush_handlers로 handler를 명시적으로 비우십시오.
unit의 재로딩 동작을 확인하지 않았다면 reload 대신 restart를 사용하십시오. 업스트림 wg-quick unit은 재로딩을 지원하고 WireGuard 설정을 동기화하지만, 주소·경로·DNS·hook 설정을 다시 실행하지는 않습니다. 따라서 경로 지시를 추가한 뒤의 reload는 성공한 것처럼 보이고 아무것도 바꾸지 않습니다.
실행 중인 장치와 파일을 비교하십시오
이 부류의 버그를 닫는 점검은 커널이 가진 것과 파일이 말하는 것의 비교입니다. 두 명령이 비교 가능한 출력을 내고, 그 차이가 바로 드리프트입니다.
sudo wg showconf wg0
sudo wg-quick strip wg0
sudo diff <(sudo wg showconf wg0) <(sudo wg-quick strip wg0)wg showconf은 개인 키를 포함한 실행 중인 장치를 출력하므로 그 출력을 비밀로 취급하고 티켓에 붙이지 마십시오. wg-quick strip은 wg-quick 전용 키를 제거한 파일을 출력하며, 그래서 둘을 비교할 수 있습니다.
peer 목록의 차이는 파일이 한 번도 적용되지 않았음을 뜻합니다. 바뀔 것이라 예상한 키나 엔드포인트에만 차이가 있다면 무언가가 부분적으로 적용한 것입니다. 차이가 전혀 없다면 설정이 최신이고 장애는 다른 곳에 있으며, 그것 자체로 유용한 결과입니다.
재시작 비용을 의식적으로 치르십시오
인터페이스를 재시작하면 터널이 끊기고, 그 터널이 관리 세션을 실어 나른다면 재시작은 그 세션을 요청한 세션을 끝냅니다. handler를 변경 창 뒤에 두거나, 인터페이스 재시작에 의존하지 않는 관리 경로에서 playbook을 실행하십시오.
그 비용 때문에 사람들이 reload로 손을 뻗습니다. 합리적인 직감이며, reload가 실제로 어떤 변경을 적용하는지 알면 올바른 선택이 됩니다. 업스트림 wg-quick unit에서 peer와 키 변경에는 충분하지만 주소·라우팅·hook에는 충분하지 않습니다. 한 번 정리해 두는 편이 변경 창에서 발견하는 것보다 쌉니다.
systemd_service 모듈 문서는 started, restarted, reloaded의 차이를 설명하며, 초록 요약에서 추론하기보다 한 번 읽어 볼 가치가 있습니다. 이 글의 모든 것은 그 하나의 구분에서 나옵니다.