Do I HAVE to ADD my Code Into the Docker Image When Deploying?
If you are asking this question, you probably started out using Docker for your local development environment.
If you have done a proper job, it comes up with a single docker-compose up
,
you can perform maintenance tasks without breaking a leg and see the effect of code changes
right away when running a development server. Shiny!
An important part of this setup, consists of mounting your project code inside the container from the host. This way, you can edit it with your IDE of choice and don’t need to rebuild images every single time something changes.
Now you’re ready to move to other environments apart from local development. But what about production? Is there something wrong with using the same approach? You’ve been reading about people who are baking their code right into the image when deploying. Do you have to do so as well? Are you missing something important?
The Two Options
To run code in a production environment, you can use both options. It’s totally okay to use a similar workflow as you do locally. But there are advantages to baking complete images, and mounting your code is only acceptable if your deployment workflow is correct. More on that below.
The two workflows which are perfectly alright, are the following:
-
Create a tagged Docker image from a commit
-
Pull the image on the server
-
Run containers from the new image
-
(Roll back to a previous image version if needed)
-
Tag a Git commit with a release version
-
Check out the tagged commit on your server
-
Start a container from your deploy-friendly generic image, mounting the code
-
(Check out old commit and roll back this way if needed)
Build Artifacts
Both of those workflows, have an important detail in common. By creating a tagged image, or tagging a release with a version, you are creating build artifacts.
This practice is in accordance with the 12factor guidelines. It specifies three stages, where a codebase is transformed into a deployment. In the build stage, you pinpoint a version of your code. Afterwards you add configs to it, and finally run it.
The tagged git repo, or tagged image are your deployment artifact. It can be used to run in different environments like staging or production, which only differ through the environment variables.
As seen above, you can achieve this implicitly by creating a tagged Docker image, but don’t have to. You can just as well only use Docker for runtime isolation, if you make up for it. If you’re using something like Git tags to pinpoint your releases, you don’t necessarily need to bake your code into your Docker images.
Startup Speed
Still, there are upsides from building an image with code baked in. A container from this kind of built image should be able to start up very quickly, as it’s already packaged up completely. For certain setups, it’s great to have your containers start and directly be in working condition. That’s not the case if you have to install dependencies first, for example using python pip - if you are mounting your code and using Python, you’ll have to do that.
Mounting volumes can be slower here. Also, you’re performing operations which may fail - for example if the dependency sources are temporarily unavailable. This makes for additional points of failure, instead of just your Docker image registry. If the source of your dependencies which are installed on startup is down, you simply won’t be able to start new containers.
One more downside which is easily fixable: You’re going to find bugs on run, which would usually be uncovered during the build process. For this, you should have an automated way of testing in place, so your new release runs in some kind of staging environment first - there you’ll see errors before they crash production.
Conclusion
If you’ve built your deployment workflow around tagging commits to mark your release versions, you’re already creating build artifacts in that way. Your Docker containers will only have one job - to provide isolation and it’s okay if you’re only mounting your code or binaries when the container starts. Without building new images. You should be able to roll back in any case. However, keep in mind that you have downsides: slower startup time and more points of failure when starting.