Làm cách nào để xác định nguyên nhân rò rỉ bộ nhớ rõ ràng trong ứng dụng web dựa trên Apache / PHP của tôi?


18

Khoảng một lần một tuần, nhưng đôi khi thậm chí một vài lần một ngày sau khi chạy tốt trong nhiều ngày, các trường hợp EC2 của tôi trở nên không phản hồi. Biểu đồ bộ nhớ của Munin kể một câu chuyện khá đơn giản: bộ nhớ được phân bổ cho "ứng dụng" bắt đầu phát triển và không dừng lại cho đến khi trao đổi được sử dụng đầy đủ và ví dụ được đưa xuống đầu gối một cách hiệu quả. Một biểu đồ tùy chỉnh khác cho thấy quá trình phát triển không ngừng là apache2.

Tôi chạy một thiết lập prefork Apache tiêu chuẩn với mod_php và một vài tập lệnh PHP. Như bạn có thể thấy trong biểu đồ bên dưới, một cái gì đó xảy ra sẽ kích hoạt các quá trình apache2 để bắt đầu tiêu thụ nhiều bộ nhớ hơn. Cành xanh đầu tiên tôi bắt kịp và khởi động lại Apache trước khi mọi thứ vượt quá tầm tay. Spike thứ hai đã xa hơn một chút và trường hợp phải được khởi động lại hoàn toàn.

Đồ thị bộ nhớ Munin

Điều tôi đang tự hỏi là làm thế nào để gỡ lỗi này tốt nhất. Thiếu thiết lập PHP với FastCGI và để nó chạy trong các quy trình riêng của mình, cách tốt nhất để tìm hiểu xem đó là Apache hay sự kết hợp giữa PHP và mã của tôi gây ra việc sử dụng bộ nhớ quá mức? Các bạn sẽ làm những bước nào để theo dõi vấn đề này?


CẬP NHẬT: Tôi đã có thể theo dõi rò rỉ sau khi tham gia bước đi, như Matt đề xuất dưới đây.

Sau khi tìm thấy một quá trình apache2 đang tăng dần và liên tục trong bộ nhớ, tôi đã thêm một vài lệnh gọi error_log () vào tập lệnh PHP của mình, in ra tổng số RSS được sử dụng tại các điểm khác nhau trong quá trình thực thi (sử dụng đầu ra của ps). Tuy nhiên, điều đó hóa ra là sai lệch - trong khi có vẻ như RSS chỉ nhảy sau khi tập lệnh của tôi được thực thi, việc gỡ lỗi sau đó cho thấy đó không phải là trường hợp thực sự. Hãy cẩn thận!

May mắn thay, cuối cùng tất cả các cuộc gọi error_log () hóa ra đều hữu ích. Khi tôi kích hoạt strace ( strace -p <pid> -tt -o trace.log -s 256), tôi thấy rằng với mỗi yêu cầu, quy trình đã phân bổ khoảng 400k bộ nhớ (tìm cuộc gọi hệ thống 'brk' và trừ tham số của cuộc gọi đầu tiên từ cuộc gọi cuối cùng - một số thường đến trong một cuộc gọi sau cái khác). Sau đó, tôi đã tìm kiếm cuộc gọi hệ thống 'ghi' gần đây nhất có chứa thông báo error_log () của tôi, thông báo cho tôi biết tại thời điểm nào trong tập lệnh mà bộ nhớ được phân bổ. Với một vài lệnh gọi error_log () được đặt một cách chiến lược hơn để xác định vị trí chính xác hơn, cuối cùng tôi đã tìm ra thủ phạm.

Bộ nhớ đã bị rò rỉ khi chúng tôi gọi curl_exec () từ tập lệnh PHP của chúng tôi. Một số mã curl liên quan đến việc xử lý kết nối SSL đang làm sai điều gì đó - sự rò rỉ đã biến mất khi tôi chuyển sang HTTP. Thay đổi của Curl tham chiếu một vài rò rỉ bộ nhớ SSL đã được sửa trong 7.19.5 (chúng tôi đã ở 7.18.2) vì vậy tôi sẽ thử tiếp theo.

Trong khi đó, tôi đang chạy với MaxRequestsPerChild rất thấp, điều đó giữ cho Apache nằm trong giới hạn hợp lý. Cảm ơn mọi người!


Làm thế nào để số lượng quá trình apache con khác nhau trong cùng một khoảng thời gian?
SimonJ

@SimonJ Simon, câu hỏi tuyệt vời, con số vẫn giữ nguyên khá nhiều, cộng với trừ một vài quy trình. Nó dao động khoảng 60 khi các máy chủ gặp sự cố cũng như khi chúng nghỉ ngơi. Tôi sẽ thiết lập một biểu đồ Munin để chắc chắn 100%.
ondrej

Không phải là một giải pháp, nhưng nếu một trong những ứng dụng được biết là ăn RAM như điên, thì tốt hơn hết là bạn nên trao đổi: khi kernel phát hiện thiếu RAM, nó sẽ giết chết bộ nhớ lớn nhất (apache). Khi bật hoán đổi, kernel sẽ giết một số tiến trình sau đó, vì trao đổi chậm hơn nhiều so với RAM. Không trao đổi - phục hồi nhanh hơn, thời gian chết nhỏ hơn. (Tôi chỉ thử vô hiệu hóa trao đổi trong trường hợp tương tự trên máy có RAM 8GiB, vì vậy YMMW.)
chronos

Câu trả lời:


5

Theo dõi những gì gây ra vấn đề có thể là một cơn đau ở mông. Điều đầu tiên tôi sẽ làm nếu tôi gặp vấn đề như vậy là giảm MaxRequestsPerChildxuống một con số cực kỳ thấp (~ 100-200) và xem điều đó có tạo ra sự khác biệt không. Nếu vậy, có lẽ bạn có mã bị rò rỉ bộ nhớ trong một vòng lặp ở đâu đó và bạn sẽ muốn chạy kiểm toán mã.

Một điều nữa cần xem xét là fullstatus của Apache, xem bạn có thể tìm ra yêu cầu cụ thể nào gây ra rò rỉ bộ nhớ không. Nhận các PID trên các quy trình bị nghi ngờ của bạn và chạy một bước trên chúng.


Cảm ơn Matt. 'ps phụ | grep apache2 'cho tôi biết rằng trong số 60 quy trình đang hoạt động, khoảng một tá đang sử dụng nhiều bộ nhớ hơn mức cần thiết (> 100MB trong RSS). Tôi đã xem kết quả đầu ra của / Proc / <pid> / smaps và thấy rằng mỗi cái có chính xác một ánh xạ ẩn danh chiếm tới 95% + không gian. Bây giờ tôi đang cố gắng tìm ra cái gì và khi được phân bổ khối bộ nhớ khổng lồ này. Tôi sẽ xem xét bước đi - cảm ơn vì tiền boa.
ondrej

2

Thứ sáu @ chính xác là 11 giờ tối? Điều đó có tương ứng với thời gian dự phòng không? Hệ thống của bạn có sẵn I / O để phục vụ các quy trình và sao lưu tại thời điểm đó không? Bạn có xu hướng phần mềm cũng xu hướng # procs hoặc thậm chí bảng điểm apache, làm thế nào về I / O đĩa?

Điều đầu tiên tôi sẽ làm là tính toán mỗi mem mất bao nhiêu mem, sau đó đặt giới hạn hợp lý cho MaxRequests trong apache để $ procmem * $ procs không thể vượt quá ram có sẵn. Tôi nghi ngờ cá thể của bạn cần phải được khởi động lại bởi vì OOM bắt đầu một cuộc săn phù thủy có khả năng (thường) không hiệu quả lắm. Bạn cần đảm bảo hộp của bạn có thể xử lý những khoảng thời gian nặng nề này bằng cách ở trong giới hạn của nó và không đi đến trao đổi và chắc chắn không phải là OOM. Điều này sẽ khó hơn nếu bạn có cronjobs và cực kỳ khó khăn nếu nói cronjobs chạy đơn phương mà không đảm bảo an toàn để chạy (tức là cứ sau 5 phút kịch bản không kiểm tra được liệu 5 phút cuối có còn chạy không).

Bây giờ bạn đã đảm bảo rằng ngay cả khi mọi thứ trở nên tồi tệ, bạn sẽ không cần phải khởi động lại hộp của mình, mọi thứ sẽ bắt đầu tốt hơn rất nhiều cho bạn. Bạn sẽ có thể đăng nhập trong những thời điểm nặng nề này và biết được những gì đang diễn ra bằng cách sử dụng top, dstat, free -m, iuler, v.v.

Phương pháp của Matt có thể đáng để thử, nhưng chỉ nên được sử dụng như một công cụ để khắc phục sự cố, tôi không khuyên bạn nên giữ nguyên như vậy vì nó sẽ khiến vấn đề chung trở nên khó khăn hơn nhiều trong lần tìm kiếm tiếp theo. Điều đó nói rằng, nó sẽ chỉ thực sự trêu chọc các vấn đề với apache / mô-đun và không có gì trong mã của bạn. Tôi nghĩ bạn sẽ đồng ý rằng rất có thể đó không phải là một loại rò rỉ bộ nhớ trong mô-đun apache (giả sử bạn đang sử dụng một bản phân phối có uy tín).


0

Câu hỏi đầu tiên cần đặt ra là ứng dụng chạy qua Apache là gì?

Đây có phải là ứng dụng bạn đã viết hoặc ứng dụng của bên thứ ba không?

Những thành phần / gói khác mà nó tham chiếu?

Bạn có cập nhật về các gói của bạn?

Bất cứ điều gì cụ thể trong các httpd.conftập tin của bạn liên quan đến hiệu suất?


0

Nếu vấn đề của bạn là do ứng dụng PHP gây ra và nếu bạn tự viết phần mềm thì tôi khuyên bạn nên sử dụng một trình lược tả như ví dụ PHP Quick Profiler . Nếu bạn có nhiều giao dịch cơ sở dữ liệu đang diễn ra thì một phần mềm như Kontrollbase có thể giúp bạn tìm ra vấn đề ở đó.


Raffael, cảm ơn. Có, ứng dụng PHP là của tôi và nó không đánh vào bất kỳ cơ sở dữ liệu SQL nào. Tôi sẽ cung cấp cho PHP Quick Profiler một cú đánh và báo cáo lại.
ondrej
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.