Containerizing a Model
Overview
This topic focuses on containerizing a FlexSim model using Docker. A containerized model can be used as a service, often as part of a web application. For more general information on Docker and Containers, see Docker's documentation.
This topic is a walkthrough which will guide you through the following steps:
- Create a properly configured license server
- Install Docker Desktop
- Create a model
- Create an image:
- Create a Build Context
- Install FlexSim on the image
- Add a license file to the image
- Add the model to the image
- Testing and debugging a container.
These steps are discussed in the following sections.
Licensing Containers
Containers can only be licensed using a license server. The reccommended strategy is to create
a public license server that has a pool of Runtime licenses for use by containers. Since the license
server is public, you can protect licenses using an arbitrary secret key, configured in the options
file. In a simple case, your options file might look like this:
TIMEOUTALL 900
PROJECT secret-key
INCLUDEALL PROJECT secret-key
In order to check out a license, a client must have an environment variable called
LM_PROJECT
set to secret-key
.
If your license server is not available yet, you can still complete this walkthrough.
When launching from the container, be sure to pass evaluationlicense
as part of the /maintenance
switch, for example:
/maintenance nogui_evaluationlicense
.
For more information about obtaining runtime licenses and creating a license server,
contact your local distributor or reseller.
Installing Docker Desktop
You can download Docker Desktop from the following page: https://www.docker.com/products/docker-desktop/ Note that some commercial use requires a paid subscription. Verify that you comply with Docker's license agreements and policies.
Once you have installed Docker Desktop, launch the application to ensure the Docker engine is running.
Creating a Model
Most FlexSim models can be used in a container. If you want to use the model as a service, you'll need to augment your model to communicate with something external. This section provides some ideas and suggested features that might be helpful when designing that communication. This section is a starting point, but may not cover your use case completely.
One common pattern is to use a Python layer to manage communication:
Python is a very popular language, and so can support connecting to a wide variety of sources. Many web services and databases provide Python packages to connect with those services.
FlexSim can communicate with Python (or other services) through a wide variety of methods, including:
- Reading and writing files
- Excel Import/Export
- XML file import/export
- JSON file import/export
- Generic file I/O
- HTTP requests
- MQTT messages
- SQL Database Connections
- Socket communication
If you are using Python, you might consider using the FlexSimPy module to launch and control FlexSim. See https://github.com/flexsim/FlexSimPy for more information.
In addition, you can use custom command line parameters when launching FlexSim. The basic command-line syntax is as follows:
flexsim.exe path\to\model.fsm /maintenance nogui [additional args]
The model path must come before any other switches or arguments (other than the executable).
The /mainentance nogui
switch is required for running in a container, so that
FlexSim does not try to create any windows. But after that, you can pass anything you want. For example, you could
use Python to retrieve data from an external source and write that data to a file. Then you could launch FlexSim
with the following arguments:
flexsim.exe path\to\model.fsm /maintenance nogui /datafile path\to\file.csv
Then you could add an On Model Open trigger and add code like the following:
string dataFilePath = commandlineparam("datafile");
if (fileexists(dataFilePath)) {
importtable(Table("GlobalTable1"), dataFilePath, 1);
// Run the model
// ...
// Write a file or communicate results somehow
// ...
}
Ultimately, the exact design of your model and container will depend on your needs and preferences.
Creating a Test Model
A useful test case is to ensure that the container can access the license server properly. We can do that by having FlexSim report what licenses it was able to find. Create a model and add the following code to the On Model Open trigger:
if (commandlineparam("showlicense") == "1") {
fileopen(modeldir() + "licenseinfo.txt");
Array features = [
["DragDropConnect", CLF_DRAG_DROP_CONNECT],
["CreateObject", CLF_CREATE_OBJECTS],
["NoModelLimit", CLF_NO_MODEL_LIMIT],
["Stochastics", CLF_STOCHASTICS],
["EntireTree", CLF_ENTIRE_TREE],
["ModelTree", CLF_MODEL_TREE],
["ConsoleScript", CLF_CONSOLE_SCRIPT],
["Compile", CLF_COMPILE],
["CommercialUse", CLF_COMMERCIAL_USE],
["OptQuest", CLF_OPTQUEST],
["XMLSaveLoad", CLF_XMLSAVELOAD],
["StudentOptQuest", CLF_OPTQUEST_STUDENT]
];
for (int i = 1; i <= features.length; i++) {
string featureName = features[i][1];
int hasFeatures = licensehasfeature(features[i][2]);
fpt(featureName + ": " + (hasFeatures ? "Yes" : "No")); fpr();
}
fileclose();
cmdexit();
}
The above code checks to see which license features are available, prints the results to a file, and exits.
To run a model and get results, you only need the core features of a Runtime
license: CLF_NO_MODEL_LIMIT
, CLF_STOCHASTICS
and CLF_COMMERCIAL_USE
.
Creating an Image
Once you have a model, you are ready to create an image. To create an image, you'll first need to create a directory. This directory will become the build context. The goal of this section is to create a folder and structure it as follows:
[folder with a meaningful name] ├─ install │ └── FlexSim_<version>_x64.msi ├─ license │ └── flexsim_server.lic ├─ test │ └── test_model.fsm └─ Dockerfile
To obtain the FlexSim installer, visit the Downloads section of FlexSim's website and download the .msi file (not the .exe file). Place that file in the structure as shown above. Note that you should use the version of FlexSim that the model requires.
To obtain flexsim_server.lic, create a text file called flexsim_server.lic
in the location shown above. That file should have the following text:
SERVER 0.0.0.0 ANY 26914
VENDOR flexsim
USE_SERVER
FEATURE serverfeature flexsim 1.000 permanent uncounted HOSTID=ANY \
SIGN="05CB 6F61 116D 06E3 A08D CAFB FC5C BEF3 DF53 BDC6 AF68 \
060C 27B8 9968 CB94 0515 2BE7 E30C 2FAF C0D6 1D77 CCEB 878E \
2D67 1434 0E3F 6BA5 1FDA BD35 F98D"
However, you should replace the IP address (just after SERVER) with the IP address of your license server.
Save the test model from the previous section in the test
directory as shown.
Saving it with the specified name is convenient, but not required.
Finally, create the Dockerfile
. Create a new text document called Dockerfile (no extension)
in the directory. Give it the following content:
# Start with a windowsservercore image
# See https://hub.docker.com/r/microsoft/windows-servercore for the latest version
FROM mcr.microsoft.com/windows/servercore:ltsc2022
# Copy the install directory from the context (this folder) to an install directory on the image
COPY ./install ./install
# Install FlexSim
RUN msiexec /i install\FlexSim_<version>_x64.msi /quiet /passive /norestart INSTALLDIR=C:\FlexSim
# Copy the license file
ARG programDir="C:/FlexSim/program/"
COPY ./license/flexsim_server.lic $programDir
# Add the programDir to the path
RUN setx path "%path%;%programDir%"
# Enter a secret key for licensing
ENV LM_PROJECT="enter-your-secret-key-here"
# Copy the test directory to make those files available
COPY ./test ./test
# Set the starting command of the image
CMD [ "Cmd.exe" ]
The Dockerfile contains instructions for modifying a base image to create a new one. In this case, we start with a windowsservercore container. Then we run the FlexSim installer and copy over additional files. Be sure to change the <version> of the installer to match the file you downloaded.
Now that you have created a context folder, you can create an image from that context. From a terminal, run the following command:
docker build -t my-container .
The -t
switch specifies an image name. You can change the name if desired.
Troubleshooting
If you run into issues in this step, try the following remedies:
- Verify that Docker is running
- Verify that Docker is in Windows mode
- Verify that the paths and names specified in the Dockerfile match the paths and names in your context folder.
Running a Container
To run a container, you can use the docker run
command:
docker run -i my-container
The last argument is the name of the image you want to run. The -i
switch indicates that you want to interact with the container's terminal.
Once the container is running, you should be able to use the container's terminal. Use the following code to run the test model:
flexsim C:\test\TestLicense.fsm /maintenance nogui /showlicense 1
The terminal command will return immediately, but FlexSim may run for a few more moments. If the license server IP address or LM_PROJECT key is incorrect, it may take more than 30 seconds to attempt to get a license. To detect whether FlexSim is still running from the command line, you can use this command:
tasklist /fi "IMAGENAME eq flexsim.exe"
Once FlexSim finishes, it should have written license info to a text file. You can read that file with the following command:
more test\licenseinfo.txt
If everything is working, you should see that FlexSim was able to get the required licenses from your license server. You can use CTRL-C to exit the container's terminal.
An Example Container
To see an To see an example of running a model as a service, follow these steps:
- Download and extract the following zip file:
ContainerDemo.zip - Download the latest installer of FlexSim as a
.msi
file. Place the installer in theContainerDemo/install
folder. - Update ContainerDemo/Dockerfile so that the installer name matches the one you downloaded.
- In a terminal, navigate to the ContainerDemo directory and build the image:
docker build -t container-demo .
- Run the container:
docker run -d -p 8000:8000 container-demo
- Visit
localhost:8000
in your browser.
You should see a page allowing you to change some model parameters, run the model, and view the result. It can take a few seconds from running the job to seeing the results.
Explanation
The demo has the following files:
- index.html - This page is the default page served up by the webserver. It provides the form. It also provides logic to transform the form to JSON and send it as a POST request to the server, and then to handle the response.
- server.py - This is the heart of the application. It handles HTTP requests from a browser. In addition, it has been customized to handle POST requests. When a POST request is received, server.py writes the JSON data to a temporary file. Then it opens the model in FlexSim, passing in the path to the temporary file. Once the process exits, the resulting data is sent as a request response.
- model.fsm - This model has an On Model Open trigger that checks for a file path. If the file exists, the model sets its parameters according to the file. Then the model runs to time 1000 and writes all Performance Measures to an output file. Finally, FlexSim exits.
- Dockerfile - The Dockerfile uses a Python image. Then it installs FlexSim and copies the server files. Finally, runs the server.py file.
This demo is only meant as a starting point. There are many different strategies that could be used to create a valid container. Each element of this demo could likely be improved. Feel free to modify and improve as needed.