Ubuntu LXC einfach aus

oom-killer ... lustig. Noch nie selbst auf meinem System gehabt.

Wie genau machst du den Kopiervorgang (hatte schon mal früher hier gefragt)? Das war doch Python. Da würde ich eher anknüpfen. Entweder ist da ein Memory-Leak oder bei großen Dateien wird was seltsames gemacht.
 
Zuletzt bearbeitet:
Der oom-killer kann auch noch andere Prozesse in Mitleidenschaft ziehen.

Wie @oicfar schreibt, untersuche mal das python-Programm oder whatever was da läuft.

Wenn du keine saubere Prozetrennung hast, d.h. der oom auf dem Host auftritt, dann solltest du System neu starten und die Aktion wiederholen was gerade lief. Wenn es nur eine VM ist, reicht es die VM neu zu starten.
 
Schon, aber das behebt ja nicht die Ursache für das Auftreten von OOM, bzw. warum der Arbeitsspeicher beim Kopieren vollläuft. Wie @oicfar sagt, man sollte den Kopierprozess untersuchen um das eigentliche Problem zu beheben, und nicht an den Symptomen rumschrauben.
 
Deshalb hatte ich ja auch gesagt, wir müssen uns das Script und sein Speichermanagement ansehen. Wird der Garbage Collector adäquat getriggert? Wie geht das Script mit allokiertem Speicher um? Da liegt der Hase im Pfeffer.
 
@UnBreakable poste mal das Skript hier. Wird langsam Zeit, dass ich mich mit Python beschäftige. ;)
 
Wenn ich mir den Stacktrace in Post #18 so ansehen, ist ziemlich weit unten im Callstak (d.h "oben" im Log, weil der Stack "rückwärts" dargestellt wird) mem_cgroup_out_of_memory zu finden.

Kernel cgroups sind ein Feature (https://en.wikipedia.org/wiki/Cgroups), dass es ermöglicht den Speicher den bestimmte Gruppen von Prozessen allokieren können, zu begrenzen. Nutzt man u.A. für Container, damit sie nicht den ganzen Speicher verbrauchen können. Deswegen bekommst der TE einen Out-of-Memory obwohl das Gesamtsystem genügend Speicher hat.

Ich habe mich leider noch nicht damit beschäftigen müssen, wie man einem Container mehr Speicher zuweist, aber meines Erachtens ist dass der Punkt an dem ansetzen muss.

Wenn man die Dumps ansieht, tritt der eigentiche Überlauf immer an etwas unterschiedlichen Stellen auf, eben halt in dem Moment wo er "verbraucht" ist. Das ist z.B. in einem Dump beim Versenden eines TCP Segments über das Netzwerk im Rahmen des cifs/smb Protokolls (Samba...).

Die eigentliche Ursache wird aber sein, dass beim kopieren großer Dateien sehr viel Blockcache allokiert wird (sieht man teilweise am Aufruf entsprechender Funktionen weiter oben im Stack).

Das Problem wird sein, das die File IO Funktionen des Kernels ja nicht "wissen" dass sie gerade in einem Container laufen.

Generell sind Kernel-intensive Dinge wie File Copies eigentlich nicht gut in einem Container aufgehoben.
 
TomH22 schrieb:
Wenn man die Dumps ansieht, tritt der eigentiche Überlauf immer an etwas unterschiedlichen Stellen auf, eben halt in dem Moment wo er "verbraucht" ist. Das ist z.B. in einem Dump beim Versenden eines TCP Segments über das Netzwerk im Rahmen des cifs/smb Protokolls (Samba...).
Ich habe bei mir auch zwei LXC Container am Start. Leider ohne eine Samba Anbindung, wo ich was testen könnte.

Deswegen wäre hier noch gut das Skript zu sehen. Ich nehme an, wenn es da irgendwelche bekannten Probleme geben könnte, dann findet man das heraus.
TomH22 schrieb:
Das Problem wird sein, das die File IO Funktionen des Kernels ja nicht "wissen" dass sie gerade in einem Container laufen.
Die Frage wäre, ob der Skript in einer VM und keinem LXC Container ohne Fehler laufen würde.
UnBreakable schrieb:
Ich nutze shutil.copy
Why the Python memory error using shutil.copyfileobj?
https://stackoverflow.com/questions/67145829/why-the-python-memory-error-using-shutil-copyfileobj

Auch interessant ...
https://stackoverflow.com/questions/67732361/python-read-write-vs-shutil-copy
 
oicfar schrieb:
Why the Python memory error using shutil.copyfileobj?
So Stackoverflow Artikel sind immer mit Vorsicht zu geniessen, da sie sich oft auf eine spezielle Situaiton beziehen, in einem anderen Fall nicht vorliegen. Gleiches Symptom muss nicht gleiche Ursache heißen.

Aber ein wenig stöbern in de Doku von shutil bringt das zu Tage:
https://docs.python.org/3/library/shutil.html#shutil-platform-dependent-efficient-copy-operations

Da wird unter anderem davon gesprochen, dass Copy Operationen effizient im Kernel abgewickelt werden. Die Stacktraces zeigen da auch in die Richtung. Ich würde zwar erwarten, dass der Kernel dabei nicht mehr Speicher allokieren sollte, als dem aufrufenden Prozess "zusteht". Denn der Caller muss sich ja auf den Kernel verlassen, dass der sich nicht selber ins Knie schiesst.

Das Problem ist halt, wenn man solche High-Level Module wie shutil benutzt, hat man wenig Kontrolle darüber, was sie machen. Die Frage ist halt auch, ob nicht lieber per os.system einfach ein cp Befehl aufruft, sofern man wirklich nur Dateien von einem Filesystem auf ein anderes kopiert.
 
TomH22 schrieb:
So Stackoverflow Artikel sind immer mit Vorsicht zu geniessen, da sie sich oft auf eine spezielle Situaiton beziehen, in einem anderen Fall nicht vorliegen. Gleiches Symptom muss nicht gleiche Ursache heißen.
Richtig. Ich habe nur ein wenig geschaut.
TomH22 schrieb:
Hatte ich auch gesehen.
TomH22 schrieb:
Das Problem ist halt, wenn man solche High-Level Module wie shutil benutzt, hat man wenig Kontrolle darüber, was sie machen. Die Frage ist halt auch, ob nicht lieber per os.system einfach ein cp Befehl aufruft, sofern man wirklich nur Dateien von einem Filesystem auf ein anderes kopiert.
Wäre ich von dem Problem betroffen, dann würde ich eine VM erstellen und schauen, ob auch hier das Problem auftritt. Vielleicht ist das Verhalten in einem LXC Container anders. Tritt es bei der VM nicht auf, dann so lassen. Ansonsten mal das Skript umschreiben ist wohl die Lösung, wenn man bei LXC bleiben will.
Ergänzung ()

Option wäre noch das Problem in der Python Community zu posten.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: TomH22
Vielen Dank erstmal für die ganzen Antworten.
Da ich bei dem ganzen Thema eher Laie bin, muss ich mir das erstmal alles in Ruhe durchlesen und versuchen zu verstehen.

Aber hier mal das Script:

Python:
#!/bin/python3
import os
import shutil
import subprocess
import time
import gc

print("Bereinige buffer memory")
gc.collect()


print("Haenge Mountpunkte ein...")
subprocess.Popen('mount -a', shell=True)
print("Warte 2 Sekunden...")
time.sleep(2)

source="/mnt/source/"
dest="/mnt/dest/"

print("Pruefe ob Inhaltsverzeichnis (daily.vbm) veraltet:")

if os.path.exists(source+"daily.vbm") == True:
  if os.path.exists(dest+"daily.vbm") == True:
    if os.path.getmtime(source+"daily.vbm") == os.path.getmtime(dest+"daily.vbm"):
      print("Inhaltsverzeichnis hat gleichen Stand, keine Aktion")
    else:
      print("Neueres Inhaltsverzeichnis gefunden. Kopiere Datei fuer Inhaltsverzeichnis... Quelle: "+source+"daily.vbm Ziel: "+dest+"daily.vbm")
      shutil.copy2(source+"daily.vbm", dest+"daily.vbm")
  else:
    print("Inhaltsverzeichnis in Ziel nicht vorhanden. Kopiere Datei fuer Inhaltsverzeichnis... Quelle: "+source+"daily.vbm Ziel: "+dest+"daily.vbm")
    shutil.copy2(source+"daily.vbm", dest+"daily.vbm")
else:
  print("Inhaltsverzeichnis nicht gefunden.")


print("Lese Ziel-Verzeichnisinhalt...")
files2=os.listdir(dest)

print("Sortiere Ziel-Verzeichnisliste...")
files2.sort(reverse=True)

print("Lese Qell-Verzeichnisinhalt...")
files=os.listdir(source)

print("Sortiere Quell-Verzeichnisliste...")
files.sort(reverse=True)

print("-------------")
for element in files:
     print("Aktuelle Datei: " + element)
     extension=element[-4]+element[-3]+element[-2]+element[-1]
     print("Dateiendung: "+extension)
     if extension == ".vib":
       if os.path.exists(dest+element):
         print("Datei existiert, keine Aktion")
       else:
         print("Kopiere "+element+"...")
         shutil.copy(source+element, dest+element)
     if extension == ".vbk":
       if os.path.exists(dest+element):
         print("Datei existiert, pruefe Dateigroesse...")
         if(os.path.getsize(dest+element) == os.path.getsize(source+element)):
           print("Datei hat gleiche groesse: Keine Aktion")
           last_vbk=element
           print("Beende Dateibearbeitung...")
           break
         else:
           print("Dateigroessen unterschiedliche, kopiere " +element+"...")
           shutil.copy(source+element, dest+element)
           last_vbk=element
           print("Beende Dateibearbeitung...")
           break
       else:
         print("Kopiere "+element+"...")
         shutil.copy(source+element, dest+element)
         last_vbk=element
         print("Beende Dateibearbeitung...")
         break

print("-----------")
print("Starte Bereinigung")
loesch_kz=0
for element2 in files2:
   print("Aktuelle Datei: " + element2)
   extension2=element2[-4]+element2[-3]+element2[-2]+element2[-1]
   print("Dateiendung "+extension2)
   if loesch_kz == 1 and extension2 != ".vbm":
     print("Datei veraltet, loesche...")
     os.remove(dest+element2)
   elif extension2 == ".vbm":
     print("VBM-Dateien werden nicht geloescht!")
   else:
     print("Dateibackup aktuell")
   if element2 == last_vbk:
     print("Aktuelleste VBK ermittelt!")
     loesch_kz=1

print("Haenge Mountpunkt aus...")

Wenn die ganze VM gekillt wird, könnte es auch sein, dass auch der OOM Killer des Host-Systems zuschlägt.
Es handelt sich um einen Container, nicht um eine VM. Teilweise wird nur das Script gekillt und teilweise der ganze Container.

Wenn du keine saubere Prozetrennung hast, d.h. der oom auf dem Host auftritt, dann solltest du System neu starten und die Aktion wiederholen was gerade lief. Wenn es nur eine VM ist, reicht es die VM neu zu starten.

Es betrifft nur diesen einen Container. Der Rest läuft ganz normal weiter.

Generell sind Kernel-intensive Dinge wie File Copies eigentlich nicht gut in einem Container aufgehoben.

Die Option das in einer VM zu machen hätte ich natürlich auch.
 
UnBreakable schrieb:
Es betrifft nur diesen einen Container.
Was liegt eigentlich direkt im Container? Normalerweise sollten Daten bei Docker doch per Volume persistent eingebunden werden, dann können die dort direkt und ohne Handstand gehandhabt werden.
 
UnBreakable schrieb:
Es betrifft nur diesen einen Container. Der Rest läuft ganz normal weiter.
Das passt tu meiner These mit den cgroups: Der Kernel schiesst den Prozess ab, weil er mehr Speicher braucht, als der cgroup zusteht.
Ergänzung ()

Ich würde anstatt des shutil.copy2 einfach ein cp commando aufrufen. Da du ja scheinbar wenige große Files kopierst, ist der shell overhead vernachlässigbar.

Sowas wie sleep(2) und blind hoffen, dass die zwei Sekunden reichen, macht man eigentlich auch nicht. Besser eine loop, die prüft ob die Mount Punkte eingehängt sind, und dann ggf. weitere zwei Sekunden wartet, bis es entweder klappt oder man z.B. nach 10 Versuchen aufgibt.
Ergänzung ()

Abgesehen davon gibt es für dass, was Du vorhast, fertige Programme wie rsync…
 
Zuletzt bearbeitet:
TomH22 schrieb:
Das passt tu meiner These mit den cgroups: Der Kernel schiesst den Prozess ab, weil er mehr Speicher braucht, als der cgroup zusteht.
Daran sieht man, dass die CGROUPS funktionieren.

Die Frage wäre aber, wieso das Kopieren bei großen Dateien viel RAM benötigt.
UnBreakable schrieb:
Es handelt sich um einen Container, nicht um eine VM. Teilweise wird nur das Script gekillt und teilweise der ganze Container.
Und der Docker Cotainer läuft in einem LXC Container.
UnBreakable schrieb:
Die Option das in einer VM zu machen hätte ich natürlich auch.
Ich finde es nicht heraus, wie shutil.copy2() genau funktioniert. An sich würde ich das Problem in einer Python Community posten.
 
oicfar schrieb:
Die Frage wäre aber, wieso das Kopieren bei großen Dateien viel RAM benötigt.
So wie er Dump aussieht, wird der Speicher vom Blockcache des Kernels angefordert, vermutlich weil er die Dateien per file mapping in den virtuellen Adressraum einblendet.
Ergänzung ()

oicfar schrieb:
Ich finde es nicht heraus, wie shutil.copy2() genau funktioniert. An sich würde ich das Problem in einer Python Community posten.
Möglich, aber vermutlich keine effiziente Problemlösungsstrategie. Einfacher ist es, einen alternativen Weg zu wählen.
 
Und der Docker Cotainer läuft in einem LXC Container.

Sorry, das war vielleicht missverständlich geschrieben. Es handelt sich um einen LXC Container, Docker läuft heir nirgends.

Ich würde anstatt des shutil.copy2 einfach ein cp commando aufrufen. Da du ja scheinbar wenige große Files kopierst, ist der shell overhead vernachlässigbar.

Werde ich mal versuchen.

Sowas wie sleep(2) und blind hoffen, dass die zwei Sekunden reichen, macht man eigentlich auch nicht. Besser eine loop, die prüft ob die Mount Punkte eingehängt sind, und dann ggf. weitere zwei Sekunden wartet, bis es entweder klappt oder man z.B. nach 10 Versuchen aufgibt.
Ich wusste, dass das (zu Recht) angemerkt werden wird ;-)
War eine quick and dirty Lösung, die ich noch anpassen werde.

Abgesehen davon gibt es für dass, was Du vorhast, fertige Programme wie rsync…

Fertige Lösungen habe ich ein par angesehen, aber irgendwie war da immer irgendwas, was dann nicht gepasst hat. Aber ich schau mir rsync nochmal im Detail an, danke für den Hinweis.

Was liegt eigentlich direkt im Container?

Nur das Script. Weder Quelle noch ziel liegt in diesem Container.
 
oicfar schrieb:
Ich finde es nicht heraus, wie shutil.copy2() genau funktioniert
Hab das hier gefunden (Patch, Diff), wo ab Python 3.8 eine Performanceverbesserung für die shutil.* Kopierfunktionen eingeführt wurde. Ab da werden, falls vorhanden, Kopieroperationen über den Kernel des Systems durchgeführt.

Starting from Python 3.8 all functions involving a file copy (:func:copyfile,
:func:copy, :func:copy2, :func:copytree, and :func:move) may use
platform-specific "fast-copy" syscalls in order to copy the file more
efficiently (see :issue:33671).
"fast-copy" means that the copying operation occurs within the kernel, avoiding
the use of userspace buffers in Python as in "outfd.write(infd.read())".
[...]
On Linux, Solaris and other POSIX platforms where :func:os.sendfile supports
copies between 2 regular file descriptors :func:os.sendfile is used.

Python:
[...]
# Zeile 143 vom Diff
def _fastcopy_sendfile(fsrc, fdst):
    """Copy data from one regular mmap-like fd to another by using
    high-performance sendfile(2) syscall.
    This should work on Linux >= 2.6.33 and Solaris only.
    """
[snip]
    offset = 0
    while True:
        try:
            sent = os.sendfile(outfd, infd, offset, blocksize)
        except OSError as err:
            # ...in oder to have a more informative exception.
            err.filename = fsrc.name
            err.filename2 = fdst.name
[...]

Wie sich das Ganze nun auf SMB, virtual Client und Host auswirkt ... keinen Plan. Aber zumindest wissen wir nun, wie shutil.copy funktioniert 😁
 
TomH22 schrieb:
Möglich, aber vermutlich keine effiziente Problemlösungsstrategie. Einfacher ist es, einen alternativen Weg zu wählen.
Ich stelle mir die Frage, ob der umgesetzte Ansatz zum Kopieren von großen Dateien eignet oder nicht. Weil ich davon ausgehe, dass der TE nicht der Erste ist, der so was macht.
frabron schrieb:
Wie sich das Ganze nun auf SMB, virtual Client und Host auswirkt ... keinen Plan. Aber zumindest wissen wir nun, wie shutil.copy funktioniert 😁
Yay.
 
@frabron : Danke, steht so auch direkt in der Doku von shutil (siehe Link in meinem Posting oben) und den Aufruf von sendfile kann man auch in den Kernel Dumps sehen.

Eigentlich sollte dieser Aufruf nicht das Speicherlimit des Containers überschreiten, denn das kann man aus dem Container heraus nicht kontrollieren. Anderseits "weiß" der Kernel überhaupt nichts von Containern, die sind ja nur ein "aufgemotztes" chroot Userland.

Es ist also vermutlich kein "Python-Problem" sondern eines im Zusammenspiel von LXC und Kernel.
 
Zurück
Oben