Additional installation steps

Hi,

We’re trying out balena, and it looks great!

I’m wondering what would be the correct approach to run one time pre & post install steps -

One of our containers we would like to run on the device is a database.
In our current installation script, we have preliminary sensitive steps - such as certificates creation,
which we don’t want to be executed on remote build machines, but locally on the device, as well as additional steps, like automatic settings files generation, which need to be done locally on the machine before the DB container starts.

Additionally, after the DB container is successfully started, and the DB starts running, we do post install steps - such as automatically creating local databases, populating initial data, defining replications, and so on.

I understand that there is no way to define some script to be run directly on the host machine outside of all containers. Then what would be the best approach to run some code only once before & after a container starts running, when performing first installation on the device? Defining one container for pre install steps and another for post install steps sounds like a huge overkill! Especially as there is no way to run them only once, and they would have to check for some flag upon startup?

Thanks!
Vitaliy

1 Like

Hi,

You can create a volume, then once a container starts you could check if a specific file exists in the volume, like a flag as you said. Volumes persist through application and host os updates.
Please feel free to ask more questions if it’s unclear.

Thanks, Kakhaber

Hi Kakhaber,

And thank you for your response.
How do you suggest to run the one time code before & after installation of the DB container?

Vitaliy

Let’s take mongodb as an example and it’s docker file: https://github.com/docker-library/mongo/blob/be3c690cae52447831b977dab9c6a66dc993df65/3.6/Dockerfile

You can have your dockerfile that has mongo’s image as base image and override it’s entrypoint. You could have custom ENTRYPOINT ["./custom.sh"] which would call your pre script (which checks for a file in volume to understand if it’s the first time) and then docker-entrypoint.sh. Regarding the post install script, I would run it inside application that connects to database, check if some records don’t exist and create them.

I have never tried this solution, please correct me if I’m saying something wrong.

Thanks for your response.
It is no doubt a better idea then using 2 additional dockers, I’m wondering if there is a more elegant option?

Hi, I think this Docker documentation page will give you some ideas on how to address this: https://docs.docker.com/compose/startup-order/.
Also possibly you can run your initialization steps inside the DB container before the database is started. This way you would avoid some extra multi-container startup syncing.
Thanks,
Zahari

Hi,

Thanks for the suggestion.
I understand that this, as well as @karaxuna suggestions are the best options we can get.
This raises one more issue - How can we send a file into the container?
I understand SCP is not supported by default?

Hi Vitaliy, you can copy the files you need as part of your Dockerfile script and just release a new version of your application. Any reason why that wouldn’t work for you? The whole point of containers is so they can easily be replicated, and manually copying files to them during runtime beats the purpose of containers. You can, of course, SSH into the container and use curl or similar to pull files from remote sources, but if the container gets recreated all those changes will be lost.

Hi @sradevski,

The reason is security.
The files we want to copy directly to the container are certificates which we generate,
we want them to travel around as little as possible.
We used to generate them as part of our installation script running on the device, but now we have to think of a different solution, like generating them on another machine on the same local network and pushing them to the device, once it is up, but before the container is running.

cUrl could be an option, a bit cumbersome, as we would have to setup a web server on that other machine, and let the container being built to know in advance the ip of that local machine. I’m hoping for a simpler option like enabling scp. Any ideas?

Many Thanks!
Vitaliy

Hi Vitaliy,

You can have a look at this forum thread: Rsync over balena ssh tunnel? on how you can make scp work. Another option is to add your certificate as an environment variable from the dashboard or the CLI, and use that from your service. We do intend to support something like balena scp but I cannot give you any ETA on that. I am not sure about your setup, but there is also the possibility of having a centralized service that stores the ceritficates, and the devices can have an API key set from an environment variable that will be used to authenticate with that service when doing curl, for example. Once we have scp support we’ll let you know.

1 Like

Hi @sradevski,

I really like the idea of environment variables as a workaround!
Can they contain binary / hexa values and be of arbitrary length?
The certificate store is about 3KB long.

Also, will the environment variable persist after a reboot?

Thanks again for the idea!
Vitaliy

Hello Vitaliy,
An environment variable will persist after a reboot. The length can be arbitrary but 3KB might be too large to set this way. Hex values should be fine but I don’t see a way to add binary data. You could also try adding an environment variable using the CLI which may give you more flexibility. (see https://www.balena.io/docs/reference/balena-cli/#env-add-name-value)

2 Likes

Thanks @sradevski & @alanb128 for the suggestions!
It works pretty well!
The only point to take into account is that the value of the environment variable cannot contain line breaks - as evidenced by the error message:

BalenaRequestError: Request error: Variable values cannot contain line break characters

This can be easily resolved by formatting the file using

xxd -ps

or the similar

Thanks again for your help!!
Vitaliy