리눅스 서버에서 특정 이벤트가 발생했을 때, AWX(Ansible Tower의 오픈소스 버전)를 통해 스크립트를 자동으로 실행하고 결과를 반환받는 방법입니다. AWX 설정, API 활용, 그리고 Python 스크립트를 이용한 구현 예제를 포함합니다.
1. AWX 설정
1.1 프로젝트 생성
AWX에서 스크립트를 실행하려면 먼저 프로젝트를 생성해야 합니다. 프로젝트는 Ansible 플레이북 또는 스크립트 파일을 저장하는 Git 리포지토리와 연결됩니다.
- Git 리포지토리 준비
check_server_status.sh
스크립트를 Git 리포지토리에 업로드합니다.
- AWX에서 프로젝트 생성
- AWX에 로그인한 뒤
Projects
메뉴로 이동합니다. +
버튼을 눌러 새 프로젝트를 생성하고, 프로젝트 이름과 SCM Type(Git)을 설정합니다.- Git 리포지토리 URL을 입력한 후 저장하고 동기화합니다.
- AWX에 로그인한 뒤
1.2 인벤토리 생성
스크립트를 실행할 서버 목록을 정의하는 인벤토리를 생성합니다.
Inventories
메뉴에서+
버튼을 눌러 새 인벤토리를 생성합니다.- 생성된 인벤토리 안에서
Hosts
탭으로 이동하여 대상 서버의 IP 또는 DNS 이름을 추가합니다.
1.3 자격 증명(Credentials) 생성
AWX에서 스크립트 실행 시 서버에 접근하기 위한 자격 증명을 생성합니다.
Credentials
메뉴에서+
버튼을 눌러 새 자격 증명을 생성합니다.- 이름과 자격 증명 유형(Machine)을 설정하고, SSH 키 또는 사용자 이름/비밀번호를 입력합니다.
1.4 작업 템플릿 생성
프로젝트, 인벤토리, 자격 증명을 기반으로 작업 템플릿을 생성합니다.
Templates
메뉴에서+
버튼을 눌러 새 작업 템플릿을 생성합니다.- 템플릿 이름을 설정하고, 작업 유형을
Run
으로 선택합니다. - 생성한 프로젝트와 인벤토리를 연결하고, 스크립트를 선택합니다.
- 자격 증명을 지정한 뒤 저장합니다.
2. AWX API를 통한 스크립트 실행
2.1 API 인증 토큰 생성
AWX API 사용을 위해 인증 토큰을 생성합니다.
- 사용자 프로필로 이동하여
Tokens
탭에서+
버튼을 눌러 새 토큰을 생성합니다. - 생성된 토큰을 복사해 저장합니다.
2.2 API를 사용한 작업 템플릿 실행
AWX의 REST API를 통해 작업 템플릿을 실행합니다.
2.2.1 작업 템플릿 ID 확인
AWX의 Templates
메뉴에서 작업 템플릿 ID를 확인합니다.
2.2.2 API 호출 예제
curl -X POST https://<AWX_URL>/api/v2/job_templates/<TEMPLATE_ID>/launch/ \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{}'
2.2.3 작업 상태 확인
curl -X GET https://<AWX_URL>/api/v2/jobs/<JOB_ID>/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
3. 이벤트 기반 자동화
이벤트 발생 시 자동으로 스크립트를 실행하려면, 이벤트 감지 시스템에서 AWX API를 호출하도록 구성합니다.
3.1 Shell 스크립트를 사용한 API 호출
#!/bin/bash
EVENT_OCCURRED=true
if [ "$EVENT_OCCURRED" = true ]; then
curl -X POST https://<AWX_URL>/api/v2/job_templates/<TEMPLATE_ID>/launch/ \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{}'
fi
4. Python 스크립트를 통한 구현 예제
Python을 활용하여 이벤트 기반으로 AWX 작업 템플릿을 실행하고 결과를 확인하는 스크립트를 작성할 수 있습니다.
4.1 Python 스크립트 예제
import requests
import time
import os
import logging
import argparse
# 로깅 설정
logging.basicConfig(
filename="awx_job.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
def launch_awx_job(targets):
"""
AWX 작업 템플릿 실행
:param targets: 대상 서버 목록 (IP 또는 호스트 이름 리스트)
:return: 실행된 작업 ID 리스트
"""
awx_url = os.getenv("AWX_URL")
awx_token = os.getenv("AWX_TOKEN")
template_id = os.getenv("AWX_JOB_TEMPLATE_ID")
if not all([awx_url, awx_token, template_id]):
logging.error("환경 변수를 확인하세요. AWX_URL, AWX_TOKEN, AWX_JOB_TEMPLATE_ID가 필요합니다.")
return []
headers = {
"Authorization": f"Bearer {awx_token}",
"Content-Type": "application/json"
}
job_ids = []
for target in targets:
payload = {"limit": target}
try:
response = requests.post(f"{awx_url}/api/v2/job_templates/{template_id}/launch/",
headers=headers, json=payload)
response.raise_for_status()
job_id = response.json().get("id")
job_ids.append(job_id)
logging.info(f"Job launched for target {target}: Job ID {job_id}")
except requests.exceptions.RequestException as e:
logging.error(f"Failed to launch job for target {target}: {e}")
return job_ids
def monitor_jobs(job_ids):
"""
AWX 작업 상태 모니터링
:param job_ids: 실행된 작업 ID 리스트
"""
awx_url = os.getenv("AWX_URL")
awx_token = os.getenv("AWX_TOKEN")
headers = {"Authorization": f"Bearer {awx_token}"}
for job_id in job_ids:
while True:
try:
response = requests.get(f"{awx_url}/api/v2/jobs/{job_id}/", headers=headers)
response.raise_for_status()
status = response.json().get("status")
if status in ["successful", "failed"]:
logging.info(f"Job {job_id} completed with status: {status}")
break
else:
logging.info(f"Job {job_id} status: {status}")
except requests.exceptions.RequestException as e:
logging.error(f"Failed to get status for job {job_id}: {e}")
time.sleep(5)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="AWX 작업 실행 스크립트")
parser.add_argument("targets", nargs="+", help="작업을 실행할 대상 서버들의 IP 또는 호스트 이름")
args = parser.parse_args()
jobs = launch_awx_job(args.targets)
if jobs:
monitor_jobs(jobs)
4.2 환경 변수 설정
export AWX_TOKEN="your_awx_token"
export AWX_URL="http://your_awx_url"
export AWX_JOB_TEMPLATE_ID="123"
4.3 실행 방법
- 스크립트를 실행하려면 Python이 설치되어 있어야 합니다.
- 필요한 패키지를 설치합니다.
pip install requests
- 대상 서버를 명령줄 옵션으로 전달하여 스크립트를 실행합니다.
python3 awx_job_launcher.py 192.168.1.100 192.168.1.101
4.4 코드 설명 요약
- launch_awx_job
- 대상 서버 리스트를 입력받아 AWX 작업 템플릿을 실행합니다.
- 실행된 작업의 ID를 반환합니다.
- 작업 실패 시 로그에 에러 기록.
- monitor_jobs
- 실행된 작업 ID 리스트를 입력받아 각 작업의 상태를 모니터링합니다.
- 작업이 완료되면 상태를 로그에 기록합니다.
- 명령줄 옵션
- 대상 서버를 하드코딩 대신 명령줄 인자로 입력받아 유연성을 높였습니다.
- 로깅
- 모든 작업 로그는
awx_job.log
파일에 저장됩니다.
- 모든 작업 로그는
AWX를 활용하면 다수의 서버에서 스크립트를 병렬로 실행하고, 상태를 자동으로 확인할 수 있습니다. 이 스크립트와 같이 다수의 서버를 효율적으로 관리할 수 있습니다. 필요에 따라 추가적인 요구사항에 맞게 확장하여 사용해 보세요.
5. n8n을 활용한 자동화
5.1 n8n 구성 단계
1. 기본 워크플로우 설계
- 이벤트 발생을 트리거로 AWX API를 호출하고, 작업 상태에 따라 알림 또는 후속 작업을 수행하는 워크플로우를 설계합니다.
2. n8n에서 워크플로우 구성
- Start 노드
- 이벤트 발생 시 워크플로우를 시작합니다.
- HTTP Request (작업 실행)
- Method:
POST
- URL:
https://sample-awx-url.com/api/v2/workflow_job_templates/123/launch/
- Headers:
{ "Authorization": "Bearer {{ $env.AWX_TOKEN }}", "Content-Type": "application/json" }
- Body:
{ "extra_vars": { "host_vars": "your_target_server" }, "ask_variables_on_launch": true }
- Method:
- Set 노드 (상태 URL 저장)
- JSONPath:
{{ $json['url'] }}
- 저장:
status_url
- JSONPath:
- HTTP Request (작업 상태 확인)
- Method:
GET
- URL:
{{ $json['status_url'] }}
- Headers:
{ "Authorization": "Bearer {{ $env.AWX_TOKEN }}" }
- Method:
- Switch 노드 (작업 상태 분기)
- 조건:
- successful: 성공 시 알림.
- failed: 실패 시 알림.
- canceled: 취소 시 알림.
- 조건:
- Slack/E-Mail 노드
- 각 상태에 따라 Slack 또는 Email 알림을 설정.
5.2 n8n JSON 워크플로우 코드
위 내용을 종합한 JSON 코드는 다음과 같습니다.
{
"nodes": [
{
"parameters": {},
"id": "1",
"name": "Start",
"type": "n8n-nodes-base.start",
"position": [
250,
300
]
},
{
"parameters": {
"url": "https://sample-awx-url.com/api/v2/workflow_job_templates/123/launch/",
"method": "POST",
"headers": {
"Authorization": "Bearer {{ $env.AWX_TOKEN }}",
"Content-Type": "application/json"
},
"bodyParametersJson": "{\n \"extra_vars\": {\n \"host_vars\": \"your_target_server\"\n },\n \"ask_variables_on_launch\": true\n}"
},
"id": "2",
"name": "Launch AWX Job",
"type": "n8n-nodes-base.httpRequest",
"position": [
500,
300
]
},
{
"parameters": {
"values": {
"string": [
{
"name": "status_url",
"value": "{{ $json[\"url\"] }}"
}
]
}
},
"id": "3",
"name": "Set Status URL",
"type": "n8n-nodes-base.set",
"position": [
750,
300
]
},
{
"parameters": {
"url": "{{ $json[\"status_url\"] }}",
"method": "GET",
"headers": {
"Authorization": "Bearer {{ $env.AWX_TOKEN }}"
}
},
"id": "4",
"name": "Check AWX Job Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
1000,
300
]
},
{
"parameters": {
"value": "{{ $json[\"status\"] }}",
"rules": [
{
"name": "successful",
"value": "successful"
},
{
"name": "failed",
"value": "failed"
},
{
"name": "canceled",
"value": "canceled"
}
]
},
"id": "5",
"name": "Switch Job Status",
"type": "n8n-nodes-base.switch",
"position": [
1250,
300
]
}
],
"connections": {}
}
Python vs n8n 비교
항목 | Python | n8n |
---|---|---|
유지보수 | 코드 작성 및 디버깅 필요 | GUI 기반으로 유지보수 쉬움 |
확장성 | 추가 서비스 연동 시 코드 확장 필요 | 다양한 노드로 서비스 쉽게 연동 가능 |
시각화 | 코드 기반으로 흐름 파악이 어려움 | 시각적인 워크플로우로 이해 용이 |
재사용성 | 스크립트 재사용 가능 | 템플릿을 통해 손쉽게 복제 및 배포 |
Python은 복잡한 커스터마이징이 필요한 경우 강력한 도구이며, n8n은 시각적 워크플로우와 서비스 통합에 적합합니다. 서버 이벤트 기반 자동화를 위해 AWX와 n8n을 조합하면 효율적이고 확장 가능한 시스템을 구축할 수 있습니다.
댓글