Vấn đề
Bạn chạy một Ansible playbook trên một instance Ubuntu hoặc Debian mới, nhưng nó bị lỗi ngay lập tức. Thông thường, lỗi sẽ trông như thế này:
FAILED! => {
"msg": "'/usr/bin/apt-get dist-upgrade' failed: E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarily unavailable)",
"rc": 100
}
Điều gì đang xảy ra?
Trình quản lý gói apt sử dụng các tệp khóa (lock files) như /var/lib/dpkg/lock-frontend để ngăn nhiều tiến trình thay đổi cơ sở dữ liệu cùng lúc. Nếu một công cụ khác đang bận cài đặt phần mềm, Ansible sẽ bị chặn.
Các nguyên nhân phổ biến bao gồm:
- **Cập nhật tự động (Unattended Upgrades):** Các phiên bản Ubuntu hiện đại thường chạy `apt update` tự động trong vòng 60 giây sau khi khởi động.
- **Các tác vụ chồng chéo:** Bạn có thể đang chạy nhiều playbook hoặc số lượng fork cao nhắm vào cùng một node.
- **Tiến trình treo (Zombies):** Một lần thử cài đặt trước đó bị sập, để lại tệp khóa cũ.
Giải pháp 1: Tham số "Kiên nhẫn" (Cách tiếp cận tốt nhất)
Bắt đầu từ Ansible 2.8, module apt tích hợp sẵn cách để chờ khóa. Thay vì báo lỗi, Ansible sẽ đợi trong số giây được chỉ định để tệp khóa biến mất.
- name: Cài đặt Nginx với thời gian chờ khóa (lock timeout)
apt:
name: nginx
state: present
lock_timeout: 300
Điều này yêu cầu Ansible chờ tối đa 5 phút. Đây là giải pháp sạch nhất vì nó xử lý các bản cập nhật chạy ngầm mà không cần logic bổ sung hay các câu lệnh shell phức tạp.
Giải pháp 2: Logic thử lại thủ công
Nếu bạn đang sử dụng phiên bản Ansible cũ hơn hoặc muốn kiểm soát nhiều hơn, hãy sử dụng until. Cách này sẽ thử lại tác vụ 10 lần với khoảng cách 10 giây, giúp các tiến trình ngầm có 100 giây để hoàn tất.
- name: Cài đặt Nginx với cơ chế thử lại
apt:
name: nginx
state: present
update_cache: yes
register: apt_result
until: apt_result is success
retries: 10
delay: 10
Giải pháp 3: Kiểm tra trước khi chạy (Pre-Flight Check)
Đôi khi bạn muốn dọn đường trước khi thực hiện bất kỳ tác vụ gói nào. Bạn có thể thêm một tác vụ ở ngay đầu playbook để đợi cho đến khi tiến trình apt rảnh.
- name: Đợi cho đến khi apt lock được giải phóng
shell: fuser /var/lib/dpkg/lock-frontend
register: lock_check
until: lock_check.rc != 0
retries: 20
delay: 10
failed_when: false
changed_when: false
Trong đoạn mã này, fuser trả về mã thoát khác không khi không có tiến trình nào đang sử dụng tệp. Ansible sẽ lặp lại cho đến khi điều đó xảy ra.
Giải pháp 4: Vô hiệu hóa cập nhật tự động
Trên các trình chạy CI/CD hoặc hạ tầng bất biến (immutable infrastructure), bạn có thể muốn dừng hoàn toàn việc hệ điều hành tự cập nhật. Điều này đảm bảo Ansible là thực thể duy nhất quản lý các gói phần mềm.
- name: Dừng và vô hiệu hóa unattended-upgrades
systemd:
name: unattended-upgrades
state: stopped
enabled: no
- name: Vô hiệu hóa các timer apt hàng ngày
systemd:
name: "{{ item }}"
state: stopped
enabled: no
loop:
- apt-daily.timer
- apt-daily-upgrade.timer
Biện pháp cuối cùng: Dùng vũ lực (Brute Force)
Nếu tệp khóa thực sự bị kẹt—có lẽ do một phiên SSH bị ngắt kết nối—bạn có thể xóa nó thủ công. Cảnh báo: Chỉ sử dụng cách này nếu bạn chắc chắn không có bản cập nhật thực sự nào đang chạy. Việc xóa các tệp này trong khi đang cập nhật có thể làm hỏng cơ sở dữ liệu dpkg của bạn.
- name: Cưỡng ép xóa các tệp khóa cũ
file:
path: "{{ item }}"
state: absent
loop:
- /var/lib/dpkg/lock
- /var/lib/dpkg/lock-frontend
- /var/lib/apt/lists/lock
Cách kiểm tra
Để xem tiến trình nào đang chiếm giữ playbook của bạn, hãy đăng nhập vào máy chủ và chạy:
sudo lsof /var/lib/dpkg/lock-frontend
Nếu nó trả về một PID (Process ID), đó chính là thủ phạm. Nếu không trả về gì, khóa đang trống. Khi chạy Ansible, hãy sử dụng -vvv để xem chính xác số lần các tác vụ của bạn đang thử lại trước khi thành công.

