___           __          _          
                           / _ \___ ___  / /__  __ __(_)__  ___ _
                          / // / -_) _ \/ / _ \/ // / / _ \/ _ `/
                         /____/\__/ .__/_/\___/\_, /_/_//_/\_, / 
                                 /_/          /___/       /___/  


                             _   _           _        _     
                            | \ | |         | |      (_)    
                            |  \| | ___   __| | ___   _ ___ 
                            | . ` |/ _ \ / _` |/ _ \ | / __|
                            | |\  | (_) | (_| |  __/_| \__ \
                            |_| \_|\___/ \__,_|\___(_) |___/
                                                    _/ |    
                                                   |__/     

                                ## to Production in 2016



                                    # NodeConf Oslo
                                   ## June 5th 2016


                                       Luke Bond
                                       @lukeb0nd
@lukeb0nd
NodeConf Oslo 2016
# WHO AM I?

- I'm a backend developer, DevOps-curious
- Mostly I do Node.js and Docker
- Built an OS project called "Paz" - _http://paz.sh_

I work for YLD.io, a London-based software engineering consultancy
that specialises in Node.js, Docker and React.

Mostly we help enterprise companies move towards continuous delivery
and embrace DevOps practices.

AMA.
@lukeb0nd
NodeConf Oslo 2016
# WHAT'S THIS TALK ABOUT?

This talk in a nutshell:

> Use your Linux init system to run your Node.js apps
> It's easy, powerful and the tooling is great!

- I'll talk about PM2 as a reference case
- Then I'll show you hands-on how to achieve the same things with systemd
- This talk will be mostly demo
- I will move quite quickly (sorry) but the final result will be available to copy-paste!




## All unit files in this talk can be found here, along with the slides:

https://github.com/lukebond/nodeconf-oslo-20160604
@lukeb0nd
NodeConf Oslo 2016
# PM2

- I'll be referring a lot to PM2 because everyone knows it
- Most of you probably use it in production; or something like forever, mon or nodemon
- _Please note_ that I have nothing against PM2 or any of these tools!

Why is PM2 so popular? Because it makes the following very easy:

1. Process management
2. Log management
3. Magic/seamless sharing of ports

PM2 has great UX too. It's a powerful tool.
@lukeb0nd
NodeConf Oslo 2016
# LEARN TO LINUX

Why learn Linux instead of sticking to PM2 or similar?

- You can learn to do all these things yourself, using basic Linux tooling
- It's easy and it's fun
- Broaden your skill-set!
- Impress your friends!
- Learn that you don't need a process monitor
- Deploy applications that any Linux sysadmin outside the Node.js world will understand
- systemd is now more or less the standard init system
@lukeb0nd
NodeConf Oslo 2016
# LINUX INIT SYSTEMS

- Linux has something called an "init system" that runs as PID1
- It's the ancestor of all processes on Linux; the ultimate process monitor!
- Each service gets an init script for start|stop|restart etc.
  - e.g. Databases, web servers, etc.
- Basically what PM2 does, but OS-wide
- Linux has been doing this for years
- Most modern distros use systemd as the init system
@lukeb0nd
NodeConf Oslo 2016
# SAMPLE APP

- I've built a contrived sample app in Node.js that talks to Redis:

> https://github.com/lukebond/demo-api-redis

- It's basically HTTP Hello World with a Redis counter
- We'll set it all up with systemd
- You will need:
  - A version of Linux with systemd *
  - Node installed
  - Redis installed





* These distros: https://en.wikipedia.org/wiki/Systemd#Adoption_and_reception
@lukeb0nd
NodeConf Oslo 2016
# MY FIRST UNIT FILE

- We tell systemd about out services by writing unit files
- Let's write our first unit file for our Node.js sample app

    $ cat /etc/systemd/system/demo-api-redis@.service
    [Unit]
    Description=HTTP Hello World
    After=network.target
    
    [Service]
    User=luke
    Environment=REDIS_HOST=localhost
    WorkingDirectory=/home/luke/Development/demo-api-redis
    ExecStart=/usr/bin/node index.js
    
    [Install]
    WantedBy=multi-user.target

- Create this file and copy it into the above directory (grab it from my GitHub repo!)
- Signal systemd to reload the config
- Enable and start the service *

    $ systemctl daemon-reload
    $ systemctl enable demo-api-redis@1
    $ systemctl start demo-api-redis@1

- Of course it fails because Redis isn't running!
- Let's explore dependencies with systemd...

* Learn more about `systemctl` here:
  https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
@lukeb0nd
NodeConf Oslo 2016
# SYSTEMD DEPENDENCIES - Wants=

- Use `Wants=` in `[Unit]` section of unit files to declare dependencies
- Starting this unit will trigger wanted units to be started also

    $ cat /etc/systemd/system/demo-api-redis@.service
    [Unit]
    Description=HTTP Hello World
    After=network.target
    Wants=redis.service
    
    [Service]
    User=luke
    Environment=REDIS_HOST=localhost
    WorkingDirectory=/home/luke/Development/demo-api-redis
    ExecStart=/usr/bin/node index.js
    
    [Install]
    WantedBy=multi-user.target

    $ systemctl daemon-reload
    $ systemctl restart demo-api-redis@1

- Note that now Redis gets started too!
@lukeb0nd
NodeConf Oslo 2016
# HANDLING CRASHES, RESTARTS, ETC. (1/3)

- Let's kill the node process and see what happens:

    $ kill -9 $(pgrep "node index.js")
    $ systemctl status demo-api-redis@1 | grep Active
    Active: failed (Result: signal) since Thu 2016-06-02 11:50:32 BST; 47s ago

- The process hasn't been automatically restarted after the "crash"
- Add the following to the `[Service]` section of the unit file to fix this:

    Restart=always
    RestartSec=500ms
    StartLimitInterval=0

- This example will restart the service indefinitely with 500ms delay
- There is great flexibility in how this can be configured!
- The above should be fine though

    $ kill -9 $(pgrep "node index.js")
    $ systemctl status demo-api-redis@1 | grep Active
    Active: active (running) since Thu 2016-06-02 12:12:05 BST; 22s ago

- It has been restarted!
- What about reboots? systemd will start units on boot that are _enabled_

    $ systemctl status demo-api-redis@1 | grep Loaded

- I'm going to risk a reboot; cross your fingers for me!
@lukeb0nd
NodeConf Oslo 2016
# LOGS (2/3)

- systemd has a powerful tool for working with logs for all services: `journalctl`
- To scroll through logs for a unit or service:

    $ journalctl -u demo-api-redis@1

- To follow said logs:

    $ journalctl -u demo-api-redis@1 -f

- You can ask for logs since the last boot:

    $ journalctl -u demo-api-redis@1 --boot

- You can ask for logs since a certain time:

    $ journalctl -u demo-api-redis@1 --since 08:00
    $ journalctl -u demo-api-redis@1 --since today
    $ journalctl -u demo-api-redis@1 --since yesterday
    $ journalctl -u demo-api-redis@1 --since 2016-06-02 15:36:00

- You can filter by log level (console.log, console.error, etc.):

    $ journalctl -u demo-api-redis@1 -p err

- There is so much more you can do; it's _super_ powerful. Great docs here:
  https://www.digitalocean.com/community/tutorials/how-to-use-journalctl-to-view-and-manipulate-systemd-logs
@lukeb0nd
NodeConf Oslo 2016
# MULTIPLE INSTANCES

- First, let's modify the unit file to set different ports for them

    # /etc/systemd/system/demo-api-redis@.service
    [Unit]
    Description=HTTP Hello World
    After=network.target
    Requires=redis.service

    [Service]
    Environment=REDIS_HOST=localhost
    Environment=LISTEN_PORT=900%i
    WorkingDirectory=/home/luke/Development/demo-api-redis
    ExecStart=/usr/bin/node index.js
    Restart=always
    RestartSec=500ms
    StartLimitInterval=0

    [Install]
    WantedBy=multi-user.target

- And now reload the unit and start and enable the other instances:

    $ systemctl daemon-reload
    $ systemctl enable demo-api-redis@{2,3}
    $ systemctl start demo-api-redis@{2,3}
    $ netstat -tlpn | grep 900
    tcp6     0     0 :::9001      :::*       LISTEN      2654/node           
    tcp6     0     0 :::9002      :::*       LISTEN      2656/node           
    tcp6     0     0 :::9003      :::*       LISTEN      2704/node           

- Cool! There is one final feature: a local load balancing proxy...
@lukeb0nd
NodeConf Oslo 2016
# SIMPLE LOAD BALANCING WITH balance (3/3)

- `balance` is a simple, light-weight load balancer

  https://www.inlab.de/balance.html

- We can set it up with a one-liner:

    $ balance -f 9000 127.0.0.1:900{1,2,3}
    $ curl localhost:9000
    "Hello, world 192.168.1.39! 20 hits."

- But let's do this the systemd way, with the following unit file
@lukeb0nd
NodeConf Oslo 2016
# SIMPLE LOAD BALANCING WITH balance

    # /etc/systemd/system/balance.service
    [Unit]
    Description=Balance - Simple TCP Load Balancer
    After=syslog.target network.target nss-lookup.target

    [Service]
    ExecStart=/usr/bin/balance -f 9000 127.0.0.1:9001 127.0.0.1:9002 127.0.0.1:9003

    [Install]
    WantedBy=multi-user.target

- As usual, signal systemd to reload then enable and start the service

    $ systemctl daemon-reload
    $ systemctl enable balance
    $ systemctl start balance

- Does it work?

    $ curl localhost:9000
    "Hello, world 172.20.10.2! 29 hits."
@lukeb0nd
NodeConf Oslo 2016
# WHERE TO FROM HERE?

- This is just the basics of systemd
  - Despite approaching feature-parity with PM2
- It should be easy to build something dynamic on top of this
  - As opposed to hardcoded ports in `balance.service`
- SSL termation, hooking up to external load balancers, etc. I'll leave to you
- Containers!
  - Normally I'd do all this with containers
    - Using *rkt* or *runc* because Docker & systemd sometimes don't play nicely together
  - I left it out today to reduce the number of new things introduced
  - Talk to me about containers, Node.js & systemd if you're interested!
@lukeb0nd
NodeConf Oslo 2016
# CONCLUSION

- Learn to use systemd for your Linux production machines
- Use my unit files as a starting point
- systemd has a learning curve but it isn't difficult
- The tools are mature and powerful
- You'll realise that you don't need a process monitor
  - What starts your process monitor, after all?

## LINKS

- Repository with slides, unit files etc. here:
  https://github.com/lukebond/nodeconf-oslo-20160604

## FURTHER READING

- systemd distros:
  https://en.wikipedia.org/wiki/Systemd#Adoption_and_reception
- Good article on using `systemctl`:
  https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
- Good article on using `journalctl`:
  https://www.digitalocean.com/community/tutorials/how-to-use-journalctl-to-view-and-manipulate-systemd-logs
- The creator of systemd talking about security features:
  https://www.youtube.com/watch?v=hiW8eIdcRgo&list=PLlh6TqkU8kg_3FpXLlHMnoVqKZysIzXlK&index=6
- Videos from systemd conf 2015:
  https://www.youtube.com/channel/UCvq_RgZp3kljp9X8Io9Z1DA
- systemd man pages:
  https://www.freedesktop.org/software/systemd/man/systemd.unit.html
  https://www.freedesktop.org/software/systemd/man/systemd.service.html
- Slides presented with *mdp*:
  https://github.com/visit1985/mdp
@lukeb0nd
NodeConf Oslo 2016
# THANKS!





Thanks for listening! Go and read the repo and play.

Any questions, contact me:


## @lukeb0nd

## luke@yld.io


Or come and say hi today!
@lukeb0nd
NodeConf Oslo 2016

NodeConf Oslo 2016

By Luke Bond

NodeConf Oslo 2016

  • 857