Skip to content

Demonstrates how do build Docker containers to run Fortran apps

Notifications You must be signed in to change notification settings

gronki/docker-demo-fortran

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Containerizing your Fortran application using Docker

Introduction

We all have been in this place before, where we created a code which runs fine on our local machine, and then face the distressing task of running it somewhere else (computing server, collaborator's machine, etc). It may turn out okay if we need to install it on another computer ourselves: we might run into some library issues (different versions) or compiler issues (particularly common in Fortran, where older compilers do not support some of the features of the language). However, it becomes a complete disaster when attempting to hand it over to someone not that proficient in Linux administration, or even worse -- running different OS, such as Windows or Mac OS. How do we deal with that? Docker comes with help.

Example program

Our example program is simple, and solver the 2x2 linear equation system, using a procedure delived by LAPACK library (particularly, its modern implementation -- OpenBLAS). The source is just one file:

program solver

    implicit none

    real :: a(2, 2), b(2), x(2)
    character(len=128) :: input_file_name

    call get_file_name(input_file_name)

    call read_problem(input_file_name, a, b)

    call solve_problem(a, b, x)

    print *, x

contains

    subroutine get_file_name(input_file_name)
        character(len=*) :: input_file_name

        call get_command_argument(1, input_file_name)

        if (input_file_name == "") then
            print '(a)', "usage: solver <input file name>"
            stop 1
        end if
    end subroutine

    subroutine read_problem(input_file_name, a, b)
        real :: a(2, 2), b(2)
        character(len=128) :: input_file_name

        open(unit=11, file=input_file_name, action='read')
        read(unit=11, fmt=*) a(1,:), b(1)
        read(unit=11, fmt=*) a(2,:), b(2)
        close(unit=11)

    end subroutine

    subroutine solve_problem(a, b, x)
        interface
            SUBROUTINE sgesv( N, NRHS, A, LDA, IPIV, B, LDB, INFO )
                INTEGER            INFO, LDA, LDB, N, NRHS
                INTEGER            IPIV( * )
                REAL               A( LDA, * ), B( LDB, * )
            end SUBROUTINE
        end interface

        real :: a(2, 2), b(2), x(2)
        integer :: ipiv(2), info

        x(:) = b(:)
        call sgesv(2, 1, a, 2, ipiv, x, 2, info)

        if (info /= 0) then
            error stop 'sgesv failed to compute the result'
        end if

    end subroutine

end program

Typically, on a Linux machine, we would compile it using a command:

f95 -g -O2 solve_problem.f90 -lopenblas -o solve_problem

The example usage would be solving a following equation system:

1x + 2y = 5
3x + 2y = 7

We must put our coefficients into a file, and the pass the file name as the program argument:

cat > input.txt <<EOF
1.0 2.0   5.0
3.0 2.0   7.0
EOF
./solve_problem input.txt

We would get an output:

   1.00000000       2.00000000    

Creating the image

Building an image can be translated as "building the minimal Linux environment, that has all the required components for our program to run". In our example, we are based on Ubuntu, a very popular Linux distribution. The manifest file where we describe how to build the environment is named Dockerfile. In the first part, we specify that our image is based on ubuntu, and install the required libraries (OpenBLAS, in our case):

# base this image on the last release of Ubuntu
FROM ubuntu

# install the required environment and then clean up the cache
# to not unnecesarily bloat the image
# notice we choose to do it in one RUN block, to not create
# extra layers
RUN apt-get update && \
    apt-get install -y gfortran libopenblas-dev && \
    apt-get clean

Next, we copy the content of the folder (assuming it is the main repository directory) into the image filesystem under the destination /opt/build, compile our program, install it and clean up after the build:

# copy the whole directory content into /opt/build
# excluding the files specified in .dockerignore
# which typically will be similar to .gitignore
COPY . /opt/build/

# in this step, we enter the build directory and compile our program.
# we then install it to /usr/local/bin directory and delete
# the build directory, again to make the image smaller.
# the directory change does not propagate to any following
# Dockerfile statements, this is why we executed it all in one
# RUN block, joining the commands with &&
RUN cd /opt/build && \
    f95 -g -O2 solve_problem.f90 -lopenblas -o solve_problem && \
    install solve_problem /usr/local/bin/ && \
    rm -rf /opt/build

The last step is to specify the working directory after a new container is created from our image and what should be executed at the start:

# this specified, that /work should be mounted as a volume
# since Docker containers are isolated from your local data
# unless you specifically grant them access
VOLUME [ "/work" ]

# this actually changes the working directory to /work
WORKDIR /work

# finally, we specify what command will be executed
# when the container is run. this is of course our program
ENTRYPOINT ["/usr/local/bin/solve_problem"]

Bulding and running the container

Building the image takes just a minute (in our simple case) and one command. Place the Dockerfile and the source file in one directory (or just download this repository) and run:

docker build -t equation_solver .

(Note. You might need to add sudo in front of the docker command, unless you configure your system otherwise.)

If the build succeeds, now we can execute our application by running:

docker run -it equation_solver input.txt

But wait, something failed! We get an error:

At line 33 of file solve_problem.f90 (unit = 11)
Fortran runtime error: Cannot open file 'input.txt': No such file or directory

Indeed, Docker isolates the application filesystem from the host filesystem, therefore our program is unable to see the input file. However, we can easily fix that by using Docker volumes. We will mount the host system directory where the input.txt file is located (it being /absolute/path/to/data in our example -- and yes, it needs to be an absolute path!) to /work directory inside the container, which is the working directory of our application (see the Dockerfile).

docker run -it -v /absolute/path/to/data:/work equation_solver input.txt

Just as before, we should get a correct result, except we can build and run our application the same way on any other system that supports Docker.

   1.00000000       2.00000000    

Building the image straight from the git repository

There is a very nice feature of Docker and Git: you are able to distribute your application, and make it as easy to run it as two commands! Try the following:

docker build -t equation_solver https://github.com/gronki/docker-demo-fortran.git

Using this one simple command, you should be able to build the image of our example application! How easy is that?

About

Demonstrates how do build Docker containers to run Fortran apps

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published