.. _containers: ========== Containers ========== .. contents:: Nesta seção: :local: :depth: 2 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”): .. code-block:: bash :caption: 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``): .. code-block:: text :caption: 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``): .. code-block:: bash :caption: 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:** .. code-block:: bash :caption: 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):** .. code-block:: bash 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:** .. code-block:: bash # Montar diretórios adicionais apptainer exec --bind /caminho/no/host:/caminho/no/container ubuntu.sif comando **No job-nanny:** .. code-block:: bash :caption: 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``: .. code-block:: bash :caption: 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: .. code-block:: bash :caption: 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: .. code-block:: bash 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:** .. code-block:: text :caption: 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):** .. code-block:: c #include #include #include 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:** .. code-block:: bash :caption: 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 .. code-block:: bash :caption: 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 .. code-block:: bash :caption: 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. .. attention:: 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 ------------------------------ .. code-block:: text :caption: 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 .. code-block:: bash :caption: 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 .. code-block:: bash :caption: 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 -------------------------------------------- .. code-block:: text :caption: 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 "$@" .. code-block:: bash :caption: 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 .. code-block:: bash :caption: 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 .. code-block:: text %post apt-get update && apt-get install -y \ git \ python3 \ && rm -rf /var/lib/apt/lists/* 2. **Versionamento** .. code-block:: text 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** .. code-block:: text 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/`` .. code-block:: text 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``. .. code-block:: bash #!/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) .. seealso:: - :ref:`usando_job_nanny` - :ref:`instalando_aplicacoes` - Alternativas de instalação - :ref:`uso_gpus` - :ref:`memoria_distribuida` - MPI - `Documentação do Apptainer `_