Helpful Information

The purpose of this page is to supplement Apptainer documentation.

Apptainer definition files (Definition Files — Apptainer User Guide 1.0 documentation):

Ex:

Bootstrap: docker
From: python:3.9

%files
   /sdf/home/p/pnispero/BuildSystem/socket_server.py /

%labels
    Author pnispero
    Version v0.0.1

%help
    This is a demo container used to illustrate a def file that uses all
    supported sections.

%post
    apt update
    echo "Hello from inside the container"

%runscript
    echo "Container was created $NOW"
    echo "Arguments received: $*"
    echo "Running socket_server.py"
    python3 /socket_server.py
    exec echo "$@"


  1. Boostrap - The boostrap is a boostrap agent (dont know what that means exactly) but in this case it is docker, which will pull a base image from https://hub.docker.com/
  2. From - Here is where you specify the base image, it can be an OS, or in this case we are just getting the packages necessary to run a python script. 
  3. Files - <source> <destination>, I find that if you copy the files, its good to specify destination, in this case I specify '/' which is the root dir of the container. 

Example ( EEDSWCM-3 - Getting issue details... STATUS )

  1. I created a basic server using Golang

    // socket-server project main.go
    package main
    import (
            "fmt"
            "net"
            "os"
    )
    const (
            SERVER_PORT = "5000"
            SERVER_TYPE = "tcp"
    )
    func main() {
        SERVER_HOST, err := os.Hostname()
        if err != nil {
            fmt.Println("Error getting host:", err.Error())
            os.Exit(1)
        }
        fmt.Println("Server Running...")
        server, err := net.Listen(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
        if err != nil {
            fmt.Println("Error listening:", err.Error())
            os.Exit(1)
        }
        fmt.Println("Listening on " + SERVER_HOST + ":" + SERVER_PORT)
        fmt.Println("Waiting for client...")
        for {
            connection, err := server.Accept()
            if err != nil {
                fmt.Println("Error accepting: ", err.Error())
                os.Exit(1)
            }
            fmt.Println("client connected")
            // 'go' calls the processClient concurrently
            // this next iteration of for loop continues if processClient is I/O bound
            go processClient(connection)
        }
        server.Close()
    }
    func processClient(connection net.Conn) {
        for { // Handle client requests forever until client disconnects
            buffer := make([]byte, 1024)
            mLen, err := connection.Read(buffer)
            if err != nil {
                fmt.Println("Error reading:", err.Error())
                break
            }
            fmt.Println("Received: ", string(buffer[:mLen]))
            _, err = connection.Write([]byte("Thanks! Got your message:" + string(buffer[:mLen])))
        }   
        connection.Close()
    }

  2. I created a definition file for it

    Bootstrap: docker
    From: golang:1.22.2

    %files
       /sdf/home/p/pnispero/container_test/socket_server.go /

    %labels
        Author pnispero
        Version v0.0.1

    %help
        This is a demo container used to illustrate a def file that uses all
        supported sections.

    %post
        apt update
        echo "Hello from inside the container"
        cd /
        go build socket_server.go

    %runscript
        echo "Container was created $NOW"
        echo "Running socket_server"
        cd /
        ./socket_server
        exec echo "$@"

  3. I also made a simple client in python

    import socket


    def client_program():
        host = 'sdfiana002' # TODO: Change this if you host it somewhere else
        port = 5000  # socket server port number

        client_socket = socket.socket()  # instantiate
        client_socket.connect((host, port))  # connect to the server

        message = input(" -> ")  # take input

        while message.lower().strip() != 'bye':
            client_socket.send(message.encode())  # send message
            data = client_socket.recv(1024).decode()  # receive response

            print('Received from server: ' + data)  # show in terminal

            message = input(" -> ")  # again take input

        client_socket.close()  # close the connection


    if __name__ == '__main__':
        client_program()

  4. So, I created the image (Fun fact you can build without a go compiler because the compiler is taken from docker hub thats specified in the second step .def file)

    (goenv) [pnispero@sdfiana002 container_test]$ apptainer build socket_server.sif socket_server.def

  5. Start the container 

    (goenv) [pnispero@sdfiana002 container_test]$ apptainer instance start socket_server.sif socket_server1
  6. Check that the container is alive 

    (goenv) [pnispero@sdfiana002 container_test]$ apptainer instance list
  7. Run the runscript of the container 

    (goenv) [pnispero@sdfiana002 container_test]$ apptainer run instance://socket_server1

    Screenshot of steps 5-7 (ignore the error)

  8. Run the client 

    (goenv) [pnispero@sdfiana001 container_test]$ python3 socket_client.py

    Screenshot

  9. Then once done, kill the container (note - If you ctrl+c the socket_server it will exit the go program, but the container will still run)

    (goenv) [pnispero@sdfiana002 container_test]$ apptainer instance stop socket_server1


  • No labels