Using Docker with Rasa for development
Tags: rasa, docker, chatbot, python
Published: 2020-05-21 15:25:48

### Requirements
- Docker Engine [install docs](https://docs.docker.com/engine/install/)
- Docker Compose [install docs](https://docs.docker.com/compose/install/)
- Rasa Open Source [install docs](https://rasa.com/docs/rasa/user-guide/installation/)

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](https://dev.to/jonathanpwheat/splitting-your-actions-in-rasa-3mfg), 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](https://dev.to/jonathanpwheat/splitting-your-actions-in-rasa-3mfg)

### 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
```

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
```
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
```
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
```

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
```

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'.
```

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](https://docs.docker.com/compose/reference/overview/), 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](https://www.dropbox.com/s/nnjizw1iewq74r1/docker-compose.yml?dl=0)

### 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!