Professor Sloth

Free web performance master class

Learn about web performance and how to make your site faster, delivered straight to your inbox.

Episode 12: Tweaking Systemd Services With Ansible

We thought we were done with infrastructure work. We were wrong. Just as we’ve started work on application code, it turns out our server configuration needs a couple changes. Our application loads its environment config based off of an environment variable. We need to ensure this variable is actually set on the server. We also want to fix our Ansible playbook which fails when run on fresh servers.

Use Systemd to Set an Environment Variable Specifying Dev/Prod

Our ASP.NET Core application needs to know which environment it is running in order to load the proper configuration. The application looks for an environment variable named WHAT_ENVIRONMENT_AM_I which is used to select the correct configuration. The variable is specified in the systemd unit file which is more contained than setting it globally:


[Unit]
Description=aspnetCoreApp

[Service]
# ...snip...

# The environment is hardcoded to "Dev"
# (Later, we'll make it dynamic using Ansible)
Environment=WHAT_ENVIRONMENT_AM_I=Dev
files/aspnetCoreApp.service -> /etc/systemd/system/aspnetCoreApp.service

Fix Ansible Failure When Service Executable Does Not Exist

We have a chicken and egg problem when provisioning brand new servers. Our .NET Core app’s systemd service is configured through Ansible. Ansible fails to start the service when code has never been deployed to the new server.

The playbook checks for the application’s binary before starting the service to avoid the error. The service is still configured ahead of time to ensure the server is ready to go when the first code deploy happens.


---
- hosts: dotnetHosts

  tasks:
    # ...snip...

    # Copy new service and set it to start automatically
    - name: copy service unit file for aspnetCoreApp
      copy: src=files/aspnetCoreApp.service dest=/etc/systemd/system/aspnetCoreApp.service
      notify: reload app service

    # Check for the service executable and do one of the following actions:
    #   - If the executable exists: start the app service
    #   - Otherwise: set the service to autostart, but keep it stopped for now
    - name: check for installed service
      stat: path=/var/www/aspnetCoreApp/app/aspnetCoreAppExecutable
      register: installedFile

    - name: ensure service is running (When app is installed)
      service: name=aspnetCoreApp state=started enabled=yes
      when: installedFile.stat.exists

    - name: ensure service is stopped to avoid errors (When app is NOT installed)
      service: name=aspnetCoreApp state=stopped enabled=yes
      when: installedFile.stat.exists == False


  handlers:
    # Tell systemd to re-read the service unit files to find our new service
    - name: reload app service
      systemd: daemon_reload=yes
systemd-dotnet-playbook.yml

These are both little changes, but polish makes all the difference. Now our Ansible playbooks work when spinning up entirely fresh machines and our application knows what environment it’s running in! Next, we’ll get back to code and explore how to do unit testing with .NET Core.

Jordan Griffin
VP Engineering Request Metrics