Deploy with Docker Compose#

One of the simplest ways of either prototyping or serving in production is to run your Flow with docker-compose.

A Flow is composed of Executors which run Python code defined to operate on DocumentArray. These Executors will live in different runtimes depending on how you want to deploy your Flow.

By default, if you are serving your Flow locally they will live within processes. Nevertheless, because Jina is cloud native your Flow can easily manage Executors that live in containers and that are orchestrated by your favorite tools. One of the simplest is Docker Compose which is supported out of the box.

You can deploy a FLow with Docker Compose with one line:

from jina import Flow
flow = Flow(...).add(...).add(...)
flow.to_docker_compose_yaml('docker-compose.yml')

Jina will generate a docker-compose.yml configuration file that you can use directly with docker-compose and corresponds to your Flow, avoiding the overhead of manually defining all the services needed for the Flow.

Caution

All Executors in the Flow should be used with jinahub+docker://... or docker://....

Caution

If you are using Executor which rely on docker image built with a jina version prior to 3.1.3, please remove the health check from the dump yaml file as they are only compatible with 3.1.3+ otherwise your docker compose services will always be unhealthy

Example: Indexing and searching images using CLIPEncoder and ANNLiteIndexer#

To follow this how-to, you should first ensure that Docker Compose is installed locally.

This example shows how to build and deploy a Flow with Docker Compose, using CLIPImageEncoder as an image encoder and PQLiteIndexer as an indexer to perform fast nearest neighbor retrieval on image embeddings.

Deploy your Flow#

First let’s define the Flow and generate the Docker Compose YAML configuration from it

In a flow.yml file :

jtype: Flow
with:
  port: 8080
  protocol: http
executors:
- name: encoder
  uses: jinahub+docker://CLIPEncoder
  replicas: 2
- name: indexer
  uses: jinahub+docker://AnnLiteIndexer
  uses_with:
    dim: 512
  shards: 2

then in a shell do:

jina export docker-compose flow.yml docker-compose.yml 

In python run

from jina import Flow

flow = (
    Flow(port=8080, protocol='http')
    .add(name='encoder', uses='jinahub+docker://CLIPEncoder', replicas=2)
    .add(
        name='indexer',
        uses='jinahub+docker://AnnLiteIndexer',
        uses_with={'dim': 512},
        shards=2,
    )
)
flow.to_docker_compose_yaml('docker-compose.yml')

Hint

You can use a custom jina Docker image for the Gateway service. Just set the envrironment variable JINA_GATEWAY_IMAGE to the desired image before generating the configuration.

let’s take a look at the generated compose file:

version: '3.3'
...
services:
  encoder-rep-0:  # # # # # # # # # # #          
                  #     Encoder       #
  encoder-rep-1:  # # # # # # # # # # #

  indexer-head:   # # # # # # # # # # #          
                  #                   #
  indexer-0:      #     Indexer       #
                  #                   #
  indexer-1:      # # # # # # # # # # #

  gateway: 
    ...
    ports:
    - 8080:8080

Tip

The default compose file generated by the Flow contains no special configuration or settings. You may want to adapt it to your own needs.

Here you can see that 5 services will be created:

  • 1 for the Gateway which is the entrypoint of the Flow.

  • 2 associated with the encoder for the two Replicas.

  • 3 associated with the indexer, one for the Head and two for the Shards.

Now, you can deploy this Flow :

docker-compose -f docker-compose.yml up

Query your Flow#

Once we see that all the services in the Flow are ready, we can start sending index and search requests.

First let’s define a client:

from jina.clients import Client

client = Client(host='http://localhost:8080')

Then let’s index the set of images we want to search:

from docarray import DocumentArray

da = DocumentArray.pull('demo-da-images-jina', show_progress=True)

da_query = da[0:1]  # one document for query
da_index = da[1:]  # the rest is for indexing

indexed_docs = client.index(inputs=da_index)
print(f'Indexed Documents: {len(indexed_docs)}')
Indexed Documents: 99

We indexer 99 Documents !

Then let’s search for the closest image to our query image:

queried_docs = client.search(inputs=da_query)

matches = queried_docs[0].matches
print(f'Matched Documents: {len(matches)}')
Matched Documents: 10

Now have the list of the 10 closest image to the query !