I worked last night to set up a generic Dockerfile I could drop into a elixir project and just build an image.
This may be useful for someone else other than me.
Of course if anyone has any suggestions or tips please drop a note. I generally use elixir/phoenix for API development and as such that is what this Dockerfile was made for.
you shouldn’t need to RUN export you can use the ENV command.
use a .dockerignore file to limit the context docker sends to the daemon, this will also allow you to ignore the _build directory, and not have to delete it.
Running mix release.init should be done in your root directory once, not on each build, because you can use those generated files to override certain things, cookies, etc.
With the new releases in 1.9.1, the apps name will be the name you give it in your mix.exs, so you could potentially standardize on that, and not need the app_name.txt file.
Generally you don’t want to pass in the start command, but just use the ENTRYPOINT command to your build, then you can run it with docker run <hash> <command> giving you a little more flexibility.
Also you want to copy in mix.exs and mix.lock and run get.deps/compile before doing a copy of the whole project. This makes it so if you only have changes in your project code and not in any dependencies they aren’t rebuilt every time you build the image since they (the deps) are in a separate layer.
I agree that I should be using ENV for prod and will make that change.
As for the .dockerignore while I generally agree the idea with this Dockerfile was to drop it in any project and be good to go so for now I think I may keep the removal of _build.
I need to look more into what mix release.init does(first time switching to mix releases)
Can you explain to me more what you mean about the appname. Currently I am reading the name in the mix.exs and storing it so I can reference it in the final image in order to have the path to the built file. Is there a cleaner way I can do this. This is something I spent considerable time trying to figure out.
I like the endpoint advice and may make that change as well(although for my specific use case of not needing to know the app name at start I may keep it for now)
I would highly recommend the .dockerignore file. If/when you move to CD, and you have some auxiliary files, sending the whole context to the daemon can slow things down pretty significantly.
$ docker run --rm -i hadolint/hadolint < Dockerfile
/dev/stdin:5 SC2002 Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead.
/dev/stdin:5 DL4006 Set the SHELL option -o pipefail before RUN with a pipe in it
/dev/stdin:19 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
/dev/stdin:19 DL3009 Delete the apt-get lists after installing something
/dev/stdin:19 DL3015 Avoid additional packages by specifying `--no-install-recommends`
I do not actually follow it blindly, but in this case, I’d at least listen to DL3009 and DL3015
Thanks for the examples. It was very helpful(mainly in getting everything installed for alpine)
I have since updated the Dockerfile
The image went down from 193mb to 26mb on the test phx API I was using.
This Dockerfile also takes into account caching deps.
This Dockerfile also allows for overriding of the cmd defaulting as start.
I did some experimenting with ENTRYPOINT but could not get down the combination of variable substitution for the directory as well as runtime substitution of the command for various reasons.
I agree that entrypoint is perfect for someone that specifies a release name but I want something I can drop into a repo and build without configuring any release config(generally the default release configs are fine for my projects) so in that case, I need to dynamically navigate with the mix.exs name. In this case, I just specify a default ENV var command that is run and allow substitution on the run command.
All in all, this was a great exercise and I think you tons for the tips. Reducing the image size around x8 and reducing the build time from around 2 minutes to less than 20 seconds with no dependency change is a huge win!
using @entone Dockerfile, everything builds, and I can start the phoenix server using the docker run, all is good. But if I executed with daemon rather than start, it exits immediately, no error etc.
I want to deploy my small app on a mac mini on my home network and have the application restart automatically when docker start.