Service configuration.
Development and production environments for the services can be very different. There may be a difference in database addresses, domains used, verbosity of service output etc. Presumably you keep those differences for your services in some kind of configuration file/files. As there is a well known pattern of keeping the configuration separate from the code, we also strongly recommend it.Legacy services.
Legacy service in this context means a service that is not yet suited to run on Armada. If the service that you want to armadize, chooses its configuration based on some environment variables, you can set them at the moment of running the service:$ armada run coffee-counter -e "DATABASE_HOST=mysql.initech.com" -e "DATABASE_USER=peter" -e "DATABASE_PASSWORD=trustno1"
$ armada run coffee-counter -v /etc/coffee-counter/users.txt
Hermes. The Armada solution for service configuration.
Previously mentioned methods can be used to quickly adapt your service to Armada platform, but none of them is the preferred way. Let's look at the preferred Armada way of doing things by trying to modify the coffee-counter service a little bit.For example, we may want to set the limit on coffees drunk in one go by each person. It can be as high as 5 coffees in development environment (called dev), and as low as 2 in production environment (called production). The preferred Armada way in its simplest form consists of these steps:
-->
Gather all your configuration data for all the environments in the files under one directory,
e.g. config.
config/
dev/
production/
qa/
...
coffee-counter$ mkdir -p config/dev config/production
coffee-counter$ echo '{ "coffee_limit": 5 }' > config/dev/config.json
coffee-counter$ echo '{ "coffee_limit": 2 }' > config/production/config.json
-->
Provide a way for the configuration data to be sent to the ship where the service will be run.
It must be placed under directory /etc/opt/SERVICE_NAME-config/,
where SERVICE_NAME is the name of your service image,
e.g. coffee-counter.
This may seem the hard part, but in fact it can be easily achieved by running Armada service called courier. Its purpose is exactly to move configuration files from one place (git repository, directory on some server etc.) to Armada ship/ships. Whether the ships are local or remote, courier can handle it.
More details about courier can be found here. For the purpose of our guide we can simply link configuration directory that we've just created, to the right destination path:
coffee-counter$ ln -s `pwd`/config /etc/opt/coffee-counter-config
-->
Read the configuration in your code and adjust its behaviour depending on the configuration data.
coffee-counter$ vim src/coffee-counter.py
coffee-counter.py
...
import hermes
...
class Drink(object):
def POST(self, user, count):
config = hermes.get_config('config.json')
coffee_limit = config.get('coffee_limit', 1)
if int(count) > coffee_limit:
return "You can't drink that much! The limit is {0}.\n".format(coffee_limit)
coffees[user] += int(count)
return "{0}'s coffee count is now {1}.\n".format(user, coffees[user])
...
We are using the function get_config() from the library hermes. It looks for specified file in the configuration directory and returns its contents. If the file happens to have an extension json (which is the case) it also parses the data for us.
Library hermes is part of Armada and the simplest way to use it is to base your service image on microservice_python instead of microservice:
coffee-counter$ vim Dockerfile
Dockerfile
- FROM dockyard.armada.sh/microservice
+ FROM dockyard.armada.sh/microservice_python
- RUN apt-get install -y python python-dev python-pip
- RUN pip install -U web.py
The idea behind hermes is that when Armada runs your service with environment specified
by --env it looks for the service configuration data
under the directory /etc/opt/SERVICE_NAME-config/ENVIRONMENT
on the ship.
If such directory exists, Armada mounts it inside the service container and sets environment variable named CONFIG_PATH that points to it. So, all that get_config() function has to do is to read the environment variable CONFIG_PATH, check if requested file exists in there and return its contents. In reality this require only slightly more work as CONFIG_PATH may consists of a series of paths separated by colon. Your function should search for configuration file in any of them in that order. It is analogous to how shell searches PATH environment variable when executing programs.
More details about hermes can be found here.
If such directory exists, Armada mounts it inside the service container and sets environment variable named CONFIG_PATH that points to it. So, all that get_config() function has to do is to read the environment variable CONFIG_PATH, check if requested file exists in there and return its contents. In reality this require only slightly more work as CONFIG_PATH may consists of a series of paths separated by colon. Your function should search for configuration file in any of them in that order. It is analogous to how shell searches PATH environment variable when executing programs.
More details about hermes can be found here.
-->
Build and run the service.
Let's build the modified service.
coffee-counter$ armada build coffee-counter
Pulling repository dockyard.armada.sh/microservice_python
5612e0d94ce7: Download complete
511136ea3c5a: Download complete
...(skipped)...
Removing intermediate container b4ccee7cb473
Successfully built 61deaeef72b8
$ armada run coffee-counter --env production -d local
Running microservice coffee-counter from dockyard: (alias: local) locally...
Service is running in container ca9ab262e9b2 available at addresses:
192.168.3.168:49241 (80/tcp)
$ curl -X POST 192.168.3.168:49241/drink/Peter/4
You can't drink that much! The limit is 2.
$ armada run coffee-counter --env dev -d local
Running microservice coffee-counter from dockyard: (alias: local) locally...
Service is running in container 6d826bf3eb92 available at addresses:
192.168.3.168:49243 (80/tcp)
$ curl -X POST 192.168.3.168:49243/drink/Peter/4
Peter's coffee count is now 4.
Additional info about hermes.
As we've shown Armada offers convenient convention for managing configuration data for your services. When writing new services, following this pattern can bring many benefits. Separation of configuration from code usually means easier to understand and maintain code. Changes to the configuration can be deployed independently of deploying the code. Running services in new environments is also much easier. Keeping configuration data in one place means we can get rid of some mysterious configuration files placed only on production servers etc.
If you just want to quickly adapt your existing service to Armada platform you can try sticking with
the intermediate solutions described in here.
Keep in mind that using MICROSERVICE_ENV environment variable
is recommended as it's understood by all Armada components such as magellan and
armada command itself.
Also don't forget to consider taking advantage of the possibilities that Armada brings.
It may prove to be well worth the initial work.