Ansible을 이용하여 Linux 업데이트 자동화 (7편) - 기존 구성 파일과 새로운 구성 파일 비교에서 이어지는 글이다.
7편에서는 Debian/Ubuntu와 RHEL 계열에서 기존 구성 파일과 새로운 구성 파일을 비교하는 방법을 다루었다.
8편에서는 이 ansible-playbook을 실제 Production 환경에 투입하기 전, 로그 저장 위치와 보존 정책을 정리하고자 한다.
목차
1) 왜 프로젝트 디렉터리보다 /var/log가 운영 환경에 적합한가
앞선 6편에서는 패키지 변경 로그를 남기기 위해 Ansible 프로젝트 디렉터리 아래에 logs/package-changes 디렉터리를 만들었다.
예를 들면 다음과 같은 구조였다.
~/ansible-lab/logs/package-changes/
└── 20260504-112708-erq1akoy/
├── reports/
└── snapshots/
이 방식은 실습 환경이나 개인 테스트 환경에서는 충분히 편리하다. Ansible 프로젝트 안에서 playbook, inventory, scripts, logs를 한눈에 볼 수 있고, 블로그 예제에서도 설명하기 쉽다.
하지만 실제 운영 환경, 즉 Production 서버 관리 목적으로 사용한다면 로그를 프로젝트 디렉터리 안에 두는 것보다 /var/log 아래에 두는 편이 더 적합하다.
가장 큰 이유는 로그의 성격 때문이다.
패키지 업데이트 로그는 단순한 임시 출력물이 아니다. 운영 중인 서버에서 언제 어떤 패키지가 업그레이드되었는지, 새로 설치되었는지, 제거되었는지 확인하기 위한 운영 감사 기록에 가깝다. 특히 커널, OpenSSH, OpenSSL, systemd, MariaDB, Apache, PHP-FPM, Docker 같은 주요 패키지가 변경되었다면, 나중에 장애가 발생했을 때 해당 업데이트가 원인이었는지 추적해야 할 수 있다.
따라서 이런 로그는 개인 홈 디렉터리나 프로젝트 작업 폴더보다, 시스템 로그를 보관하는 표준 위치인 /var/log 아래에 두는 것이 권장된다.
예를 들어 다음과 같이 구성할 수 있다.

/var/log/ansible/controller/update_all.log는 Ansible control node에서 ansible-playbook을 실행했을 때의 실행 로그를 남기는 위치로 사용할 수 있다.
반면 /var/log/ansible/package-changes는 각 실행마다 생성되는 패키지 변경 리포트와 스냅샷을 저장하는 위치로 사용할 수 있다.
또 다른 이유는 계정 의존성을 줄일 수 있다는 점이다. 예를 들어 로그 경로가 다음과 같다고 하자./home/rocky/ansible-lab/logs/package-changes
이 경우 로그는 rocky 사용자의 홈 디렉터리와 프로젝트 경로에 강하게 의존한다. 나중에 Ansible 실행 계정을 다른 사용자로 바꾸거나, 프로젝트 경로를 다른 디렉터리로 이동하면 로그가 남는 위치가 바뀐다.
반면 /var/log/ansible/package-changes는 특정 사용자의 디렉터리에 종속되지 않는다. 이 경로는 Ansible 업데이트 자동화가 남기는 운영 로그라는 의미가 더 분명하다. 사용자 계정이 바뀌어도, playbook 경로가 바뀌어도, 로그 위치는 일정하게 유지할 수 있다.
운영 환경에서는 권한 관리도 중요하다.
패키지 변경 로그에는 비밀번호나 API 토큰 같은 민감정보가 직접 들어가지는 않더라도, 패키지 목록, 커널 버전, 서비스 구성, 호스트 이름 같은 시스템 정보가 포함될 수 있다. 이런 정보는 다른 사용자, 다른 그룹에서 접근하지 못하도록 하는 게 좋다. 패키지 목록, 버전 등은 관리자가 아닌 다른 사용자에게 노출되면 공격 표면이 늘어난다.
따라서 /var/log/ansible 아래에 별도 디렉터리를 만들고, Ansible 실행 계정과 필요한 운영자만 읽을 수 있게 제한하는 것이 좋다.
또한 로그를 /var/log 아래에 두면 보존 정책을 적용하기 쉽다.
운영 로그는 무한정 쌓아둘 수 없다. 패키지 변경 로그도 시간이 지나면 오래된 기록을 정리하는 것이 좋다. 이때 /var/log/ansible/package-changes처럼 위치를 고정해 두면, systemd-tmpfiles 같은 도구를 사용해 일정 기간이 지난 로그 디렉터리를 자동으로 정리할 수 있다.
이처럼 /var/log 아래에 로그 위치를 잡아두면, 단순히 파일을 저장하는 것을 넘어 운영 로그의 위치, 권한, 보존 기간을 일관되게 관리할 수 있다.
정리하면, 프로젝트 디렉터리의 logs/는 실습과 개발에는 편리하다. 하지만 실제 Production 환경에서는 패키지 변경 기록을 /var/log/ansible/package-changes 아래에 두는 편이 더 적합하다.
이유는 다음과 같다.
- 패키지 변경 로그는 운영 감사 기록에 가깝다.
/var/log는 시스템 로그를 보관하기에 자연스러운 위치다.- 특정 사용자 디렉터리에 의존하지 않는다.
- 권한 관리가 명확하다.
- 보존 기간과 자동 정리 정책을 적용하기 쉽다.
- 나중에 중앙 로그 수집이나 백업 정책과 연계하기 쉽다.
2) /var/log/ansible/package-changes 디렉터리 구조
패키지 변경 로그 저장 위치를 기존 프로젝트 디렉터리에서 다음 경로로 변경한다.
/var/log/ansible/package-changes
이를 위해 ~/ansible-lab/playbooks/update_all.yml에서 패키지 변경 로그의 기본 저장 경로를 지정하는 변수도 함께 수정한다.
기존 설정이 다음과 같았다면,
pkg_change_log_base_dir: "{{ playbook_dir }}/../logs/package-changes"
운영 환경에서는 다음과 같이 변경한다.
pkg_change_log_base_dir: /var/log/ansible/package-changes
이후 ansible-playbook playbooks/update_all.yml을 실제로 실행하면, /var/log/ansible/package-changes 아래에 새로운 실행 단위 디렉터리가 생성된다.
예를 들면 다음과 같은 구조다.
/var/log/ansible/package-changes/
└── 20260504-112708-erq1akoy/
├── reports/
│ ├── debian13.txt
│ ├── debian13.json
│ ├── ubuntu2404.txt
│ └── ubuntu2404.json
└── snapshots/
├── debian13.before.tsv
├── debian13.after.tsv
├── ubuntu2404.before.tsv
└── ubuntu2404.after.tsv
가장 바깥의 20260504-112708-erq1akoy는 한 번의 Ansible 실행을 구분하기 위한 디렉터리다. 날짜와 시간, 임의 문자열을 조합해 만들기 때문에 여러 번 실행해도 이전 로그와 섞이지 않는다.
그 아래에는 크게 두 종류의 디렉터리가 있다.
reports/
snapshots/
reports/ 디렉터리에는 사람이 바로 확인하기 쉬운 패키지 변경 결과가 저장된다.
예를 들어 debian13.txt 파일에는 해당 호스트에서 어떤 패키지가 업그레이드되었는지, 새로 설치되었는지, 제거되었는지 정리된다.
.json 파일은 같은 내용을 기계가 처리하기 쉬운 형태로 저장한 것이다. 나중에 다른 스크립트나 모니터링 시스템과 연계할 때 사용할 수 있다.
snapshots/ 디렉터리에는 업데이트 전후의 패키지 목록 원본이 저장된다.
debian13.before.tsv
debian13.after.tsv
*.before.tsv는 업데이트 전에 설치되어 있던 패키지 목록이고, *.after.tsv는 업데이트 후의 패키지 목록이다.
즉, 전체 구조를 역할별로 정리하면 다음과 같다.
package-changes/
└── 실행 단위 디렉터리/
├── reports/ # 최종 패키지 변경 리포트
└── snapshots/ # 업데이트 전후 패키지 목록 원본
이렇게 실행 단위로 디렉터리를 나누면, 나중에 특정 날짜의 업데이트 결과를 확인하기 쉽다.
또한 한 번의 업데이트 실행에서 생성된 리포트와 스냅샷이 같은 디렉터리 안에 묶이기 때문에, 장애 분석이나 변경 이력 확인 시에도 추적하기 편하다.
3) 로그 디렉터리 권한 설정
/var/log/ansible/package-changes 디렉터리를 사용하기 전에 먼저 install 명령으로 로그 저장 디렉터리를 생성하고, 이때 소유자/그룹/권한을 함께 지정한다.
이번 예제에서는 Ansible control node에서 rocky 사용자가 ansible-playbook을 실행한다고 가정한다. 따라서 /var/log/ansible/package-changes 디렉터리의 소유자도 rocky로 지정한다.
sudo install -d -o rocky -g rocky -m 0750 /var/log/ansible
sudo install -d -o rocky -g rocky -m 0750 /var/log/ansible/package-changes
위 명령에서 사용한 옵션의 의미는 다음과 같다.
-o rocky 디렉터리 소유자를 rocky로 지정
-g rocky 디렉터리 그룹을 rocky로 지정
-m 0750 디렉터리 권한을 0750으로 지정
-d 디렉터리를 생성
패키지 변경 로그에는 비밀번호나 API 토큰 같은 민감정보가 직접 저장되지는 않는다. 하지만 서버의 패키지 목록, 패키지 버전, 커널 버전, 호스트 이름 같은 운영 정보가 포함될 수 있다.
이런 정보는 시스템 구성과 보안 상태를 추측하는 데 사용될 수 있고, 공격 표면이 늘어나기에 모든 사용자가 읽을 수 있게 두는 것은 좋지 않다.
따라서 운영 환경에서는 로그 디렉터리를 0755처럼 넓게 열어두기보다, 0750처럼 필요한 사용자와 그룹만 접근할 수 있게 제한하는 편이 좋다.
권한이 제대로 설정되었는지는 다음 명령으로 확인할 수 있다.
ls -ld /var/log/ansible
ls -ld /var/log/ansible/package-changes
예상 출력은 다음과 비슷하다.

4) ansible.cfg의 log_path 설정 여부 및 logrotate
앞에서 설정한 /var/log/ansible/package-changes는 패키지 변경 리포트와 스냅샷을 저장하는 위치다. 즉, 업데이트 전후 패키지 목록을 비교해서 생성한 결과물이 저장된다.
반면 Ansible 자체의 실행 로그를 별도로 남기고 싶다면 ansible.cfg의 log_path 설정을 사용할 수 있다.
예를 들어 다음과 같이 설정할 수 있다.
[defaults]
log_path = /var/log/ansible/controller/update_all.log
display_args_to_stdout = False
이렇게 설정하면 ansible-playbook playbooks/update_all.yml을 실행했을 때 화면에 출력되는 Ansible 실행 로그가 다음 파일에도 함께 기록된다.
/var/log/ansible/controller/update_all.log
이 로그에는 어떤 play와 task가 실행되었는지, 어떤 호스트에서 ok, changed, skipped, failed가 발생했는지 등이 기록된다. 따라서 패키지 변경 리포트와는 성격이 다르다.
log_path는 신중하게 사용해야 한다. Ansible 실행 출력에는 task 이름, 호스트 이름, 패키지 목록, 경로, 모듈 실행 결과 등이 포함될 수 있다. 또한 다른 용도의 playbook에서 비밀번호나 토큰 같은 값을 출력하면, 해당 내용도 로그에 남을 수 있다.
따라서 이 ansible.cfg를 여러 용도로 재사용한다면 log_path를 고정으로 넣는 것은 조심해야 한다. 하지만 해당 Ansible 프로젝트를 패키지 업데이트, 커널 업데이트, 자동 재부팅 용도로만 사용한다면 log_path를 설정해도 괜찮다.
Ansible controller 실행 로그도 함께 남기기 위해서는 다음과 같이 설정한다.
[defaults]
inventory = ./inventory/hosts.yml
host_key_checking = True
retry_files_enabled = False
interpreter_python = auto_silent
timeout = 30
forks = 10
gathering = smart
log_path = /var/log/ansible/controller/update_all.log
display_args_to_stdout = False
[privilege_escalation]
become = False
become_method = sudo
become_ask_pass = False
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
여기서 display_args_to_stdout = False는 task 인자가 불필요하게 자세히 출력되는 것을 줄이기 위한 설정이다. 운영 환경에서는 로그를 남기되, 필요 이상의 정보가 노출되지 않도록 하는 것이 좋다.
또한 log_path로 지정한 파일이 저장될 디렉터리도 미리 준비한다.
sudo install -d -o rocky -g rocky -m 0750 /var/log/ansible/controller
sudo touch /var/log/ansible/controller/update_all.log
sudo chown rocky:rocky /var/log/ansible/controller/update_all.log
sudo chmod 0640 /var/log/ansible/controller/update_all.log
log_path를 사용하면 update_all.log 파일에 실행 로그가 계속 누적된다. 따라서 운영 환경에서는 logrotate를 함께 설정해 로그가 무한정 커지지 않도록 관리하는 것이 좋다.
이 예제에서는 Ansible controller 실행 로그를 약 6개월 정도 보관하도록 설정한다.
sudo vi /etc/logrotate.d/ansible-controller
파일 내용은 다음과 같이 작성한다.
/var/log/ansible/controller/update_all.log {
weekly
rotate 26
missingok
notifempty
compress
delaycompress
dateext
dateformat -%Y%m%d
create 0640 rocky rocky
su rocky rocky
}
각 설정의 의미는 다음과 같다.
weekly 매주 1회 로그 회전
rotate 26 최근 26개 로그 보관, 약 6개월
missingok 로그 파일이 없어도 오류로 처리하지 않음
notifempty 빈 로그 파일은 회전하지 않음
compress 오래된 로그 압축
delaycompress 직전 회전 로그는 한 번 늦게 압축
dateext 회전된 로그 파일 이름에 날짜 추가
create 새 로그 파일의 권한과 소유자 지정
su 지정한 사용자와 그룹 권한으로 logrotate 수행
설정이 올바른지 확인하려면 다음 명령을 사용할 수 있다.
# logrotate dry-run
sudo logrotate -d /etc/logrotate.d/ansible-controller
-d 옵션은 실제로 로그 파일을 회전하거나 압축하지 않고, 어떤 작업이 수행될지 미리 확인하는 디버그 모드다.
# notifempty 설정 때문에 빈 로그 파일은 회전되지 않으므로 테스트 내용을 추가
echo "logrotate test $(date)" >> /var/log/ansible/controller/update_all.log
# logrotate 강제 회전 테스트
sudo logrotate -vf /etc/logrotate.d/ansible-controller
# 확인
ls -l /var/log/ansible/controller
-f는 force 옵션이다. 원래 logrotate는 weekly, daily, 파일 크기 조건 등을 보고 회전 여부를 판단하지만, -f를 사용하면 조건을 기다리지 않고 즉시 회전을 시도한다.
이 명령을 실행하면 update_all.log가 회전되고, 새 update_all.log 파일이 생성되는지 확인할 수 있다.
정상이라면 대략 이런 식으로 보인다.

그리고 지금 설정에 delaycompress가 있기 때문에, 방금 회전된 파일은 바로 .gz로 압축되지 않을 수 있다. 다음 회전 때 압축된다. 그래서 update_all.log-20260508.gz가 바로 안 보여도 정상이다.
log_path는 필수는 아니지만 Ansible 실행 과정을 나중에 확인하고 싶을 때 유용하다. 다만 운영 환경에서는 로그 파일이 계속 커질 수 있으므로, logrotate를 함께 설정해 적절한 기간만 보관하는 것이 좋다.
5) package-changes 보존 정책
/var/log/ansible/package-changes 아래에는 ansible-playbook을 실제로 실행할 때마다 새로운 실행 디렉터리가 생성된다.
예를 들면 다음과 같다.
/var/log/ansible/package-changes/
└── 20260504-112708-erq1akoy/
├── reports/
└── snapshots/
이 구조는 실행 단위로 로그를 확인하기에는 편리하지만, 오래 운영하면 디렉터리가 계속 쌓이게 된다. 따라서 운영 환경에서는 로그 보관 기간을 미리 정해두는 것이 좋다.
이 예제에서는 패키지 변경 로그를 365일 동안 보관하도록 설정한다.
- 보관 대상: /var/log/ansible/package-changes
- 보관 기간: 365일
- 정리 방식: systemd-tmpfiles 사용
패키지 변경 로그는 단순 임시 파일이 아니라, 장애 분석이나 변경 이력 확인에 사용할 수 있는 운영 기록이다. 따라서 너무 짧게 보관하기보다는, 최소 몇 개월에서 1년 정도 보관하는 편이 좋다.
이번 글에서는 다음 기준을 사용한다.
- controller 실행 로그: logrotate로 약 6개월 보관
- package-changes 리포트: systemd-tmpfiles로 약 1년 보관
여기서 controller/update_all.log는 하나의 파일에 계속 누적되는 실행 로그이므로 logrotate로 관리한다.
반면 package-changes는 실행할 때마다 별도의 디렉터리와 파일이 생성되는 구조이므로, 파일 회전보다는 오래된 실행 디렉터리를 정리하는 방식이 더 적합하다.
이를 위해 다음 파일을 생성한다.
sudo vi /etc/tmpfiles.d/ansible-package-changes.conf
내용은 다음과 같이 작성한다.
d /var/log/ansible/package-changes 0750 rocky rocky 365d
이 설정은 /var/log/ansible/package-changes 디렉터리를 rocky:rocky 소유와 0750 권한으로 관리하고, 365일이 지난 항목을 정리 대상으로 지정한다.
설정을 적용하고 확인하려면 다음 명령을 사용할 수 있다.
sudo systemd-tmpfiles --create /etc/tmpfiles.d/ansible-package-changes.conf
sudo systemd-tmpfiles --clean /etc/tmpfiles.d/ansible-package-changes.conf
--create는 설정에 따라 디렉터리를 생성하거나 권한을 맞추는 용도이고, --clean은 보존 기간이 지난 항목을 정리하는 용도다.
여기서 주의할 점은 --clean을 실행한다고 해서 방금 생성된 로그가 바로 삭제되는 것은 아니라는 점이다. 위 설정에서는 365d를 지정했으므로, 보존 기간이 지나지 않은 항목은 정리되지 않는 것이 정상이다.
systemd-tmpfiles 설정은 직접 --clean 명령을 실행할 때만 사용하는 것이 아니다. systemd 기반 배포판에서는 systemd-tmpfiles-clean.timer가 주기적으로 실행되어 age 조건이 지정된 항목을 정리한다. 따라서 위와 같이 365d를 지정해 두면, 보존 기간이 지난 package-changes 하위 항목은 이후 정리 대상이 된다.
자동 정리 타이머 상태는 다음 명령으로 확인할 수 있다.
systemctl status systemd-tmpfiles-clean.timer
타이머가 어떤 설정으로 동작하는지 확인하려면 다음 명령을 사용할 수 있다.
systemctl cat systemd-tmpfiles-clean.timer
정리하면, package-changes는 업데이트 실행 결과를 보관하는 감사성 로그이므로 일정 기간은 남겨두는 것이 좋다.
이번 구성에서는 운영 환경을 고려해 package-changes 리포트를 365일 동안 보관하고, 이후에는 systemd-tmpfiles를 통해 자동 정리되도록 설정한다.
6) controller 실행 로그와 package change report의 역할 구분
이번 글에서는 두 종류의 로그를 다루었다. 하나는 Ansible 자체의 실행 로그이고, 다른 하나는 패키지 변경 리포트다.
• /var/log/ansible/controller/update_all.log
Ansible playbook 실행 로그
• /var/log/ansible/package-changes/
패키지 변경 리포트와 업데이트 전후 스냅샷
controller/update_all.log는 ansible-playbook을 실행했을 때의 전체 흐름을 확인하는 용도다. 어떤 play와 task가 실행되었는지, 어떤 호스트에서 ok, changed, skipped, failed가 발생했는지 확인할 수 있다.
즉, 이 로그는 Ansible 실행 과정 확인용 로그다.
반면 package-changes 아래에 저장되는 리포트는 실제 패키지 변경 내용을 확인하기 위한 로그다. 예를 들어 다음과 같은 정보를 확인할 수 있다.
upgraded=52
installed=1
removed=0
report=/var/log/ansible/package-changes/.../reports/debian13.txt
json=/var/log/ansible/package-changes/.../reports/debian13.json
즉, 이 리포트는 업데이트 전후에 어떤 패키지가 실제로 바뀌었는지 확인하기 위한 감사성 로그다.
두 로그의 역할을 정리하면 다음과 같다.
controller 실행 로그
- Ansible이 어떤 순서로 실행되었는지 확인
- task 성공, 실패, skipped 여부 확인
- playbook 실행 흐름 추적
package change report
- 어떤 패키지가 업그레이드되었는지 확인
- 어떤 패키지가 새로 설치되었는지 확인
- 어떤 패키지가 제거되었는지 확인
- 업데이트 전후 패키지 목록 비교
따라서 장애가 발생했을 때는 먼저 controller/update_all.log에서 Ansible 실행이 정상적으로 끝났는지 확인하고, 그다음 package-changes 리포트에서 실제 변경된 패키지를 확인하면 된다.
정리하면, controller/update_all.log는 실행 과정 로그이고, package-changes는 패키지 변경 결과 로그다.
두 로그를 분리해 두면 Ansible 실행 문제와 패키지 변경 이력을 각각 명확하게 추적할 수 있다.
7) 최종 Ansible 코드 파일 링크
https://drive.google.com/file/d/1nNHWwDZrBA4Zp1WULxTL9CW2ygQ3LIN8
8편의 최종 Ansible 코드이다. 위 링크의 압축 파일에는 ansible.cfg, update_all.yml 등 모든 Ansible 코드가 포함되어 있다.
8) 마무리하며
이번 8편에서는 패키지 변경 로그를 프로젝트 디렉터리에서 /var/log 아래로 옮기고, controller 실행 로그와 package change report의 역할을 분리했다. 또한 logrotate와 systemd-tmpfiles를 이용해 로그가 무한정 쌓이지 않도록 보존 정책도 함께 정리했다.
9편에서는 Linux 재부팅 후 부팅 체크 과정의 안정성을 높이는 방법을 다룰 예정이다.