Containers

Containers são uma forma de empacotar software com todas as suas dependências, criando ambientes isolados e reproduzíveis. No GridUnesp, utilizamos o Apptainer (anteriormente Singularity) para executar containers.

Por que Usar Containers?

  1. Reprodutibilidade: O mesmo ambiente exato pode ser usado por diferentes usuários

  2. Isolamento: Dependências conflitantes não interferem

  3. Portabilidade: O mesmo container funciona em diferentes sistemas

  4. Facilidade: Distribua seu ambiente com um único arquivo

  5. Segurança: Containers Apptainer não exigem privilégios root

Apptainer vs Docker

Apptainer é similar ao Docker, mas projetado para HPC:

Característica

Docker

Apptainer

Permissões

Requer root

Usuário normal

Integração HPC

Limitada

Nativa (MPI, GPUs)

Imagens

Camadas

Arquivo único (.sif)

Segurança

Menos segura em HPC

Projetada para ambientes compartilhados

Conceitos Básicos

  • Imagem: Arquivo contendo o sistema de arquivos do container (.sif)

  • Definição: Arquivo de texto com instruções para construir a imagem (.def)

  • Sandbox: Diretório para desenvolvimento interativo (apptainer shell image.sif)

  • Registry: Repositório de imagens (Docker Hub, etc.)

Obtendo Imagens

Método 1: Pull do Docker Hub

Job para fazer o download e construir o container (um arquivo com sufixo “.sif”):

pull_container.sh
#!/bin/bash
#SBATCH -J pull_container
#SBATCH -t 01:00:00
#SBATCH -n 1

export INPUT=""
export OUTPUT="ubuntu.sif"

job-nanny apptainer pull ubuntu.sif docker://ubuntu:20.04

Neste caso, é construído o container ubuntu.sif, o qual é uma imagem do Ubuntu 20.04.

Método 2: Construir a partir de definição

Arquivo de definição (ubuntu.def):

ubuntu.def
Bootstrap: docker
From: ubuntu:20.04

%post
    apt-get update
    apt-get install -y python3 python3-pip
    pip3 install numpy pandas matplotlib

%environment
    export LC_ALL=C

%runscript
    echo "Container Ubuntu com Python e pacotes científicos"
    python3 "$@"

Script de construção (ubuntu.sif):

build_container.sh
#!/bin/bash
#SBATCH -J build_container
#SBATCH -t 02:00:00
#SBATCH -n 1
#SBATCH --mem=4G

export INPUT="ubuntu.def"
export OUTPUT="ubuntu.sif"

job-nanny apptainer build ubuntu.sif ubuntu.def

Executando Containers

Modos de execução:

  1. apptainer run: Executa o comando padrão definido no container

  2. apptainer exec: Executa um comando específico

  3. apptainer shell: Abre um shell interativo

Exemplo básico:

run_container.sh
#!/bin/bash
#SBATCH -J run_container
#SBATCH -t 01:00:00
#SBATCH -n 1

export INPUT="ubuntu.sif script.py dados/"
export OUTPUT="resultados/"

job-nanny apptainer exec ubuntu.sif python3 script.py

Exemplo com shell interativo (para testes):

apptainer shell ubuntu.sif

# Dentro do container
ls -la
python3
exit

Compartilhando Arquivos

Por padrão, Apptainer monta automaticamente:

  • $HOME do usuário

  • Diretório atual de trabalho

  • Diretórios temporários do sistema

Montagem explícita:

# Montar diretórios adicionais
apptainer exec --bind /caminho/no/host:/caminho/no/container ubuntu.sif comando

No job-nanny:

container_com_dados.sh
#!/bin/bash
#SBATCH -J container_dados
#SBATCH -t 02:00:00
#SBATCH -n 1

export INPUT="ubuntu.sif dados/ script.py"
export OUTPUT="resultados/"

# O job-nanny copia INPUT para a área de trabalho
# Dentro do container, os arquivos estarão no diretório atual
job-nanny apptainer exec ubuntu.sif python3 script.py

Containers com GPU

Para usar GPUs, adicione a flag --nv:

container_gpu.def
Bootstrap: docker
From: nvidia/cuda:11.8.0-runtime-ubuntu20.04

%post
    apt-get update
    apt-get install -y python3 python3-pip
    pip3 install torch torchvision

%runscript
    python3 "$@"

Construção:

job_gpu_container.sh
# Submeter job
#!/bin/bash
#SBATCH -J gpu_container
#SBATCH --partition=gpu
#SBATCH --gres=gpu:1
#SBATCH -t 02:00:00
#SBATCH -n 1

export INPUT="container_gpu.def treinamento.py"
export OUTPUT="modelo.pth"

module load cuda/11.8  # Opcional, Apptainer usa CUDA do container

job-nanny apptainer build --nv cuda_container.sif container_gpu.def
job-nanny apptainer exec --nv cuda_container.sif python3 treinamento.py

Execução:

sbatch job_gpu_container.sh

Containers com MPI

Apptainer pode executar aplicações MPI em múltiplos nós.

Arquivo de definição com MPI:

mpi_container.def
Bootstrap: docker
From: ubuntu:22.04

%environment
    export OMPI_DIR=/opt/ompi
    export PATH=$OMPI_DIR/bin:$PATH
    export LD_LIBRARY_PATH=$OMPI_DIR/lib:$LD_LIBRARY_PATH

%post
    apt-get update
    apt-get install -y wget build-essential

    # Instalar OpenMPI
    export OMPI_DIR=/opt/ompi
    export OMPI_VERSION=4.1.5
    mkdir -p /tmp/ompi
    cd /tmp/ompi
    wget https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-$OMPI_VERSION.tar.bz2
    tar -xjf openmpi-$OMPI_VERSION.tar.bz2
    cd openmpi-$OMPI_VERSION
    ./configure --prefix=$OMPI_DIR
    make -j4 install

    # Instalar compiladores e bibliotecas
    apt-get install -y gcc g++ gfortran

%files
    mpitest.c /opt/mpitest.c

Código MPI de exemplo (mpitest.c):

#include <mpi.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

    int world_rank, world_size;
    char hostname[256];

    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
    gethostname(hostname, sizeof(hostname));

    printf("Processo %d de %d em %s\n", world_rank, world_size, hostname);

    MPI_Finalize();
    return 0;
}

Scripts para construção, compilação e execução:

build_mpi_container.sh
#!/bin/bash
#SBATCH -J build_mpi
#SBATCH -t 02:00:00
#SBATCH -n 1

export INPUT="mpi_container.def"
export OUTPUT="mpi_container.sif"

job-nanny apptainer build mpi_container.sif mpi_container.def
compile_in_container.sh
#!/bin/bash
#SBATCH -J compile_mpi
#SBATCH -t 00:30:00
#SBATCH -n 1

export INPUT="mpi_container.sif mpitest.c"
export OUTPUT="mpitest"

job-nanny apptainer exec mpi_container.sif mpicc -o mpitest /opt/mpitest.c
run_mpi_container.sh
#!/bin/bash
#SBATCH -J run_mpi
#SBATCH -N 4
#SBATCH --ntasks-per-node=28
#SBATCH -t 01:00:00
#SBATCH --mem-per-cpu=2G

export INPUT="mpi_container.sif mpitest"
export OUTPUT="mpi_output/"

module load openmpi/4.1.5  # MPI do sistema para orquestração

# --sharens evita conflitos de namespace
job-nanny mpirun -n 112 apptainer exec --sharens mpi_container.sif ./mpitest

Este job irá executar 112 processos, sendo 28 deles em cada nó. O OpenMPI utilizado é aquele já instalado no container (mpi_container.sif) e o executável (mpitest) é processado dentro deste container. O MPI instaldo no container é processado em conjunto com aquele do sistema, carregado via módulo (module load openmpi/4.1.5). Note que a versão do OpenMPI instalada no container, para o exemplo acima, é a 4.1.5, que é a mesma versão do módulo. As versões de ambos os OpenMPI devem ser compatíveis.

Atenção

Segundo a seção Using –sharens mode da página do Apptainer, no intuito de garantir que não haja conflito relacionado a namespace na paralelização, recomenda-se incluir o parâmetro --sharens logo após o comando exec. Assim: apptainer exec --sharens.

Containers para Aplicações Específicas

Exemplo: GROMACS em container

gromacs.def
Bootstrap: docker
From: ubuntu:22.04

%post
    apt-get update
    apt-get install -y wget cmake build-essential

    # Instalar GROMACS
    wget ftp://ftp.gromacs.org/pub/gromacs/gromacs-2023.tar.gz
    tar -xzf gromacs-2023.tar.gz
    cd gromacs-2023
    mkdir build
    cd build
    cmake .. -DGMX_BUILD_OWN_FFTW=ON -DCMAKE_INSTALL_PREFIX=/usr/local/gromacs
    make -j4
    make install

%environment
    export PATH=/usr/local/gromacs/bin:$PATH
    export LD_LIBRARY_PATH=/usr/local/gromacs/lib:$LD_LIBRARY_PATH
build_gromacs.sh
#!/bin/bash
#SBATCH -J build_gromacs
#SBATCH -t 04:00:00
#SBATCH -n 1

export INPUT="gromacs.def"
export OUTPUT="gromacs.sif"

job-nanny apptainer build gromacs.sif gromacs.def
run_gromacs.sh
#!/bin/bash
#SBATCH -J gromacs_container
#SBATCH -N 2
#SBATCH --ntasks-per-node=28
#SBATCH -t 24:00:00

export INPUT="gromacs.sif topol.tpr"
export OUTPUT="resultado_gromacs/"

module load openmpi/4.1.5

job-nanny mpirun -n 56 apptainer exec gromacs.sif mdrun_mpi -deffnm resultado

Exemplo: Python com bibliotecas específicas

python_sci.def
Bootstrap: docker
From: python:3.9-slim

%post
    pip install --upgrade pip
    pip install numpy scipy pandas matplotlib scikit-learn jupyter

%runscript
    python "$@"
build_python_sci.sh
#!/bin/bash
#SBATCH -J build_python_sci
#SBATCH -t 02:00:00
#SBATCH -n 1

export INPUT="python_sci.def"
export OUTPUT="python_sci.sif"

job-nanny apptainer build python_sci.sif python_sci.def
run_python.sh
#!/bin/bash
#SBATCH -J python_container
#SBATCH -n 1
#SBATCH -t 02:00:00

export INPUT="python_sci.sif analise.py dados.csv"
export OUTPUT="resultados_analise/"

job-nanny apptainer exec python_sci.sif python analise.py

Boas Práticas com Containers

  1. Imagens leves

    • Use imagens base mínimas (slim, alpine)

    • Remova caches desnecessários no %post

      %post
      
           apt-get update && apt-get install -y \
           git \
           python3 \
           && rm -rf /var/lib/apt/lists/*
      
  2. Versionamento

    Bootstrap: docker
    From: ubuntu:20.04  # Versão fixa, não "latest"
    
  3. Reprodutibilidade

    • Inclua versões exatas dos pacotes

    • Documente o processo de construção

  4. Segurança

    • Não coloque senhas ou chaves na imagem

    • Use variáveis de ambiente para configurações sensíveis

  5. Organização

    meus_containers/
    ├── ubuntu-python/
    │   ├── ubuntu-python.def
    │   ├── build.slurm
    │   └── ubuntu-python.sif
    ├── gromacs/
    │   ├── gromacs.def
    │   └── build.slurm
    └── mpi-base/
        ├── mpi.def
        └── README.md
    
  6. Limpeza

    • Remova caches desnecessários em /home/$USER/.apptainer/

      apptainer cache clean
      

Solução de Problemas

Erro: “no space left on device” durante build

  • Use a opção LARGE_FILES="true" do job-nanny.

    #!/bin/bash
    #SBATCH -J build_ubuntu
    #SBATCH -t 02:00:00
    #SBATCH -n 1
    
    export INPUT="ubuntu.def"
    export OUTPUT="ubuntu.sif"
    
    export LARGE_FILES="true"
    
    job-nanny apptainer build ubuntu.sif ubuntu.def
    

Erro: “permission denied”

  • Verifique permissões do arquivo .sif

  • Certifique-se de que o container não requer root

Erro: “GPU not available” dentro do container

  • Use flag --nv

  • Verifique se CUDA está instalado no container

Erro: “MPI version mismatch”

  • Use a flag --sharens

  • Compile MPI no container com mesma versão do sistema (ou versão menor)