I'm finding inspiration from the twelve-factor app approach to organize the deployment process of small applications. I'm stuck on the release stage on guidelines that sound contradictory.
The twelve factor app says that the config should not be stored in files but in the environment, like environment variables. (I imagine that files sitting somewhere on the host can also serve as "config stored in the environment", such as a ssh private key in .ssh/private_key that will give access to some protected resource through ssh.)
I thus imagine just setting up my various hosts manually by setting those environment variables by hand (or in .bashrc or similar so I don't have to do it again every time they reboot). I only usually have 2 hosts: my laptop for development and a server for showing my work to others. If I had more hosts, I could think of a way to automate this, but this is out of the scope of my question.
Then the twelve factor app guidelines define the release stage as producing a release that contains both the build and the config. This could simply mean sending your build (for example docker images of your app) to the target host. The built app and the target host configuration being at the same place (on the same host), they are de facto combined.
I don't however have any way to uniquely identify a release or have the possibility to rollback. In order to do that, I would have to store the config with the build somewhere so that I can get back to them if I need to. That's where I'm stuck: I can't figure out how one approaches this in practice.
What sounds contradictory is that config should be read from the environment and the possibility to rollback to a previous release, which implies a previous config.
Perhaps the following workflow would be an answer, but maybe convoluted:
- send the build to the host,
- read the host config (environment variables, etc.) and copy them to make a snapshot of this host's config at that moment,
- store both the build and the config copy in a uniquely identified place
Such that when you want to run a particular release on a given host, you :
- apply that release config to the host environment
- run the build which will read the config from the environment
The step of making a snapshot of the environment's config to apply it again seem somewhat convoluted and I'd like to know if there is a more sensible way to think about the release stage.
Lets assume we are using Docker as the tool to combine the build and the config.
To make a release: Build a docker image with the code build and the config for a particular environment. Then tag this docker build with a unique id and store the image somewhere. This step should really be done in a CI/CD pipeline where it can read configurations from some config store.
This release image can now be used and deployed to the target environment.
To roll back: Deploy a previous release version from the storage location.
All that being said, I don't think all configurations should be packaged with the release image. Secrets, for example, should come from a secret store/vault of some sort.