This video shows the way Docker can split continuous deployment into manageable parts that can be composed to build easy continuous deployment systems.
- [Instructor] I mentioned earlier using Docker to partition your environment and split things up. I'm going to go a little more in detail about what goes into those partitions and which things can go where. So taking our classic environment here, we have a developer's laptop, and the thing that customers actually see. The pieces that make up this relation include all of the programs that need to be there to actually run the code. Of course, the code itself needs to be included, and all those external bits and libraries and all the code fetched from out there on the Internet needs to be wrapped up in a coherent way and included in the final result, as well as all your internal libraries needed to interface with the rest of your organization, and of course, the result of all this compilation needs to be included in the result as well.
And all of that is built on top of a particular version of the operating system. There's a lot of pieces here that get tied together by hand. There's really no chance that this is going to be the same on your laptop as it is in production, but we're professionals, we make it work. One way to make these components tie together much more nicely is to wrap them up in Docker containers, which is, of course, what this course is all about. But to be a little more nuanced about it, we don't necessarily need to take one big bundle of all of these pieces, and build the whole thing every time.
Docker images are designed to be built on top of each other, so you can take the pieces that don't change, the pieces you don't want to change, and build them up into their own image, with all that stuff fixed. Then, every time you have some new code, you take the base image that has all the fixed stuff, you compile the code, you stick it into that image, and you have an image that you can actually run. So the base image comes out of the Docker registry, gets the code stirred into it, and then gets put back in the Docker registry to be passed on to the rest of the flow.
So then your flow ends up looking a little bit more like this. You have a builder, which takes the base image, produces a runnable product Docker image. That image gets tested. Then the identical image gets staged and maybe reviewed by humans, and the identical image goes to production, and it's the Docker registry that ties it all together. As you can see, the Docker registry is the anchor point that ties our environment together. You're almost certainly going to want to have your own private, secure registry for storing your images.
You can run it locally on a machine you already have. In fact, if you're running other services like Nexus within your environment, you may already have one of these. Or, it's easy to rent them from a variety of sources. In this course, I'm going to rely on Amazon's AWS hosted registry, the ECR, though the Docker hosted registries work very well, as well as offerings from many other companies, such as Google. Now, it is totally possible to not use your own private registry.
You can move Docker images around by saving them locally, SCPing them over to a machine and loading them there. It totally works. It's not as much fun, and it is possible. Now, like all good things, Docker can create its own set of problems too. It's really easy to lose track of what went into an image. What were the build tools that built this image? What version of NPM were we using last September? So it's very helpful to save all that stuff in some central location in your organization, so that if you ever have to, you can go back and rebuild an image or build a new version of an image that has some updated library.
Storing all of your dependencies separately makes rebuilding old images possible, when otherwise it might be a bit daunting. So here's some best practices that really do help make working with images just a little bit smoother. Start by giving every image a unique name that describes where the source code that built that image came from. So a convenient way to go about this is to just use the Git hash as the image tag. That way if you go to production and see that you're running a particular image, you can go straight over to Git, look for the same Git hash, and know immediately which code was used to build that image.
Also, generally prefer the full tag structure of organization name/project colon tag over simple names like project or latest, that don't give you much of a clue where the code came from, and also some hosting providers only work with the full format, so you probably will end up adding it eventually. And most importantly, build all your Docker images from Docker files. It's just a lot easier to go back and figure out what went into an image if you've got a Docker file than trying to go to the person who built it and say, hey, do you remember what you did when you build that image last Tuesday? I'd kind of like to change it.
You can also put multiple tags on an image. Let's say you really do want to have a latest tag that just includes the newest thing. Sure, go ahead and tag that image as latest, and also tag it with the Git hash. That way you can know what was built and you can easily get the latest thing if you're not in a position to be super concerned about exactly which release you have. And of course, automate early, automate often. That's what this is all about. Try to avoid things that tie a container so that it can only be run on a particular host, things like linking to shared libraries that are on the host instead of inside the container.
This can really frustrate things when you try to move to a new hosting environment. And also, carefully avoid hand building images. It's just too easy to forget how something was built and then get to the point where you're afraid to upgrade it because you don't know how it was made and so you don't know if you can change a thing safely without breaking something in production, and it's just best to avoid that whole line of thinking and carefully build each image from a Docker file. And keeping in mind all of this great advice, try to accept that you're going to have to build something that was just not made with containers in mind.
Maybe it makes bi-directional network connections or depends on some shared library that's really only reasonable to store in a central host. And also, try to accept that at some point exceptions will arise, things that are difficult to put into Docker containers. Don't let this derail your process. Keep working towards automating everything and just accept that there will be a couple of things that don't get automated right away.
- How Docker can greatly simplify continuous deployment
- Building your CI/CD toolbox
- Building a deployment job
- Continuous deployment using hosted Docker
- Deploying to AWS with Jenkins
- Automated testing using Docker and AWS
- Goals and expectations for integration testing
- Creating and deploying an integration test job