Sunday, February 28, 2021

The Devops Case For Jupyter Note(Run)books

 With all the excitement watching NASA successfully land perseverance on Mars I thought if we can land a rover on Mars why can't we go to Jupyter.....Notebooks that is. The Jupyter project has been around since 2014 and used by Data Scientists and the scientific community for quite some time. Before we can dive into how we can use these notebooks for DevOps we first need an understanding of what they are and can provide.

What is a Jupyter Notebook?

A Jupyter Notebook in a nutshell is a special json file (*.ipjnb) that when rendered by a Jupyter Server renders as a web application that allows the user to blend content, data, equations, and executable code to create interactive notebooks that can be saved, shared, and exported into a number of formats. While most notebooks use the python programming language, Jupyter supports over 100 languages through its kernels.


Each notebook is made up of one or more cells. Each cell can hold markdown, code or raw content. Code cells are cells that are going to perform a task, using cell magic this could be to render html, write a file to disk or execute code written in any language you have a kernel installed for.

Code cells can even be used to install additional libraries and/or plugins to add additional functionality to your Jupyter notebooks and server.


When the example code cell below is run the html output is included on the notebook below the cell. Sometimes it may not make sense to show both the code cell (input) and the output, for this you can add the meta tag 'hide-input' which will collapse the code cell but still render its output.

Pretty cool stuff right. But I'm not a data scientist and don't really imagine myself rendering complex equations to study gravitational theories, so how does this all apply to a DevOps use case? 

DevOps Runbooks

Runbooks have been around in one form or another for a long time, Systems Admins have been using them for years as a way to share information between engineers in  the admin group and even other IT personnel. The format and medium for runbooks has changed over the years and can vary depending on your project and clients needs.

A standard runbook will contain at a minimum the following information:
  • Systems architecture and design diagrams
  • Technical Requirements for the system 
  • List of key personnel supporting the system
  • Troubleshooting workflows
  • List of common issues
  • Who to contact during outages and emergencies
  • List of all changes to the system
These runbooks are typically found on internal wiki or systems like Confluence the format and content may vary depending on who created them or updated them last.

Jupyter supports markdown as well so using a Jupyter notebook for static information is definitely possible and will give us the rich text output we need.  Jupyter notebooks go even further than just static content they allow us to have markdown and executable code on the same page. 

What this means is instead of having a wiki page that gives you step-by-step commands, SQL queries, etc that you hope are up-to-date and were copied correctly you can execute these directly from the notebook and evaluate the output. No more fumbling for credentials, ssh sessions, dealing with incomplete or incorrect commands.

So lets pick apart a typical wiki runbook and see how we could replace each bit. The first requirement is these notebooks need to be shareable. So far all we have talked about is jupyter notebooks which are typically single-user and can be run locally, what we need is a central server for running and sharing.

JupyterHub & JupyterLab

JupyterHub gives us a central hub to spawn single-user notebook servers on-demand so we don't need to tie up our local machines with running processes. The Hub also makes it easy for us to share these notebooks so we don't have to email them back-and-forth to each other in order to share them. 

The hub provides an authentication mechanism and proxy server so we can add additional users and share our notebooks between users. 

By itself, JupyterHub solves some of the core issues but still leaves off some functionality, such as how do we best manage our secrets. We can run JupyterHub and JupyterLab together to lay the groundwork for providing the functionality that we will need.

JupyterLab is the new enhanced web interface for jupyter notebooks, it provides a better interface where we can browse, create, and view multiple notebooks in a tabbed environment, open a terminal for quick access to a command line, install additional plugins and language kernels, etc.


In this post I won't go in to the how to install JupyterHub and JupyterLab, but if you are interested in setting up a quick POC or just want to try it out there are a few different options:

Shh! It's a Secret

A secret can be a password, token, identity, or any other piece of data that should not be known by everyone except those that need to know and should not be checked in to code repositories. 

In terms of DevOps, this could be a token used to deploy code to a client's production servers. There may be only one token that the team supporting the project should use. So the use of the token should be shared among the team.

Typical jupyter notebooks use environment variables to store and use secrets; while this prevents those secrets from being checked-in to a repo it does have some drawbacks. Environment variables are not the best use if we are to share our notebooks with other members of the team. The two major drawbacks to this approach are:
  • Each team member would have to create their own environment variables to hold the data.
  • Any time an environment value changed we would need to restart our jupyter server.

If your organization uses VaultAWS Secrets Manager or any other secrets manager implementation you can easily implement a python method to retrieve the secrets from your secrets manager and use them in your notebooks. 

If your not lucky enough to have access to a secrets manager another option would be to use IPython Secrets. The IPython Secrets library makes it easy to store and retrieve secrets from within your notebook. The library uses the python keyring project which utilizes the system's keystore or credential locker or a number of different backends to encrypt and store them. 

The nice thing about this approach is you can start using the system's default keystore and later migrate the backend to a 3rd party provided backend or a custom one.

Below is an example of how this could be done using IPython Secrets:

    from ipython_secrets import *
    import requests
    
    deploy_token = get_secret('DEPLOY_TOKEN')
    
    headers = {
     'PRIVATE-TOKEN': deploy_token
    }
    
    response = requests.request("GET",
      "https://gitlab.yourdomain.com/api/v4/projects/16/deployments",
       headers=headers)
  
The first call to 'get_secret()' will prompt for a password to the keyring, then if the key is not in use will ask us for the secret to store after entering the secret it will be removed from the front-end view. If the secret we are requesting already exists, after asking us for the keyring password, the secret is decrypted and assigned to our variable for use.


Head Stuck In the Clouds

One of the many DevOps tasks is managing your cloud instances whether they are running on GCP, AWS, Azure, or an on-prem Kubernetes cluster.  Jupyter Notebooks give us flexibility to run shell scripts, access the command line and run python code. 

With this type of flexibility we can install, configure and run the aws cli from a notebook or use the AWS SDK (boto3) to access AWS resources. In the example below we use a notebook to perform each command line entry that you would normally perform to download and install the AWS Cli. 


  The awscli is installed to the system running the notebook so we only need to perform those steps once then we can use it in our notebook code cells just like we would from any command line or script.


While the examples above are using awscli this could have easily been terraform, ansible or any other utility or SDK you need access to.


Descriptive Runbooks

So far we have seen examples of using different notebook cells for markdown and code and you may be thinking pretty cool but how does this make for a better runbook?

Below is an example notebook that shows using both markdown and code together to describe the solution and the code that will perform the task. 


In the above notebook there are four cells, two that contain markdown and two that contain shell scripts.




In this example we use the markdown cells to describe what each script will do and if there are any special considerations the engineer should be aware of prior to running the script.

The DevOps engineer can adjust the variables in the scripts in the notebook if needed or run it as-is right from the notebook.




After the engineer has run the script the output is displayed under the code cell and is saved with the notebook by default. While you can always clear the output(s) from the notebook, this does allow the engineer or senior engineer to go back and examine the output to determine if there are further issues.

Some sections of a runbook may contain sql or api calls to gather data to create reports or dashboards.

Dashboards

Some dashboards may be created and managed by the devops team but the main consumer of that dashboard may not be as technical so we don't always want the code cells to be available, some dashboards should only contain output.

Voila allows us to create interactive dashboards from notebooks but removes the code cells from the users.  Voila can be installed as a standalone app or as part of our Jupyter server. 

One example for this could be a report that displays all commits to a particular release branch so we can better track what is being deployed. Release notes or reports are an important part of every release so we can track what is changing or if issues arise later we can use the dashboard to see what all changed to the code and which developers committed that code.

Below is an example of this where we gather the commits from a particular branch for the date/time period of the sprint and display the output in an html table.


The output of the code cell displays all commits, who committed them, when, the url to that commit, etc. While it may be a bit much for release notes where Jira story IDs and short descriptions may be more suited for, this gives a better picture of everything that changed and following the links we can examine that code a bit more.



Conclusion

After reading through some of these examples it is easy to see the power and flexibility that Jupyter can bring to our runbooks. While these are just a few examples there is much more that could be done and many more extensions available or we could write our own if we can't find what we need.




Sunday, January 24, 2021

Hockey Experience Project - Part 4

 If you landed on this page and haven't read the first few parts I would recommend at least starting on Part 2.  

This is a multi-part blog post on how to recreate a hockey experience at home.  The experience we are going for is to have a goal horn, light, our favorite teams song and of course food, drinks, and rally towels that one would expect when you go to a game, it'll just be cheaper :).

In the last part we put together our web application, have it running and sort of looking good, we can always improve upon it. Sadly where we left off was the web app just printing a message to the screen.


So now let's make it do something!

If you remember from our hardware list I'm using a bluetooth speaker that I had laying around the house that no one was using. Turns out that is a bit of a pain in the @$$ and I'll be looking to replace that with a wired speaker in the future, but if you got here and all you have is the bluetooth speaker let's get it working.

Pair Up and Make The Play

One of the main problems with a bluetooth device is auto pairing the device to our raspberry pi, and keeping that device powered on while it's not in use. I mean nothing says "whomp, whomp" more than seeing Aho score a goal you reach over tap the button on the web app, the light spins but no sound. 

The raspberry pi has a utility bluetoothctl that we can use from the command line to find our device and pair it. This should really be done once so that we can tell the raspberry pi to trust the device. 

From the terminal type in:
sudo bluetoothctl

Next, we want to turn on and set the default agent:
agent on
default-agent

Now put your device in pairing mode,  some devices you can just hold the power button till a blue light flashes or announces its in pairing mode but check your manual if your unsure.

Once the device is in pairing mode we go back to our terminal and scan for the device:
scan on

You should see a few devices pop-up that will look like the following:


When you see your device in the list copy the mac address, the 6 digits separated by colons next to the name, then we want to pir to that device by typing the following (use your device mac address).

pair FC:58:FA:B2:A5:85

You should see the device pair and hear an audible sound on your bluetooth speaker. Next we want to add the device to our trusted device list. Type in the following:

trust FC:58:FA:B2:A5:85

And finally to connect we type in:

connect FC:58:FA:B2:A5:85

When you are done just type in exit to get out of the bluetoothctl utility.

Now anytime we play a sound on our raspberry pi it should come through the bluetooth speaker.  But we still have an issue of auto-pairing the speaker so that we don't have to type all that in everytime, we want to just turn the speaker on and have sound.

We can auto connect with a helper script that will do this for us, then all we need to do is have our app call the script and ensure we are connected to our bluetooth device. So to our project let's add the a file called 'autopair' and add the following to the file:

#!/bin/bash
bluetoothctl << EOF
connect FC:58:FA:B2:A5:85
EOF

After we save the file we need to make it executable, go to the command line and type in:

sudo chmod +x autopair

Since the script is just a plain shell script we can run it separately from the command line to test it is working. So let's turn on the bluetooth device and run the script.

./autopair

You should hear the audible noise of the raspberry pi connecting and any sound the raspberry pi plays should now come through the bluetooth speaker.

Now that we have the audio connected, how do we play the mp3 file(s) of our horn and goal song? There are a few different options, I'll be using VLC Player as I'm a bit more familiar with it but we could have used the default OMXPlayer. Since the vlc player is not installed by default we will need to run apt-get in the following command to install the player.

sudo apt-get install vlc

VLC Player can be controlled from the command line with cvlc, our web app will call cvlc and pass the mp3 file to it to be played. while it is playing we want our goal light to be activated until the song stops. So let's create a new script called goal.py, this script will be responsible for playing the song and activating the light.

Let's create that goal.py file and add the following to our file:


import os, time
from gpiozero import LED
from signal import pause

led = LED(17)

def activate_experience():
  led.on()
  play_horn()
  led.off()

def play_horn():
  os.system('sudo ./autopair') # Ensure our speaker is still paired with our rpi
os.system('cvlc static/media/Carolina_Hurricanes.mp3 vlc://quit')

In the above code you can see we assign the GPIO pin 17 to the LED object. While our rotating light is technically not an led the LED object is a basic switch with on/off functionality. While the object does contain other functionality we will be using it as a simple switch.

The pin number will depend on which pin you have wired your raspberry pi to the controller. In this example I'm using 17.  If you are unsure of which pin is which your raspberry pi should have come with a handy GPIO charts or you can find your model and the pins here.

While it may be a bit hard to tell from the picture we attach a ground wire to a ground pin and the positive to pin GPIO 17 on the raspberry pi, then connect the positive/negative sides to our controller. If you connect to a different pin then just adjust the pin number used for the LED object in the code.


On thing, well actually a couple of things I really like about this controller is it looks more finished than having a breadboard and a bunch of wires everywhere and it has an "always on" plug so we can actually plug our raspberry pi's power cable into it and keep everything a little more self-contained. It would have been better if the raspberry pi's plug didn't cover up our "normally on" plug but for this project we are only plugging on thing in to be controlled so it'll work for now.



In the activate_experience method, we turn the light on, call the play_horn method and then turn the light off. 

In the play_horn method, we call our autopair script to ensure our bluetooth device is connected to the raspberry pi then play the mp3 that contains the goal horn and song. If you are playing the horn then song as separate mp3s just add an additional line(s) to play your files. I like the idea of modifying both into a single file as you can cross-fade and make the experience more seamless.

The last step in this series is to call our goal.py code from the web app. To do this we want to open the app.py file, add an import to the top of the file so that we can call the script.

import goal

then in the same file find the line that matches:

print("Play Horn!")

and replace it with 

goal.activate_experience()

Now our completed code should look like the following:

from flask import Flask, render_template, request
import goal

app = Flask(__name__)

templateData = {
    'title': "Carolina Hurricanes"
}

@app.route('/', methods=['GET','POST'])

def index():
  if request.method == 'POST':  goal.activate_experience()
    return render_template("index.html", **templateData)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')


That's it! The only thing left to do is place the tablet in an open area so anyone can activate the experience. For the rotating bean, we tried several places but found it works best behind the t.v. , it keeps it out of the way and doesn't accidentally blind your family.

To complete our experience we had a good friend of ours use their cricut to make some rally towels.


Finally, get some Storm Brew, fire up the smoker for some pulled pork bbq nachos and other arena snacks and immerse yourself in the game.

And the next time Svech scores that Lacrosse goal....



Friday, January 22, 2021

Hockey Experience Project - Part 3

 If you just happened to land here on this page by accident or without reading part 1 or 2, this multi-part blog is how to bring home an NHL experience to your living room, complete with goal light, horn, song, etc.  I would recommend going back and reading part 1 and 2 to get fully up-to-speed with where we are and why we chose to do things in this manner.


Carolina GOOOAAALLL!

So hopefully somewhere between part 2 and today you did a little research and obtained the mp3s for the goal horn, song, and maybe a few logos. I won't be uploading or sharing my files due to possible copyright issues, I don't own the rights to those so I'll only be using them for my personal at home enjoyment.


I did find a pretty cool hockey rink image that is freely available that would be perfect to use as the main background for our webapp. 


We will save this to our project as hockey_rink.jpg in the static/media folder along with any other media files for goal horns etc.  Since I want to ensure the goal song is played after the goal horn, I combined the two together into a single mp3, that way there are less media files to worry about and I'm guaranteed the effects I want. Obviously, I could have kept them separate and just queued them one after another, but I like the simple approach better.

Our project structure should now look like this.




Time to get on that ice and make something happen!

So at this point we have a functioning webapp, although it is kinda lame and boring at this point and we have our media files. So let's add our background image and some functionality to the page.

If you were wondering why we created an empty libs and templates folder, well now we shall add some files to them. The libs folder will hold any css (Cascading Style Sheets) and js (JavaScript) we write. This will make maintaining the webpage much easier and also it is best practice to not just throw everything in one file.

If you are not familiar, Flask uses the Jinja templating language which will allow us to create html pages with certain data as variables then when we render the page we can pass the values of those variables. Technically Jinja will provide so much more to us but since this is a simple webapp for our own personal use and won't be exposed to the Internet there isn't really a need to cover it all in this blog.

Why is this useful to us?

Let's say we want to add a banner at the top of the page that should display the teams playing, such as: "Hurricanes vs Red Wings".  We could add this directly to the page in html like such:

<h1>Hurricanes vs Red Wings</h1>

The problem with this approach is we will have to manually update the html everytime there is a new game and that can be a pain and if you are like me you will eventually forget to do it before game time, you'll be rushed to change it and probably make a mistake and miss the first goal of the game.

With jinja we can simply add the team names as variables from the APIs we explored in part 1. If you skipped part 1 this may be a good time to go review.  So now our html with jinja would look like:

<h1>{{ away_team }} at {{ home_team }}</h1>

We could do this with any bit of data we want to change dynamically on the page, team logos, scores, power play time, empty goal status, etc.  We could even use jinja to control the design of our page by using jinja in our css class names for the html, tags this would allow us to give the page a 'theme' for the home team or just our favorite team(s). If you follow more than one team this may be the approach you want to go with. I'll be using this approach in my examples so when I want to watch Marc-Andre Fleury play I can give the page a Vegas Golden Knights theme.


Every good website always has a default 'index.html' page that is usually used for the homepage. For our use that will pretty much be the only page we need. We can always add more later if we have a need to. So let's add a new page called index.html under templates and add the following markup to the file.

<!DOCTYPE html>
  <head>
    <title>{{ title }}</title>
    <link rel="icon" href={{ url_for('static', 
          filename='media/Carolina_Hurricanes.svg') }}>
    <link rel="stylesheet" href={{ url_for('static', 
          filename='libs/css/site.css') }}>
  </head>
  <body>
    <div class="header"></div>
    <div class="main__content">
      <form action="/" method="POST">
        <button type="submit" value="" class="btn__goal">
          <img src={{ url_for('static', 
  		filename='media/Carolina_Hurricanes.svg') }}>
        </button>
      </form>
     </div>
  </body>

Our html is pretty basic as you can see from above.  We will add more to it later to give it a better team theme.

The main important part in our markup above is the form section.

    <form action="/" method="POST">
       <button type="submit" value="" class="btn__goal">
          <img src={{ url_for('static', 
  		filename='media/Carolina_Hurricanes.svg') }}>
       </button>
    </form>
Our form action "/" tells our application to go to the root of the website, which is the index page. So essentially it will just refresh itself when the form is sent to the backend server. The HTTP method we will use is a POST method, this is what the web app will look for to determine if it should kick off our 'experience'.

You'll notice I've used an image for my button, in this case I'm using a team logo, so instead of  a cheezy "Click me" button we just touch the team logo to make magic happen.

Now that we have our index.html template completed, for now, we will want to add some design to it.  In the head section you'll notice I've already linked our main css (Cascading Style Sheets) file so lets go ahead and create that file and add some styles.

Under the directory 'libs' we will create a new subdirectory called "css", this css folder is where we will put all our styles for our web app.  Go ahead and create our main css file called 'site.css' and add the following to that file.


body, html {
height: 100%;
   margin: 0;
}
.main__content {
    background-color: white;
    background-image: url('../../media/hockey_rink.png');
    background-repeat: no-repeat;
    background-size: cover; height: 100%;
}
.header {
    height:10%; margin: 0;
    background-color: rgba(204, 0, 0);
}
form {
    position: fixed;
    top: 60%; left: 50%;
    transform: translate(-50%, -50%);
}
.btn__goal {
   background-color:rgba(255, 255, 255, 0.1); border: none;
}
In the above css we set the background color of the header section to one of the team's color of red, position the form that contains our button to be in the center of the screen, and add our hockey rink image as the background to the page. So when it is all put together what does the page look like?

 

Originally I thought about using a puck as the button icon, thought it would be more fitting but the logo is a nice size for someone to lean over and tap and still looks pretty good in my opinion.

Ok, so we have a basic page layout and some styling so that it is a little less boring, now we just need to add a bit more code to our main application file so that when the button is pushed and the post request is made we can tell the server to do something.

For this we will add the following code in our app.py file.

    from flask import Flask, render_template, request
    
    app = Flask(__name__)
    templateData = {
       'title': "Carolina Hurricanes"
    }
    
    @app.route('/', methods=['GET','POST'])
    def index():
        if request.method == 'POST':
           print("Play Horn!")    
        return render_template("index.html", **templateData)
    
    if __name__ == '__main__':
        app.run(debug=True, host='0.0.0.0')
    

Most of this should look familiar, we added the template data which is a json object to store the key: value pairs our template will render. Right now we are only setting the title of the page.
templateData = {
    'title': "Carolina Hurricanes"
}

We also modified the route to allow both GET and POST requests. 

To prevent the experience from kicking off everytime the page is loaded we added an if statement to check the request object's method, if the method was post then we know someone touched the logo aka button on the main page. To test this we will just print out "Play Horn!" anytime the button is pushed.

if request.method == 'POST':
   print("Play Horn!")

Once we save everything and run our app, if you forgot how from the command line or our terminal type in the following command:

python3 app.py
Now we should see our server come up, go to the server's ip and port 5000 in your browser and you should now see the page that looks similar to the one above, clicking the logo should also print the "Play Horn!" message to our console.

If your having issues or want to double-check your code you can go to my repo and pull or review the code for this section from my github repo.

In the next section Part 4 of our hockey experience will probably be the last section of this series where we put the rest of the solution together.

Friday, January 8, 2021

Hockey Experience Project - Part 2

 Putting it all together

This is the second part of a multi-part blog post on recreating an arena hockey experience at home. If you got here without reading Part I you can find it here

Part I TL;DR

In part 1 we covered the NHL APIs, which ones to use, how to find games, etc. We also realized that an automated approach may not work due to TV delays and such, so we came up with something a bit more manual. I may go back and update my web app to include automated score tracking later. I mean how cool would that be to display the current score on my web app.


Equipment Used

UPDATE: Turns out the bluetooth speaker is a bigger hassle than it is worth. The steps in this series continues to show how to work with it but I'll be trading mine out for a wired speaker.

Since our goal light plugs in to the wall, we could either splice the wires and wire up a breadboard to a relay or use the control relay I linked above that does this for us and we can just plug in multiple devices. I like this approach as it is cleaner, supports multiple devices, and I don't have to worry about electrocuting myself which is always a plus.

For the Android Tablet, this can be any tablet or device you can get a web browser on. I like the idea of the tablet as it makes it real easy to setup and anyone in the room could use it. If you didn't read part 1, I chose the Altec Lansing speaker merely based on the fact I already had one sitting around that wasn't being used. You could use any compatible speaker it doesn't have to be a bluetooth one. But I do like the idea that the speaker can be setup anywhere in the room.

Programs! Get Your Programs Here!

Our raspberry Pi can support a number of different programming languages, Java, Python, Node.js, Go,  Bash, etc, but before we begin writing code we need to pick one.  Most of my professional career as a Developer was spent writing Java code for both standalone and web applications. When I started doing more DevOps and systems related work I started using more shell scripting and Python. For this project I want something robust, fun, and easy to get started with.

I feel like this is a Pokemon moment, "I choose you Python" or more specifically Flask.

Why Python/Flask?
Somewhere a long time ago I read some Python documentation that suggested when you comment your code it should be done in the style of Monty Python, the comedy group. While by today's standards that is probably a bad idea, I've always enjoyed watching Monty Python and loved the idea of this so I use Python whenever I can.

Ok so here is a more valid list of reasons:
  • Python is easy to learn
  • Most *nix systems, including our raspberry pi already has Python installed
  • Python is an interpreted language so we don't have to compile it
  • Python is a mature language and we can easily find support on forums, etc
  • Freely available libraries, including the GPIO Zero library we will use to activate our goal light.
  • Flask is a web application framework that can be easily customized to our needs.
  • Flask has a built-in webserver we can use for development.
  • Our application is static, meaning the content won't really change, Flask is perfect for small static apps.
What about Django you say. We could very well have used Django for this, it is a very mature web framework with a lot of support, but my focus right now is on time. We need to get something going quickly, and I know with a few lines of code I can get a static Flask app up and going pretty quick.

 Puck Drop

If you haven't done so already, you will need to setup your raspberry Pi. I'm not going to go over how to put your raspberry pi together and do the initial setup, but if you need them you can find them here. Go ahead and get the Raspbian OS installed, WiFi setup, etc. You can plug a monitor, keyboard, mouse, etc in and work directly on the raspberryPi if you wish or following these instructions you can setup a Remote Desktop (RDP) session, which I will be using in my examples.

I prefer the RDP method as it allows me to use my MacBook to connect remotely to the pi where my development environment and IDE will be installed at. If you are going to hookup a mouse and keyboard directly to the raspberry pi, just make sure you use USB devices and not bluetooth ones since our speaker will be paired via bluetooth.

If you don't have a favorite IDE to use on the raspberry pi I would recommend installing VSCode as your editor it has a lot of great extensions to make your life so much easier and it is faster than Eclipse or IntelliJ and IMHO it is way better than the Python editors Thonny or IDLE. 

VSCode isn't in the package repository but you can use the web browser on your raspberry pi to download it, just be sure to grab the 32-bit ARM .deb package unless you know for sure you have a 64-bit OS on your raspberry pi.

Ok, let's get started!

Lets create a new directory for our application, we will call it 'nhl_webapp'. VSCode allows us to open a terminal directly in the IDE which can be faster to create the base files/folders on the command line.

mkdir -p nhl_webapp nhl_webapp/templates nhl_webapp/static nhl_webapp/static/media nhl_webapp/libs
cd nhl_webapp
Initialize our project as a git project
git init

There are a few files we will need to get started. We can use the 'touch' command to create the empty files in one entry.

touch app.py .gitignore README.md
  • app.py  - will hold our main web application code
  • .gitignore - Since I'll be using git to manage my code this allows me to omit certain files/folders from being checked in.
  • README.md - Markdown file that will describe our project and tell others how to use it.
Next we will create our virtual environment and activate it. The virtual environment will be used to install our dependencies to and run our application.

python3 -m venv env
source env/bin/activate

Great, now lets install Flask, GPIOZero and their dependencies to our virtual environment.

python -m pip install Flask gpiozero

We don't need to check in our entire virtual environment to git but we do want to ensure we capture the requirements for others, so lets add the `env/*` folder to our .gitignore and capture our requirements in a separate file.

python -m pip freeze > requirements.txt

cat env/* > .gitignore

So now our file structure should look like the screenshot below. 


Let's add some code and test that everything is working.

In VSCode open the app.py file and add the following code and save the file. If you don't want to type it out you can find the source on my github repo.


Now to test this basic application. 

In the terminal window type the following to start the application. Because Flask comes with a built-in dev web server, starting the app will also launch the web server on its default port of 5000.

python3 app.py

If everything worked we should see the server startup.


Now let's see what the web app looks like in the web browser. Open Chromium or whatever browser you have on the raspberry pi and go to the address of http://localhost:5000 If all went well we should see the page with "Our NHL WebApp" on it.


So now we have a functioning Flask web app, although at this point it is very basic and doesn't do anything other than print our text. 

If you are using git and have a remote repository, now may be a good time to commit those changes.  If you are unfamiliar with git there are a lot of free resources on the web to learn git. Git is a great way to keep track of all your changes in a code repository so if you ever accidentally delete a file or make a change that breaks something it is easy to go back to a previous version of your code. It also allows you to see what you changed and when.

In our next steps we will add some colors, graphics, and a button to activate the goal horn and goal light to tie it all together. So this is a good time to search the web for some free hockey graphics, team logos, and an mp4 of the goal horn.