Lỗi gặp phải
Bạn chạy một Ansible playbook trên host RHEL hoặc CentOS và ngay task đầu tiên đã thất bại:
fatal: [web01]: FAILED! => {
"msg": "Aborting, target uses selinux but python bindings (libselinux-python) are not installed!"
}
Không có gì chạy được cả. Ansible phát hiện SELinux trên host từ xa, cố gắng tải Python bindings của nó, không tìm thấy gì, rồi dừng lại. Nó sẽ không tiếp tục khi chưa chắc chắn — nguy cơ làm hỏng file context quá cao.
Tại sao lỗi này xảy ra
Ansible dựa vào Python để giao tiếp với hệ điều hành từ xa. Nhiều module — file, copy, template, quản lý package — đều âm thầm kiểm tra SELinux context trước khi thực hiện công việc. Việc kiểm tra đó yêu cầu libselinux-python (Python 2) hoặc python3-libselinux (Python 3) trên máy đích, không phải trên control node của bạn.
Có bốn tình huống thường gặp gây ra lỗi này:
- Cài đặt RHEL/CentOS minimal mới — SELinux bindings không được cài mặc định, dù SELinux đang được bật
- Chuyển từ CentOS 7 lên CentOS 8/RHEL 8 — tên package đã đổi, và các playbook cũ của bạn chưa biết điều đó
- Docker hoặc cloud image tối giản đã cắt bỏ mọi thứ không thiết yếu
- Thiết lập tường minh
ansible_python_interpreter=/usr/bin/python3trên host chỉ cài phiên bản Python 2 của bindings
Cách sửa 1: Cài package đúng trên máy đích
SSH vào máy và cài theo phiên bản hệ điều hành. Tên package đã thay đổi giữa RHEL 7 và RHEL 8.
RHEL/CentOS 7 — Python 2
sudo yum install -y libselinux-python
RHEL/CentOS 8 / Rocky Linux 8 / AlmaLinux 8 — Python 3
sudo dnf install -y python3-libselinux
RHEL/CentOS 9 / Rocky Linux 9 / AlmaLinux 9
sudo dnf install -y python3-libselinux
Không chắc Ansible đang dùng phiên bản Python nào? Chạy lệnh này từ control node:
ansible all -i inventory.ini -m setup -a 'filter=ansible_python_version'
Cách sửa 2: Thêm pre-task vào playbook (phù hợp cho nhóm)
SSH thủ công vào 20 host là việc rất mất thời gian. Hãy để Ansible tự xử lý việc cài đặt thông qua pre-task:
---
- name: Deploy web application
hosts: all
become: yes
pre_tasks:
- name: Ensure SELinux Python bindings are installed (RHEL/CentOS 7)
yum:
name: libselinux-python
state: present
when:
- ansible_os_family == "RedHat"
- ansible_distribution_major_version | int == 7
- name: Ensure SELinux Python bindings are installed (RHEL/CentOS 8+)
dnf:
name: python3-libselinux
state: present
when:
- ansible_os_family == "RedHat"
- ansible_distribution_major_version | int >= 8
tasks:
- name: Your actual tasks here
...
Có một vấn đề cần lưu ý. Đây là tình huống con gà và quả trứng kinh điển: Ansible cần bindings để chạy, nhưng bạn lại đang cài chúng thông qua Ansible. Thực tế thường vẫn hoạt động được vì module yum/dnf không phải lúc nào cũng kích hoạt kiểm tra SELinux trước khi quá trình cài đặt hoàn tất. Nếu vẫn thất bại trong pre-task, hãy chuyển sang Cách sửa 3.
Cách sửa 3: Dùng raw để bootstrap package
Module raw bỏ qua Python hoàn toàn — nó chạy lệnh SSH thuần túy. Không Python, không kiểm tra SELinux, không vấn đề con gà và quả trứng.
---
- name: Bootstrap SELinux bindings before anything else
hosts: all
become: yes
gather_facts: no # bắt buộc: thu thập facts sẽ kích hoạt kiểm tra SELinux
tasks:
- name: Install libselinux Python bindings (raw, no Python required)
raw: |
if command -v dnf &>/dev/null; then
dnf install -y python3-libselinux
else
yum install -y libselinux-python
fi
- name: Main playbook
hosts: all
become: yes
tasks:
- name: Your actual tasks
...
gather_facts: no là bắt buộc ở đây. Thu thập facts chính xác là thứ kích hoạt kiểm tra SELinux binding — hãy bỏ qua bước này cho đến khi play bootstrap đã chạy xong.
Cách sửa 4: Tắt kiểm tra SELinux (không dùng cho production)
Cần chạy được ngay trong 5 phút tới và sẽ xử lý đúng cách sau? Bạn có thể bảo Ansible bỏ qua hoàn toàn việc kiểm tra SELinux.
Trong inventory:
# inventory.ini
[webservers]
web01 ansible_selinux_enabled=false
Hoặc dưới dạng biến playbook:
vars:
ansible_selinux_enabled: false
Khi thiết lập này, Ansible sẽ không quản lý SELinux file context nào cả. Trên hệ thống permissive thì tạm thời có thể chấp nhận được. Trên hệ thống enforcing, bạn đang tự rước rủi ro bị cấu hình sai âm thầm — file bị gán nhãn sai, service thất bại theo những cách khó hiểu. Đừng để thiết lập này tồn tại lâu dài.
Xử lý xung đột Python 2 và Python 3
Trên CentOS 8+, bạn có thể cài cả hai package mà vẫn thấy lỗi. Điều này xảy ra khi Ansible đang chạy Python 3 nhưng chỉ có package Python 2 (libselinux-python) được cài — chúng không thể thay thế nhau.
Kiểm tra xem Ansible đang thực sự dùng interpreter nào:
ansible web01 -i inventory.ini -m debug -a 'var=ansible_python_interpreter'
/usr/bin/python → cài libselinux-python. /usr/bin/python3 → cài python3-libselinux. Quy tắc đơn giản vậy thôi.
Để không phải đoán mò, hãy chỉ định tường minh interpreter trong inventory:
# RHEL 8+
[webservers]
web01 ansible_python_interpreter=/usr/bin/python3
# RHEL 7
web01 ansible_python_interpreter=/usr/bin/python
Xác nhận lỗi đã được sửa
Ba kiểm tra nhanh để xác nhận mọi thứ hoạt động trước khi chạy lại playbook.
- Xác nhận package đã được cài trên máy đích:
# RHEL 7
rpm -q libselinux-python
# RHEL 8+
rpm -q python3-libselinux
- Ping để xác minh Ansible có thể kết nối và thu thập facts bình thường:
ansible web01 -i inventory.ini -m ping
- Lấy SELinux facts để xác nhận Ansible nhận diện được chúng:
ansible web01 -i inventory.ini -m setup -a 'filter=ansible_selinux'
Kết quả bình thường trông như thế này:
"ansible_selinux": {
"config_mode": "enforcing",
"mode": "enforcing",
"policyvers": 33,
"status": "enabled",
"type": "targeted"
}
Cả ba đều qua? Chạy lại playbook gốc của bạn. Lỗi sẽ không xuất hiện nữa.
Phòng ngừa: đưa vào base role
Quản lý một hạ tầng 50+ host RHEL đồng nghĩa với việc lỗi này sẽ xuất hiện trên mỗi máy mới bạn provision. Giải pháp gọn gàng: thêm SELinux bindings vào base server role để chúng luôn có mặt trước khi bất kỳ role nào khác chạy.
# roles/base/tasks/main.yml
- name: Install SELinux Python bindings
package:
name: "{{ 'python3-libselinux' if ansible_distribution_major_version | int >= 8 else 'libselinux-python' }}"
state: present
when: ansible_os_family == "RedHat"
Module package (thay vì yum hay dnf) giúp đoạn code này tương thích về lâu dài — nó tự động chọn đúng package manager. Biểu thức ternary xử lý sự khác biệt tên giữa Python 2 và Python 3 dựa trên phiên bản hệ điều hành. Một task duy nhất, hoạt động trên toàn bộ hạ tầng RHEL của bạn.

