Tại sao các ứng dụng trong bộ chứa LXC bị giới hạn bộ nhớ ghi các tệp lớn vào đĩa bị OOM giết?


10

EDIT2: Vấn đề này dường như tồn tại dưới 3,8.0-25 chung # 37-Ubuntu SMP

EDIT: Tôi đã sửa đổi câu hỏi từ tiêu đề ban đầu của "Tại sao trình quản lý bộ nhớ Linux sẽ được kích hoạt bằng cách ghi vào một tệp có dd?" để phản ánh tốt hơn rằng tôi lo lắng về vấn đề chung được mô tả dưới đây:

Tôi đang gặp phải một kịch bản rắc rối trong đó kẻ giết người OOM đang xử lý các quy trình khó trong bộ chứa LXC của tôi khi tôi viết một tệp có kích thước vượt quá giới hạn bộ nhớ (được đặt thành 300MB). Sự cố không xảy ra khi tôi chạy ứng dụng trên máy ảo Xen (EC2 t1.micro) mà thực sự chỉ có 512 MB RAM, do đó dường như có một số vấn đề với bộ đệm đệm tuân theo giới hạn bộ nhớ của bộ chứa.

Một ví dụ đơn giản, tôi có thể chứng minh làm thế nào một tệp lớn được viết bởi dd sẽ gây ra vấn đề. Một lần nữa, vấn đề này làm khổ tất cả các ứng dụng. Tôi đang tìm cách giải quyết vấn đề chung về bộ nhớ cache của ứng dụng trở nên quá lớn; Tôi hiểu làm thế nào tôi có thể làm cho "dd" hoạt động.

Kịch bản:

Tôi có một thùng chứa LXC trong đó memory.limit_in_bytes được đặt thành 300 MB.

Tôi cố gắng dd một tệp ~ 500 MB như sau:

dd if=/dev/zero of=test2 bs=100k count=5010

Khoảng 20% ​​thời gian, trình quản lý OOM của Linux được kích hoạt bởi lệnh này và một quy trình bị hủy. Không cần phải nói, đây là hành vi rất ngoài ý muốn; dd có nghĩa là mô phỏng một tập tin "hữu ích" thực sự được ghi bởi một chương trình chạy bên trong container.

Chi tiết: Mặc dù bộ đệm tệp có dung lượng lớn (260 MB), rss và bản đồ tệp dường như vẫn ở mức khá thấp. Đây là một ví dụ về bộ nhớ. Nó có thể trông như thế nào trong khi viết:

cache 278667264
rss 20971520
mapped_file 24576
pgpgin 138147
pgpgout 64993
swap 0
pgfault 55054
pgmajfault 2
inactive_anon 10637312
active_anon 10342400
inactive_file 278339584
active_file 319488
unevictable 0
hierarchical_memory_limit 300003328
hierarchical_memsw_limit 300003328
total_cache 278667264
total_rss 20971520
total_mapped_file 24576
total_pgpgin 138147
total_pgpgout 64993
total_swap 0
total_pgfault 55054
total_pgmajfault 2
total_inactive_anon 10637312
total_active_anon 10342400
total_inactive_file 278339584
total_active_file 319488
total_unevictable 0

Đây là một dán từ dmesg nơi OOM kích hoạt một kill. Tôi không quá quen thuộc với sự khác biệt giữa các loại bộ nhớ; một điều nổi bật là trong khi "Nút 0 Bình thường" rất thấp, thì có rất nhiều bộ nhớ Node 0 DMA32. Bất cứ ai có thể giải thích tại sao một tập tin ghi gây ra OOM? Làm thế nào để tôi ngăn chặn điều này xảy ra?

Nhật ký:

[1801523.686755] Task in /lxc/c-7 killed as a result of limit of /lxc/c-7
[1801523.686758] memory: usage 292972kB, limit 292972kB, failcnt 39580
[1801523.686760] memory+swap: usage 292972kB, limit 292972kB, failcnt 0
[1801523.686762] Mem-Info:
[1801523.686764] Node 0 DMA per-cpu:
[1801523.686767] CPU    0: hi:    0, btch:   1 usd:   0
[1801523.686769] CPU    1: hi:    0, btch:   1 usd:   0
[1801523.686771] CPU    2: hi:    0, btch:   1 usd:   0
[1801523.686773] CPU    3: hi:    0, btch:   1 usd:   0
[1801523.686775] CPU    4: hi:    0, btch:   1 usd:   0
[1801523.686778] CPU    5: hi:    0, btch:   1 usd:   0
[1801523.686780] CPU    6: hi:    0, btch:   1 usd:   0
[1801523.686782] CPU    7: hi:    0, btch:   1 usd:   0
[1801523.686783] Node 0 DMA32 per-cpu:
[1801523.686786] CPU    0: hi:  186, btch:  31 usd: 158
[1801523.686788] CPU    1: hi:  186, btch:  31 usd: 114
[1801523.686790] CPU    2: hi:  186, btch:  31 usd: 133
[1801523.686792] CPU    3: hi:  186, btch:  31 usd:  69
[1801523.686794] CPU    4: hi:  186, btch:  31 usd:  70
[1801523.686796] CPU    5: hi:  186, btch:  31 usd: 131
[1801523.686798] CPU    6: hi:  186, btch:  31 usd: 169
[1801523.686800] CPU    7: hi:  186, btch:  31 usd:  30
[1801523.686802] Node 0 Normal per-cpu:
[1801523.686804] CPU    0: hi:  186, btch:  31 usd: 162
[1801523.686806] CPU    1: hi:  186, btch:  31 usd: 184
[1801523.686809] CPU    2: hi:  186, btch:  31 usd:  99
[1801523.686811] CPU    3: hi:  186, btch:  31 usd:  82
[1801523.686813] CPU    4: hi:  186, btch:  31 usd:  90
[1801523.686815] CPU    5: hi:  186, btch:  31 usd:  99
[1801523.686817] CPU    6: hi:  186, btch:  31 usd: 157
[1801523.686819] CPU    7: hi:  186, btch:  31 usd: 138
[1801523.686824] active_anon:60439 inactive_anon:28841 isolated_anon:0
[1801523.686825]  active_file:110417 inactive_file:907078 isolated_file:64
[1801523.686827]  unevictable:0 dirty:164722 writeback:1652 unstable:0
[1801523.686828]  free:445909 slab_reclaimable:176594
slab_unreclaimable:14754
[1801523.686829]  mapped:4753 shmem:66 pagetables:3600 bounce:0
[1801523.686831] Node 0 DMA free:7904kB min:8kB low:8kB high:12kB
active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB
unevictable:0kB isolated(anon):0kB isolated(file):0kB present:7648kB
mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB
slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB
unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:0
all_unreclaimable? no
[1801523.686841] lowmem_reserve[]: 0 4016 7048 7048
[1801523.686845] Node 0 DMA32 free:1770072kB min:6116kB low:7644kB
high:9172kB active_anon:22312kB inactive_anon:12128kB active_file:4988kB
inactive_file:2190136kB unevictable:0kB isolated(anon):0kB
isolated(file):256kB present:4112640kB mlocked:0kB dirty:535072kB
writeback:6452kB mapped:4kB shmem:4kB slab_reclaimable:72888kB
slab_unreclaimable:1100kB kernel_stack:120kB pagetables:832kB unstable:0kB
bounce:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
[1801523.686855] lowmem_reserve[]: 0 0 3031 3031
[1801523.686859] Node 0 Normal free:5660kB min:4616kB low:5768kB
high:6924kB active_anon:219444kB inactive_anon:103236kB
active_file:436680kB inactive_file:1438176kB unevictable:0kB
isolated(anon):0kB isolated(file):0kB present:3104640kB mlocked:0kB
dirty:123816kB writeback:156kB mapped:19008kB shmem:260kB
slab_reclaimable:633488kB slab_unreclaimable:57916kB kernel_stack:2800kB
pagetables:13568kB unstable:0kB bounce:0kB writeback_tmp:0kB
pages_scanned:0 all_unreclaimable? no
[1801523.686869] lowmem_reserve[]: 0 0 0 0
[1801523.686873] Node 0 DMA: 2*4kB 3*8kB 0*16kB 2*32kB 4*64kB 3*128kB
2*256kB 1*512kB 2*1024kB 2*2048kB 0*4096kB = 7904kB
[1801523.686883] Node 0 DMA32: 129*4kB 87*8kB 86*16kB 89*32kB 87*64kB
65*128kB 12*256kB 5*512kB 2*1024kB 13*2048kB 419*4096kB = 1769852kB
[1801523.686893] Node 0 Normal: 477*4kB 23*8kB 1*16kB 5*32kB 0*64kB 3*128kB
3*256kB 1*512kB 0*1024kB 1*2048kB 0*4096kB = 5980kB
[1801523.686903] 1017542 total pagecache pages
[1801523.686905] 0 pages in swap cache
[1801523.686907] Swap cache stats: add 0, delete 0, find 0/0
[1801523.686908] Free swap  = 1048572kB
[1801523.686910] Total swap = 1048572kB
[1801523.722319] 1837040 pages RAM
[1801523.722322] 58337 pages reserved
[1801523.722323] 972948 pages shared
[1801523.722324] 406948 pages non-shared
[1801523.722326] [ pid ]   uid  tgid total_vm      rss cpu oom_adj
oom_score_adj name
[1801523.722396] [31266]     0 31266     6404      511   6       0
    0 init
[1801523.722445] [32489]     0 32489    12370      688   7     -17
-1000 sshd
[1801523.722460] [32511]   101 32511    10513      325   0       0
    0 rsyslogd
[1801523.722495] [32625]     0 32625    17706      838   2       0
    0 sshd
[1801523.722522] [32652]   103 32652     5900      176   0       0
    0 dbus-daemon
[1801523.722583] [  526]     0   526     1553      168   5       0
    0 getty
[1801523.722587] [  530]     0   530     1553      168   1       0
    0 getty
[1801523.722593] [  537]  2007   537    17706      423   5       0
    0 sshd
[1801523.722629] [  538]  2007   538    16974     5191   1       0
    0 python
[1801523.722650] [  877]  2007   877     2106      157   7       0
    0 dd
[1801523.722657] Memory cgroup out of memory: Kill process 538 (python)
score 71 or sacrifice child
[1801523.722674] Killed process 538 (python) total-vm:67896kB,
anon-rss:17464kB, file-rss:3300kB

Tôi đang chạy trên Linux ip-10-8-139-98 3.2.0-29-virtual # 46-Ubuntu SMP Thứ Sáu 27 tháng 7 17:23:50 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux trên Amazon EC2.


1
Như một bản tóm tắt nhanh chóng cho tất cả những ai đọc nó, đây là một lỗi kernel linux
UsAaR33

Câu trả lời:


13

Chỉnh sửa: Tôi sẽ giữ câu trả lời ban đầu của mình bên dưới, nhưng tôi sẽ cố gắng giải thích những gì đang xảy ra ở đây và cung cấp giải pháp chung cho bạn.

Chỉnh sửa 2: Cung cấp tùy chọn khác.

Vấn đề mà bạn gặp phải ở đây liên quan đến cách thức quản lý I / O của kernel. Khi bạn ghi vào hệ thống tập tin của mình, ghi đó không được cam kết ngay lập tức vào đĩa; Điều đó sẽ vô cùng kém hiệu quả. Thay vào đó, ghi được lưu vào bộ nhớ cache trong một vùng bộ nhớ được gọi là bộ đệm trang và được ghi định kỳ theo từng phần ra đĩa. Phần "bẩn" trong nhật ký của bạn mô tả kích thước của bộ đệm trang này chưa được ghi ra đĩa:

dirty:123816kB

Vì vậy, những gì làm trống bộ nhớ cache bẩn này? Tại sao nó không hoạt động?

'Flush' trên Linux chịu trách nhiệm ghi các trang bẩn ra đĩa. Đó là một daemon thức dậy định kỳ để xác định xem có cần ghi vào đĩa hay không và nếu có thì thực hiện chúng. Nếu bạn là một chàng trai loại C, hãy bắt đầu ở đây . Flush là vô cùng hiệu quả; nó làm một công việc tuyệt vời là xả các thứ vào đĩa khi cần thiết. Và nó hoạt động chính xác như thế nào.

Flush chạy bên ngoài container LXC của bạn, vì container LXC của bạn không có kernel riêng. Các thùng chứa LXC tồn tại như một cấu trúc xung quanh các nhóm , đây là một tính năng của nhân Linux, cho phép hạn chế và cách ly tốt hơn các nhóm quy trình, nhưng không phải là nhân riêng của nó hoặc trình nền da lộn.

Vì LXC của bạn có giới hạn bộ nhớ thấp hơn bộ nhớ mà hạt nhân có sẵn, điều kỳ lạ xảy ra. Flush giả định rằng nó có bộ nhớ đầy đủ của máy chủ lưu trữ để ghi vào bộ đệm. Một chương trình trong LXC của bạn bắt đầu ghi một tệp lớn, nó đệm ... bộ đệm ... và cuối cùng đạt đến giới hạn cứng và bắt đầu gọi trình quản lý OOM. Đây không phải là một thất bại của bất kỳ thành phần cụ thể; đó là hành vi mong đợi. Loại. Loại điều này nên được xử lý bởi các nhóm, nhưng có vẻ như không phải vậy.

Điều này hoàn toàn giải thích hành vi bạn thấy giữa các kích thước cá thể. Bạn sẽ bắt đầu xả vào đĩa sớm hơn nhiều trong trường hợp vi mô (với 512MB RAM) so với trên một ví dụ lớn

Được rồi, cái đó có lý. Nhưng nó vô dụng. Tôi vẫn cần phải viết cho tôi một tập tin lớn.

Chà, tuôn ra không biết giới hạn LXC của bạn. Vì vậy, thay vì vá kernel, có một vài tùy chọn ở đây cho những thứ bạn có thể thử điều chỉnh:

/proc/sys/vm/dirty_expire_centiseconds

Điều này kiểm soát thời gian một trang có thể được giữ trong bộ đệm bẩn và ghi vào đĩa. Theo mặc định là 30 giây; hãy thử đặt nó thấp hơn để bắt đầu đẩy nó ra nhanh hơn.

/proc/sys/vm/dirty_background_ratio

Điều này kiểm soát bao nhiêu phần trăm bộ nhớ hoạt động được phép điền vào trước khi nó bắt đầu ghi. Có một chút khó khăn trong việc phân loại tổng số chính xác ở đây, nhưng giải thích đơn giản nhất là chỉ nhìn vào tổng bộ nhớ của bạn. Theo mặc định, nó là 10% (trên một số bản phát hành là 5%). Đặt cái này thấp hơn; nó sẽ buộc ghi ra đĩa sớm hơn và có thể giữ cho LXC của bạn không chạy ra khỏi giới hạn của nó.

Tôi có thể chỉ vít với hệ thống tập tin một chút không?

À vâng. Nhưng hãy chắc chắn rằng bạn đã kiểm tra điều này .. bạn có thể ảnh hưởng đến hiệu suất. Trên các gắn kết của bạn trong / etc / fstab nơi bạn sẽ viết cái này vào, thêm tùy chọn gắn kết ' đồng bộ hóa '.

Câu trả lời gốc:

Hãy thử giảm kích thước khối được sử dụng bởi DD:

dd if=/dev/zero of=test2 bs=512 count=1024000

Bạn chỉ có thể viết một sector tại một thời điểm (512 byte trên các ổ cứng cũ hơn, 4096 trên mới hơn). Nếu DD đang đẩy ghi vào đĩa nhanh hơn đĩa có thể chấp nhận chúng, nó sẽ bắt đầu lưu bộ ghi vào bộ nhớ. Đó là lý do tại sao bộ đệm tập tin của bạn đang phát triển.


Tôi nên lưu ý rằng nếu tôi chạy các thử nghiệm tương tự trong python nơi tôi tự xóa đối tượng tệp, lỗi vẫn xảy ra với xác suất tương tự. Tất nhiên, bộ đệm phát triển, nhưng điều đó sẽ bị xóa, người ta sẽ nghĩ hơn là quá trình bị giết.
UsAaR33

1
Dù sao tôi cũng sẽ cho nó một phát súng. Tôi đã tìm thấy việc buộc một fsync () với Python không phải lúc nào cũng làm những gì bạn mong đợi.
alexphilipp

1
@ UsAaR33 Nhận đĩa nhanh hơn.
tink

1
@ UsAaR33 Một ứng dụng sẽ viết nhanh nhất có thể; nó hy vọng kernel sẽ xử lý IO. Trước đây tôi chưa sử dụng bộ chứa LXC, nhưng nhìn thoáng qua có vẻ như nó không cung cấp hạt nhân của chính nó trong chroot mà nó tạo ra? Nếu đó là trường hợp, hạt nhân đang cung cấp IO với giả định rằng nó có bộ nhớ đầy đủ của hệ thống máy chủ có sẵn. Nó không có ý tưởng rằng bạn đánh giá giới hạn ở mức 300MB. Khi giới hạn đó được thực hiện, OOM bắt đầu giết các quá trình.
alexphilipp

1
@ UsAaR33: Cài đặt xấu gây ra kết quả xấu. Một phần của hệ thống được cho biết rất nhiều bộ nhớ có thể được sử dụng làm bộ đệm, một phần khác của hệ thống được yêu cầu hủy các tiến trình nếu bộ đệm quá lớn. Tại sao nó phải đợi đĩa khi có sẵn nhiều RAM? Và nếu có sẵn nhiều RAM, tại sao bạn không để nó sử dụng?
David Schwartz

3

Là tập tin của bạn viết vào / tmp? Nếu vậy, nó có thể không nằm trên một hệ thống tập tin thực tế mà nằm trên đĩa. Do đó, khi bạn viết thư cho nó, ngày càng nhiều bộ nhớ bị lấy đi để đáp ứng nhu cầu của tập tin. Cuối cùng, bạn hết bộ nhớ + không gian hoán đổi và hiệu suất của bạn giảm xuống đến mức thất vọng hoàn toàn.


Nó được ghi vào $ HOME, nằm trên mount AUFS, kích hoạt ghi vào đĩa bên dưới. (EC2 EBS)
UsAaR33

2

trừ khi bạn đang ghi vào đĩa RAM, bạn có thể tránh bộ nhớ đệm bằng cách sử dụng oflag = direct

dd if=/dev/zero of=test2 bs=100k oflag=direct count=5010

trực tiếp gây ra lỗi "Đối số không hợp lệ", nhưng sử dụng oflag = dsync không hoạt động.
UsAaR33

Tôi xin lỗi nếu nó không hiệu quả với bạn, vì theo trang nam "sử dụng trực tiếp I / O trực tiếp cho dữ liệu"
Kevin Parker
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.