---
# System prepended metadata

title: Docker Images and Dockerfiles

---

<style>
    .present {
        text-align: left;
    }
</style>

# Docker Images and Dockerfiles
## Week 19 Day 3

---

## Lecture Videos 1 (22 minutes)
Watch:
- Docker Images and Layers (5:00)
- Docker Building and Pushing (12:00)


---


### Docker images
Docker images are the templates that we use to run containers.

Every time you run a container, you specify an image name.

All images are created from Dockerfiles, which are the instructions for building the image.

But you can also make your own images, by writing a custom Dockerfile.


---

### Docker image commands
| Command                               | Description                                        |
| ---------------------------------     | -----------------------------------                |
| `docker image ls`                     | list all images currently on machine               |
| `docker image history <IMAGE NAME>`   | show the layers in an image                        |
| `docker image inspect <IMAGE NAME>`   | show all of the metadata associated with an image  |
| `docker image rm <IMAGE NAME>`        | remove a cached image from your system             |
| `docker image push <IMAGE NAME>`      | push an image (that is already built) to dockerhub |
| `docker image build [OPTIONS] <PATH>` | build an image from a Dockerfile based on the path |


---

### Composing Dockerfiles
Each line of execution in a Dockerfile will have its own image layer.

Each line contains an instruction, which must begin with a keyword.

---

### Dockerfile Keywords: `FROM` ([Official documentation](https://docs.docker.com/engine/reference/builder/#from))

All Dockerfiles begin with a `FROM` statement.

In this statement you specify the parent image to start from.

For example, if I wanted to run a Flask server, I might start with a parent image that is the official Python image.

Usage:
```dockerfile=
FROM <IMAGE NAME>
```

---

### Dockerfile Keywords: `WORKDIR` ([Official documentation](https://docs.docker.com/engine/reference/builder/#workdir))

`WORKDIR` changes the working directory in the image, sort of like using `cd` at the command line. That means any subsequent commands will take place in that directory.

Additionally, `WORKDIR` will create a directory if it does not exist yet.

Usage:
```dockerfile=
WORKDIR <DIRECTORY PATH>
```

---

### Dockerfile Keywords: `COPY` ([Official documentation](https://docs.docker.com/engine/reference/builder/#copy))

`COPY` moves a file, files, or directory from your host machine to a location on the image.


Usage:
```dockerfile=
COPY <LOCAL DIR PATH> <IMAGE DIR PATH>
```

---

### Dockerfile Keywords: `ENV` ([Official documentation](https://docs.docker.com/engine/reference/builder/#env))

`ENV` lets you set environment variables in the image.


Usage:
```dockerfile=
ENV <ENV VARIABLE>=<VALUE>
```

---

### Dockerfile Keywords: `EXPOSE` ([Official documentation](https://docs.docker.com/engine/reference/builder/#expose))

`EXPOSE` indicates the port that should be exposed. 

Note that this does not actually publish the port—when you run a container based on the image you would still have to use the `-p external_port:internal_port` flag. The command just sets the intended port in the image's metadata. 

Usage:
```dockerfile=
EXPOSE <PORT NUMBER>
```

---

### Dockerfile Keywords: `RUN` ([Official documentation](https://docs.docker.com/engine/reference/builder/#run))

`RUN` executes a command on the image.

Usage:
```dockerfile=
RUN <COMMAND>
```

---

### Dockerfile Keywords: `CMD` ([Official documentation](https://docs.docker.com/engine/reference/builder/#cmd))


`CMD` specifies the default process that a container based on the image should execute. An image can only have one `CMD`—if more than one is specified, only the latter will be used.

If you include a command after the image name when running a container with `docker container run ...`, that will replace the CMD.

Usage:
```dockerfile=
CMD ["<COMMAND>"]
```

---


### Dockerfile demo
Let's use a Dockerfile to create an image for a simple express server, and then push it to Docker hub.

First we'll need a Dockerfile, so create a file named "Dockerfile" inside our demo directory


---


### Dockerfile demo

Let's start with a base image, and let's include a specific version tag. We want to run an express server, so let's use an official Node image. 


---

### Dockerfile demo

Let's add an `EXPOSE` to indicate the port where we will be serving content.

```dockerfile=
FROM node:15-alpine3.10
# add an EXPOSE
```

---

### Dockerfile demo

Next, let's create a folder called "app" and set it as our working directory so we can build our server.

```dockerfile=
FROM node:15-alpine3.10
EXPOSE 3000
# add a WORKDIR
```


---

### Dockerfile demo


Now we can copy our package.json and package-lock.json to /app—just the files we need to do install dependencies
```dockerfile=
FROM node:15-alpine3.10
EXPOSE 3000
WORKDIR /app
# copy overpackage installation files
```


---


### Dockerfile demo

And now we can install those dependencies.

```dockerfile=
FROM node:15-alpine3.10
EXPOSE 3000
WORKDIR /app
COPY package*.json ./
# install dependencies
```


---

### Dockerfile demo

Then we can copy over the rest of the application files.

```dockerfile=
FROM node:15-alpine3.10
EXPOSE 3000
WORKDIR /app
COPY package*.json ./
RUN npm install
# copy files
```

---

### Dockerfile demo

And provide a command to start the application


```dockerfile=
FROM node:15-alpine3.10
EXPOSE 3000
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# run app
```

---

### Dockerfile demo
```dockerfile=
FROM node:15-alpine3.10
EXPOSE 3000
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
```

---

### .dockerignore
Next, we'll need to create a .dockerignore file.
- Why do we ignore node_modules when we're going to npm install anyway?
    - We want the build process to take care of all setup for us, so it should create node_modules
    - we don't want anything about the build to depend on the host machine (you might not have node installed, modules might be gitignored)
```
node_modules
*Dockerfile*
```

---

## Building and pushing the container
```bash=
# log in to dockerhub
docker login
# build from the dockerfile located in our current directory
# and tag with your dockerhub username
docker build -t your-dockerhub-username/my_demo_app .
# push the image to dockerhub
docker image push your-dockerhub-username/my_demo_app
# you can remove the local version of the image
docker image rm your-dockerhub-username/my_demo_app
# make a container based on the image—because it does not exist locally, 
# it will pull down the image from your dockerhub
docker container run -p 3000:3000 --name demo your-dockerhub-username/my_demo_app
```
