Using Docker with Rasa for development

jonathanpwheat

Jonathan Wheat

Posted on May 21, 2020

Using Docker with Rasa for development

Requirements

If you're developing a chatbot with Rasa, no doubt you've gone down the same path as me and opened multiple consoles / terminals to launch the Rasa server, Action server, Duckling, your webchat ui and possibly more depending on how advanced your bot has become.

This is a really easy way to develop, although unless your terminal app can do tabs, it becomes quite tiresome tracking down the proper one to restart.

I'm going to show you how you can setup docker to launch them all with one command, and give you the flexibility to watch the logs, connect directly to a service, and of course stop and restart them when needed.

We're going to use the docker-compose.yml file to achieve this.

Docker Compose

Briefly, the docker-compose.yml file is a configuration file that tells docker how to build a stack of containers. That means, it will fire up a single container for each service (chat server / action server / chatUI / etc.) all with one command. It's pretty sweet, especially if you're used to opening up multiple consoles to launch each service like I mentioned above.

I've attached my docker-compose.yml file to this post in case you want to just jump ahead, because who has time to actually read blog posts these days. Am I right?

If you do happen to have time, good for you, I'm going to explain how to get this working, and you'll be 30 minutes ahead of the people that skipped this section, wondering why my file didn't work straight away.

If you have a Rasa project, you can download and drop my file into the root of your project. My file assumes your actions.py (and other action files) are located in an actions directory off your project directory. If you followed my post about the actions files, then you're good to skip the next section.

Configure your actions

To use my file as-is, you just need to do a couple things

  • create an actions directory off your project root
  • move actions.py into it
  • copy __init.py__ from your project root into it

For more information about the actions files and the actions directory read my post here

docker-compose.yml - Some items of note

We're going to talk briefly about a few of the config options in the docker-compose.yml file.

First up is volumes. The volumes option takes an array of values, meaning you indent and use a hypen ( - ) for each item. The items here are directories you want to map to docker. In my file the volumes section looks like this for the rasa-server service:

    volumes:
      - ./:/app
Enter fullscreen mode Exit fullscreen mode

You can see the value on the volumes: line is split with a :. This tells Docker what local directory (the left side of the :) to make available to the service container and where in the container (right side of the :) to mount them to.

You end up with something like ./:/app That looks like a lot of dots and slashes, but essentially it is saying to map the current local project directory ./ to /app in the container. The result if this notation is that all files and directories in the project folder (think configuration, models, nlu data, etc) are made available to the rasa server container when it's fired up.

This makes sense if you think about it because the server requires all of those config files and your models in order to run. It would be useless if you couldn't get your models or configuration to be read by Rasa in the container.

To beat a dead horse, we'll look at the volumes: value for the action-server service configuration. It is similar.

    volumes:
      - ./actions:/app/actions
Enter fullscreen mode Exit fullscreen mode

I know what you may be thinking, we just told docker to use the entire project directory (including the actions directory), why do we need to do it again?

you'll notice in the docker-compose.yml file there are different sections called services:. We have a rasa-server service and an action-server service. Docker allows us to configure each one independently giving us some crazy flexibility and allows us to tune each service to use only what it needs.

You also may be thinking, why didn't you use the same ./:/app value from the rasa-server? You could and it would work, but that would bloat the action-server container because it would also have access to the models and other config data. Best practices say to limit access to just what is needed and make your containers as small as possible.

Back to the beating that horse with actual value now.

    volumes:
      - ./actions:/app/actions
Enter fullscreen mode Exit fullscreen mode

The actions server is mounting the actions directory we created above (./actions) and making it available at /app/actions inside the action-server container. The action server docker image that is being pulled, knows to load the actions from it's local /app/actions directory. Make sense?

Why is this important?

The volume: parameter is important because it allows outside files into the container, meaning data can be saved from the container to the volume and will persist next time you run the container. The same in reverse as well. If you make changes to your domain.yml, nlu.md, stories.md, or any other config file, and retrain rasa, you just stop and restart the container and it will use your new models and configuration.

These volume mappings allow you to continue to develop / train, etc locally with Docker and have your data and configuration persist.

How to use Docker Compose

Provided you have the requirements mentioned at the top of this article, it is pretty easy to use docker compose.

Drop the file into your rasa project directory run docker-compose up and should be good to go.

docker-compose up will keep the terminal live, and you'll see all the logs for each of the services you're firing up. You would use CTRL+C to stop everything.

You can also run docker-compose up -d and that will launch them in the background and give you your terminal back. To then stop them all if running in the background, run docker-compose down and they'll all stop.

One thing that may (or may not be) obvious is that you need to be in the same directory as your docker-compose.yml file to run these commands. Since that is our configuration file, docker compose uses that to know what to launch or stop.

Becoming more proficient

When I first started out, I never used the -d flag because I like to watch logs as I test and develop. This is especially useful if you're starting out because you can immediately spot issues with configurations, etc. But, after a while you'll want to fire this off and you'll want your cursor back :)

The -d flag is handy because they run in the background. This allows you to be able to stop different containers of needed, so if for example you're editing your actions.py file, you can just stop and restart the action server instead of bouncing all of your containers each time. That is ok if you do this, to me it is just a waste of time.

Restarting a container

First you need to know the name of your container. To get a list of your running containers, run docker ps. You'll see something like this:

CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                               NAMES
af21abaf9997        rasa/rasa:1.9.0       "rasa run --cors * -…"   2 hours ago         Up 8 seconds        0.0.0.0:5005->5005/tcp              rasa-r2-server
f631aa0cd485        mysql:5.7             "docker-entrypoint.s…"   2 hours ago         Up 8 seconds        0.0.0.0:3306->3306/tcp, 33060/tcp   mysql-rasa-r2
e18dc702ff5b        rasa/rasa-sdk:1.9.0   "./entrypoint.sh sta…"   2 hours ago         Up 8 seconds        0.0.0.0:5055->5055/tcp              rasa-r2-action-server
Enter fullscreen mode Exit fullscreen mode

If you wanted to restart the action server for example, you can take the CONTAINER ID for the action server (the bottom one) and run docker container restart e18dc702ff5b

When that's completed, you can look with docker ps again and see it was restarted under the CREATED column

CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                               NAMES
af21abaf9997        rasa/rasa:1.9.0       "rasa run --cors * -…"   2 hours ago         Up 3 minutes        0.0.0.0:5005->5005/tcp              rasa-r2-server
f631aa0cd485        mysql:5.7             "docker-entrypoint.s…"   2 hours ago         Up 3 minutes        0.0.0.0:3306->3306/tcp, 33060/tcp   mysql-rasa-r2
e18dc702ff5b        rasa/rasa-sdk:1.9.0   "./entrypoint.sh sta…"   2 hours ago         Up 29 seconds       0.0.0.0:5055->5055/tcp              rasa-r2-action-server
Enter fullscreen mode Exit fullscreen mode

For those of you still reading, here's some coolness that we've implemented that you didn't even know about.

What's in a name

Those hashes change each time a container is launched, and are a pain to deal with especially if you need to bounce a container a lot during development.

You'll notice that each container has an actual name (NAME column), our action server is named rasa-r2-action-server whether you realized it or not, we defined this in our file with the parameter container-name:. Now we know what the container will be called all the time, it won't change no matter how many times you bounce it.

You can use this name to restart the container like this:
docker container restart rasa-r2-action-server

That makes it easier to restart because now you don't need to always get the new hash with docker ps

If you leave out the container-name: parameter in docker-compose.yml, docker will auto generate a name for the container which is fine, but not ideal, it is just easier to give each of our containers a name, then we dictate what it is called.

Killing a container

Occasionally you may need to forcibly kill a container, or even just stop one for a while instead of restarting it. This is very similar to restarting a container, you just use the keyword kill instead. docker kill rasa-r2-action-server If you run docker ps you'll see it is now gone.

Starting one service

Great, you've killed a service using the container name, fixed whatever the issue is and want to fire it up again? Well you'd think you can access it via the container name because that's what we've done in the other sections. Don't be fooled, there is no container with the name of the one you just stopped. We need to access it by the service name. In our example you'd run docker-compose up -d action-server where action-server is the service name defined in docker-compose.yml Check docker ps and you'll see it is fired back up.

Watch that log

It can be quite important to look at the logs in the event something does wrong. You can use the container name we defined above and watch the logs like this:

docker logs rasa-r2-action-server -f

2020-05-20 16:38:04 INFO     rasa_sdk.endpoint  - Starting action endpoint server...
2020-05-20 16:38:04 INFO     rasa_sdk.executor  - Registered function for 'action_hello_world'.
Enter fullscreen mode Exit fullscreen mode

I added the -f flag to keep the logs active. This command will take over the terminal and display changes to the log in real-time. Hit CTRL+C to stop and get your terminal back.

Get me in there!

For Rasa, it would be rare for you to need to ssh into a container, but I'll round out the article with how to do that in case you need to verify some library version or something.

docker exec -it rasa-r2-server bash

This will drop you into the /app directory and you'll be inside the container. You can run something like rasa --version or python --version, or pip list whatever you need to. Just type exit to pop back out.

Docker Compose Docs

Take a look at the docker compose docs, there are some other useful commands like pause, start, top, etc that may prove useful as you get more into developing using docker.

My docker-compose.yml file

Here's a link to the file I use
docker-compose.yml

That's it

That is everything you need to know to develop Rasa using Docker. If you notice something I typed or said incorrectly, please let me know and I'll fix it right away. I tried to perform these examples as I wrote this, hopefully everything works for you. Good luck!

💖 💪 🙅 🚩
jonathanpwheat
Jonathan Wheat

Posted on May 21, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related