update get started app

This commit is contained in:
craig-osterhout 2022-11-28 10:11:50 -08:00
parent ba1ecb30b3
commit fdc72f8a89
No known key found for this signature in database
GPG Key ID: 497A5E49261C73B5
5 changed files with 141 additions and 248 deletions

View File

@ -77,9 +77,7 @@ In order to build the [container image](../get-started/overview.md/#docker-objec
2. Using a text editor or code editor, add the following contents to the Dockerfile:
```dockerfile
# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production

View File

@ -47,32 +47,23 @@ So, let's do it!
```console
$ docker run -dp 3000:3000 \
-w /app -v "$(pwd):/app" \
node:12-alpine \
node:18-alpine \
sh -c "yarn install && yarn run dev"
```
If you are using Windows, then use the following command in PowerShell.
```powershell
PS> docker run -dp 3000:3000 `
$ docker run -dp 3000:3000 `
-w /app -v "$(pwd):/app" `
node:12-alpine `
node:18-alpine `
sh -c "yarn install && yarn run dev"
```
If you are using an Apple silicon Mac or another ARM64 device, then use the following command.
```console
$ docker run -dp 3000:3000 \
-w /app -v "$(pwd):/app" \
node:12-alpine \
sh -c "apk add --no-cache python2 g++ make && yarn install && yarn run dev"
```
- `-dp 3000:3000` - same as before. Run in detached (background) mode and create a port mapping
- `-w /app` - sets the "working directory" or the current directory that the command will run from
- `-v "$(pwd):/app"` - bind mount the current directory from the host into the `/app` directory in the container
- `node:12-alpine` - the image to use. Note that this is the base image for our app from the Dockerfile
- `node:18-alpine` - the image to use. Note that this is the base image for our app from the Dockerfile
- `sh -c "yarn install && yarn run dev"` - the command. We're starting a shell using `sh` (alpine doesn't have `bash`) and
running `yarn install` to install _all_ dependencies and then running `yarn run dev`. If we look in the `package.json`,
we'll see that the `dev` script is starting `nodemon`.
@ -82,7 +73,7 @@ So, let's do it!
```console
$ docker logs -f <container-id>
nodemon src/index.js
[nodemon] 1.19.2
[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] starting `node src/index.js`

View File

@ -53,30 +53,18 @@ For now, we will create the network first and attach the MySQL container at star
-v todo-mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=todos \
mysql:5.7
```
If you are using an ARM based chip, e.g. Macbook M1 Chips / Apple Silicon, then use this command.
```console
$ docker run -d \
--network todo-app --network-alias mysql \
--platform "linux/amd64" \
-v todo-mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=todos \
mysql:5.7
mysql:8.0
```
If you are using Windows then use this command in PowerShell.
```powershell
PS> docker run -d `
$ docker run -d `
--network todo-app --network-alias mysql `
-v todo-mysql-data:/var/lib/mysql `
-e MYSQL_ROOT_PASSWORD=secret `
-e MYSQL_DATABASE=todos `
mysql:5.7
mysql:8.0
```
You'll also see we specified the `--network-alias` flag. We'll come back to that in just a moment.
@ -145,7 +133,7 @@ which ships with a _lot_ of tools that are useful for troubleshooting or debuggi
And you'll get an output like this...
```text
; <<>> DiG 9.14.1 <<>> mysql
; <<>> DiG 9.18.8 <<>> mysql
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
@ -183,7 +171,7 @@ The todo app supports the setting of a few environment variables to specify MySQ
> **Setting Connection Settings via Env Vars**
>
> While using env vars to set connection settings is generally ok for development, it is **HIGHLY DISCOURAGED**
> when running applications in production. Diogo Monica, the former lead of security at Docker,
> when running applications in production. Diogo Monica, a former lead of security at Docker,
> [wrote a fantastic blog post](https://diogomonica.com/2017/03/27/why-you-shouldnt-use-env-variables-for-secret-data/){:target="_blank" rel="noopener" class="_"}
> explaining why.
>
@ -212,34 +200,21 @@ With all of that explained, let's start our dev-ready container!
-e MYSQL_USER=root \
-e MYSQL_PASSWORD=secret \
-e MYSQL_DB=todos \
node:12-alpine \
node:18-alpine \
sh -c "yarn install && yarn run dev"
```
If you are using an ARM based chip, e.g. Macbook M1 Chips / Apple Silicon, then use this command.
```console
$ docker run -dp 3000:3000 \
-w /app -v "$(pwd):/app" \
--network todo-app \
-e MYSQL_HOST=mysql \
-e MYSQL_USER=root \
-e MYSQL_PASSWORD=secret \
-e MYSQL_DB=todos \
node:12-alpine \
sh -c "apk add --no-cache python2 g++ make && yarn install && yarn run dev"
```
If you are using Windows then use this command in PowerShell.
```powershell
PS> docker run -dp 3000:3000 `
$ docker run -dp 3000:3000 `
-w /app -v "$(pwd):/app" `
--network todo-app `
-e MYSQL_HOST=mysql `
-e MYSQL_USER=root `
-e MYSQL_PASSWORD=secret `
-e MYSQL_DB=todos `
node:12-alpine `
node:18-alpine `
sh -c "yarn install && yarn run dev"
```
3. If we look at the logs for the container (`docker logs <container-id>`), we should see a message indicating it's
@ -247,7 +222,7 @@ With all of that explained, let's start our dev-ready container!
```console
$ nodemon src/index.js
[nodemon] 1.19.2
[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] starting `node src/index.js`

View File

@ -31,21 +31,11 @@ $ docker compose version
1. At the root of the app project, create a file named `docker-compose.yml`.
2. In the compose file, we'll start off by defining the schema version. In most cases, it's best to use
the latest supported version. You can look at the [Compose file reference](../compose/compose-file/index.md)
for the current schema versions and the compatibility matrix.
2. In the compose file, we'll start off by defining the list of services (or containers) we want to run as part of our application.
```yaml
version: "3.7"
```
3. Next, we'll define the list of services (or containers) we want to run as part of our application.
```yaml
version: "3.7"
services:
```
```yaml
services:
```
And now, we'll start migrating a service at a time into the compose file.
@ -61,102 +51,79 @@ $ docker run -dp 3000:3000 \
-e MYSQL_USER=root \
-e MYSQL_PASSWORD=secret \
-e MYSQL_DB=todos \
node:12-alpine \
sh -c "yarn install && yarn run dev"
```
If you are using PowerShell then use this command:
```powershell
PS> docker run -dp 3000:3000 `
-w /app -v "$(pwd):/app" `
--network todo-app `
-e MYSQL_HOST=mysql `
-e MYSQL_USER=root `
-e MYSQL_PASSWORD=secret `
-e MYSQL_DB=todos `
node:12-alpine `
node:18-alpine \
sh -c "yarn install && yarn run dev"
```
1. First, let's define the service entry and the image for the container. We can pick any name for the service.
The name will automatically become a network alias, which will be useful when defining our MySQL service.
```yaml
version: "3.7"
```yaml
services:
app:
image: node:12-alpine
```
services:
app:
image: node:18-alpine
```
2. Typically, you will see the `command` close to the `image` definition, although there is no requirement on ordering.
So, let's go ahead and move that into our file.
```yaml
version: "3.7"
services:
app:
image: node:12-alpine
command: sh -c "yarn install && yarn run dev"
```
```yaml
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
```
3. Let's migrate the `-p 3000:3000` part of the command by defining the `ports` for the service. We will use the
[short syntax](../compose/compose-file/compose-file-v3.md#short-syntax-1) here, but there is also a more verbose
[long syntax](../compose/compose-file/compose-file-v3.md#long-syntax-1) available as well.
```yaml
version: "3.7"
services:
app:
image: node:12-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 3000:3000
```
```yaml
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 3000:3000
```
4. Next, we'll migrate both the working directory (`-w /app`) and the volume mapping (`-v "$(pwd):/app"`) by using
the `working_dir` and `volumes` definitions. Volumes also has a [short](../compose/compose-file/compose-file-v3.md#short-syntax-3) and [long](../compose/compose-file/compose-file-v3.md#long-syntax-3) syntax.
One advantage of Docker Compose volume definitions is we can use relative paths from the current directory.
```yaml
version: "3.7"
services:
app:
image: node:12-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 3000:3000
working_dir: /app
volumes:
- ./:/app
```
```yaml
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 3000:3000
working_dir: /app
volumes:
- ./:/app
```
5. Finally, we need to migrate the environment variable definitions using the `environment` key.
```yaml
version: "3.7"
services:
app:
image: node:12-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 3000:3000
working_dir: /app
volumes:
- ./:/app
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_PASSWORD: secret
MYSQL_DB: todos
```
```yaml
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 3000:3000
working_dir: /app
volumes:
- ./:/app
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_PASSWORD: secret
MYSQL_DB: todos
```
### Define the MySQL service
@ -168,82 +135,65 @@ $ docker run -d \
-v todo-mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=todos \
mysql:5.7
```
If you are using PowerShell then use this command:
```powershell
PS> docker run -d `
--network todo-app --network-alias mysql `
-v todo-mysql-data:/var/lib/mysql `
-e MYSQL_ROOT_PASSWORD=secret `
-e MYSQL_DATABASE=todos `
mysql:5.7
mysql:8.0
```
1. We will first define the new service and name it `mysql` so it automatically gets the network alias. We'll
go ahead and specify the image to use as well.
```yaml
version: "3.7"
```yaml
services:
app:
# The app service definition
mysql:
image: mysql:5.7
```
services:
app:
# The app service definition
mysql:
image: mysql:8.0
```
2. Next, we'll define the volume mapping. When we ran the container with `docker run`, the named volume was created
automatically. However, that doesn't happen when running with Compose. We need to define the volume in the top-level
`volumes:` section and then specify the mountpoint in the service config. By simply providing only the volume name,
the default options are used. There are [many more options available](../compose/compose-file/compose-file-v3.md#volume-configuration-reference) though.
```yaml
version: "3.7"
```yaml
services:
app:
# The app service definition
mysql:
image: mysql:8.0
volumes:
- todo-mysql-data:/var/lib/mysql
services:
app:
# The app service definition
mysql:
image: mysql:5.7
volumes:
- todo-mysql-data:/var/lib/mysql
volumes:
todo-mysql-data:
```
volumes:
todo-mysql-data:
```
3. Finally, we only need to specify the environment variables.
```yaml
version: "3.7"
```yaml
services:
app:
# The app service definition
mysql:
image: mysql:8.0
volumes:
- todo-mysql-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: todos
services:
app:
# The app service definition
mysql:
image: mysql:5.7
volumes:
- todo-mysql-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: todos
volumes:
todo-mysql-data:
```
volumes:
todo-mysql-data:
```
At this point, our complete `docker-compose.yml` should look like this:
```yaml
version: "3.7"
services:
app:
image: node:12-alpine
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 3000:3000
@ -257,7 +207,7 @@ services:
MYSQL_DB: todos
mysql:
image: mysql:5.7
image: mysql:8.0
volumes:
- todo-mysql-data:/var/lib/mysql
environment:
@ -300,7 +250,7 @@ Now that we have our `docker-compose.yml` file, we can start it up!
```plaintext
mysql_1 | 2019-10-03T03:07:16.083639Z 0 [Note] mysqld: ready for connections.
mysql_1 | Version: '5.7.27' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
mysql_1 | Version: '8.0.31' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
app_1 | Connected to mysql db at host mysql
app_1 | Listening on port 3000
```

View File

@ -105,9 +105,7 @@ times for your container images.
Let's look at the Dockerfile we were using one more time...
```dockerfile
# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
@ -126,9 +124,7 @@ a change to the `package.json`. Make sense?
1. Update the Dockerfile to copy in the `package.json` first, install dependencies, and then copy everything else in.
```dockerfile
# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
FROM node:18-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --production
@ -160,34 +156,23 @@ a change to the `package.json`. Make sense?
You should see output like this...
```plaintext
Sending build context to Docker daemon 219.1kB
Step 1/6 : FROM node:12-alpine
---> b0dc3a5e5e9e
Step 2/6 : WORKDIR /app
---> Using cache
---> 9577ae713121
Step 3/6 : COPY package.json yarn.lock ./
---> bd5306f49fc8
Step 4/6 : RUN yarn install --production
---> Running in d53a06c9e4c2
yarn install v1.17.3
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.2.9: The platform "linux" is incompatible with this module.
info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 10.89s.
Removing intermediate container d53a06c9e4c2
---> 4e68fbc2d704
Step 5/6 : COPY . .
---> a239a11f68d8
Step 6/6 : CMD ["node", "src/index.js"]
---> Running in 49999f68df8f
Removing intermediate container 49999f68df8f
---> e709c03bc597
Successfully built e709c03bc597
Successfully tagged getting-started:latest
[+] Building 16.1s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 175B
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [internal] load metadata for docker.io/library/node:18-alpine
=> [internal] load build context
=> => transferring context: 53.37MB
=> [1/5] FROM docker.io/library/node:18-alpine
=> CACHED [2/5] WORKDIR /app
=> [3/5] COPY package.json yarn.lock ./
=> [4/5] RUN yarn install --production
=> [5/5] COPY . .
=> exporting to image
=> => exporting layers
=> => writing image sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25
=> => naming to docker.io/library/getting-started
```
You'll see that all layers were rebuilt. Perfectly fine since we changed the Dockerfile quite a bit.
@ -197,30 +182,26 @@ a change to the `package.json`. Make sense?
5. Build the Docker image now using `docker build -t getting-started .` again. This time, your output should look a little different.
```plaintext
Sending build context to Docker daemon 219.1kB
Step 1/6 : FROM node:12-alpine
---> b0dc3a5e5e9e
Step 2/6 : WORKDIR /app
---> Using cache
---> 9577ae713121
Step 3/6 : COPY package.json yarn.lock ./
---> Using cache
---> bd5306f49fc8
Step 4/6 : RUN yarn install --production
---> Using cache
---> 4e68fbc2d704
Step 5/6 : COPY . .
---> cccde25a3d9a
Step 6/6 : CMD ["node", "src/index.js"]
---> Running in 2be75662c150
Removing intermediate container 2be75662c150
---> 458e5c6f080c
Successfully built 458e5c6f080c
Successfully tagged getting-started:latest
[+] Building 1.2s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 37B
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [internal] load metadata for docker.io/library/node:18-alpine
=> [internal] load build context
=> => transferring context: 450.43kB
=> [1/5] FROM docker.io/library/node:18-alpine
=> CACHED [2/5] WORKDIR /app
=> CACHED [3/5] COPY package.json yarn.lock ./
=> CACHED [4/5] RUN yarn install --production
=> [5/5] COPY . .
=> exporting to image
=> => exporting layers
=> => writing image sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda
=> => naming to docker.io/library/getting-started
```
First off, you should notice that the build was MUCH faster! And, you'll see that steps 1-4 all have
`Using cache`. So, hooray! We're using the build cache. Pushing and pulling this image and updates to it
First off, you should notice that the build was MUCH faster! And, you'll see that several steps are using previously cached layers. So, hooray! We're using the build cache. Pushing and pulling this image and updates to it
will be much faster as well. Hooray!
## Multi-stage builds
@ -238,7 +219,6 @@ that JDK isn't needed in production. Also, you might be using tools like Maven o
Those also aren't needed in our final image. Multi-stage builds help.
```dockerfile
# syntax=docker/dockerfile:1
FROM maven AS build
WORKDIR /app
COPY . .
@ -259,8 +239,7 @@ and more into static HTML, JS, and CSS. If we aren't doing server-side rendering
for our production build. Why not ship the static resources in a static nginx container?
```dockerfile
# syntax=docker/dockerfile:1
FROM node:12 AS build
FROM node:18 AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
@ -272,7 +251,7 @@ FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
```
Here, we are using a `node:12` image to perform the build (maximizing layer caching) and then copying the output
Here, we are using a `node:18` image to perform the build (maximizing layer caching) and then copying the output
into an nginx container. Cool, huh?
## Next steps