Desativar o assassino do Linux OOM por padrão?

37

O assassino de OOM no Linux causa estragos em vários aplicativos de vez em quando, e parece que pouco é realmente feito no lado do desenvolvimento do kernel para melhorar isso. Não seria melhor, como prática recomendada ao configurar um novo servidor , reverter o padrão na confirmação excessiva de memória, ou seja, desativá-lo ( vm.overcommit_memory=2) a menos que você saiba que deseja usá-lo para seu uso específico? E quais seriam esses casos de uso em que você sabe que deseja supercomprometimento?

Como um bônus, como o comportamento em caso de vm.overcommit_memory=2depende vm.overcommit_ratioe troca de espaço, qual seria uma boa regra para dimensionar os dois últimos para que toda essa configuração continue funcionando razoavelmente?

Peter Eisentraut
fonte

Respostas:

63

Uma analogia interessante (de http://lwn.net/Articles/104179/ ):

Uma empresa de aeronaves descobriu que era mais barato pilotar seus aviões com menos combustível a bordo. Os aviões seriam mais leves e consumiriam menos combustível, economizando dinheiro. Em raras ocasiões, porém, a quantidade de combustível era insuficiente e o avião colidia. Esse problema foi resolvido pelos engenheiros da empresa pelo desenvolvimento de um mecanismo especial de OOF (sem combustível). Em casos de emergência, um passageiro foi selecionado e jogado para fora do avião. (Quando necessário, o procedimento foi repetido.) Um grande corpo de teoria foi desenvolvido e muitas publicações foram dedicadas ao problema de selecionar adequadamente a vítima a ser ejetada. A vítima deve ser escolhida aleatoriamente? Ou deve-se escolher a pessoa mais pesada? Ou o mais velho? Caso os passageiros paguem para não serem expulsos, para que a vítima fosse a mais pobre a bordo? E se, por exemplo, a pessoa mais pesada foi escolhida, deveria haver uma exceção especial no caso de ser o piloto? Os passageiros de primeira classe devem ser isentos? Agora que o mecanismo OOF existia, ele era ativado de vez em quando e expulsava passageiros mesmo quando não havia falta de combustível. Os engenheiros ainda estão estudando com precisão como esse mau funcionamento é causado.

An̲̳̳drew
fonte
11
Eu gostei muito disso, obrigado por desenterrá-lo.
Nick Bolton
32

O assassino do OOM só causa estragos se você sobrecarregou o sistema. Troque o suficiente e não execute aplicativos que de repente decidam consumir grandes quantidades de RAM, e você não terá problemas.

Para responder especificamente às suas perguntas:

  • Não acho que seja uma boa ideia desativar a supercomprometimento no caso geral; muito poucos aplicativos são gravados para lidar adequadamente com brk(2) (e os wrappers que o utilizam, como malloc(3)) retornando um erro. Quando experimentei isso no meu trabalho anterior, considerou-se um incômodo obter tudo capaz de lidar com erros de falta de memória do que apenas lidar com as consequências de uma OOM (que, no nosso caso, era muito pior do que ter que reiniciar o serviço ocasional se um OOM ocorresse - tivemos que reiniciar um cluster inteiro, porque o GFS é uma pilha de fezes fumegante).
  • Você deseja confirmar demais qualquer processo que comprometa demais a memória. Os dois culpados mais comuns aqui são o Apache e a JVM, mas muitos aplicativos fazem isso em maior ou menor grau. Eles acham que podem precisar de muita memória em algum momento no futuro, então pegam uma grande parte imediatamente. Em um sistema habilitado para overcommit, o kernel diz "meh, tanto faz, venha me incomodar quando você realmente deseja escrever nessas páginas" e nada de ruim acontece. Em um sistema de supercomprometimento, o kernel diz "não, você não pode ter tanta memória, se escrever sobre isso em algum momento no futuro em que estou desossada, então não há memória para você!" e a alocação falha. Já que nadalá fora, "oh, ok, posso ter essa quantidade menor de segmento de dados do processo?", então o processo (a) termina com um erro de falta de memória ou (b) não verifica o código de retorno de malloc, acha que está OK e grava em um local de memória inválido, causando um segfault. Felizmente, a JVM faz tudo o que é pré-alocado na inicialização (para que sua JVM inicie ou morra imediatamente, o que você geralmente nota), mas o Apache faz coisas divertidas com cada novo filho, o que pode ter efeitos interessantes na produção (improdutível "não manipulando conexões "tipos de emoção).
  • Eu não gostaria de definir meu overcommit_ratio acima do padrão de 50%. Novamente, dos meus testes, embora configurá-lo em torno de 80 ou 90 possa parecer uma boa idéia, o kernel exige grandes pedaços de memória em momentos inconvenientes, e um sistema totalmente carregado com uma alta taxa de supercomprometimento provavelmente não possui memória sobressalente suficiente quando o kernel precisa (levando ao medo, pestilência e oopses). Portanto, brincar com o supercomprometimento apresenta um novo modo de falha ainda mais divertido - em vez de apenas reiniciar qualquer processo que tenha OOM quando você ficar sem memória, agora sua máquina falha, causando uma interrupção de tudo na máquina. IMPRESSIONANTE!
  • A troca de espaço em um sistema sem supercomprometimento depende da quantidade de memória solicitada, mas não utilizada, que seus aplicativos precisam, além de uma margem de segurança saudável. Descobrir o que é necessário em um caso específico é deixado como um exercício para o leitor.

Basicamente, minha experiência é que desativar o overcommit é um bom experimento que raramente funciona tão bem na prática quanto parece na teoria. Isso corresponde muito bem às minhas experiências com outros ajustes no kernel - os desenvolvedores do kernel Linux são quase sempre mais inteligentes que você, e os padrões funcionam melhor para a vasta e vasta maioria dos casos. Deixe-os em paz e, em vez disso, procure qual processo tem o vazamento e corrija-o.

mulher
fonte
2
Não quero que meu processo de backup seja morto porque alguém está DoS-ing no meu servidor web. As exceções são boas, mas o padrão deve ser segurança e consistência. Otimizações como OOM devem ser ativadas manualmente IMHO. É como codificar, você codifica de maneira limpa e otimiza. A confirmação excessiva é um recurso interessante, mas não deve ser o padrão.
Aki
11
Se você não deseja que seu processo de backup seja interrompido porque alguém está DoS usando seu servidor da Web, não configure seu servidor da Web de tal maneira que um DoS possa causar sobrecarga nos recursos do sistema.
womble
Eu tenho 8 GB de RAM e apenas rodar o Firefox e uma máquina virtual às vezes resulta no assassino do OOM matando a VM. Compilando o Unreal Engine 4, cada chamada de clang leva 1 a 1,5 GB de memória e, novamente, o OOM killer mata uma de vez em quando. Agora, em geral, estou bem com isso, sem o assassino do OOM, eles provavelmente iriam falhar de qualquer maneira. É que toda vez que o OOM killer quer matar um processo, meu sistema congela por 10 minutos antes que o processo ruim seja realmente morto. Bug talvez? Provavelmente. Eu quero isso? Definitivamente não. E é por isso que alguém pode querer desativar o assassino de OOM.
Shahbaz
11
Se você estiver fazendo tudo isso em uma caixa, precisará de mais RAM e desabilitar a supercomprometimento apenas piorará.
Ben Lutgens
6

Hmm, eu não estou totalmente convencido por argumentos a favor do overcommit e do OOM killer ... Quando o womble escreve,

"O assassino do OOM só causa estragos se você sobrecarregou o sistema. Faça uma troca suficiente e não execute aplicativos que de repente decidam consumir grandes quantidades de RAM, e você não terá problemas".

Ele descreve um cenário de ambiente em que a supercomprometimento e o OOM killer não são impostos ou não agem realmente (se todos os aplicativos alocassem memória conforme necessário e houvesse memória virtual suficiente para ser alocada, as gravações seguiriam de perto as alocações de memória sem erros, por isso não podíamos falar sobre um sistema supercomitido, mesmo que uma estratégia de supercomprometimento estivesse ativada). Trata-se de uma admissão implícita de que o supercomprometimento e o assassino da OOM funcionam melhor quando sua intervenção não é necessária, o que é de alguma forma compartilhado pela maioria dos apoiadores dessa estratégia, tanto quanto eu posso dizer (e admito que não posso dizer muito ...). Além disso, referir-me a aplicativos com comportamentos específicos ao pré-alocar memória me faz pensar que um tratamento específico pode ser ajustado em um nível de distribuição, em vez de ter um padrão,

Para a JVM, bem, é uma máquina virtual, até certo ponto, ela precisa alocar todos os recursos necessários na inicialização, para criar seu ambiente 'falso' para seus aplicativos e manter seu recurso disponível separado do host ambiente, tanto quanto possível. Portanto, pode ser preferível que falhe na inicialização, em vez de decorrido um tempo como consequência de uma condição OOM 'externa' (causada por supercomprometimento / morte de OOM / qualquer que seja), ou mesmo assim sofra por essa condição interferir na sua própria estratégias internas de manipulação de OOM (em geral, uma VM deve obter todos os recursos necessários desde o início e o sistema host deve "ignorá-los" até o final, da mesma forma que qualquer quantidade de memória RAM compartilhada com uma placa gráfica nunca - e não pode ser - tocado pelo sistema operacional).

Sobre o Apache, duvido que ter todo o servidor ocasionalmente morto e reiniciado seja melhor do que permitir que um único filho, juntamente com uma única conexão, falhe desde o início (= da criança / da conexão) (como se fosse uma instância totalmente nova de a JVM criada após outra instância é executada por um tempo). Eu acho que a melhor 'solução' pode depender de um contexto específico. Por exemplo, considerando um serviço de comércio eletrônico, pode ser muito preferível que algumas vezes algumas conexões com o gráfico de compras falhem aleatoriamente, em vez de perder todo o serviço, com o risco, por exemplo, de interromper uma finalização em andamento do pedido, ou (talvez pior) um processo de pagamento, com todas as consequências do caso (talvez inofensivo, mas talvez prejudicial - e com certeza, quando surgirem problemas,

Da mesma forma, em uma estação de trabalho, o processo que consome mais recursos e, portanto, a primeira escolha para o assassino de OOM, pode ser um aplicativo com muita memória, como um transcodificador de vídeo ou um software de renderização, provavelmente o único aplicativo o usuário quer ser intocado. Essas considerações sugerem que a política padrão do OOM killer é muito agressiva. Ele usa uma abordagem de "pior ajuste", que é de alguma forma semelhante à de alguns sistemas de arquivos (o OOMK tenta e libera o máximo de memória possível, enquanto reduz o número de subprocessos mortos, a fim de impedir qualquer intervenção adicional em curto espaço de tempo, como bem como um fs pode alocar mais espaço em disco do que o necessário para um determinado arquivo, para impedir qualquer alocação adicional se o arquivo crescer e, assim, impedir a fragmentação).

No entanto, acho que uma política oposta, como uma abordagem de "melhor ajuste", poderia ser preferível, de modo a liberar a memória exata necessária em um determinado ponto e não se incomodar com processos "grandes", que podem estar desperdiçando memória, mas também pode não ser, e o kernel não pode saber disso (hmm, eu posso imaginar que manter o rastreamento dos acessos à página conte e tempo poderia sugerir que, se um processo está alocando memória, ele não precisa mais, portanto, para adivinhar se um processo está desperdiçando memória ou apenas usando muito, mas os atrasos de acesso devem ser ponderados nos ciclos da CPU para distinguir uma perda de memória de um aplicativo intensivo em memória e CPU, mas, embora potencialmente imprecisos, essas heurísticas podem ter uma sobrecarga excessiva).

Além disso, pode não ser verdade que matar o menor número possível de processos seja sempre uma boa escolha. Por exemplo, em um ambiente de área de trabalho (vamos pensar em um nettop ou um netbook com recursos limitados, por exemplo), um usuário pode estar executando um navegador com várias guias (portanto, consumindo memória - vamos assumir que essa é a primeira escolha para o OOMK) , além de alguns outros aplicativos (um processador de texto com dados não salvos, um cliente de email, um leitor de pdf, um media player, ...), além de alguns daemons (do sistema), além de algumas instâncias do gerenciador de arquivos. Agora, ocorre um erro do OOM, e o OOMK opta por matar o navegador enquanto o usuário está fazendo algo considerado "importante" na rede ... o usuário ficaria desapontado. Por outro lado, fechando os poucos gerenciadores de arquivos '

De qualquer forma, acho que o usuário deve ter a capacidade de tomar uma decisão por conta própria sobre o que fazer. Em um sistema de desktop (= interativo), isso deve ser relativamente fácil, desde que sejam reservados recursos suficientes para solicitar ao usuário que feche qualquer aplicativo (mas até fechar algumas guias pode ser suficiente) e lidar com sua escolha (uma opção pode consiste em criar um arquivo de troca adicional, se houver espaço suficiente). Para serviços (e em geral), eu também consideraria dois aprimoramentos possíveis: um é registrar os intervalos do OOM killer, além de processar falhas de início / bifurcação, de forma que a falha possa ser facilmente depurada (por exemplo, uma API pode informe o processo que está emitindo a nova criação ou bifurcação do processo - assim, um servidor como o Apache, com um patch adequado, poderia fornecer um registro melhor para certos erros); isso pode ser feito independentemente da supercomissão / OOMK estar se esforçando; em segundo lugar, mas não por importância, um mecanismo poderia ser estabelecido para ajustar o algoritmo OOMK - eu sei que é possível, até certo ponto, definir uma política específica em um processo por processo, mas eu gostaria de mecanismo de configuração 'centralizado', baseado em uma ou mais listas de nomes de aplicativos (ou IDs) para identificar processos relevantes e dar a eles um certo grau de importância (conforme os atributos listados); esse mecanismo deve (ou pelo menos poderia) também ser em camadas, para que possa haver uma lista definida pelo usuário de nível superior, uma lista definida pelo sistema (distribuição) e entradas definidas pelo aplicativo (de nível inferior) , por exemplo, um gerenciador de arquivos DE pode instruir o OOMK a matar com segurança qualquer instância,

Além disso, uma API pode ser fornecida para permitir que os aplicativos aumentem ou diminuam seu nível de 'importância' no tempo de execução (com relação aos objetivos de gerenciamento de memória e independentemente da prioridade de execução), para que, por exemplo, um processador de Word possa começar com uma 'importância' baixa, mas aumente-a, pois alguns dados são mantidos antes da liberação para um arquivo ou uma operação de gravação está sendo executada e diminua a importância novamente assim que essa operação terminar (analogamente, um gerenciador de arquivos pode mudar de nível quando passar de apenas litar arquivos para lidar com dados e vice-versa, em vez de usar processos separados, e o Apache poderia dar diferentes níveis de importância a crianças diferentes ou alterar um estado filho de acordo com alguma política decidida por administradores de sistemas e exposta através do Apache - ou qualquer outro tipo de servidor - configurações). Claro, essa API poderia e seria abusada / mal utilizada, mas acho que essa é uma preocupação menor em comparação com o kernel arbitrariamente matar processos para liberar memória sem nenhuma informação relevante sobre o que está acontecendo no sistema (e consumo de memória / tempo de criação ou similares) é relevante ou validar o suficiente para mim) - somente usuários, administradores e gravadores de programas podem realmente determinar se um processo ainda é necessário por algum motivo, qual é o motivo e / ou se o aplicativo está em um estado líder perda de dados ou outros danos / problemas se mortos; no entanto, alguma suposição ainda pode ser feita, por exemplo, procurando recursos de um determinado tipo (descritores de arquivo, soquetes de rede etc.) adquiridos por um processo e com operações pendentes poderiam dizer se um processo deveria estar em um 'estado' mais alto do que o único conjunto,

Ou, evite o excesso de confirmação e deixe o kernel fazer exatamente o que um kernel deve fazer, alocando recursos (mas não os resgatando arbitrariamente como o assassino do OOM), agendando processos, prevenindo fome e deadlocks (ou resgatando-os), garantindo total preempção e separação de espaços de memória e assim por diante ...

Também gastaria mais algumas palavras sobre abordagens de supercomprometimento. Em outras discussões, criei a idéia de que uma das principais preocupações com a supercomprometimento (tanto como motivo para querer isso quanto como fonte de possíveis problemas) consiste no manuseio dos garfos: honestamente, não sei exatamente como A estratégia on-write é implementada, mas acho que qualquer política agressiva (ou otimista) pode ser mitigada por uma estratégia de localidade de troca semelhante. Ou seja, em vez de apenas clonar (e ajustar) as páginas de código do processo bifurcado e as estruturas de agendamento, algumas outras páginas de dados podem ser copiadas antes de uma gravação real, escolhendo entre as páginas que o processo pai acessou para gravar com mais frequência (ou seja, usando um contador para operações de gravação).

Tudo, é claro, IMHO.


fonte
5
"Além disso, uma API pode ser fornecida para permitir que os aplicativos aumentem ou diminuam seu nível de 'importância' em tempo de execução" /proc/$PID/oom_adj.
Vi.
11
Em relação à JVM, há uma pegadinha que faz com que você deseje a supercomprometimento de memória em alguns casos: caso deseje criar outra JVM a partir da sua JVM original, ela chamará fork (). Uma chamada de bifurcação alocará tanta memória quanto o processo original (primeiro), até realmente iniciar o processo. Então, digamos que você tem um 4GB JVM e quer criar um novo 512KB JVM a partir dele, a menos que você tem overcommit, você terá 8GB de memória para fazer isso ...
alci
4
@Vi. Parece que agora é/proc/$PID/oom_score_adj
m3nda
1

Se sua memória for exaustivamente usada pelos processos na medida em que possa ameaçar a estabilidade do sistema, o assassino do OOM entrará em cena. É tarefa do assassino OOM matar os processos até que seja liberada memória suficiente para o bom funcionamento do restante do processo. O OOM Killer precisa selecionar o "melhor" processo para matar. "Melhor" aqui se refere ao processo que liberará o máximo de memória após a morte e também é menos importante para o sistema. O objetivo principal é eliminar o menor número de processos que minimiza o dano causado e ao mesmo tempo maximizar a quantidade de memória liberada. Para facilitar isso, o kernel mantém oom_score para cada um dos processos. Você pode ver o oom_score de cada um dos processos no sistema de arquivos / proc no diretório pid

# cat /proc/10292/oom_score

Quanto maior o valor de oom_score de qualquer processo, maior a probabilidade de ser morto pelo OOM Killer em uma situação de falta de memória.

Crédito: - O kernel do Linux está iniciando o killer do OOM

dinkey jhanwar
fonte