Blog Post

FICO® Xpress Optimization Blog
10 MIN READ

From Code to Container: Deploying FICO® Xpress Python Applications with Dockerfiles

FICO's avatar
FICO
Community Manager
1 year ago
In the world of optimization and decision analytics, deploying applications consistently across environments can be a challenge, especially when dealing with expert dependencies like optimization models and solvers. Docker offers a powerful solution to this problem by allowing developers to package their applications, along with all required libraries and configurations, into lightweight, portable containers.

In this post, we’ll walk through how to containerize a Python application that uses the FICO® Xpress Python interface. You’ll learn how to write a Dockerfile (for a Python application), configure licensing, build and run the container, and even how to enable developer mode (for example, for debugging while running containers). Whether you are a data scientist, operations researcher, or software engineer, this guide will help you streamline your deployment process and make your optimization models easier to share, test, and scale.

You can find the official Dockerfiles and other instructions in the public Git repository for both the Xpress distribution (root) and for the Xpress Python interface (folder “python”). Pre-built images are available in Docker Hub, both for the Xpress distribution and Python. We provide further information on how to use the pre-built images at the end of this post.

View a video demonstration on “Deploying FICO® Xpress Python Applications using Dockerfiles”

What Is a Dockerfile and Why Do You Need One?
A Dockerfile is essentially a blueprint for building a Docker image. It contains a series of instructions that define the environment your application will run in. This includes the base operating system, installed packages, environment variables, and the command that should be executed when the container starts.

Let’s start with a simple facility location example (to select the location of recreational parks over a set of candidate sites that are meant to serve public schools at minimum distance), adjusted from this Python notebook to run the optimization model for a certain number of parks defined as a command line argument. You can download the source file used in this example from our Examples repository.

Assuming the Python script is called facility_location.py, one way the base Python Dockerfile can be adjusted to run your facility location model is as follows:


ARG PYTHON_VERSION="3.12.0"
FROM python:${PYTHON_VERSION}-slim-bullseye
ARG XPRESS_VERSION="9.7.0"
LABEL version="${XPRESS_VERSION}"

WORKDIR /app

COPY src/facility_location.py src/facility_location.py
COPY requirements.txt .

RUN apt-get update -y && apt-get upgrade -y \
    && apt-get install --no-install-recommends -y ca-certificates \
    && update-ca-certificates \
          && pip install --upgrade pip setuptools \
    && pip install xpress==${XPRESS_VERSION} \
    && pip install -r requirements.txt \
    && rm -rf /var/lib/apt/lists/*

CMD ["python", "src/facility_location.py", "4"]

This file tells Docker to start from a Python 3.12 base image (other Python versions can be specified via the PYTHON_VERSION argument), sets the working directory for the containers to be inside the “app” folder (to avoid conflicts with Docker folders and files), and copies your project files into the container’s working directory. The RUN section then runs a series of commands to update the package list and upgrade installed packages, installs certificate authorities needed for secure connections and refreshes the system's list of trusted certificates. Then, it upgrades pip and setuptools to ensure compatibility with Python packages, installs the Xpress package and the packages specified in the file requirements.txt, which is in this case located at the root of the local directory. The last line runs the Python script for the case of building 4 parks. It’s a minimal setup that gives you a reproducible environment (if all the versions are fixed) that can be deployed anywhere Docker is supported.

Configuring the Dockerfile for FICO Xpress licensing – Static and Web-floating
Although the community license of FICO Xpress is included in the Xpress Python package, a valid full license is commonly necessary to run models for industry-size problems. To include your (not node-locked) static or web-floating license, we need to make sure your license file is available inside the container and that the application knows where to find it. To do this, we can copy the license file from a local_lic_dir folder to an analogous path inside the image by extending the COPY commands with:

COPY local_lic_dir/xpauth.xpr lic/xpauth.xpr

Next, we can use the ENV instruction in the Dockerfile to define the path to the license file inside the image as an environment variable:

ENV XPAUTH_PATH=lic/xpauth.xpr

In your Python code, you need to explicitly retrieve the path to the license file and initialize the license using the FICO Xpress API:

import xpress as xp
import os
...
xpauth_path = os.getenv("XPAUTH_PATH")
xp.init(xpauth_path)

From Xpress 9.6 onwards a web-floating licensing system is available, suitable for running Xpress in Docker. The license file uses entries in JSON format containing the license key, and credentials for OAuth2 authorization as follows:

{
"oauth2": {
"client_id": "your_client_id",
"client_secret": "your_client_secret"
},
"license": "AAAA-BBBB-CCCC-DDDD",
"options": {
"floating_timeout_ratio": 2
}
}

Replacing the contents of the regular xpauth.xpr file with this dictionary enables the web floating license to be used in your program instead of a static license. At the end of your script, remember to call  xp.free() to release the license to be used by other users.

Building the Docker Image
Once your Dockerfile and other local files are ready, you can build the image by running the “docker build” command in a terminal from the working directory where the Dockerfile is located. Here’s an example that passes in Xpress 9.7 and Python 3.12 versions as build arguments:

docker image build \
--build-arg XPRESS_VERSION=9.7.0 \
--build-arg PYTHON_VERSION=3.12.0 \
--tag xpress/python .

This command tells Docker to build an image for the specified build arguments and tag the resulting image as “xpress/python”. The dot in the end sets the current directory as the context for the build process. If you are using Docker Desktop, you can visually confirm that the image was created by opening the "Images" tab. From there, you can click "Run" to launch a container and test your setup.

Running the Container
To run the container from the command line, use the following command:

docker run --interactive --tty xpress/python

This starts the container in interactive mode, giving you access to a terminal session inside the container. It’s a convenient way to test and debug your application. To run the container for a different argument value simply add “python <script name> <arg1> <arg2> …” to the previous command. For instance, to run the container for the case of building 3 parks with our example script:

docker run --interactive --tty xpress/python python src/facility_location.py 3

This will override the CMD instruction in the Dockerfile when running the container. If your application logs output to the console, you can view it using:

docker logs <container_id>

You can also use Docker Desktop to inspect the container, view logs, and even open a terminal session directly from the UI.

Developing Inside a Container with VS Code Dev Containers extension
If you are a developer who is familiar with Visual Studio (VS) Code and would like to be able to edit or debug your files while your Docker container is running, the Dev Containers extension (part of the Remote Development pack) offers a seamless way to develop inside a Docker container. This is especially useful when working with complex environments where you want to ensure consistency between development and production setups.

Dev Containers allow you to open your project inside a fully configured Docker container, use the same environment across your team, install extensions and dependencies directly in the container, and avoid polluting your local machine with project-specific packages.

To set up a Dev Container, create a .devcontainer folder in the root folder of your project (where the Dockerfile is located) and create a devcontainer.json file inside that folder with the following contents:

{
  "name": "FICO Xpress Dev Container",
  "build": {
    "dockerfile": "../Dockerfile",
    "context": "..",
    "workspaceFolder": "/app",
    "args": {
      "XPRESS_VERSION": "9.7.0",
      "PYTHON_VERSION": "3.12.0"
    }
  },
  "extensions": [
    "ms-python.python"
  ],
  "mounts": [
    "source=${localWorkspaceFolder}/local_src_dir,target=/app/src,type=bind",
    "source=${localWorkspaceFolder}/local_lic_dir,target=/app/lic,type=bind"
  ],
  "postCreateCommand": "python /app/src/facility_location.py 4"
}

The "build" section plays a key role in telling Docker where to look for the files it needs to build the container. The inner “dockerfile” field states the path to the Dockerfile used to build the container image, the “context” field sets the build context to the parent directory (..) relative to the location of the devcontainer.json file, and “args” defines the custom variables for flexible Dockerfile builds. The section “extensions” tells VS Code to install the official Python extension (ms-python.python) inside the container environment for development support with syntax highlighting, debugging, etc.

To bind mount the local source and license files for development, you must add a “mounts” section. This field defines what files are available to be edited after the container starts by defining the bind mounts that make local directories available inside the container. By using ${localWorkspaceFolder} in your mounts you will be sure to bind local folders (available in the /workspaces/<project_name> folder inside your container) to the app directory at the root of the container. Changes in the local files will be automatically reflected in the application files. Since the container’s working directory is now at /workspaces/<project_name>, you must use absolute paths when referring to external files, such as when running the Python script via the “postCreateCommand” field, or when defining the license file path in the Dockerfile:

ARG PYTHON_VERSION="3.12.0"
FROM python:${PYTHON_VERSION}-slim-bullseye
ARG XPRESS_VERSION="9.7.0"
LABEL version="${XPRESS_VERSION}"

WORKDIR /app

COPY requirements.txt .

ENV XPAUTH_PATH=/app/lic/xpauth.xpr

RUN apt-get update -y && apt-get upgrade -y \
    && apt-get install --no-install-recommends -y ca-certificates \
    && update-ca-certificates \
    && pip install --upgrade pip setuptools \
    && pip install xpress==${XPRESS_VERSION} \
    && pip install -r requirements.txt \
    && rm -rf /var/lib/apt/lists/*

By opening the root directory of your project using VS code with the Dev Containers extension installed, the devcontainer.json file is automatically detected. Select the option “Reopen in container” to instruct VS code to build a Docker container based on the detected configuration and open your project inside that container, so your development environment runs inside the Docker container instead of your local machine. Then you can edit/debug the files set up with mount binds and re-run the container on the fly.

View a video demonstration on “Deploying FICO® Xpress Python Applications using Dockerfiles”

Running a container from the pre-built image available in Docker Hub
Running a container from the FICO-supported pre-built image available on Docker Hub offers a fast and convenient way to get started, especially when working with widely used tools or frameworks. It saves time by eliminating the need to write and maintain a Dockerfile, and ensures consistency by using a standardized, community-tested environment. This is particularly useful for prototyping, testing, or onboarding new team members quickly.

To run a container from the FICO provided image for the example use case described above, you can simply run the following command from the command line ([local_path] should be replaced by the full local path to the corresponding file or directory):

docker run \
    --interactive --tty --name xpress_ctn --hostname xpressmp \
    --env XPAUTH_PATH=app/lic/xpauth.xpr \
    --volume "[local_path]/requirements.txt:/app/requirements.txt" \
    --volume "[local_path]/local_src_dir:/app/src " \
    --volume "[local_path]/local_lic_dir:/app/lic " \
    ficoxpress/xpress-solver-python \
    bash -c "apt-get update -y \
            && apt-get install --no-install-recommends -y ca-certificates \
            && update-ca-certificates \
            && pip install -r app/requirements.txt \
            && /bin/bash"

This command runs a container from the ficoxpress/xpress-solver-python image, giving it a custom name (xpress_ctn) and a hostname (xpressmp). It then sets an environment variable XPAUTH_PATH to point to the license file inside the container. It mounts three volumes from your local machine into the container: a requirements.txt file, a source code directory (src), and a license directory (lic). Once the container starts, it updates the package list, installs up-to-date SSL certificates to allow use of HTTPS, installs Python dependencies from requirements.txt, and finally opens an interactive shell (/bin/bash) so you can work inside the container.

Once the container starts, run the command python app/src/facility_location.py 4 from within the container to run the Python script for building 4 parks and visualize the outputs.

Keep in mind that when using a pre-built image you have limited control over the image’s contents, which may include unnecessary packages or outdated dependencies. Building your own image, while more time-consuming, gives you full control over the environment, allowing for improved efficiency, security, and reproducibility tailored to your specific needs.

Conclusion
By containerizing your FICO Xpress Python applications, you gain a powerful tool for ensuring consistency (for fixed versions), portability, and scalability of your optimization project/model runs. With this blog, you have learned how to write a Dockerfile, handle licensing, build and run containers, and even how to extend your setup using developer mode for VS Code. This approach is ideal for teams working on optimization models who want to streamline deployment, reduce environment-related bugs, and prepare for cloud-native workflows.

Try it yourself: clone the GitHub repository with the base Dockerfiles and experiment with customizing them for your own use case, or download the Python source file used in this post. Share your feedback or questions in the comments—we’d love to hear from you. Subscribe to our blog or YouTube channel and follow our Jupyter notebooks repo for more tutorials on optimization, Python, and DevOps best practices.
Updated 1 year ago
Version 3.0
No CommentsBe the first to comment