TL;DR: Cách khắc phục nhanh
Nếu Ansible playbook của bạn bị lỗi vì một lệnh từ xa trả về các ký tự không phải UTF-8, giải pháp nhanh nhất là ép buộc môi trường từ xa sử dụng UTF-8. Bạn cũng có thể chuyển đổi đầu ra bằng iconv trước khi Ansible cố gắng phân tích nó.
- name: Chạy lệnh với thiết lập locale UTF-8 bắt buộc
shell: "/usr/local/bin/legacy_report.sh"
environment:
LC_ALL: "en_US.UTF-8"
LANG: "en_US.UTF-8"
Nếu dữ liệu nguồn thực sự bị hỏng hoặc được mã hóa theo định dạng như Latin-1 (ISO-8859-1), hãy dùng lệnh pipe chuyển qua iconv để làm sạch luồng dữ liệu:
- name: Bắt buộc chuyển đổi sang UTF-8 và bỏ qua lỗi
shell: "cat legacy_inventory.txt | iconv -f ISO-8859-1 -t UTF-8//IGNORE"
register: output
Cơn đau đầu Production lúc 2 giờ sáng
Đó là lúc 2 giờ sáng, và việc triển khai của bạn đang diễn ra suôn sẻ. Đột nhiên, một tác vụ thông thường—có lẽ là đọc một bản log cũ hoặc một đường dẫn tệp Windows—kích hoạt 50 dòng thông báo lỗi Python (traceback). Thay vì một lỗi hữu ích, bạn lại thấy thông báo gây ức chế này:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xA0 in position 142: invalid start byte
Ansible phụ thuộc rất nhiều vào phương thức decode() của Python. Khi nó thu thập stdout hoặc stderr từ một node từ xa, nó mong đợi một luồng UTF-8 sạch. Nếu node trả về một chuỗi byte không khớp—như một ký tự 0xA0 thừa từ một hệ thống Latin-1 cũ—Python sẽ gặp lỗi. Điều này làm dừng toàn bộ quá trình chạy playbook ngay lập tức.
Tại sao điều này xảy ra
Thông thường, vấn đề bắt nguồn từ một trong ba tình huống sau:
- Sai lệch Locale: Shell từ xa sử dụng locale
ChoặcPOSIX. Khi một lệnh xuất ra một dấu trọng âm hoặc một dấu ngoặc kép cong, nó sẽ gửi các byte thô không tuân thủ chuẩn UTF-8. - Dữ liệu nhị phân ẩn: Bạn đang chạy một lệnh như
grephoặccattrên một tệp vô tình chứa dữ liệu nhị phân, chẳng hạn như một bản log nén hoặc một đoạn trích xuất từ cơ sở dữ liệu. - Vấn đề mã hóa trên Windows: WinRM thường giao tiếp bằng
UTF-16hoặc các Code Page Windows cụ thể (như CP1252) xung đột với mong đợi của trình điều khiển Ansible.
Cách khắc phục
1. Chuẩn hóa Locale từ xa
Hầu hết các hệ thống hiện đại đều hỗ trợ UTF-8, nhưng không phải lúc nào chúng cũng sử dụng nó theo mặc định cho các shell không tương tác. Bạn có thể chèn các biến locale chính xác vào tác vụ của mình một cách rõ ràng. Điều này ép buộc tiến trình từ xa giao tiếp bằng ngôn ngữ mà Ansible có thể hiểu.
- name: Thực thi script với môi trường UTF-8
command: /opt/app/check_status.sh
environment:
LANG: "en_US.UTF-8"
LC_ALL: "en_US.UTF-8"
LC_CTYPE: "en_US.UTF-8"
2. Làm sạch đầu ra bằng iconv
Các biến môi trường sẽ không giúp ích nếu bạn đang đọc một tệp đã được lưu ở định dạng mã hóa không tương thích. Trong trường hợp này, bạn phải chuyển mã dữ liệu trực tiếp. Cờ //IGNORE rất quan trọng ở đây; nó sẽ âm thầm loại bỏ bất kỳ byte nào không thể chuyển đổi thay vì để playbook bị lỗi.
- name: Đọc log cũ một cách an toàn
shell: "cat /var/log/old_system.log | iconv -t UTF-8//IGNORE"
register: sanitized_log
3. Sử dụng Base64 cho dữ liệu nhị phân thô
Đôi khi bạn thực sự cần dữ liệu thô, chẳng hạn như chứng chỉ SSL hoặc một khối firmware nhỏ. Đừng cố gắng thu thập chúng dưới dạng văn bản. Thay vào đó, hãy mã hóa chúng sang Base64 trên máy chủ từ xa. Điều này chuyển đổi các byte gây lỗi thành một chuỗi ASCII an toàn mà Ansible có thể vận chuyển dễ dàng.
- name: Thu thập dữ liệu nhị phân dưới dạng Base64
shell: "cat /path/to/binary_file | base64"
register: binary_output
- name: Hiển thị chuỗi an toàn
debug:
msg: "Dữ liệu đã mã hóa: {{ binary_output.stdout }}"
Để kiểm tra nội dung của chuỗi Base64 trong quá trình gỡ lỗi, các công cụ như Base64 Encoder/Decoder rất tiện lợi. Chúng cho phép bạn xác minh xem đầu ra sau khi giải mã có thực sự là những gì bạn mong đợi hay chỉ là dữ liệu rác trước khi bạn cập nhật logic của mình.
Xác minh giải pháp
Đừng đợi chạy toàn bộ playbook mới kiểm tra bản sửa lỗi của bạn. Hãy sử dụng một lệnh ad-hoc nhắm mục tiêu vào máy chủ gặp sự cố để xác minh mã hóa:
# Kiểm tra xem việc ép buộc locale có hoạt động không
ansible webserver -m shell -a "locale; date" -e "ansible_env={'LANG':'en_US.UTF-8'}"
Tiếp theo, hãy kiểm tra loại đầu ra của lệnh cụ thể. Nếu lệnh your_command | file - trả về UTF-8 Unicode text, bạn đã sẵn sàng. Nếu nó vẫn báo cáo ISO-8859 text hoặc data, thì phương pháp iconv là lựa chọn tốt nhất của bạn.
Mẹo cuối cùng
- Kiểm tra
localectl statustrên các node mục tiêu của bạn để xem các giá trị mặc định của hệ thống. - Khi tìm kiếm trong các tệp log, hãy sử dụng
grep --text. Điều này ngăngrepbị lỗi nếu nó gặp phải byte rỗng hoặc ký tự nhị phân. - Nếu bạn thấy các ký tự mã hóa URL lạ trong log, bạn có thể sử dụng URL Encoder/Decoder để xem liệu có các ký tự không phải UTF-8 ẩn nấp trong các tham số của bạn hay không.

