hauleth
I have written (and published) small Erlang library that helps with integrating your release with systemd
https://github.com/hauleth/erlang-systemd
Features
The idea of this library is to port some parts of the libsystemd into Erlang without need for NIFs. Currently implemented features:
- Notify socket which allows for sending notifications to systemd. This for example allows blocking startup until our application is completely ready, so not only it started up, but is ready to do it’s work. Unfortunately the feature of saving FDs between runs (which would allow for restarting application without disconnecting users) is not currently possible, but will be probably added as soon as Erlang stabilise
socketinterface instead ofinet. - Watchdog which allows systemd to watch for keep-alive messages via notify socket. This allows you to provide simple information to the systemd when it should restart your application before your failures hit the
initprocess. - Listen FDs which allow for socket activation of your application (i.e. application is started only when there is incoming data in socket) and keeping sockets opened between restarts. However while it is implemented it is currently pointless feature as you cannot use these
fds in any way due to fact thatgen_tcpandgen_udprequires unbind(3)ed file descriptors. Maybe this will became more usable oncesocketinterface land. - Check for systemd booted system - pretty pointless, as all functionalities above are safe which mean that you can use all of above even on non-systemd OS and it will not (or at least should not) cause any problems. This was implemented just for functionality parity.
The best thing? It is mostly automagical. Watchdog and notify socket will be established without any user interaction. Only thing needed is to tell systemd that your application is ready (not possible to be done automatically unfortunately).
TODO in future:
journaldbackend forlogger(will be useful for Elixir 1.10+ as well, as this shares the logging implementation with Erlang)
Example
Just fetch repo on machine with systemd, and run make (requires sudo to setup systemd services). Then you can see that service is up and running:
hauleth@test-ubuntu:~/plug$ systemctl status example-watchdog.service
● example-watchdog.service - Example BEAM application that uses watchdog
Loaded: loaded (/usr/local/lib/systemd/system/example-watchdog.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2020-01-16 14:46:02 UTC; 3s ago
Main PID: 1662 (beam.smp)
Tasks: 20 (limit: 1108)
CGroup: /system.slice/example-watchdog.service
├─1662 /home/hauleth/plug/_build/prod/rel/plug_systemd_example/erts-10.6.2/bin/beam.smp -- -root /home/hauleth/plug/_build/prod/rel/plug_systemd_example -progname erl
├─1701 /home/hauleth/plug/_build/prod/rel/plug_systemd_example/erts-10.6.2/bin/epmd -daemon
└─1704 erl_child_setup 1024
Jan 16 14:46:01 test-ubuntu systemd[1]: Starting Example BEAM application that uses watchdog...
Jan 16 14:46:02 test-ubuntu systemd[1]: Started Example BEAM application that uses watchdog.
We simulate reload by curl http://localhost:4000/reload:
hauleth@test-ubuntu:~/plug$ curl http://localhost:4000/reload
hauleth@test-ubuntu:~/plug$ systemctl status example-watchdog.service
● example-watchdog.service - Example BEAM application that uses watchdog
Loaded: loaded (/usr/local/lib/systemd/system/example-watchdog.service; enabled; vendor preset: enabled)
Active: reloading (reload) since Thu 2020-01-16 14:46:02 UTC; 2min 10s ago
Main PID: 1662 (beam.smp)
Tasks: 20 (limit: 1108)
CGroup: /system.slice/example-watchdog.service
├─1662 /home/hauleth/plug/_build/prod/rel/plug_systemd_example/erts-10.6.2/bin/beam.smp -- -root /home/hauleth/plug/_build/prod/rel/plug_systemd_example -progname erl
├─1701 /home/hauleth/plug/_build/prod/rel/plug_systemd_example/erts-10.6.2/bin/epmd -daemon
└─1704 erl_child_setup 1024
Jan 16 14:46:01 test-ubuntu systemd[1]: Starting Example BEAM application that uses watchdog...
Jan 16 14:46:02 test-ubuntu systemd[1]: Started Example BEAM application that uses watchdog.
And after 10 seconds it is “up” again:
hauleth@test-ubuntu:~/plug$ systemctl status example-watchdog.service
● example-watchdog.service - Example BEAM application that uses watchdog
Loaded: loaded (/usr/local/lib/systemd/system/example-watchdog.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2020-01-16 14:46:02 UTC; 2min 27s ago
Main PID: 1662 (beam.smp)
Tasks: 20 (limit: 1108)
CGroup: /system.slice/example-watchdog.service
├─1662 /home/hauleth/plug/_build/prod/rel/plug_systemd_example/erts-10.6.2/bin/beam.smp -- -root /home/hauleth/plug/_build/prod/rel/plug_systemd_example -progname erl
├─1701 /home/hauleth/plug/_build/prod/rel/plug_systemd_example/erts-10.6.2/bin/epmd -daemon
└─1704 erl_child_setup 1024
Jan 16 14:46:01 test-ubuntu systemd[1]: Starting Example BEAM application that uses watchdog...
Jan 16 14:46:02 test-ubuntu systemd[1]: Started Example BEAM application that uses watchdog.
We can also force it to restart by curl http://localhost:4000/stop which will trigger Watchdog to restart application:
hauleth@test-ubuntu:~/plug$ curl http://localhost:4000/
1760
hauleth@test-ubuntu:~/plug$ curl http://localhost:4000/stop
hauleth@test-ubuntu:~/plug$ curl http://localhost:4000/
1846
Requests on / return current PID of the VM, so we clearly see that the restart happened in the meantime.
If you are using systemd for supervising your application, then I highly suggest you to try it and see if this can improve your experience.
Trending in Discussions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
hauleth
Version 0.3.0 released:
This update adds support for systemd’s journal, which makes library feature complete for now. Now it needs a little polishing and real-world usage before I mark it as a 1.0.0.
Again - feel free to test this out and to submit issues if any. If someone needs any additional feature, then also feel free to submit it, however for me it is (as said above) feature complete.
hauleth
I have also prepared simple Elixir application that uses this library:
https://github.com/hauleth/plug_systemd_example
Unfortunately, as mentioned in original post, it cannot use socket activation (yet), but rest of features is implemented there.
OvermindDL1
Ooo, this looks useful!! Thanks for it!!
hauleth
There are still some bugs there and here (mostly in biggest part of the repo which is
systemd_journal_formatter) with fixes onmaster(I will publish 0.3.3 soon).Additionally I have finally got socket activation working. Unfortunately due to ERL-1138 the passed FDs can be used only in UNIX sockets (as these do not
bind(3)and this is the culprit there), so I just have done some “socket jumping” and translate TCP port to UNIX Stream socket and then to the Cowboy.So in other words there are 5 systemd units:
example-application.service- which is THE application service file, it will just simply run the application when startedexample-application.socket- UNIX Stream socket definition that will be handled by systemd, this is required due to the above bug; this will startexample-application.serviceon any connection incomingexample-activation.service- service that will take the systemd activated FD and forward all it all to another socket provided as an argument, think. of it as an alternative implementation ofsocatexample-activation.socket- definition of TCP socket that systemd will listen on and will pass it down when there will be any request incomingexample.target- simple target for ease of useWithin the application the magic happens there:
https://github.com/hauleth/plug_systemd_example/blob/1a03a50b461f3657870489523e9cd3302eec2a45/lib/plug_systemd_example/application.ex#L34
It creates new
gen_tcpsocket from the provided file descriptor and then pass it to the Ranch (via Cowboy). The only problem is that currently each first request (when service is down) will fail with no response sent, but all consecutive requests will work as expected.amnu3387
Hi,
I use systemd and saw this but could you expand on the normal use case where you would use this and how? My interpretation is that instead of running the usual
systemctlcommands at the OS level you run them from inside the VM itself?hauleth
It depends, because it provides few features. I tried to describe them the best I could in the docs (if you find something unclear or missing, then open the issue, I will try my best to fix it).
The features are as follows:
Notify socket
This is used to inform
systemdabout state of your application. So if you haveNotifyAccess=mainorNotifyAccess=allthen you can for example notifysystemdwhen your application is truly ready (especially when you will also useType=notify). Erlang applications rarely are ready to work just when VM start and there is need for some setup which happens in the supervisor (DB connection, HTTP server setup, etc.) and this can take some time as well. So with this library you can just call:systemd.notify(:ready)when everything is done andsystemctlwill block until it receives that message. So whensystemctl start my_app.serviceends you are sure that your app is actually ready.Additionally to that also will receive information when the application will be shutting down, but not yet completely dead. So for example if you use
Plug.Cowboy.Drainerin your application, which will hold the application from shutting down until all HTTP requests will not be served, then you will have information about application shutting down insystemctl status my_app.service, like this:https://asciinema.org/a/jqTbUdgFkc7206vFK4AScqq5p
You can also send other notifications but I am not truly sure how these are handled.
In future, when ERL-1139 and ERL-1138 will land, it will also be possible to “save” sockets between restarts, so your application will be able to restart without closing existing connections.
Watchdog
Watchdog is heart-beat
systemdsubsystem that expects to receive special messages within specified timespan to make sure that application is alive. This is controlled byWatchdogSec=option in[Service]section ofsystemdunit. This option will be handled automatically for you (so the pinging process is automatically handled), but you still have an option to call:systemd.watchdog(:trigger)which will send special triggering message which will work like there would be no keep-alive message sent within requested timespan. This will trigger action that depends onRestart=option. More can be found insystemd.service(5)andsd_watchdog_enabled(3).Listen FDs
This is just the function that will return all file descriptors passed from
systemdto the service. This is useful for socket activation, and in future may be used for keeping sockets open between restarts. It is simply call:systemd.listen_fds(false)that will return list of descriptors in form ofinteger() | {integer(), charlist()}where 2nd entry in tuple is descriptor name.Journal
This is set of 3 modules for Erlang’s
loggerutility (usable as well with Elixir 1.10+):systemd_stderr_formatter- this is formatter that will append special severity (log level) tag to messages passed to stdout/stderr which will allow journal to assign required severity level. Configuration is simple, it takes map with one additional field:parentthat should contain the formatter module where all other options will be passed, defaults tologger_formatter.systemd_journal_handsystemd_journal_formatter- these two SHOULD always be used together unless you really know what you are doing, this will usesystemdjournal datagram socket to send log messages which will allow for:journalctlcommand, by default these metadata fields are:SYSLOG_PRIORITY- log level in syslog integer formatSYSLOG_TIMESTAMP- timestamp of log entrySYSLOG_PID- OS PID of the VMERL_PID- PID of the Erlang process that sent messageCODE_LINECODE_FILECODE_MFA- module, function, and arity of the function in the Erlang formatModule:Function/ArityWhile controlling
systemdoverall would in theory possible via DBus, but IMHO this is out of scope for this library, and should be implemented as a separate one as this is not that needed in Erlang applications.amnu3387
@hauleth thank you very much for such an extensive overview.
So this is to be used with a systemd service/task that is set to take advantage of the functionality you expose right? (like the NotifyAccess, Type, etc, settings)
hauleth
Yes. This isn’t for interfacing with systemd, this could be written as a separate library that will implement DBus, this is for integrating of “operating” and “observability” features.
hauleth
Version 0.4.0 released.
The minor version bump was due the fact that I have removed function
systemd:listen_fds/1which was taking boolean argument that defined whether remove environment variables after fetching them. Now this is replaced by callingsystemd:unset_env(listen_fds). Rest is left as is, however now user can decide that library should not automatically clear environment variables on startup (however it is discouraged to do so).hauleth
Version 0.5.0 released
This introduced few breaking changes:
systemd_journal_formatter- now everything happenes in thesystemd_journal_hand do not require user to remember to use one particular formattersystemd_stderr_formatterwas renamed tosystemd_kmsg_formatteras it can be used with other output streams, not onlystandard_error, new name is derivation of format that the journal uses which is based on Linux kernel messages known askmsgOther notable changes:
systemd:notify/2was removedsystemd:notify/1can now take list of messages to be sent at oncesystemd_kmsg_formatterwill be used automatically for all handlers that uselogger_std_hwith typestandard_ioorstandard_errorif these are attached to thejournalstream (wich is checked viaJOURNAL_STREAMenv variable set automatically by systemd)systemd:ready/0helper function that returns child spec for temporary child that will send ready notification, useful for adding it in the supervisor children list