Cách sửa lỗi: 'cannot stop container' - Xử lý lỗi Docker Exit Event

intermediate🐳 Docker2026-06-17| Linux (Ubuntu, Debian, CentOS), Docker Engine 20.10+, Docker Compose

Error Message

Error response from daemon: cannot stop container my-container: tried to kill container, but did not receive an exit event
#docker#container#dừng#kill#tín hiệu#sigterm

Khi Container của bạn bỏ qua lệnh Stop

Ít có điều gì gây khó chịu như một container từ chối dừng lại. Bạn chạy docker stop, đợi 10 giây mặc định, và thay vì thoát sạch sẽ, terminal của bạn bị treo. Sau đó Docker thông báo lỗi 'cannot stop container'. Các lệnh tiêu chuẩn như docker kill hoặc docker-compose down đột nhiên cảm thấy vô dụng trước những tiến trình zombie này. Container của bạn rơi vào trạng thái "ma": nó không hoàn toàn chạy, nhưng cũng không hẳn đã chết.

Thông báo lỗi chính xác

Bạn có thể sẽ thấy đầu ra cụ thể này trong terminal hoặc log của Docker:

Error response from daemon: cannot stop container my-container: tried to kill container, but did not receive an exit event

Tại sao Container bị treo

Docker dừng các container bằng cách gửi tín hiệu SIGTERM đến PID 1. Nếu tiến trình không thoát trong vòng 10 giây, Docker sẽ nâng cấp lên SIGKILL. Lỗi này xảy ra khi ngay cả SIGKILL cũng thất bại hoặc Docker daemon bị mất dấu trạng thái của tiến trình. Điều này thường do một vài điểm nghẽn kỹ thuật sau:

  • Không khớp tín hiệu: Ứng dụng của bạn đang chạy dưới dạng PID 1 nhưng không được lập trình để bắt hoặc chuyển tiếp các tín hiệu Linux.
  • Ngủ không thể ngắt (Trạng thái D): Tiến trình bị kẹt khi chờ I/O, chẳng hạn như thời gian chờ 90 giây trên một ổ đĩa NFS bị treo hoặc SSD bị lỗi.
  • Khóa bảo mật: Các cấu hình AppArmor hoặc SELinux đang chặn daemon gửi tín hiệu dừng cuối cùng.
  • Mất đồng bộ Kernel: Nhân Linux và containerd đã mất trạng thái chia sẻ về tiến trình cụ thể đó.

Các bước khắc phục từng bước

Cách 1: Ép dừng qua dòng lệnh

Đừng vội thực hiện các thay đổi hệ thống phức tạp. Hãy thử tín hiệu mạnh mẽ nhất trước để xem daemon có còn tiếp cận được tiến trình hay không.

docker kill --signal=SIGKILL my-container

Nếu terminal trả về cùng một lỗi 'exit event', Docker daemon đã mất kiểm soát. Chúng ta cần bỏ qua Docker và giao tiếp trực tiếp với hệ điều hành máy chủ.

Cách 2: Dừng tiến trình thông qua PID của máy chủ

Vì các container Docker thực chất chỉ là các tiến trình bị cô lập trên máy chủ Linux của bạn, bạn có thể chấm dứt chúng bằng các công cụ Linux tiêu chuẩn. Đầu tiên, chúng ta cần tìm ID tiến trình (PID) thực sự từ góc nhìn của máy chủ.

  • Trích xuất PID của máy chủ:

docker inspect --format '{{.State.Pid}}' my-container

  
  - Nếu bạn nhận được một con số (ví dụ: 4502), hãy ép dừng nó một cách thủ công:
    ```
sudo kill -9 4502

Nếu PID trả về là 0, điều đó có nghĩa là Docker nghĩ rằng tiến trình đã biến mất, nhưng metadata của container vẫn bị kẹt ở trạng thái 'Running'.

Cách 3: Xóa Containerd Shim

Docker sử dụng containerd-shim để quản lý vòng đời của container. Đôi khi tiến trình chính đã chết, nhưng shim này vẫn tồn tại và làm daemon bối rối.

  • Tìm tiến trình shim liên quan đến container của bạn:

ps aux | grep containerd-shim | grep my-container

  
  - Xác định PID và xóa nó:
    ```
sudo kill -9 <shim_pid>

Cách 4: Xác định các tiến trình ở trạng thái 'D'

Nếu một tiến trình từ chối dừng ngay cả sau lệnh kill -9, có khả năng nó đang ở trạng thái Ngủ không thể ngắt (D). Những tiến trình này đang chờ đợi phần cứng và bỏ qua tất cả các tín hiệu. Bạn có thể phát hiện chúng bằng cách kiểm tra cột trạng thái tiến trình trong ps.

ps -eo pid,stat,comm | grep " D "

Nếu container của bạn xuất hiện ở đây, hãy kiểm tra các mount của bạn. Một ổ chia sẻ NFS bị treo hoặc ổ đĩa có mức iowait 100% thường là thủ phạm. Bạn phải khắc phục tình trạng treo phần cứng hoặc mạng trước khi tiến trình có thể biến mất. Thông thường, khởi động lại máy chủ là cách duy nhất để xóa một tiến trình ở trạng thái D.

Cách 5: Khởi động lại Docker Engine

Khi trạng thái nội bộ của containerd bị hỏng, việc làm mới dịch vụ thường sẽ xóa các sự kiện ma.

sudo systemctl restart docker

Cảnh báo: Hành động này sẽ dừng tất cả các container đang chạy trừ khi bạn đã cấu hình "live-restore": true trong tệp daemon.json của mình.

Cách ngăn chặn tình trạng Container bị treo

1. Sử dụng một tiến trình Init phù hợp

Hầu hết các ứng dụng không được xây dựng để trở thành PID 1. Chúng không xử lý các tín hiệu như một tiến trình khởi tạo hệ thống. Hãy sử dụng cờ --init để bao bọc ứng dụng của bạn trong tini, một binary init nhẹ giúp xử lý việc chuyển tiếp tín hiệu một cách chính xác.

docker run --init my-image

2. Thêm logic đóng ứng dụng an toàn

Đảm bảo ứng dụng của bạn lắng nghe tín hiệu SIGTERM. Nếu bạn đang sử dụng Node.js, ứng dụng của bạn nên trông như thế này:

process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Shutting down gracefully...');
    process.exit(0);
  });
});

3. Điều chỉnh thời gian chờ dừng

Nếu cơ sở dữ liệu của bạn cần 20 giây để dọn dẹp bộ đệm, mặc định 10 giây của Docker sẽ gây ra tình trạng ép dừng. Hãy cho container của bạn thêm thời gian để thoát một cách sạch sẽ.

docker stop --time=30 my-container

Kiểm tra cuối cùng

Xác nhận container đã được giải phóng khỏi bộ nhớ:

docker ps -a | grep my-container

Nếu trạng thái là Exited hoặc container đã biến mất, việc khắc phục đã thành công. Bây giờ bạn có thể xóa phần còn lại một cách an toàn bằng lệnh docker rm my-container và bắt đầu lại từ đầu.

Related Error Notes