Deploying to Docker with a CLI runner
Nextmv engines and apps are capable of being deployed to Docker containers. The following recipe demonstrates building and deploying our Knapsack engine into a Docker container using the CLI runner.
Files
This recipe can be built in any directory. We use knapsack-cli as the top
level folder. We will create the following files.
knapsack-cli
├── Dockerfile
├── data
│ └── input-11items.json
├── knapsack-cli
├── go.mod
├── go.sum
└── main.go
Dockerfile
Create a Dockerfile in the project root that contains the following code.
FROM gcr.io/distroless/static:nonroot
ENV HOP_RUNNER_OUTPUT_SOLUTIONS last
ENV HOP_SOLVER_LIMITS_DURATION 1s
COPY knapsack-cli /
ENTRYPOINT ["/knapsack-cli"]
We use a "distroless" base Docker image. We set environment variables in the image to limit model runtime and to print only the last solution to standard out.
More configuration options can be found in the
Environment Variables and CLI Options section of our docs. The
COPY command copies the executable from our local machine to the Docker image.
The ENTRYPOINT configures our container as an executable that accepts input
parameters.
main.go
Within the model's main.go file, import the CLI runner and call it in the
main() function.
package main
import (
"github.com/nextmv-io/code/hop/run/cli"
"github.com/nextmv-io/code/engines/pack/knapsack"
)
func main() {
cli.Run(knapsack.DefaultSolver)
}
data/input-11items.json
We also need some test data. Put this JSON file in your data directory.
{
"items": [
{"item_id": "cat", "value": 100, "weight": 20},
{"item_id": "dog", "value": 20, "weight": 45},
{"item_id": "water", "value": 40, "weight": 2},
{"item_id": "phone", "value": 6, "weight": 1},
{"item_id": "book", "value": 63, "weight": 10},
{"item_id": "rx", "value": 81, "weight": 1},
{"item_id": "tablet", "value": 28, "weight": 8},
{"item_id": "coat", "value": 44, "weight": 9},
{"item_id": "laptop", "value": 51, "weight": 13},
{"item_id": "keys", "value": 92, "weight": 1},
{"item_id": "nuts", "value": 18, "weight": 4}
],
"capacity": 50
}
Docker
Now we'll build a Docker image with the knapsack solver and run it inside a Docker container.
Building a binary executable
To download and install the packages needed to build our binary we run the following commands at the root of the project.
go mod init knapsack-cli
go get ./...
First, we create a go.mod file which defines the import path of the current Go
module as well as other dependencies. This is done by running the command
go mod init on the root of the project. Next, run go get ./.... You'll
notice that a go.sum file is created on the project's root. This contains a
list of hashes associated to each package to ensure the same files are used for
each build.
To build our executable we then run the following commands:
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o knapsack-cli
Here, we set environment variables so Go cross compiles the binary for a Linux
operating system. The go build command builds an executable from the model's
main.go file.
Building a Docker image
To build a Docker image, run the following command from the the root of the project.
docker build -t example-nextmv/knapsack-cli .
docker build constructs a Docker image based on the contents of our
Dockerfile. Note that the image is automatically tagged with latest. If we
want to version the image, we add a tag to the build command.
Running a Docker container
At this point our Docker image is ready to run. Enter the following command in the terminal.
cat data/input-11items.json | docker run -i --rm example-nextmv/knapsack-cli
Using the -i flag lets Docker read piped data from the host. The container
prints output to standard out. The --rm flag cleans up the container after it
finishes execution.
Mounting a volume
The following command mounts the data/ directory of our host machine to the
/data/ directory in the Docker container. Using this flag allows the
Docker container to access input and write output files on the host machine.
docker run --rm -v $PWD/data:/data example-nextmv/knapsack-cli \
-hop.runner.input.path /data/input-11items.json \
-hop.runner.output.path /data/output-11items.json
The Hop model will write an output file to the data/ folder on our local
machine.