F5에서 Virtual Server, Pool, Member Status를 확인할 수 있는 Network Map을 제공합니다.
그러나 GUI로 확인만 가능할 뿐 파일로 다운로드 받을 수는 없어서 통계를 내기에는 아쉽습니다.
Ansible을 이용해서 위와 같은 내용을 다운로드 받고 각 서비스들의 상태를 파악할 수 있도록 합니다.
[Ansible-PlayBook]
---
- name: show_vip_pool_member_status
hosts: all
vars_files: # 변수 파일 지정, 미 사용시 인벤토리 호스트 그룹명과 group_vars 파일명이 일치하는 파일 자동매칭 사용
- /etc/ansible/inventory/group_vars/f5.yml
connection: local
gather_facts: no
collections:
- f5networks.f5_modules
tasks:
- name: Collect pool info
bigip_device_info:
gather_subset:
- ltm-pools
provider: "{{ f5_provider }}"
delegate_to: localhost
register: device_pools
- name: Collect virtual-server info
bigip_device_info:
gather_subset:
- virtual-servers
provider: "{{ f5_provider }}"
delegate_to: localhost
register: device_virt
- name: Prepare data for CSV
set_fact:
csv_lines: |
Virtual Server Name,Virtual Server IP,Virtual Server Port,Pool Name,Pool Member Address,Pool Member Port,Member State
{% for virt in device_virt['virtual_servers'] %}
{% if 'default_pool' in virt and virt.default_pool %}
{% set pool_name_parts = virt.default_pool.split('/') %}
{% set pool_name = pool_name_parts[-1] %}
{% set pool = (device_pools['ltm_pools'] | selectattr('name', 'equalto', pool_name) | first) %}
{% if pool %}
{% if 'members' in pool and pool.members %}
{% for member in pool.members %}
{% set member_port_parts = member.name.split(':') %}
{% set member_port = member_port_parts[-1] %}
{{ virt.name }},{{ virt.destination_address }},{{ virt.destination_port }},{{ pool.name }},{{ member.address }},{{ member_port }},{{ member.real_state }}
{% endfor %}
{% else %}
{{ virt.name }},{{ virt.destination_address }},{{ virt.destination_port }},{{ pool.name }},None,None,None
{% endif %}
{% endif %}
{% else %}
{{ virt.name }},{{ virt.destination_address }},{{ virt.destination_port }},None,None,None,None
{% endif %}
{% endfor %}
- name: create directory
file:
path: /etc/ansible/result/f5/
state: directory
- name: Save CSV file
copy:
content: "{{ csv_lines | replace('\n\n', '\n') | trim }}"
dest: "/etc/ansible/result/f5/{{ inventory_hostname}}_show_vip_pool_status.csv"
| Code | Description |
| - name: Collect pool info - name: Prepare data for CSV - name: Collect virtual-server info |
bigip_device_info 모듈을 이용해 Pool, Virtual Server 정보를 수집 후 device_pools, device_virt 변수에 저장합니다. (리스트 형식) |
| - name: Prepare data for CSV set_fact: csv_lines: | |
수집한 정보의 데이터 파싱 및 csv 파일 형태 저장합니다. |
| Virtual Server Name, Virtual Server IP, Virtual Server Port, Pool Name, Pool Member Address, Pool Member Port, Member State |
CSV 형식에서 각 열의 헤더를 지정합니다. |
| {% for virt in device_virt['virtual_servers'] %} {% endfor %} |
device_virt 에 저장된 virtual_servers들을 하나씩 가져와 virt 지역 변수에 저장하여 사용합니다. |
| {% if 'default_pool' in virt and virt.default_pool %} {% endif %} |
'default_pool' in virt : virt에 'default_pool'이라는 키가 존재하는지 확인합니다. virt.default_pool : virt의 default_pool 속성이 True(즉, 값이 존재하고 비어있지 않음)인지 확인합니다. |
| {% set pool_name_parts = virt.default_pool.split('/') %} {% set pool_name = pool_name_parts[-1] %} {% set pool = (device_pools['ltm_pools'] | selectattr('name', 'equalto', pool_name) | first) %} |
virt.default_pool의 값을 / 기호를 기준으로 나눕니다. pool_name_parts 리스트의 마지막 요소를 가져와서 pool_name에 저장합니다. device_pools['ltm_pools'] 리스트에서 name 속성이 pool_name과 같은 첫 번째 요소를 찾습니다. selectattr('name', 'equalto', pool_name)는 리스트에서 각 항목의 name 속성과 pool_name을 비교하여 해당하는 항목들을 선택합니다. 최종적으로 결과를 pool 변수에 저장됩니다. |
| {% if pool %} {% endif %} {% if 'members' in pool and pool.members %} {% endif %} |
pool이 존재하는지 확인합니다. 'members' in pool : pool에 'members'라는 키가 존재하는지 확인합니다. pool.members : pool의 members 속성이 True(즉, 값이 존재하고 비어있지 않음)인지 확인합니다. |
| {% for member in pool.members %} {% set member_port_parts = member.name.split(':') %} {% set member_port = member_port_parts[-1] %} {% endfor %} |
pool.members 로 member들의 정보를 가져와 member 변수에 저장합니다. member.name의 값을 . 기호를 기준으로 나눕니다. member_port_parts 리스트의 마지막 요소를 가져와서 member_port에 저장합니다. |
| {{ virt.name }}, {{ virt.destination_address }}, {{ virt.destination_port }}, {{ pool.name }}, {{ member.address }}, {{ member_port }}, {{ member.real_state }} |
변수에 저장되어 있는 정보를 이용해 CSV 각 열에 맞게 저장합니다. |
| {% else %} {{ virt.name }}, {{ virt.destination_address }}, {{ virt.destination_port }}, {{ pool.name }}, None, None, None |
Pool에 member가 없는 경우 None 상태로 처리합니다. |
| - name: create directory | CSV 파일을 생성할 경로의 디렉토리를 생성합니다. |
| - name: Save CSV file | Virtual Server, Pool 상태 데이터를 저장한 csv_lines를 CSV 파일로 저장합니다. |
아래는 참고용으로 Virtual Server, Pool 조회 시 JSON 형식으로 조회되는 데이터의 정보입니다.
[VIP 객체]
{"virtual_servers":
[
{
"full_path": "/Common/vs_PC_Front_443",
"name": "vs_PC_Front_443",
"auto_lasthop": "default",
"cmp_enabled": "yes",
"connection_limit": 0,
"enabled": "yes",
"translate_port": "yes",
"translate_address": "yes",
"destination": "/Common/xxx.xxx.xxx.84:443",
"nat64_enabled": "no",
"source_port_behavior": "preserve",
"protocol": "tcp",
"default_pool": "/Common/p_PC_Front_8100",
"rate_limit_mode": "object",
"rate_limit_source_mask": 0,
"rate_limit": -1, "gtm_score": 0,
"rate_limit_destination_mask": 0,
"source_address": "0.0.0.0/0",
"connection_mirror_enabled": "no",
"type": "standard",
"profiles":
[
{
"name": "clientssl_multi_SNI_http2",
"context": "client-side",
"full_path": "/Common/clientssl_multi_SNI_http2"
},
{
"name": "http2_100",
"context": "all",
"full_path": "/Common/http2_100"
},
{
"name": "http_X-Forwarded",
"context": "all",
"full_path": "/Common/http_X-Forwarded"
},
{
"name": "tcp_300",
"context": "all",
"full_path": "/Common/tcp_300"
}
],
"destination_address": "xxx.xxx.xxx.84",
"destination_port": 443,
"availability_status": "available",
"status_reason": "The virtual server is available",
"total_requests": 5413601568,
"client_side_bits_in": 139595364699776,
"client_side_bits_out": 2277328896232424,
"client_side_current_connections": 1868,
"client_side_evicted_connections": 0,
"client_side_max_connections": 18502,
"client_side_pkts_in": 47945985529,
"client_side_pkts_out": 58647314986,
"client_side_slow_killed": 0,
"client_side_total_connections": 938109116,
"cmp_mode": "all-cpus",
"ephemeral_bits_in": 0,
"ephemeral_bits_out": 0,
"ephemeral_current_connections": 0,
"ephemeral_evicted_connections": 0,
"ephemeral_max_connections": 0,
"ephemeral_pkts_in": 0,
"ephemeral_pkts_out": 0,
"ephemeral_slow_killed": 0,
"ephemeral_total_connections": 0,
"total_software_accepted_syn_cookies": 0,
"total_hardware_accepted_syn_cookies": 20307,
"total_hardware_syn_cookies": 0,
"hardware_syn_cookie_instances": 0,
"total_software_rejected_syn_cookies": 28,
"software_syn_cookie_instances": 0,
"current_syn_cache": 1,
"syn_cache_overflow": 0,
"total_software_syn_cookies": 0,
"syn_cookies_status": "not-activated",
"max_conn_duration": 4023859019,
"mean_conn_duration": 440698,
"min_conn_duration": 4,
"cpu_usage_ratio_last_5_min": 1,
"cpu_usage_ratio_last_5_sec": 1,
"cpu_usage_ratio_last_1_min": 1
}
]
}
[Pool 객체]
{"ltm_pools":
[
{
"full_path": "/Common/p_PC_Front_8100",
"name": "p_PC_Front_8100",
"allow_nat": "yes",
"allow_snat": "yes",
"ignore_persisted_weight": "no",
"client_ip_tos": "pass-through",
"server_ip_tos": "pass-through",
"client_link_qos": "pass-through",
"server_link_qos": "pass-through",
"lb_method": "round-robin",
"minimum_active_members": 0,
"minimum_up_members": 0,
"minimum_up_members_action": "failover",
"minimum_up_members_checking": "no",
"monitors": ["/Common/HNT_HTTP_Channel_health"],
"queue_depth_limit": 0,
"queue_on_connection_limit": "no",
"queue_time_limit": 0,
"reselect_tries": 0,
"service_down_action": "reset",
"slow_ramp_time": 10,
"priority_group_activation": 0,
"members":
[
{
"name": "xxx.xxx.xxx.31:8100",
"partition": "Common",
"address": "xxx.xxx.xxx.31",
"ephemeral": "no",
"logging": "no",
"ratio": 1,
"connection_limit": 0,
"dynamic_ratio": 1,
"full_path": "/Common/xxx.xxx.xxx.31:8100",
"inherit_profile": "yes",
"priority_group": 0,
"rate_limit": "no",
"fqdn_autopopulate": "no",
"monitors": [],
"real_session": "monitor-enabled",
"real_state": "up",
"state": "present"
},
{
"name": "xxx.xxx.xxx.32:8100",
"partition": "Common",
"address": "xxx.xxx.xxx.32",
"ephemeral": "no",
"logging": "no",
"ratio": 1,
"connection_limit": 0,
"dynamic_ratio": 1,
"full_path": "/Common/xxx.xxx.xxx.32:8100",
"inherit_profile": "yes",
"priority_group": 0,
"rate_limit": "no",
"fqdn_autopopulate": "no",
"monitors": [],
"real_session": "monitor-enabled",
"real_state": "up",
"state": "present"
},
{ 이하 맴버 생략 }
],
"active_member_count": 7,
"available_member_count": 7,
"availability_status": "available",
"enabled_status": "enabled",
"status_reason": "The pool is available",
"all_max_queue_entry_age_ever": 0,
"all_avg_queue_entry_age": 0,
"all_queue_head_entry_age": 0,
"all_max_queue_entry_age_recently": 0,
"all_num_connections_queued_now": 0,
"all_num_connections_serviced": 0,
"pool_max_queue_entry_age_ever": 0,
"pool_avg_queue_entry_age": 0,
"pool_queue_head_entry_age": 0,
"pool_max_queue_entry_age_recently": 0,
"pool_num_connections_queued_now": 0,
"pool_num_connections_serviced": 0,
"current_sessions": 20,
"member_count": 7,
"total_requests": 5433726999,
"server_side_bits_in": 159121658406792,
"server_side_bits_out": 2338484477973656,
"server_side_current_connections": 284,
"server_side_max_connections": 6927,
"server_side_pkts_in": 41460251958,
"server_side_pkts_out": 209621700218,
"server_side_total_connections": 1192157412
}
]
}
'IaC > Ansible' 카테고리의 다른 글
| [Ansible] Ansible, AWS SES 연동으로 이메일 보내기 (0) | 2025.09.25 |
|---|---|
| [Ansible] Ansible에서 현재 날짜, 시간 값 얻기 (2) | 2024.09.03 |
| [Ansible] Ansible 기본 개념 (0) | 2023.04.24 |