AWX는 서버 등록뿐 아니라 승인한 운영 작업을 반복 실행하는 데 사용한다. 여기서는 커널 패치, Apache HTTP Server 패치, Tomcat 바이너리 교체를 예로 든다. 기본 대상은 fml-dev-app-01 한 대이며 DEV에서 결과와 복구 방법을 확인한 뒤 STG, PROD 순서로 별도 승인한다. 여러 대를 같은 작업으로 처리할 때에도 serial: 1로 한 대씩 적용하고, 실패하면 다음 대상을 멈춘다.
이 절의 플레이북은 추가 운영 예제다. 현재 실습 VM의 커널·Apache·Tomcat을 이 코드로 변경하거나 재부팅한 실행 결과를 뜻하지 않는다. 커널·Apache의 정확한 목표 RPM은 현재 저장소와 대상에서 확인해 입력해야 한다. 이 예제는 로드밸런서에서 대상을 자동 제외하지 않는다. 커널은 재부팅, Apache·Tomcat은 서비스 중단이 있으므로 중단 시간과 점검 기간을 승인한 후 실행한다.
구축 보완 자료와 전체 운영 플레이북 받기. ZIP의 upgrade-examples/에는 네 플레이북, 변수 예제, RPM manifest 도구, Tomcat 파일 검증 도구, 복구 절차가 있다. 파일을 기존 자동화 Git 저장소의 automation/maintenance/로 복사한다. 기존 automation/files/known_hosts와 inventory/bootstrap.yml을 함께 사용한다. 다운로드 ZIP을 읽는 것만으로 AWX Project나 실행 컨테이너에 파일이 생기지는 않는다.
| 작업 | 고정하는 대상 | 정상 판정 | 실패 시 복구 |
|---|---|---|---|
| 커널 | 정확한 커널 release와 의존 RPM manifest | 재부팅 뒤 uname -r, 기본 서비스, SELinux, 실제 지표 재수집 | 이전 커널 boot entry로 복귀. SSH가 없으면 VirtualBox 콘솔 사용 |
| Apache | Rocky httpd와 모듈/의존 RPM의 정확한 NEVRA | 설치 버전, httpd -t, VirtualHost의 HTTP 200과 기대 응답 | 검토한 이전 RPM과 설정 백업으로 복구 후 같은 요청 재검사 |
| Tomcat | 같은 10.1 계열의 더 높은 patch, 예제 목표 10.1.60 | 외부 base를 사용하는 configtest, 시작, JSP 응답의 실제 목표 버전 | 이전 바이너리 링크로 복귀해 응답 확인. base/DB 변경은 별도 복구 |
Rocky 패키지는 단순히 httpd 2.4처럼 쓰지 않고 NEVRA, 즉 패키지명·Epoch·Version·Release·Architecture를 고정한다. 온라인 준비 VM은 실제 대상과 같은 Rocky 10/x86_64로 만들고, 승인한 BaseOS/AppStream snapshot에서 목표 RPM과 의존 패키지를 다운로드한다. tools/fetch-rpms-online.sh는 검토한 정확 NEVRA 목록을 읽고 dnf download --resolve --alldeps로 수집한다. tools/rpm_manifest.py는 각 파일의 정확한 이름과 SHA256을 기록한다. 준비 VM의 의존 패키지를 이미 설치했다는 이유로 반입 목록에서 빠뜨리지 않는다. DNF 다운로드 기준
온라인망에서도 다운로드를 마친 승인 파일을 고정해서 배포한다. 폐쇄망에서는 같은 RPM 전체, manifest, 서명 키와 검증 기록, Tomcat archive를 조직의 반입 절차로 전달한다. 두 경우 모두 적용 플레이북은 외부 repository를 끄고 로컬 RPM 경로를 사용한다. 누락된 dependency나 잘못된 서명은 중단 사유다. Minimal ISO와 기존 Agent RPM만으로 새 커널·Apache·JDK의 모든 파일이 준비되는 것은 아니다. 새 Apache를 받는 동시에 변경되는 기존 의존 패키지의 이전 RPM도 확보해야 복구를 준비했다고 할 수 있다.
ZIP의 README에서 repository 설정·다운로드·manifest 생성·폐쇄망 반입·복구 명령을 순서대로 확인한다. vars/kernel.yml, vars/httpd.yml의 CHOOSE는 해당 서버에서 확인한 정확한 값으로 바꾼다. 예제는 존재 여부를 확인하지 않은 Rocky RPM 버전 숫자를 임의로 채워 넣지 않는다.
RPM의 SHA256 일치와 실제 서명 존재는 별도로 검사한다. 검증 도구는 rpm --checksig --verbose의 실제 Signature …: OK를 요구하고 unsigned/digest-only, NOKEY, BAD를 거부한다. 적용용 DNF config에 gpgcheck=True, localpkg_gpgcheck=True를 명시하고 dnf task가 그 config를 사용한다. 로컬 RPM의 서명 검사는 기본 설정만 믿지 않는다. DNF 로컬 패키지 서명 설정
실행은 02에서 구성한 Linux 관리 노드 또는 AWX Execution Environment에서 한다. 대상 SSH 키, sudo 방식, Bastion 경로와 승인된 SSH host key가 먼저 준비되어야 한다. vars/change.yml을 만들고 아래처럼 입력한다. 기본값은 사전 점검용이다. 실제 변경은 승인 후 boolean을 true로 바꾼다. 암호와 토큰은 이 파일에 넣지 않는다.
maintenance_targets: [fml-dev-app-01]
change_id: DEV-MAINT-001
maintenance_approved: false
outage_approved: false
cd /opt/fullmoon-lab/automation/maintenance
# 02에서 준비한 archive checksum을 확인한 뒤 Docker에도 EE를 load한다.
sudo docker load -i /opt/fullmoon-lab/offline/images/fullmoon-ee-r2.tar
run_maintenance() {
sudo docker run --rm --pull never --network host --user 0 \
-v /opt/fullmoon-lab/automation:/runner/project:ro,z \
-v /opt/fullmoon-lab/private/automation_ed25519:/root/.ssh/id_ed25519:ro,Z \
-w /runner/project/maintenance \
-e ANSIBLE_CONFIG=/runner/project/ansible.cfg \
-e ANSIBLE_PRIVATE_KEY_FILE=/root/.ssh/id_ed25519 \
localhost/fullmoon-ee:24.6.1-netbox3.23.0-r2 "$@"
}
run_maintenance ansible-inventory -i ../inventory/bootstrap.yml --host fml-dev-app-01
run_maintenance ansible-playbook -i ../inventory/bootstrap.yml preflight.yml \
--limit fml-dev-app-01 -e @vars/change.yml -e change_id=DEV-KERNEL-001 -e maintenance_kind=kernel
maintenance_kind는 kernel, httpd, tomcat 중 작업에 맞게 선택한다. 사전 점검은 대상 이름/IP, OS, SSH, 기본 서비스, 파일 해시를 읽는 단계다. 패키지 설치·dependency 해석·업무 서비스 호환성 검증 결과를 뜻하지 않는다. 적용 플레이북에는 serial: 1, max_fail_percentage: 0, any_errors_fatal: true가 들어 있고, Limit과 승인 대상 목록이 정확히 일치하는지 검사한다.
새 ops01에 native Ansible이 있다고 가정하지 않고 02에서 준비한 EE image를 Docker에서 실행한다. K3s import와 Docker load는 각각 필요하다. 위 함수는 read-only 프로젝트와 개인키를 EE에 연결하고 대상 SSH와 ProxyCommand의 Bastion SSH가 같은 기본 identity를 사용하게 한다. 개인키 내용은 출력하지 않는다. 각 작업은 DEV-KERNEL-001, DEV-HTTPD-002, DEV-TOMCAT-003처럼 고유한 변경 번호를 사용하며, 실제 번호로 바꿔 기록한다.
대상의 Ansible Python에는 python3-rpm 바인딩이 미리 있어야 한다. 온라인 초기 준비 또는 폐쇄망의 승인 RPM·의존 파일 반입 단계에서 구성하고 preflight로 import를 확인한다. 새 kernel-core 파일과 현재 실행 커널의 설치 RPM header를 읽어 Epoch·Version·Release 전체를 rpm.labelCompare로 비교하며, 같은 EVR이나 낮은 EVR이면 중단한다. 커널은 install-only 패키지이므로 allow_downgrade: false만으로 낮은 커널 선택을 막을 수 없다.
vars/kernel.yml에 정확한 kernel_target_release를 입력하고, 변경 변수에 reboot_approved: true, console_recovery_confirmed: true를 추가한다. 이전 실행 커널의 파일과 boot entry를 남기고, /boot 공간과 백업 공간, 외부 드라이버·Secure Boot 서명을 확인한다. 백업과 VirtualBox 콘솔을 준비하지 않은 상태에서 재부팅하지 않는다.
run_maintenance ansible-playbook -i ../inventory/bootstrap.yml kernel-upgrade.yml \
--limit fml-dev-app-01 -e @vars/change.yml -e change_id=DEV-KERNEL-001
플레이북은 기존 커널과 기본 boot entry를 기록하고 /boot를 백업한다. 새 RPM을 설치한 뒤 새 커널과 이전 커널의 boot entry를 검사하고, grubby로 정확한 새 커널을 선택한다. 재부팅과 SSH 재접속 후 uname -r가 목표와 같은지 확인한다. sshd·chronyd·firewalld·zabbix-agent2가 active이며 SELinux가 Enforcing이어야 한다. Ansible reboot 동작
운영자는 이어서 Zabbix Latest data의 system.uptime과 실제 값이 다시 갱신되는지, 업무 앱의 주요 요청이 정상인지 확인한다. SSH가 돌아오지 않으면 뒤의 대상을 중단하고 VirtualBox 콘솔에서 이전 커널로 부팅한다. 이전 실행 버전은 /var/backups/fullmoon-maintenance/변경번호/호스트명/kernel-before.json에 기록되어 있다. 부팅 실패 상태에서 AWX가 자동으로 모든 복구를 수행한다고 가정하지 않는다.
httpd-upgrade.yml은 Rocky RPM으로 설치된 기존 Apache의 패치 변경 예제다. 먼저 rpm -q httpd, 추가 모듈, systemctl cat httpd, httpd -t로 기존 설치를 확인한다. vars/httpd.yml에 현재/목표 release와 실제 VirtualHost의 Host 헤더, health URL, 기대 문자열을 입력한다. 기본 예제는 http://127.0.0.1/healthz.txt가 HTTP 200과 FULLMOON_HTTPD_OK를 반환하는 DEV 서비스를 전제로 한다. 실제 앱의 health endpoint가 있으면 그것을 사용한다.
run_maintenance ansible-playbook -i ../inventory/bootstrap.yml httpd-upgrade.yml \
--limit fml-dev-app-01 -e @vars/change.yml -e change_id=DEV-HTTPD-002
현재 버전과 변경 전의 정상 응답을 확인하고, Apache를 중단한 뒤 /etc/httpd와 /var/www를 백업한다. 다른 프로세스가 쓰는 콘텐츠는 그 쓰기도 별도로 정지한다. 승인 RPM을 설치하고 정확한 설치 NEVRA를 대조한 뒤 httpd -t와 시작 후 health 응답을 확인한다. 추가 DocumentRoot, 환경 파일, 인증서, 외부 DB는 별도 백업 목록에 넣는다. 단순 정적 파일 응답 외에 로그인·DB 연결 같은 주요 업무 요청도 확인한다. Apache 설정 검사 기준
실패하면 서비스를 멈추고 Job을 실패 처리한다. 이전 RPM 목록과 설정 tar를 대조해 승인된 downgrade·설정 복원 절차를 수행하고, 이전 버전의 설정 검사와 같은 요청을 다시 확인한다. 복구용 RPM이 없으면 dnf history undo만으로 되돌아간다고 약속할 수 없다. 파일 복원은 새로 생긴 파일을 자동 삭제하지 않으므로 .rpmnew, .rpmsave와 모듈 파일도 확인한다.
Tomcat 예제 목표는 공식 배포에서 확인한 10.1.60이며, 같은 10.1 계열의 더 높은 패치로만 이동한다. 기존 CATALINA_HOME은 /opt/tomcat/current 링크, CATALINA_BASE는 /var/lib/tomcat/fullmoon 외부 디렉터리로 분리한다. 설정·WAR·JDBC 파일은 base에 있고 바이너리는 버전별 release 디렉터리에 둔다. 기존 설치가 다른 구조라면 먼저 unit·경로와 정상 응답을 검토한다. Tomcat HOME/BASE 분리 구성
공식 SHA512와 OpenPGP 서명을 확인한 archive의 SHA256을 vars/tomcat.yml에 고정한다. JDK 경로와 systemd unit을 확인하고 tomcat_compatibility_approved: true, tomcat_config_reviewed: true를 추가한다. Tomcat 10.1은 Java 11+, Tomcat 11은 Java 17+가 필요하며, Tomcat 9의 javax.* 앱을 10/11의 jakarta.*로 옮기는 작업은 앱 수정과 별도 호환성 검증이 필요하다. 이 예제로 major 전환과 JDK 변경을 함께 처리하지 않는다. Tomcat 버전별 요구 조건, 공식 이행 가이드
DEV 데모의 webapps/ROOT/healthz.jsp에는 다음 응답을 둘 수 있다. 실제 앱에는 앱의 준비 상태와 주요 업무 요청을 별도로 확인한다.
<%@ page contentType="text/plain; charset=UTF-8" %>
FULLMOON_TOMCAT_OK
TOMCAT=<%= application.getServerInfo() %>
JAVA=<%= System.getProperty("java.version") %>
run_maintenance ansible-playbook -i ../inventory/bootstrap.yml tomcat-upgrade.yml \
--limit fml-dev-app-01 -e @vars/change.yml -e change_id=DEV-TOMCAT-003
archive 경로와 해시를 검사한 뒤 새 release 디렉터리에 해제한다. Tomcat을 중단하고 외부 base를 백업한 후 current 링크를 교체한다. 같은 JDK/base로 catalina.sh configtest, 시작, 실제 JSP 응답을 확인하며 목표 Tomcat 버전까지 대조한다. 실패하면 이전 HOME 링크로 복귀해 다시 시작하고 응답을 확인한다. 복귀했어도 변경 Job은 실패로 남긴다. WAR·base 설정·DB schema까지 변경했다면 링크 원복만으로 되돌릴 수 없으므로 해당 데이터 복구 절차가 필요하다.
Resources → Templates → Add → Job Template에서 커널·Apache·Tomcat별 Template을 만들고 기존 Project, FullMoon approved bootstrap Inventory, SSH/become Credential, maintenance/*-upgrade.yml을 연결한다./runner/project/maintenance/artifacts/에서 읽을 수 있게 승인된 산출물 전달 절차를 마련한다. control VM의 디스크에 있는 파일과 execution pod에서 읽는 파일을 혼동하지 않는다. 이 경로가 아직 준비되지 않았다면 CLI로 DEV 한 대를 먼저 시험한다.maintenance/preflight.yml Template을 실행한 뒤 승인한 적용 Job을 실행한다. Workflow는 Project sync → Preflight → Approval → Upgrade → Verify와 앱 확인 순서로 연결할 수 있다. boolean 가드는 권한 분리나 사람의 변경 승인을 대신하지 않는다.AWX Job Template 공식 문서. 단순 Check 모드나 상위 Workflow의 초록색 상태만으로 커널 재부팅과 업무 정상 여부를 확인했다고 기록하지 않는다.
이 체크리스트는 예제를 실행할 때 기록할 항목이며, 현재 실습에서 완료된 결과로 표시하지 않는다.