Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

El corrector deja de procesar entregas si un TP crea archivos sin parar. #17

Open
ndvazquez opened this issue Jun 25, 2018 · 10 comments
Open

Comments

@ndvazquez
Copy link
Contributor

Aparentemente si un alumno envía una entrega en la que por error se crean archivos de 0 bytes dentro de un while (true), el worker que lo ejecuta se cuelga y no se "mata" (perdón por los términos burdos). A causa de esto fetchmail deja de levantar mails y se nos "cuelga" el corrector.

@sportelliluciano sugirió que el problema es que se debe estar quedando sin espacio en la tabla de inodes y parece ser esto.

Antes de ejecutar el TP:

$ df --inodes
Filesystem       Inodes  IUsed   IFree IUse% Mounted on
udev             974766    595  974171    1% /dev
tmpfs            982171    941  981230    1% /run
/dev/sda5      10059776 841725 9218051    9% /

Y 10 segundos después...

$ df --inodes
Filesystem       Inodes   IUsed   IFree IUse% Mounted on
udev             974766     595  974171    1% /dev
tmpfs            982171     941  981230    1% /run
/dev/sda5      10059776 1638916 8420860   17% /

TP que trajo a la luz esto -> https://github.com/algoritmos-rw/algo2_entregas/blob/a4ecac2d21ee97826622bcdb20f95ad6a0b96d2b/tp2/2018_1/100541_100948/analog.c

@sportelliluciano
Copy link

Posible solución: usar Quota para limitar el espacio en disco usado y cantidad de inodes

@maxyz
Copy link
Member

maxyz commented Jun 26, 2018

Los disk quotas son por usuario, por lo que me parece que esto necesitaría lanzar el corrector (la parte que corre el tp entregado) con un usuario "limpio" cada vez (si se corren varios tps distintos a la vez los usuarios también tendrían que ser distintos). Además el corrector tendría que correr también en otro contexto para agarrar el problema. Hace bastante que no veo el código del corrector, por lo que no sé cuanto cambio implica esto.

@sportelliluciano
Copy link

El corrector es un servicio, si corre los procesos de los alumnos de a uno por vez sólo haría falta tener un único usuario con límites y hacer cleanup al final de la ejecución limpiando lo que sea que haya creado.

Otra posible solución, un poco menos invasiva, podría ser crear una imagen con ext4 (un archivo), montarlo, y ejecutar el proceso del alumno con su raíz en una carpeta dentro de esta imagen. De este modo si el proceso floodea los inodes o genera archivos grandes para el corrector no sería más que un sólo archivo grande que puede borrar rápidamente. Y si el tamaño de la imagen aumenta a más de un determinado límite puede optar por matar el proceso del alumno. Crear la imagen y montarla es relativamente rápido y se podría hacer para cada proceso (tal vez habría que pensarlo un poco más).

@maxyz
Copy link
Member

maxyz commented Jun 27, 2018

Mmh, me gusta el segundo approach, podría ser un sparse file temporal de unos 4gb, montado en un punto de montaje temporal. Eso permitiría en el futuro poder correr varias entregas a la vez.
Ahora esto implica darle permiso al corrector de montar/desmontar loop files (que se puede hacer con sudo), pero supongo que no queremos que los alumnos puedan hacer esto (esto podría ser otro usuario o correr el código en un chroot, sin posibilidad de usar sudo). Tengo la idea que el corrector corre las cosas en un chroot...

Ahora faltaría probar que tipo de señal obtiene el proceso invocante cuando un hijo se pasa de su "quota".

@sportelliluciano
Copy link

Yo me imaginaria algo del estilo un proceso maestro, encargado de iniciar un proceso child con el corrector + el proceso del alumno en el entorno chrooted/user correspondiente; y el mismo corrector revisar inodes/espacio utilizado. Así como se suicida si supera los 120seg, podría hacerlo si supera también un threshold de inodes/espacio.

En cuanto tenga un minuto reviso el código del corrector, pero en principio no me parecería una idea descabellada; en el fondo ni el corrector ni el proceso del alumno tendrían permisos de sudo, solo lo tendría el daemon que obtiene los mails y arranca el corrector.

@ndvazquez
Copy link
Contributor Author

@dato Este issue quedó olvidado y creo que estaría bueno solucionarlo, en su momento dio bastantes problemas!

Sería mucho trabajo hacer algo como lo que sugirió Lucho?

@dato
Copy link
Member

dato commented Jan 8, 2019

No entiendo nada de este report.

El corrector corre el código de los alumnos con un tmpfs montado en /tmp; el resto del filesystem se monta como sólo lectura. Así que no entiendo cómo se va a estar comiendo los ínodos del sistema. Además, ¿en qué host corrió ese df? No hay /dev/sda5 en turing.

Acabo de enviar un TP2 con el siguiente algueiza.c:

#include <stdlib.h>

int main(void) {
    system("while true; do mktemp; done");
}

La respuesta fue:

ERROR: El proceso tardó más de 120 segundos

rm -rf algueiza  *.o *.d *.dSYM
cc -g -std=c99 [...] -c -o algueiza.o algueiza.c
cc -g -std=c99 [...] algueiza.o -o algueiza 
cd pruebas && ./pruebas.sh ../algueiza
Ejecución de pruebas unitarias:

01 Prueba: agregar_un archivo

A menos que alguien pueda decirme cómo reproducir el problema, creo que este bug es para cerrar. (Quizá convenga volver a enviar el ZIP de 100541 de nuevo, perdón pero no lo he hecho porque imagino que el deps.mk no encaja, etc. Habría que rearmar el ZIP.)

@ndvazquez
Copy link
Contributor Author

Recién llevé skel al commit con las pruebas del TP2 de 2018_1 e intenté realizar esta misma entrega, y efectivamente deja de responder el corrector. Como bien decís no se come inodes, desconocía lo de tmpfs, mala mía por no investigar apropiadamente.

Luego borré (sí, un asco perdón) el mail del mal en la casilla del corrector, reinicié el servicio e intenté dos entregas diferentes y respondió sin problemas.

Al enviar la entrega de 100541, el proceso del worker se queda colgado aunque no sabría bien por qué, pero asumo que está relacionado con lo de crear archivos de tamaño 0 ya que una vez que 100541 solucionó el bug que creaba archivos sin parar, el corrector le tomó bien la entrega y no hubo más dramas.

Lo que veo con systemctl al realizar la entrega de 100541 es lo siguiente

nvazquez@turing:/srv/apps/corrector-algo2/data/skel$ sudo systemctl status corrector@algo2.service
● corrector@algo2.service - Corrector automático para algo2
   Loaded: loaded (/etc/systemd/system/corrector@.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2019-01-08 19:08:15 -03; 5min ago
 Main PID: 17840 (fetchmail)
    Tasks: 18 (limit: 4915)
   CGroup: /system.slice/system-corrector.slice/corrector@algo2.service
           ├─17840 /usr/bin/fetchmail --daemon 180 --nodetach --mda /srv/apps/corrector-algo2/bin/corrector
           ├─17841 sh -c /srv/apps/corrector-algo2/bin/corrector
           ├─17842 python3.6 /srv/apps/corrector-algo2/bin/corrector
           └─17843 docker run --rm --interactive --net none --env LANG=C.UTF-8 --memory 512m --memory-swap 512m --user nobody:nogroup --cap-drop ALL --read-only --tmpfs /tmp:exec,size=75M --ru

Y estuvo así un buen rato hasta que borré el mail y reinicié el servicio. Parece que no se termina el proceso del worker incluso pasados los 120 segundos (sería el que empieza con docker run -rm blablabla, correcto?).

Si es algo muy falopa y particular de la entrega de 100541 entonces mejor cerrar esto, pero me preocupaba un poco que nos volviera a pasar algo similar durante la cursada.

@dato
Copy link
Member

dato commented Jan 8, 2019

No, si hay un ZIP que cuelga al corrector, eso es un bug y definitivamente se debe solucionar. Perdón si soné brusco en mi respuesta sobre los ínodos.

@dato
Copy link
Member

dato commented Jan 9, 2019

El retraso lo introduce la llamada a shutil.rmtree() para borrar el directorio temporal creado. Es MUY lento. (Si se la sustituye con rm -rf, se bajan dos órdenes de magnitud el tiempo de ejecución.)

El issue de fondo que hay que arreglar es #28: no se aborta al worker tras un timeout porque el que cuelga es código Python nuestro, no el compilado de los alumnos. En la implementación actual solo se controla este último.

Dejo este issue abierto porque, ortogonalmente a arreglar #28, habría que determinar si realmente nos hace falta borrar los contenidos del directorio temporal creado: como expliqué arriba, todos los archivos se crean en un tmpfs efímero, lo cual quiere decir que desaparecen al terminar el container. Cualquier tiempo empleado en cleanup() es tiempo perdido.

No obstante, sí me gustaría que el worker se comporte bien si se ejecuta afuera de Docker (por ejemplo, en una de nuestras máquinas), en cuyo caso sí debería borrar los archivos temporales.

Mi propuesta sería introducir un flag --no-cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants