generated from btholt/next-course-starter
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
212 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--- | ||
--- | ||
|
||
So far we've just been running containers with random tags that I chose. If you run `docker run -it node` the tag implicitly is using the `latest` tag. When you say `docker run -it node`, it's the same as saying `docker run -it node:latest`. The `:latest` is the tag. This allows you to run different versions of the same container, just like you can install React version 17 or React version 18: some times you don't want the latest. Let's say you have a legacy application at your job and it depends on running on Node.js 12 (update your app, Node.js is already past end-of-life) then you can say | ||
|
||
```bash | ||
docker run -it node:12 bash | ||
``` | ||
|
||
Once in the shell, run `node --version` and you'll see the Node.js version is 12._._! Neat! This is helpful because now we can fix our Node.js version to the one our app expects. Hop back over to [the Docker Hub page for the node container][node]. Take a look at all the version of the node container you can download. Let's try another one. | ||
|
||
```bash | ||
docker run node:20-alpine cat /etc/issue | ||
``` | ||
|
||
You'll see this is running an entirely different OS all together: Alpine! [Alpine Linux][alpine] is a very, very tiny distro of Linux made for containers and specifically because it is tiny. Alpine containers are bare bones: if you want _anything_ in them, you're going to have to do it yourself. This is in opposition to the Ubuntu and Debian containers: they ship the kitchen sink with them which is both convenient and much bigger in size. Alpine images are about five megabytes whereas Ubuntu is close to two hundred megabytes. As you can imagine, this can make a difference in how fast you can deploy and can cost significantly less in terms of storage and network traffic. It's also in general better to have less unnecessary things in your containers: less is more in terms of security. If an attacker tries to execute a Python exploit on your container but your container doesn't have Python then their attack won't work. | ||
|
||
We'll get more into how to ship containers to production but I'll leave you with this pro-tip: have a development container which has all the bells, whistles, debugging tools, etc. that you need. Then have a production container that's minimalist as possibly can be. You'll get the best of both worlds. | ||
|
||
[node]: https://hub.docker.com/_/node/ | ||
[alpine]: https://hub.docker.com/_/alpine |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
--- | ||
title: "Docker CLI" | ||
--- | ||
|
||
Let's take a look at some more cool features of the Docker CLI. | ||
|
||
### pull / push | ||
|
||
`pull` allows you to pre-fetch container to run. | ||
|
||
```bash | ||
# this just downloads and caches the image, it doesn't do anything else with it | ||
docker pull jturpin/hollywood | ||
|
||
# notice it's already loaded and cached here; it doesn't redownload it | ||
docker run -it jturpin/hollywood hollywood | ||
``` | ||
|
||
That will pull the hollywood container from the user jturpin's user account. The second line will execute this fun container which is just meant to look a hacker's screen in a movie (it doesn't really do anything than look cool.) | ||
|
||
`push` allows you to push containers to whatever registry you're connected to (probably normally Docker Hub or something like Azure Container Registry or GitHub Container Registry). | ||
|
||
### inspect | ||
|
||
```bash | ||
docker inspect node:20 | ||
``` | ||
|
||
This will dump out a lot of info about the container. Helpful when figuring out what's going on with a container | ||
|
||
### pause / unpause | ||
|
||
As it looks, these pauses or unpause all the processes in a container. Feel free to try | ||
|
||
```bash | ||
docker run -dit --name hw --rm jturpin/hollywood hollywood | ||
docker ps # see container running | ||
docker pause hw | ||
docker ps # see container paused | ||
docker unpause hw | ||
docker ps # see container running again | ||
docker kill hw # see container is gone | ||
``` | ||
|
||
### exec | ||
|
||
This allows you to execute a command against a running container. This is different from `docker run` because `docker run` will start a new container whereas `docker exec` runs the command in an already-running container. | ||
|
||
```bash | ||
docker run -dit --name hw --rm jturpin/hollywood hollywood | ||
|
||
# see it output all the running processes of the container | ||
docker exec hw ps aux | ||
``` | ||
|
||
If you haven't seen `ps aux` before, it's a really useful way to see what's running on your computer. Try running `ps aux` on your macOS or Linux computer to see everything running. | ||
|
||
### import / export | ||
|
||
Allows you to dump out your container to a tar ball (which we did above.) You can also import a tar ball as well. | ||
|
||
### history | ||
|
||
We'll get into layers in a bit but this allow you to see how this Docker image's layer composition has changed over time and how recently. | ||
|
||
```bash | ||
docker history node:20 | ||
``` | ||
|
||
### info | ||
|
||
Dumps a bunch of info about the host system. Useful if you're on a VM somewhere and not sure what the environment is. | ||
|
||
```bash | ||
docker info | ||
``` | ||
|
||
### top | ||
|
||
Allows you to see processes running on a container (similar to what we did above) | ||
|
||
```bash | ||
docker run -dit --name my-mongo --rm mongo | ||
docker top my-mongo # you should see MongoDB running | ||
docker kill my-mongo | ||
``` | ||
|
||
### rm / rmi | ||
|
||
If you run `docker ps --all` it'll show all containers you've stopped running in addition to the runs you're running. If you want to remove something from this list, you can do `docker rm <id or name>`. | ||
|
||
You can run `docker container prune` to remove _all_ of the stopped containers. | ||
|
||
If you want to remove an image from your computer (to save space or whatever) you can run `docker rmi mongo` and it'll delete the image from your computer. This isn't a big deal since you can always reload it again | ||
|
||
### logs | ||
|
||
Very useful to see the output of one of your running containers. | ||
|
||
```bash | ||
docker run --name my-mongo --rm -dit mongo | ||
docker logs my-mongo # see all the logs | ||
docker kill my-mongo | ||
``` | ||
|
||
### restart | ||
|
||
Pretty self explanatory. Will restart a running container | ||
|
||
### search | ||
|
||
If you want to see if a container exists on Docker Hub (or whatever registry you're connected to), this will allow you to take a look. | ||
|
||
```bash | ||
docker search python # see all the various flavors of Python containers you can run | ||
docker search node # see all the various flavors of Node.js containers you can run | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
--- | ||
--- | ||
|
||
So far we've been focusing a lot on running containers and haven't much dug into building them. This is on purpose because most of benefit of containers for developers comes from the running of containers. If you learn one thing, it should be how to run them. In fact I'll event venture to say that _most_ developers really only ever need to know how to run them. But you, you're going to learn how to write them. It's an extra superpower. | ||
|
||
That said, let's learn to build our own containers. We'll again be using Docker for this though there are other ways to do this. Docker has a special file called a `Dockerfile` which allows you to outline how a container will be built. Each line in a Docker file is a new a directive of how to change your Docker container. | ||
|
||
A _big key_ with Docker container is that they're supposed to be disposable. You should be able to create them and throw them away as many times as necessary. In other words: adopt a mindset of making everything short-lived. There are other, better tools for long-running, custom containers. | ||
|
||
The (imperfect) analogy that people use sometimes is your containers should be "[cattle, not pets][cattle]". You design containers so you can easily create and destroy them as much as necessary. The analogy here is that you name your pets and take special care of them whereas you have a thousand cattle and can't name or take special care of them, just the herd. | ||
|
||
Let's make the most basic Dockerfile ever. Let's make a new folder, maybe on your desktop. Put a file in there called `Dockerfile` (no extension.) In your file, put this. | ||
|
||
## The most basic Dockerfile-based Container | ||
|
||
```dockerfile | ||
FROM node:20 | ||
|
||
CMD ["node", "-e", "console.log(\"hi lol\")"] | ||
``` | ||
|
||
The first thing on each line (`FROM` and `CMD` in this case) are called _instructions_. They don't technically have to be all caps but it's convention to do so so that the file is easier to read. Each one of these instruction incrementally changes the container from the state it was in previously, adding what we call a _layer_. | ||
|
||
Let's go ahead and build our container. Run (from inside of the directory of where your Dockerfile is) | ||
|
||
```bash | ||
docker build . | ||
``` | ||
|
||
You should see it out put a bunch of stuff and it'll leave you with the hash of an image. After each instruction, you'll see a hash similar to the ones we've been using for the IDs for the containers. You know why that is? It's because each one of those layers is in-and-of themselves a valid container image! This ends up being important later and we'll discuss it in a bit. | ||
|
||
Our container has two instructions in its Dockerfile, but actually it has many, many more. How? The first instruction, `FROM node:20` actually means _start_ with the `node` container. That container itself [comes from another Dockerfile][docker-node] which build its own container, which itself [comes from another Dockerfile][buildpack], which comes ultimately from the [Debian][debian] image. | ||
|
||
This is something very powerful about Docker: you can use images to build other images and build on the work of others. Instead of having to worry about how to install Debian and all the necessary items to build Node.js from its source, we can just start with a well-put-together image from the community. | ||
|
||
Okay, so we start with `node:20` and then we add the `CMD` instruction. There will only ever be one of these in effect in a Dockerfile. If you have multiple it'll just take the last one. This is what you want Docker to do when someone runs the container. In our case, we're running `node -e "console.log('hi lol')"` from within the container. `node -e`, if you don't know, will run whatever is inside of the quotes with Node.js. In this case, we're logging out `hi lol` to the console. | ||
|
||
You _can_ put `CMD node -e "console.log('hi lol')"` as that last line and it'll work but it's not the preferred way of doing it. This won't actually go through bash which itself is simpler and usually safer. I do it this way because the docs strongly encourage you to do it this way. | ||
|
||
So, in essence, our containers nabs a `node:20` container and then when we have it execute a `node` command when you run it. Let's try it. Grab the hash from your build and run | ||
|
||
```bash | ||
docker run <ID> | ||
``` | ||
|
||
It's a little inconvenient to always have to refer to it by ID, it'd be easier if it had a name. So let's do that! Try | ||
|
||
```bash | ||
docker build . --tag my-node-app ## or -t instead of --tag | ||
docker run my-node-app | ||
``` | ||
|
||
Much easier to remember the name rather than a hash. If you want to version it yourself, you can totally do this: | ||
|
||
```bash | ||
docker build -t my-node-app:1 . | ||
docker run my-node-app:1 | ||
``` | ||
|
||
Now change your `Dockerfile` so that it logs out `wat` instead of `hi lol`. After you do that. | ||
|
||
```bash | ||
docker build -t my-node-app:2 . | ||
docker run my-node-app:2 | ||
docker run my-node-app:1 | ||
``` | ||
|
||
You can version your containers and hold on to older ones, just in case! | ||
|
||
[buildpack]: https://github.com/docker-library/buildpack-deps | ||
[debian]: https://hub.docker.com/_/debian/ | ||
[docker-node]: https://github.com/nodejs/docker-node/blob/master/Dockerfile-debian.template | ||
[cattle]: http://cloudscaling.com/blog/cloud-computing/the-history-of-pets-vs-cattle/ |