2. Servers and Configuration files¶
In this chapter, we’ll lay the foundation for using Baseplate.py in the service.
Install Baseplate.py¶
First off, let’s install Baseplate.py in your virtual environment so we can start using its components.
$ pip install 'git+https://github.com/apache/thrift#egg=thrift&subdirectory=lib/py'
$ pip install git+https://github.com/reddit/baseplate.py
In the previous chapter, we made our service run its own HTTP/WSGI server. Now we’re going to use Baseplate.py’s server instead which is run with baseplate-serve.
$ baseplate-serve
usage: baseplate-serve [-h] [--debug] [--reload] [--app-name NAME]
[--server-name NAME] [--bind ENDPOINT]
config_file
baseplate-serve: error: the following arguments are required: config_file
Uh oh! config_file
!? I guess we have got some more to do first.
A configuration file¶
Baseplate services rely on configuration to allow them to behave differently in
different environments (development, staging, production, etc.). For
Baseplate.py, configuration is stored in a file in standard Python INI file
format as understood by configparser
.
Open a new helloworld.ini
in the tutorial directory and copy this into it:
[app:main]
factory = helloworld:make_wsgi_app
[server:main]
factory = baseplate.server.wsgi
Breaking it down, there are two sections to this configuration file,
[app:main]
and [server:main]
.
[app:main]
factory = helloworld:make_wsgi_app
The first section defines the entrypoint and settings for the application
itself. The factory
is a function that returns an application object. In
this case, it lives in the Python module helloworld
and the function is
called make_wsgi_app
.
[server:main]
factory = baseplate.server.wsgi
The second section defines what kind of server we’ll run and the settings for that server. Since our application is built for HTTP/WSGI, we use the WSGI server in Baseplate.py.
Note
You might notice that both application and server sections have :main
in
their names. By default, Baseplate.py tools like baseplate-serve
will look for the sections with main
in them, but you can override this
with --app-name=foo
to look up [app:foo]
or --server-name
similarly. This allows you to have multiple applications and servers
defined in the same configuration file.
OK! Now let’s try baseplate-serve with our configuration file.
$ baseplate-serve helloworld.ini
Traceback (most recent call last):
File "/home/user/tutorial/venv/bin/baseplate-serve", line 14, in <module>
load_app_and_run_server()
File "/home/user/tutorial/venv/lib/python3.7/site-packages/baseplate/server/__init__.py", line 226, in load_app_and_run_server
app = make_app(config.app)
File "/home/user/tutorial/venv/lib/python3.7/site-packages/baseplate/server/__init__.py", line 180, in make_app
return factory(app_config)
TypeError: make_wsgi_app() takes 0 positional arguments but 1 was given
It looks like we’ll need a little bit more.
Run the service with baseplate-serve¶
In the previous section, we learned that the [app:main]
section both tells
baseplate-serve where to find the application and holds
configuration for that application. The function that we specify in factory
needs to take a dictionary of the raw configuration values as an argument.
Let’s add that to our service.
from pyramid.config import Configurator
from pyramid.view import view_config
@view_config(route_name="hello_world", renderer="json")
def hello_world(request):
return {"Hello": "World"}
def make_wsgi_app(app_config):
configurator = Configurator(settings=app_config)
configurator.add_route("hello_world", "/", request_method="GET")
configurator.scan()
return configurator.make_wsgi_app()
All we had to do was add one parameter. We also pass it through to Pyramid’s Configurator so any framework-specific settings can be picked up.
Since we’re not using the wsgiref
server anymore, we can drop the
whole if __name__ == "__main__":
section at the end of the file now.
Alright, third time’s the charm, right?
$ baseplate-serve --debug helloworld.ini
12593:MainThread:baseplate.server.runtime_monitor:INFO:No metrics client configured. Server metrics will not be sent.
12593:MainThread:baseplate.server:INFO:Listening on ('127.0.0.1', 9090)
Success! The --debug
flag will turn on some extra log messages, so we can
see a request log when we try hitting the service with curl again.
$ curl localhost:9090
{"Hello": "World"}
And something shows up in the server’s logs:
12593:DummyThread-1:baseplate.server.wsgi:DEBUG:127.0.0.1 - - [2019-08-07 23:42:32] "GET / HTTP/1.1" 200 147 0.007743
You’ll notice the logs look a bit different from before.
baseplate-serve adds some extra info to help give context to your
log entries. That DummyThread-1
is pretty useless though, so we’ll make it
useful in the next chapter.
Summary¶
We have now made a configuration file and made it possible to run our service with baseplate-serve.
So what did any of this do for us? baseplate-serve is how we let production infrastructure run our application and interact with it. It knows how to process multiple requests simultaneously and will handle things like the infrastructure asking it to gracefully shut down.
But the real fun of Baseplate.py comes when we start using its framework integration to get some visibility into the guts of the application. Let’s see what that looks like in the next chapter.