ansible logo
이 글은 서버 모니터링 텔레그램 자동화를 Ansible·Jinja2·크론으로 구현하는 방법을 정리합니다.
VM과 Docker/Kubernetes 상태를 수집하고 Jinja2로 요약 리포트를 만든 뒤 매일 텔레그램으로 전달하는 전체 흐름을 설치부터 코드 예제, 스케줄링, 보안까지 단계별로 안내합니다.
목차
1. 왜 Ansible + Telegram인가?
2. 아키텍처 개요
3. 사전 준비물
4. 폴더 구조와 설치
5. 인벤토리 & 변수 설정
6. 수집 역할(roles)과 템플릿
7. 메인 플레이북: 수집→전송
8. 스케줄 플레이북: 매일 전송
9. 실행 방법
10. 운영 팁 & 확장 아이디어
왜 Ansible + Telegram인가?
Zero agent : 대상 서버에 별도 에이전트 설치 없이 SSH로 점검
재사용성 : 플레이북/역할(roles)로 팀 표준화 용이
알림의 가벼움 : 텔레그램은 모바일/데스크톱 어디서나 빠르게 확인
비용 효율 : 오픈소스 스택 중심, 운영비용 최소화
아키텍처 개요
컨트롤 노드에서 Ansible 실행
VM, Docker(옵션), Kubernetes(옵션) 상태 수집
Jinja2 템플릿으로 텍스트 리포트 생성
텔레그램 봇을 통해 하루 1회 요약 전송(메시지 길이 초과 시 자동 분할)
사전 준비물
보안 팁: 토큰/Chat ID는 Ansible Vault 에 보관합니다.
폴더 구조와 설치
# ansible-galaxy collection install community.general community.docker kubernetes.core
monitoring/
├─ inventory/
│ └─ hosts.ini
├─ group_vars/
│ └─ all.yml
├─ roles/
│ └─ collect/
│ ├─ tasks/main.yml
│ └─ templates/report.j2
├─ send_telegram.yml
└─ schedule.yml
인벤토리 & 변수 설정
inventory/hosts.ini
[control]
localhost ansible_connection=local
[vms]
vm1 ansible_host=10.0.0.11
vm2 ansible_host=10.0.0.12
[containers_hosts]
vm1
vm2
[k8s_control]
localhost ansible_connection=local
group_vars/all.yml (Vault 권장)
telegram_bot_token: “123456:ABCDEF…” # vault 암호화 권장
telegram_chat_id: “-1001234567890”
monitor_docker: true
monitor_k8s: false
disk_warn_percent: 80
top_n_processes: 5
Vault 암호화: ansible-vault encrypt group_vars/all.yml
수집 역할(roles)과 템플릿
roles/collect/tasks/main.yml
—
– name: 기본 팩트 수집
ansible.builtin.setup:
gather_subset: [“!all”,”hardware”,”network”,”virtual”,”facter”]
– name: 디스크 사용량 수집
ansible.builtin.command: df -hPT –exclude-type=tmpfs –exclude-type=devtmpfs
register: df_out
changed_when: false
– name: 상위 CPU 프로세스
ansible.builtin.shell: ps -eo pid,ppid,cmd,%mem,%cpu –sort=-%cpu | head -n {{ top_n_processes|default(5) + 1 }}
register: topcpu_out
changed_when: false
– name: 상위 MEM 프로세스
ansible.builtin.shell: ps -eo pid,ppid,cmd,%mem,%cpu –sort=-%mem | head -n {{ top_n_processes|default(5) + 1 }}
register: topmem_out
changed_when: false
– name: Docker 컨테이너 정보(옵션)
when: monitor_docker | default(false)
block:
– community.docker.docker_container_info:
gather_network_data: false
register: docker_info
– set_fact:
docker_unstable: “{{ docker_info.containers
| selectattr(‘State.RestartCount’,’defined’)
| selectattr(‘State.RestartCount’,’>=’,3) | list }}”
– name: K8s 파드 상태(옵션, 컨트롤에서 1회)
when: monitor_k8s | default(false)
run_once: true
delegate_to: localhost
kubernetes.core.k8s_info:
kind: Pod
namespace: all
register: k8s_pods
– name: 호스트별 요약 구조
set_fact:
host_report:
ansible_hostname: “{{ ansible_facts.hostname | default(inventory_hostname) }}”
os: “{{ ansible_facts.distribution }} {{ ansible_facts.distribution_version }}”
cpu_count: “{{ ansible_facts.processor_vcpus | default(ansible_facts.processor_cores|default(”)) }}”
mem_mb: “{{ ansible_facts.memtotal_mb }}”
ip: “{{ ansible_facts.default_ipv4.address | default(‘N/A’) }}”
disk_df: “{{ df_out.stdout }}”
topcpu: “{{ topcpu_out.stdout }}”
topmem: “{{ topmem_out.stdout }}”
docker_summary: >-
{% if monitor_docker|default(false) %}
{{ (docker_info.containers | map(attribute=’Name’)) | list }}
{% else %}[]{% endif %}
docker_unstable: “{{ docker_unstable | default([]) }}”
changed_when: false
– name: 모든 호스트 리포트 병합(컨트롤에서 1회)
run_once: true
set_fact:
_all_reports: “{{ hostvars | dict2items
| selectattr(‘value.host_report’,’defined’)
| map(attribute=’value.host_report’) | list }}”
수집 역할(roles)과 템플릿
roles/collect/templates/report.j2
{{ ansible_date_time.date }} {{ ansible_date_time.time }} 시스템/컨테이너 리포트
{% for r in _all_reports %}
🔹 호스트: {{ r.ansible_hostname }} ({{ r.ip }})
– OS: {{ r.os }}
– CPU(vCPU): {{ r.cpu_count }}, 메모리: {{ r.mem_mb }}MB
– 디스크 사용량(df -hPT):
{{ r.disk_df | indent(6) }}
– TOP CPU:
{{ r.topcpu | indent(6) }}
– TOP MEM:
{{ r.topmem | indent(6) }}
{% if r.docker_summary is string and r.docker_summary|length > 0 %}
– Docker 컨테이너: {{ r.docker_summary }}
{% endif %}
{% if r.docker_unstable | length > 0 %}
⚠️ 재시작 빈번: {{ r.docker_unstable | map(attribute=’Name’) | list }}
{% endif %}
{% endfor %}
{% if monitor_k8s %}
=== Kubernetes 요약 ===
파드 개수: {{ (k8s_pods.resources | length) if k8s_pods is defined else 0 }}
{% if k8s_pods is defined %}
상태별:
{%- for phase, items in (k8s_pods.resources | groupby(‘status.phase’)) %}
– {{ phase }}: {{ items | length }}
{%- endfor %}
불안정 파드(재시작>3):
{%- for p in k8s_pods.resources %}
{%- set rc = (p.status.containerStatuses | map(attribute=’restartCount’) | sum if p.status.containerStatuses is defined else 0) -%}
{%- if rc > 3 %} – {{ p.metadata.namespace }}/{{ p.metadata.name }} (restarts={{ rc }})
{%- endif %}{%- endfor %}
{% endif %}
{% endif %}
임계치 안내: 디스크 {{ disk_warn_percent }}% 이상 경고
운영 중 장애 조짐을 빠르게 파악하려면 서버 모니터링 텔레그램 리포트를 기본 채널로 두세요
Docker/K8s 옵션을 켜면 컨테이너 상태까지 서버 모니터링 텔레그램 메시지에 포함됩니다
메인 플레이북: 수집→전송
send_telegram.yml
—
– name: VM/컨테이너 상태 수집 및 텔레그램 전송
hosts: “vms:containers_hosts:k8s_control”
gather_facts: true
roles:
– role: collect
vars:
report_file: “/tmp/ansible_daily_report.txt”
tasks:
– name: 리포트 렌더
ansible.builtin.template:
src: “roles/collect/templates/report.j2”
dest: “{{ report_file }}”
run_once: true
delegate_to: localhost
– name: 리포트 읽기
ansible.builtin.slurp:
src: “{{ report_file }}”
register: report_blob
run_once: true
delegate_to: localhost
– name: 텔레그램 전송(자동 분할)
vars:
report_text: “{{ report_blob.content | b64decode }}”
chunk_size: 3500
run_once: true
delegate_to: localhost
block:
– set_fact:
chunks: “{{ (report_text | regex_replace(‘\\r’,”))
| regex_findall(‘(?s).{1,’ ~ chunk_size ~ ‘}’) }}”
– community.general.telegram:
token: “{{ telegram_bot_token }}”
chat_id: “{{ telegram_chat_id }}”
msg: “{{ item }}”
parse_mode: “Markdown”
disable_web_page_preview: true
loop: “{{ chunks }}”
이 플레이북은 서버 모니터링 텔레그램 알림을 하루 1회 자동 전송합니다
스케줄 플레이북: 매일 전송
schedule.yml
—
– name: 매일 09:00 리포트 전송(Asia/Seoul)
hosts: control
gather_facts: false
vars:
playbook_path: “{{ playbook_dir }}/send_telegram.yml”
inventory_path: “{{ playbook_dir }}/inventory/hosts.ini”
tasks:
– name: ansible-playbook 경로 확인
ansible.builtin.command: which ansible-playbook
register: ap
changed_when: false
– name: 크론 등록
ansible.builtin.cron:
name: “Daily VM/Container Report via Ansible”
minute: “0”
hour: “9”
job: “{{ ap.stdout }} -i {{ inventory_path }} {{ playbook_path }} \
–vault-password-file ~/.vault_pass.txt >> /var/log/ansible-monitor.log 2>&1″
이 플레이북은 서버 모니터링 텔레그램 알림을 하루 1회 자동 전송합니다
실행 방법
# (선택) 시크릿 암호화
ansible-vault encrypt group_vars/all.yml
# 수동 테스트
ansible-playbook -i inventory/hosts.ini send_telegram.yml –ask-vault-pass
# 또는 –vault-password-file ~/.vault_pass.txt
# 스케줄 등록
ansible-playbook -i inventory/hosts.ini schedule.yml –ask-vault-pass
운영 팁 & 확장 아이디어
경보 상단 요약 : 임계 초과 항목만 모아 리포트 최상단에 “⚠️ Alerts” 섹션 추가
서비스 헬스체크 : service_facts로 Nginx/Redis 등 systemd 상태 포함
Docker Health : State.Health.Status가 unhealthy인 컨테이너만 강조
K8s 품질 : CrashLoopBackOff, ImagePullBackOff 파드만 필터링하여 경보
리포트 분리 : 서버가 많다면 “요약 메시지 + 상세 메시지” 2건으로 전송
Slack/메일 병행 : 같은 텍스트를 Slack Webhook 또는 SMTP로 재사용
외부 링크(공식 문서)
Ansible Collections: community.general
Ansible Collections: community.docker
Ansible Collections: kubernetes.core
내부 링크 참고
Ansible 기본 설정 및 패스워드 변경 주기 설정
Ansible을 활용한 Kibana 및 Elasticsearch 설치 및 구성