Resolving cross-node dependencies¶
In practice, it is a common scenario that a service running on Node A requires another service to run on Node B. Consider the following example:
The overall service consists of two applications - a webservice rendering an HTML and a CDN providing assets such as images.
The following sections will provide a simplistic implementation of this example. In addition, it is assumed that a running CDN service on the raspberry pi is required by the webservice on the laptop. This cross-node dependency is then resolved by employing BlueChi’s proxy service feature - so starting the webservice will also start the CDN.
Note
A detailed explanation of how Proxy Services are implemented in BlueChi based on systemd can be found here.
Setting up the CDN¶
On the raspberry pi, first create a temporary directory and add an example image to it:
mkdir -p /tmp/bluechi-cdn
cd /tmp/bluechi-cdn
wget https://raw.githubusercontent.com/containers/bluechi/main/doc/docs/img/bluechi_architecture.jpg
For the sake of simplicity, python’s http.server module is used to simulate a CDN. Create a new file /etc/systemd/system/bluechi-cdn.service
and paste the following systemd unit definition:
[Unit]
Description=BlueChi's CDN from the cross-node dependency example
[Service]
Type=simple
ExecStart=/usr/bin/python3 -m http.server 9000 --directory /tmp/bluechi-cdn/
Reload the systemd manager configuration so it can find the newly created service:
$ systemctl daemon-reload
Lets verify that the service works as expected. First, start the service via:
$ systemctl start bluechi-cdn.service
Then submit a query to it. The response should be an HTML page that contains the previously added image as a list item:
$ curl localhost:9000
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="bluechi_architecture.jpg">bluechi_architecture.jpg</a></li>
</ul>
<hr>
</body>
</html>
Finally, stop the service again:
$ systemctl stop bluechi-cdn.service
Setting up the WebService¶
On the laptop, also create a temporary directory as well as an empty python file:
mkdir -p /tmp/bluechi-webservice
cd /tmp/bluechi-webservice
touch service.py
The following code is a rudimentary implementation of the webservice which should be able to run without any other dependencies. On any request, it will return a small HTML string containing a link to the image in the CDN. Copy and paste it into the previously created service.py
.
import sys
import wsgiref.simple_server
port: int = None
cdn_host: str = None
def service(environ, start_response):
status = "200 OK"
headers = [("Content-Type", "text/html")]
response = f"""
<html>
<body>
<img src="http://{cdn_host}/bluechi_architecture.jpg" alt="bluechi_architecture" />
</body>
</html>
"""
start_response(status, headers)
return [response.encode("utf-8")]
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python3 webservice.py <port> <cdn-host>")
exit(1)
port = int(sys.argv[1])
cdn_host = sys.argv[2]
server = wsgiref.simple_server.make_server(
host="localhost",
port=port,
app=service
)
while True:
server.handle_request()
Lets create a new systemd unit to wrap the service.py
. Copy and paste the following unit definition into /etc/systemd/system/bluechi-webservice.service
:
[Unit]
Description=BlueChi's webservice from the cross-node dependency example
[Service]
Type=simple
ExecStart=/usr/bin/python3 /tmp/bluechi-webservice/service.py 9000 192.168.178.55:9000
Note
Make sure to replace the IP address 192.168.178.55
with the corresponding one of the raspberry pi in your setup.
Start the service via systemctl
to verify it works as expected:
$ systemctl start bluechi-webservice.service
$ curl localhost:9000
<html>
<body>
<img src="http://192.168.178.55:9000/bluechi_architecture.jpg" alt="bluechi_architecture" />
</body>
</html>
Make sure to stop the service again before proceeding:
$ systemctl stop bluechi-webservice.service
Resolving the dependency via BlueChi¶
After implementing both applications, lets ensure that starting the webservice will also start the CDN. Add the following lines to bluechi-webservice.service
in the [Unit]
section:
Wants=bluechi-proxy@pi_bluechi-cdn.service
After=bluechi-proxy@pi_bluechi-cdn.service
bluechi-proxy@.service
is a systemd service template provided by BlueChi that enables resolving cross-node dependencies. The argument between the @
symbol and the .service
postfix follows the pattern bluechi-proxy@<node>_<service>.service
. BlueChi will split the argument accordingly and start the specified <service>
on the <node>
. In the case above, it will start bluchi-cdn.service
on the node pi
.
The systemd unit mechanism Wants is used for declaring that dependency and starting it (if not already running). In addition, the After setting is used to ensure that the webservice starts only after the CDN has been started.
The final bluechi-webservice.service
should look like this:
$ cat /etc/systemd/system/bluechi-webservice.service
[Unit]
Description=BlueChi's webservice from the cross-node dependency example
Wants=bluechi-proxy@pi_bluechi-cdn.service
After=bluechi-proxy@pi_bluechi-cdn.service
[Service]
Type=simple
ExecStart=/usr/bin/python3 /tmp/bluechi-webservice/service.py 9000 192.168.178.55:9000
Since the dependency is encoded into the systemd unit, the webservice can be started either via bluechictl
or systemctl
:
bluechictl start laptop bluechi-webservice.service
# or use
systemctl start bluechi-webservice.service
On the laptop - where bluechi-controller
is running - use bluechictl
to verify that both services are running now:
$ bluechictl status laptop bluechi-webservice.service
UNIT | LOADED | ACTIVE | SUBSTATE | FREEZERSTATE | ENABLED |
----------------------------------------------------------------------------------------------------------------
bluechi-webservice.service | loaded | active | running | running | static |
$ bluechictl status pi bluechi-cdn.service
UNIT | LOADED | ACTIVE | SUBSTATE | FREEZERSTATE | ENABLED |
--------------------------------------------------------------------------------------------------------
bluechi-cdn.service | loaded | active | running | running | static |
Use a webbrowser on the laptop and navigate to localhost:9000
. The resulting page is served by the bluechi-webservice.service
(laptop) and it displays the image fetched from the bluechi-cdn.service
(raspberry pi).