Image and Build

When deploying any application, you have two primary options: using a pre-built container image or utilizing a BuildPack within ServiceFoundry to create a custom image. This guide outlines the key concepts of the image-building process in Truefoundry

  • Docker Image These are pre-packaged environments that encapsulate your application along with all its dependencies.
  • Dockerfiles Think of Dockerfiles as the recipes that you use to create these Docker images.

If you don't have a pre-built and pushed Docker Image, you can explore the Build process instead.

However, if you do have a pre-built Docker Image stored in a Container Registry such as DockerHub, you can do that as well.

Image Build configuration

  • Image URI:
    Specify the name of the image and the tag. If the image is in Dockerhub, you can skip registry-url (for e.g. tensorflow/tensorflow). You can use an image from a private registry using Advanced fields
  • Docker Registry:
    FQN of the container registry. You can the FQN of your desired container registry (or add one)
    in the Integrations pageIntegrations page
  • Command:
    Override the command to run when container starts. When deploying a Job, the command can be templatized by defining params and referencing them in command
    E.g. python main.py --learning_rate {{learning_rate}}

Deploying a pre-built Image

You can deploy a pre-built Image using different methods:

  • Via the User Interface (UI)
  • Via the Python SDK
  • Via the CLI

Via the User Interface (UI)

  1. Within the Deploy a Docker image or build and deploy from source code section, click on the Docker Image (Deploy an existing image) button.
  1. Complete the fields with the relevant information for your Docker Image.

Via the Python SDK

In your Service deployment code deploy.py, include the following:

from servicefoundry import Image
...

service = Service(
    ...
    image = Image(
      image_uri="<your_image_uri>",
      docker_registry="<docker_registry_fqn>",
      command="<command override>",
    )
  	...
)

...

Via the CLI

In your Service deployment configuration servicefoundry.yaml, include the following:

...
image:
	image_uri: "<your_image_uri>"
	docker_registry: "<docker_registry_fqn>"
	command: "<command override>"
...

When deploying a job or service, you must provide either a container image or a build specification` that ServiceFoundry uses to build an image. This guide outlines the key concepts and components of a build specification.

A build specification is composed of two components.

  1. A build source. This defines the source code location.
  2. A build spec. This defines how to build a container image out of the build source.
from servicefoundry import Service, Build

service = Service(
    name="fastapi",
    image=Build(
        build_source=..., 
        build_spec=...,
    ),
    ...
)
name: fastapi
components:
  - name: fastapi
    type: service
    image:
      type: build
      build_source:
        ...
      build_spec:
        ...

Build source

A build source defines the source code location using which we will build a container image.

Local build source

You will need to use local build source if you want to deploy code present on your local machine (This can be your laptop, VM, CI environment, etc.). In this case, we copy the code present on your local directory to our build server.

from servicefoundry import Service, Build, LocalSource

service = Service(
    name="fastapi",
    image=Build(
        build_source=LocalSource(
            project_root_path="./my_project_root_path"
        ), 
        build_spec=...,
    ),
    ...
)
name: fastapi
components:
  - name: fastapi
    type: service
    image:
      type: build
      build_source:
        type: local
        project_root_path: "./my_project_root_path"
      build_spec:
        ...

Parameters

NameTypeRequiredDescription
typestringtrue+value=local
project_root_pathstringtrueLocal project root path.
local_buildbooleantruerun docker build locally

Github build source

You can also use a public github repository as build source.

# We will use the `GithubSource` class here.
from servicefoundry import Service, Build, GithubSource

service = Service(
    name="fastapi",
    image=Build(
        build_source=GithubSource(
            repo_url="https://github.com/srihari-tf/tfy-demo",
            ref="main",
        ), 
        build_spec=...,
    ),
    ...
)
# The type: local identifies it as a local build source.
name: fastapi
components:
  - name: fastapi
    type: service
    image:
      type: build
      build_source:
        type: github
        repo_url: "https://github.com/srihari-tf/tfy-demo"
        ref: main
      build_spec:
        ...

Parameters

NameTypeRequiredDescription
typestringtrue+value=git
repo_urlstringtrueThe repository URL.
refstringtrueThe commit SHA.
branch_namestringfalseSelecting branch will select latest commit SHA of the branch.

Build Spec

Our build servers use the build spec to build a container image from the source code defined using build source.

Dockerfile build

Use dockerfile build if you have already written a Dockerfile.

#We will use the `DockerFileBuild` class here.

from servicefoundry import Service, Build, DockerFileBuild

service = Service(
    name="fastapi",
    image=Build(
        build_spec=DockerFileBuild(
            dockerfile_path="Dockerfile"
            build_context_path="./",
            build_args={
            	"param": "value",
              "param1": "value1",
            },
        ),
    ),
    ...
)
name: fastapi
components:
  - name: fastapi
    type: service
    image:
      type: build
      build_source:
        type: local
      build_spec:
        type: dockerfile
        dockerfile_path: Dockerfile
        build_context_path: "./"
        build_args:
      		param: value
      		param1: value1

Parameters

NameTypeRequiredDescription
typestringtrue+value=dockerfile
dockerfile_pathstringtrueThe file path of the Dockerfile relative to project root path.
build_context_pathstringtrueBuild context path for the Dockerfile relative to project root path.
commandanyfalseOverride the command to run when the container starts
When deploying a Job, the command can be templatized by defining params and referencing them in command
E.g. python main.py --learning_rate {{learning_rate}}
build_argsobjectfalseBuild arguments to pass to docker build.

Our build server uses this spec to build the container image using a command equivalent to,

docker build -f dockerfile_path build_context_path --build-arg param=value --build-arg param1=value1 ...

Python build

If you do not have a Dockerfile you can use the Python build to build a container image with a specific python version and pip packages installed.

from servicefoundry import Service, Build, PythonBuild

service = Service(
    name="fastapi",
    image=Build(
        build_spec=PythonBuild(
            python_version="3.9",
            build_context_path="./",
            requirements_path="my-requirements.txt",
            pip_packages=["fastapi==0.82.0", "uvicorn"],
            command="uvicorn main:app --port 8000 --host 0.0.0.0"
        ),
    ),
    ...
)
# type: tfy-python-buildpack` identifies it as a _Python build_. 
name: fastapi
components:
  - name: fastapi
    type: service
    image:
      type: build
      build_source:
        type: local
      build_spec:
        type: tfy-python-buildpack
        python_version: 3.9
        build_context_path: "./"
        requirements_path: my-requirements.txt
        pip_packages:
          - fastapi==0.82.0
          - uvicorn
        command: uvicorn main:app --port 8000 --host 0.0.0.0

Parameters

NameTypeRequiredDescription
typestringtrue+value=tfy-python-buildpack
python_versionstringtruePython version to run your application. Should be one of the tags listed on Official Python Docker Page
build_context_pathstringtrueBuild path relative to project root path.
requirements_pathstringfalsePath to requirements.txt relative to
Path to build context
pip_packages[string]falseDefine pip package requirements.
In Python/YAML E.g. ["fastapi>=0.90,<1.0", "uvicorn"]
apt_packages[string]falseDebian packages to install via apt get.
In Python/YAML E.g. ["git", "ffmpeg", "htop"]
commandanytrueCommand to run when the container starts.
Command will be set as the Entrypoint of the generated image.
When deploying a Job, the command can be templatized by defining params and referencing them in command
E.g. python main.py --learning_rate {{learning_rate}}
cuda_versionstringfalseVersion of CUDA Toolkit and CUDNN to install in the image
These combinations are based off of publically available docker images on docker hub
You can also specify a valid tag of the form {cuda_version_number}-cudnn{cudnn_version_number}-{runtime/devel}-ubuntu{ubuntu_version}
Refer https://hub.docker.com/r/nvidia/cuda/tags for valid set of values
Note: We use deadsnakes ubuntu ppa to add Python that currently supports only Ubuntu 18.04, 20.04 and 22.04

NOTE: If you pass both requirements_path and pip_packages then we will install the union of the packages defined. IE: if you have fastapi defined in requirements.txt and pass ["numpy"] in pip_packages, we will install both numpy and fastapi.

Configuring Build via UI

  1. Within the Deploy a Docker image or build and deploy from source code section, click on the Source Code (Build and deploy source code) button.
  2. Specify your Build Source by entering the relevant information regarding Github Source
  3. Fill the relevant information in the Build using DockerFIle or using Buildpack section, to configure your build spec

Local Development

If you have Docker installed on your laptop, when you initiate a build on TrueFoundry, it will happen directly on your local machine instead of a remote server. This local build approach offers faster build times.

However, if you don't prefer to build locally you can do the following:

from servicefoundry import LocalBuild, Build
...

service = Service(
    ...
    image=Build(
        build_source=LocalBuild(
          ...
          local_build=False
          ...
        ),
    )
  	...
)

...