unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Thoughts on stateful services in Guix
@ 2020-02-01 20:38 Alex Sassmannshausen
  2020-02-05 13:03 ` Giovanni Biscuolo
  2020-02-05 14:04 ` Ludovic Courtès
  0 siblings, 2 replies; 3+ messages in thread
From: Alex Sassmannshausen @ 2020-02-01 20:38 UTC (permalink / raw)
  To: Guix-devel

Hello,

As a result of FOSDEM conversations today I felt inspired to put some
thoughts on paper about an area where I think we currently run into
complications.

Not sure if it is appropriate to write this blog-post-ish contribution
here, but as I don't have a blog, and it's about Guix development, I
figured it might be OK.

Best wishes,

Alex

1 Introduction
══════════════

  Guix is amazing.  A large part of why it continues to be amazing is
  because it provides strong guarantees to the end-user and developer.
  As such it makes reasoning about packages and deployments relatively
  straight-forward.

  The functional paradigm cleary works fantastically for packages.
  Unfortunately it is not quite clear that it works just as well for
  services.  The reason for this is that too many useful services are
  inherently stateful.  Their statefulness means they have side-effects,
  which in turn cause issues when relying on Guix features such as
  roll-back or automated deployment & guaranteed reproducability.

  Disciplined developers can implement many services in such a way that
  their statefulness is delegated to other dedicated services.  In this
  way the problem of statefulness can be isolated.  However, many
  existing useful services /have not/ been implemented in such a way.

  These notes are an attempt to think through ways of formalising how we
  mitigate stateful services in Guix.

  The notes below are organised around the example of a popular PHP
  content management system, Drupal.  Similar problems will apply to
  many other end-user services.

  My intent here is to communicate my thinking in the hopes that others
  can point to obvious flaws in my reasoning — or to stimulate
  conversation about the topic.  I hope at the very least that it is an
  interesting read!


2 Learning by Doing: Packaging & Deploying Drupal
═════════════════════════════════════════════════

2.1 Problem (1): the Drupal tarball is a binary blob!
─────────────────────────────────────────────────────

  Released Drupal tarballs are shipped with a bunch of PHP dependencies,
  as well as compiled JS files.  A fully source-distributed installation
  of Drupal would:
  1) delete all shipped PHP dependencies
  2) independently build all PHP dependencies and make them available to
     Drupal (through it's vendor directory as symlinks?)
  3) delete all compiled JS files & libraries
  4) independently build the JS dependencies and make them avalable to
     Drupal.


2.2 Packages must always be stateless!
──────────────────────────────────────

  What does this mean in practice?  Let's look at Drupal again.  When
  you download Drupal, the resulting tarball contains the source code,
  and an empty sites/ folder.  The sites folder is intended to contain
  /state/ files.  The normal installation procedure is to simply drop
  the drupal distribution in your web root directory, and for state
  files to live underneath sites/ within your webroot.

  In Guix, Drupal is packaged so that it is installed in the store.

  The store is read-only and hence no /state/ files can live under the
  sites/ directory in the store.

  How do we get around this?
  1) either we patch drupal to expect the sites/ directory outside of
     its own folder tree.
  2) or we symlink /gnu/store/…drupal…/sites/ to a different location on
     the filesystem (e.g. /var/lib/drupal/sites/)

  The latter solution, while easy, means that a *successful*
  installation of the package Drupal in Guix results in an installation
  in the store with a *broken symlink* pointing outside the store.

  But the package itself has been rendered stateless!


2.3 Drupal: a stateful service
──────────────────────────────

  Services in Guix are rich and multidimensional entities.  At core they
  are promises of things that will have happened when a system is up and
  running.  These promises can be arbitrary, like generating a
  configuration file every boot; or they can be an extension of other,
  already existing services.

  A particularly popular service to extend is the shepherd service,
  which ensures that particular daemons are started as soon as possible
  after the system has started.

  What would a Drupal service look like?  Essentially the software is
  just a bunch of files in a webroot — so at it's heart it simply
  extends a web service (e.g. nginx) with new location directives.

  On the other hand it also requires that tha web server, a sql backend
  and php-fpm are running, so that drupal can actually function.

  These two requirements are easily met with the usual service
  infrastructure: simply extend nginx with a location definition
  pointing to the Drupal folder in the store as the webroot and extend
  shepherd to require mysql, nginx and php-fpm.


2.3.1 Enter the state dragon
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  But hang-on… if Mysql service is a dependency, and we store state in a
  Mysql DB, what happens when we upgrade to a newer version of Drupal,
  with a different schema?  Would this service instantiation change the
  state of our system?  Could we still roll-back?

  And remember that broken symlink we introduced in the package?  That
  files/ subdirectory contains /at least/ a stateful configuration file
  — but also Drupal modules, themes, uploaded files and caches.  What
  happens when we upgrade Drupal?  Can we guarantee they won't be
  irrevocably changed as part of the upgrade?  What happens when we
  roll-back?

  The truth is that we have introduced state into our deployment and we
  hence lose a lot of the beautiful hard guarantees that Guix provides
  us with.


2.3.2 Fakin' it til we're makin' it
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  Can we get those guarantees back?  I fear not.  But we can add some
  shims and mitigations that might take the edge off the sting.

  We've elaborated, above, the extend of the state problem in Drupal.
  What would a /state shim/ look like?

  Working backwards, to ensure we can roll-back we need to make sure
  that we have access to the state at that point in history that we want
  to roll-back to.  When we roll-back from version 2 of Drupal to
  version 1, we must make sure we still have the state of version 1.

  So therefore we need to simply make an addressable state dump just
  before we activate a new service instance, so that when we roll-back,
  we can flip back to that state dump.  In the case of our Drupal
  example, this means that we need to dump the sites/ directory and the
  mysql databases.

  This state dump needs to fulfill the following criteria:
  1) It needs to be reversible: a transaction into a new state needs to
     make a dump before transition and then carry out state changes; a
     transaction into a past state must make a dump before transition
     and then load the old state's dump as it's new state.
  2) It (optionally) needs to be secured: state will likely contain
     personal information, and this information needs to be protected.


2.4 The stateful service shimmy
───────────────────────────────

  Here's the proposed general shim then:

  1) We define a new type of shepherd service, a /stateful service/,
     which has additional actions: state-dump-shim and state-load-shim.
  2) Each stateful service (e.g. drupal) is responsible for, through
     provision of imperative code snippets in the state-dump-shim and
     state-load-shim fields, providing state dumpers/restorers.
  3) Whenever a new service revision is installed:
     1) before it is installed, state-dump-shim is called.  It should
        dump the service's state in a way that when state-load-shim is
        called with the new service revision's store hash, it can locate
        any state dumps that were stored for that particular hash.
     2) after it is installed, state-load-shim is called with the store
        hash of the new service revision.  It should then be able to
        locate and load any state associated with this hash.  If no
        state is associated (e.g. when we install a wholly new
        revision), nothing is done.

  What does this give us?

  When a migration to a new service revision causes a deployed bit of
  software to stop working as expected, we can do a `guix system
  –roll-back`.  This will then also restore the service's state to the
  previous version and thus *should* restore operations.

  *BEWARE*: any state changes that occured since the previous state-dump
  /will/ be lost.  We are restoring to a backup — so user generated
  state that was added after the backup will not be present after
  restoration!


3 A generalisation: the Stateful-Service Service
════════════════════════════════════════════════

  State dumping and restoration *should* be generalisable.

  It should normally consist of one or more operations of:
  • running a special program to dump data, encrypting the dump, and
    storing it, together with the service revision hash, in a known
    location.
  • tarring up a directory tree, encrypting the archive and storing it,
    together wtih the service revision hash, in a known location.

  These operations should be able to be provided by a daemon, managed by
  shepherd, which can be configured with a gpg key for encryption, a
  mechanism for decryption (interactive or programmatic), a location for
  storing/retrieving dumps (local or remote?), and a DSL for mapping
  data dump / data restoration program invocations or file-system
  locations to data dumps it already knows of.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Thoughts on stateful services in Guix
  2020-02-01 20:38 Thoughts on stateful services in Guix Alex Sassmannshausen
@ 2020-02-05 13:03 ` Giovanni Biscuolo
  2020-02-05 14:04 ` Ludovic Courtès
  1 sibling, 0 replies; 3+ messages in thread
From: Giovanni Biscuolo @ 2020-02-05 13:03 UTC (permalink / raw)
  To: alex.sassmannshausen, Guix-devel

[-- Attachment #1: Type: text/plain, Size: 10451 bytes --]

Hi Alex,

I'm very interested in deploying "as stateless as possible" services
with Guix, thank you for sharing your thougts on this!

I'm still non able to contribute code fot this task... I'm working on it

Alex Sassmannshausen <alex.sassmannshausen@gmail.com> writes:

> As a result of FOSDEM conversations today I felt inspired to put some
> thoughts on paper about an area where I think we currently run into
> complications.

I also think that deploying services in a functional way (ala Guix
packages to be clear) is _complicated_ and... it's a problem

to be clear: this complication it's *absolutely not* Guix specific, but
it's something that becomes very clear when you want to use the
declarative approach to "everithing" once you've got Guix :-O

(and after you were deluded by the promises from tools like Puppet and
alike)

> Not sure if it is appropriate to write this blog-post-ish contribution
> here, but as I don't have a blog, and it's about Guix development, I
> figured it might be OK.

maybe with a little help by someone more qualified than me this should
become a Guix blog post... or better be generalized ("how to write
staletess services") and included in the cookbook

anyway, I think your analisys is very helpful in discussing how Guix can
help fighting the "state dragon"

I've some comment and questions

> Best wishes,
>
> Alex
>
> 1 Introduction
> ══════════════

[...]

>   Disciplined developers can implement many services in such a way that
>   their statefulness is delegated to other dedicated services.  In this
>   way the problem of statefulness can be isolated.  However, many
>   existing useful services /have not/ been implemented in such a way.

do you mean upstream?

[...]

from now on you used Drupal as an example: I do not know how Drupal
works, but I know other problematic web services :-)

> 2.3.1 Enter the state dragon
> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
>
>   But hang-on… if Mysql service is a dependency, and we store state in a
>   Mysql DB,

in this regard some time ago I found
http://gfxmonk.net/2015/01/03/nixos-and-stateless-deployment.html (see
"Lumps of impurity") a good explanation on what should be state and what
not; this is the relevant part:

--8<---------------cut here---------------start------------->8---
NixOS keeps the “OS” parts of the machine stateless, meaning that the
only state you need to manage is that which you create yourself. From
the top of my head (not an exhaustive list):

- /var, /tmp, /run: stateful, unmanaged

- each user’s $HOME directory: stateful, unmanaged (rarely used on a server, though)

- Users & Groups: stateless

- Installed software: stateless

- Installed services (using systemd): stateless

- All program configuration (i.e all of /etc): stateless

- Kernel & kernel modules, grub configuration: stateless (but requires a reboot to activate)

- Disk mounts: stateless

By “stateless”, I mean that the given item is generated entirely from
the pure nix expression of the system, and isn’t affected by any
previous state.
--8<---------------cut here---------------end--------------->8---

I was not able to find a similar description in some Guix
document... but it's OT now :-)

(...and software config stored in $HOME should be stateless, also;
guix-home-manager [1] is a _very_ interesting work on this topic)

storing state it's obviously the job of many services, what's bad it's
some software (Drupal?  Discourse for sure, Wordpress for sure) store
(part of?) configuration as status; some software provides a CLI tool
(usually as a afterthougt, e.g. wp-cli) to imperatively configure it,
but it's always treated as status

in other words: some (usually web) services "embeds" config with status
and provide one or more interface to configure, others clearly separates
config from status leaving the "burden" of writing configuration
(usually in /etc) to sysadmins... and that software is "easy" to
integrate as a stateless (and functional) Guix service

my question is: can we work around this very bad design choiche?  can we
patch the package and have stateless config for, e.g., Drupal?

I see this someway similar to "de-blobbing" our packages: how hard it's
to do depends on upstream... unfortunately not all developers agrees
that stateful config is a problem (in a similar way not all agrees that
distributing binary-blobs it's a problem)

>   what happens when we upgrade to a newer version of Drupal,
>   with a different schema?  Would this service instantiation change the
>   state of our system?  Could we still roll-back?

oh yes, schema migration of status is also a problem to manage when we
upgrade software... and usually we do it "by hand" (making backups of
status, ecc.)

schema migration is usual when status is stored in a database,
theoretically schema migration could be applyed also when storing state
in XML (YAML?) in files, but I've never seen this use case

your proposal (below) is interesting!

>   And remember that broken symlink we introduced in the package?  That
>   files/ subdirectory contains /at least/ a stateful configuration
>   file

and that's the main problem: configuration as status

>   — but also Drupal modules, themes,

modules and themes should be packaged separately, IMHO (and yes, they
sometimes can be installed via the web interface... and that's a problem)

the general problem is that some services have become a software blob
providing all sort of features: package installation, configuration
editing, what else?

> uploaded files and caches.

uploaded files are status and generally not a problem, no?

caches are status and sometimes a problem and they need to be wiped
since not all software is "obsolete-cache-resilient"; however it's not
still clear how to manage this kind of problem Guix-wide, I've read
several problems was resolved in the past wiping cache (e.g. with gnome)
but AFAIU there is no general consensus on a general automated method

anyway I think it's not a good idea to backup caches as part of the
state-dump/state-load procedure you propose below

> What
>   happens when we upgrade Drupal?  Can we guarantee they won't be
>   irrevocably changed as part of the upgrade?  What happens when we
>   roll-back?

disaster :-O (even with state, in case of schema migrations)

[...]

> 2.3.2 Fakin' it til we're makin' it
> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
>
>   Can we get those guarantees back?  I fear not.

AFAIU there is no point in defining services in Guix if we have no
guarantees on the main promise of Guix: no?

...or put in another way: is there any defined Guix service with a
stateful (non declarative) configuration file?

what I mean is: we still do not have services like Drupal, or Discourse
or VTiger _because_ they have a stateful configuration system ad it is
not easy to "workaround" this foundamental pitfall (in some case it's
also not easy to package the software)

[...]

>   Working backwards, to ensure we can roll-back we need to make sure
>   that we have access to the state at that point in history that we want
>   to roll-back to.  When we roll-back from version 2 of Drupal to
>   version 1, we must make sure we still have the state of version 1.

this approach is *very* interesting to allow rolling-back in the event
of a failed (usually automated) migration of the state (e.g. for a
schema change): thanks!

but using this approach also for config would mean we lose the
declarative approach for (some of) our services, and I'm not sure it's a
viable long term solution; please consider that declarative
configuration means that one day (it's not an "if") Guix users will be
able to configure an entire infrastructure via one (we have Emacs today)
or more UI, one of them will be GUI and/or HTML5

[...]

> 2.4 The stateful service shimmy
> ───────────────────────────────
>
>   Here's the proposed general shim then:
>
>   1) We define a new type of shepherd service, a /stateful service/,
>      which has additional actions: state-dump-shim and
>   state-load-shim.

state-dump and state-load could be useful for stateless services, too

[...]

>   What does this give us?
>
>   When a migration to a new service revision causes a deployed bit of
>   software to stop working as expected, we can do a `guix system
>   –roll-back`.  This will then also restore the service's state to the
>   previous version and thus *should* restore operations.

this would be a *great* piece of automation for all of us, since it's
good practice to "manually" back up state before migrations, so
rolling-back to a previous schema is possible in case of failures (I've
had some of that in my experience); as I said, this approach is _very_
useful even for stateless Guix services

[...]

> 3 A generalisation: the Stateful-Service Service
> ════════════════════════════════════════════════
>
>   State dumping and restoration *should* be generalisable.
>
>   It should normally consist of one or more operations of:
>   • running a special program to dump data, encrypting the dump, and
>     storing it, together with the service revision hash, in a known
>     location.
>   • tarring up a directory tree, encrypting the archive and storing it,
>     together wtih the service revision hash, in a known location.
>
>   These operations should be able to be provided by a daemon, managed by
>   shepherd, which can be configured with a gpg key for encryption, a
>   mechanism for decryption (interactive or programmatic), a location for
>   storing/retrieving dumps (local or remote?), and a DSL for mapping
>   data dump / data restoration program invocations or file-system
>   locations to data dumps it already knows of.

very interesting, I'd wish I'd be able to provide a POC! :-S


thanks a lot! Giovanni



[1] https://framagit.org/tyreunom/guix-home-manager

-- 
Giovanni Biscuolo

Xelera IT Infrastructures

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Thoughts on stateful services in Guix
  2020-02-01 20:38 Thoughts on stateful services in Guix Alex Sassmannshausen
  2020-02-05 13:03 ` Giovanni Biscuolo
@ 2020-02-05 14:04 ` Ludovic Courtès
  1 sibling, 0 replies; 3+ messages in thread
From: Ludovic Courtès @ 2020-02-05 14:04 UTC (permalink / raw)
  To: Alex Sassmannshausen; +Cc: Guix-devel

Hi Alex,

Thanks for sharing your thoughts and this tricky but important use case!

Alex Sassmannshausen <alex.sassmannshausen@gmail.com> skribis:

> 3 A generalisation: the Stateful-Service Service
> ════════════════════════════════════════════════
>
>   State dumping and restoration *should* be generalisable.
>
>   It should normally consist of one or more operations of:
>   • running a special program to dump data, encrypting the dump, and
>     storing it, together with the service revision hash, in a known
>     location.
>   • tarring up a directory tree, encrypting the archive and storing it,
>     together wtih the service revision hash, in a known location.
>
>   These operations should be able to be provided by a daemon, managed by
>   shepherd, which can be configured with a gpg key for encryption, a
>   mechanism for decryption (interactive or programmatic), a location for
>   storing/retrieving dumps (local or remote?), and a DSL for mapping
>   data dump / data restoration program invocations or file-system
>   locations to data dumps it already knows of.

Back in my Nix days, Wouter den Breejen worked on “S-Nix”, whose goal
was to explore how Nix could be extended to support state¹.  Back then,
I think the experiment was not considered fruitful, in part due to its
complexity, the implementation’s reliance on ext3cow, things like that.

There may still be good ideas to take from that, although it’s probably
more reasonable to start with something simple and practical, trying to
address a couple of concrete use cases before trying to generalize.
What you propose above sounds like it could be a good start!

Ludo’.

¹ “Managing state in a purely functional deployment model”, master
  thesis,
  <http://www.cs.uu.nl/education/scripties/scriptie.php?SID=INF/SCR-2007-053>.
  I couldn’t find a copy on-line so here’s one from my attic:
  <http://web.fdn.fr/~lcourtes/tmp/snix-thesis.pdf>.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-02-05 14:04 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-02-01 20:38 Thoughts on stateful services in Guix Alex Sassmannshausen
2020-02-05 13:03 ` Giovanni Biscuolo
2020-02-05 14:04 ` Ludovic Courtès

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).