diff --git a/content/get-started/workshop/02_our_app.md b/content/get-started/workshop/02_our_app.md index 563871f9c233..200f58fe7402 100644 --- a/content/get-started/workshop/02_our_app.md +++ b/content/get-started/workshop/02_our_app.md @@ -45,7 +45,6 @@ Before you can run the application, you need to get the application source code │ ├── README.md │ ├── spec/ │ ├── src/ - │ └── yarn.lock ``` ## Build the app's image @@ -58,18 +57,21 @@ To build the image, you'll need to use a Dockerfile. A Dockerfile is simply a te ```dockerfile # syntax=docker/dockerfile:1 - FROM node:lts-alpine + FROM node:24-alpine WORKDIR /app COPY . . - RUN yarn install --production + RUN npm install --omit=dev CMD ["node", "src/index.js"] EXPOSE 3000 ``` + This Dockerfile does the following: - This Dockerfile starts off with a `node:lts-alpine` base image, a - light-weight Linux image that comes with Node.js and the Yarn package - manager pre-installed. It copies all of the source code into the image, - installs the necessary dependencies, and starts the application. + - Uses `node:24-alpine` as the base image, a lightweight Linux image with Node.js pre-installed + - Sets `/app` as the working directory + - Copies source code into the image + - Installs the necessary dependencies + - Specifies the command to start the application + - Documents that the app listens on port 3000 2. Build the image using the following commands: @@ -85,9 +87,9 @@ To build the image, you'll need to use a Dockerfile. A Dockerfile is simply a te $ docker build -t getting-started . ``` - The `docker build` command uses the Dockerfile to build a new image. You might have noticed that Docker downloaded a lot of "layers". This is because you instructed the builder that you wanted to start from the `node:lts-alpine` image. But, since you didn't have that on your machine, Docker needed to download the image. + The `docker build` command uses the Dockerfile to build a new image. You might have noticed that Docker downloaded a lot of "layers". This is because you instructed the builder that you wanted to start from the `node:24-alpine` image. But, since you didn't have that on your machine, Docker needed to download the image. - After Docker downloaded the image, the instructions from the Dockerfile copied in your application and used `yarn` to install your application's dependencies. The `CMD` directive specifies the default command to run when starting a container from this image. + After Docker downloaded the image, the instructions from the Dockerfile copied in your application and used `npm` to install your application's dependencies. Finally, the `-t` flag tags your image. Think of this as a human-readable name for the final image. Since you named the image `getting-started`, you can refer to that image when you run a container. diff --git a/content/get-started/workshop/04_sharing_app.md b/content/get-started/workshop/04_sharing_app.md index a11b2df72010..ae172c131075 100644 --- a/content/get-started/workshop/04_sharing_app.md +++ b/content/get-started/workshop/04_sharing_app.md @@ -81,55 +81,7 @@ Let's try to push the image to Docker Hub. ## Run the image on a new instance -Now that your image has been built and pushed into a registry, try running your app on a brand -new instance that has never seen this container image. To do this, you will use Play with Docker. - -> [!NOTE] -> -> Play with Docker uses the amd64 platform. If you are using an ARM based Mac with Apple silicon, you will need to rebuild the image to be compatible with Play with Docker and push the new image to your repository. -> -> To build an image for the amd64 platform, use the `--platform` flag. -> ```console -> $ docker build --platform linux/amd64 -t YOUR-USER-NAME/getting-started . -> ``` -> -> Docker buildx also supports building multi-platform images. To learn more, see [Multi-platform images](/manuals/build/building/multi-platform.md). - - -1. Open your browser to [Play with Docker](https://labs.play-with-docker.com/). - -2. Select **Login** and then select **docker** from the drop-down list. - -3. Sign in with your Docker Hub account and then select **Start**. - -4. Select the **ADD NEW INSTANCE** option on the left side bar. If you don't see it, make your browser a little wider. After a few seconds, a terminal window opens in your browser. - - ![Play with Docker add new instance](images/pwd-add-new-instance.webp) - -5. In the terminal, start your freshly pushed app. - - ```console - $ docker run -dp 0.0.0.0:3000:3000 YOUR-USER-NAME/getting-started - ``` - - You should see the image get pulled down and eventually start up. - - > [!TIP] - > - > You may have noticed that this command binds the port mapping to a - > different IP address. Previous `docker run` commands published ports to - > `127.0.0.1:3000` on the host. This time, you're using `0.0.0.0`. - > - > Binding to `127.0.0.1` only exposes a container's ports to the loopback - > interface. Binding to `0.0.0.0`, however, exposes the container's port - > on all interfaces of the host, making it available to the outside world. - > - > For more information about how port mapping works, see - > [Networking](/manuals/engine/network/_index.md#published-ports). - -6. Select the 3000 badge when it appears. - - If the 3000 badge doesn't appear, you can select **Open Port** and specify `3000`. +Now that your image has been built and pushed into a registry, you can run your app on any machine that has Docker installed. Try pulling and running your image on another computer or a cloud instance. ## Summary diff --git a/content/get-started/workshop/06_bind_mounts.md b/content/get-started/workshop/06_bind_mounts.md index dce58dfe4f72..0023eef6adbc 100644 --- a/content/get-started/workshop/06_bind_mounts.md +++ b/content/get-started/workshop/06_bind_mounts.md @@ -116,7 +116,7 @@ filesystem you can share with containers. For details about accessing the settin ```console root@ac1237fad8db:/# cd src root@ac1237fad8db:/src# ls - Dockerfile node_modules package.json spec src yarn.lock + Dockerfile node_modules package.json package-lock.json spec src ``` 6. Create a new file named `myfile.txt`. @@ -124,7 +124,7 @@ filesystem you can share with containers. For details about accessing the settin ```console root@ac1237fad8db:/src# touch myfile.txt root@ac1237fad8db:/src# ls - Dockerfile myfile.txt node_modules package.json spec src yarn.lock + Dockerfile myfile.txt node_modules package.json package-lock.json spec src ``` 7. Open the `getting-started-app` directory on the host and observe that the @@ -136,9 +136,9 @@ filesystem you can share with containers. For details about accessing the settin │ ├── myfile.txt │ ├── node_modules/ │ ├── package.json + │ ├── package-lock.json │ ├── spec/ - │ ├── src/ - │ └── yarn.lock + │ └── src/ ``` 8. From the host, delete the `myfile.txt` file. @@ -146,7 +146,7 @@ filesystem you can share with containers. For details about accessing the settin ```console root@ac1237fad8db:/src# ls - Dockerfile node_modules package.json spec src yarn.lock + Dockerfile node_modules package.json package-lock.json spec src ``` 10. Stop the interactive container session with `Ctrl` + `D`. @@ -181,8 +181,8 @@ You can use the CLI or Docker Desktop to run your container with a bind mount. ```console $ docker run -dp 127.0.0.1:3000:3000 \ -w /app --mount type=bind,src="$(pwd)",target=/app \ - node:lts-alpine \ - sh -c "yarn install && yarn run dev" + node:24-alpine \ + sh -c "npm install && npm run dev" ``` The following is a breakdown of the command: @@ -192,11 +192,11 @@ You can use the CLI or Docker Desktop to run your container with a bind mount. command will run from - `--mount type=bind,src="$(pwd)",target=/app` - bind mount the current directory from the host into the `/app` directory in the container - - `node:lts-alpine` - the image to use. Note that this is the base image for + - `node:24-alpine` - the image to use. Note that this is the base image for your app from the Dockerfile - - `sh -c "yarn install && yarn run dev"` - the command. You're starting a - shell using `sh` (alpine doesn't have `bash`) and running `yarn install` to - install packages and then running `yarn run dev` to start the development + - `sh -c "npm install && npm run dev"` - the command. You're starting a + shell using `sh` (alpine doesn't have `bash`) and running `npm install` to + install packages and then running `npm run dev` to start the development server. If you look in the `package.json`, you'll see that the `dev` script starts `nodemon`. @@ -227,8 +227,8 @@ You can use the CLI or Docker Desktop to run your container with a bind mount. ```powershell $ docker run -dp 127.0.0.1:3000:3000 ` -w /app --mount "type=bind,src=$pwd,target=/app" ` - node:lts-alpine ` - sh -c "yarn install && yarn run dev" + node:24-alpine ` + sh -c "npm install && npm run dev" ``` The following is a breakdown of the command: @@ -238,11 +238,11 @@ You can use the CLI or Docker Desktop to run your container with a bind mount. command will run from - `--mount "type=bind,src=$pwd,target=/app"` - bind mount the current directory from the host into the `/app` directory in the container - - `node:lts-alpine` - the image to use. Note that this is the base image for + - `node:24-alpine` - the image to use. Note that this is the base image for your app from the Dockerfile - - `sh -c "yarn install && yarn run dev"` - the command. You're starting a - shell using `sh` (alpine doesn't have `bash`) and running `yarn install` to - install packages and then running `yarn run dev` to start the development + - `sh -c "npm install && npm run dev"` - the command. You're starting a + shell using `sh` (alpine doesn't have `bash`) and running `npm install` to + install packages and then running `npm run dev` to start the development server. If you look in the `package.json`, you'll see that the `dev` script starts `nodemon`. @@ -273,8 +273,8 @@ You can use the CLI or Docker Desktop to run your container with a bind mount. ```console $ docker run -dp 127.0.0.1:3000:3000 ^ -w /app --mount "type=bind,src=%cd%,target=/app" ^ - node:lts-alpine ^ - sh -c "yarn install && yarn run dev" + node:24-alpine ^ + sh -c "npm install && npm run dev" ``` The following is a breakdown of the command: @@ -284,11 +284,11 @@ You can use the CLI or Docker Desktop to run your container with a bind mount. command will run from - `--mount "type=bind,src=%cd%,target=/app"` - bind mount the current directory from the host into the `/app` directory in the container - - `node:lts-alpine` - the image to use. Note that this is the base image for + - `node:24-alpine` - the image to use. Note that this is the base image for your app from the Dockerfile - - `sh -c "yarn install && yarn run dev"` - the command. You're starting a - shell using `sh` (alpine doesn't have `bash`) and running `yarn install` to - install packages and then running `yarn run dev` to start the development + - `sh -c "npm install && npm run dev"` - the command. You're starting a + shell using `sh` (alpine doesn't have `bash`) and running `npm install` to + install packages and then running `npm run dev` to start the development server. If you look in the `package.json`, you'll see that the `dev` script starts `nodemon`. @@ -319,8 +319,8 @@ You can use the CLI or Docker Desktop to run your container with a bind mount. ```console $ docker run -dp 127.0.0.1:3000:3000 \ -w //app --mount type=bind,src="/$(pwd)",target=/app \ - node:lts-alpine \ - sh -c "yarn install && yarn run dev" + node:24-alpine \ + sh -c "npm install && npm run dev" ``` The following is a breakdown of the command: @@ -330,11 +330,11 @@ You can use the CLI or Docker Desktop to run your container with a bind mount. command will run from - `--mount type=bind,src="/$(pwd)",target=/app` - bind mount the current directory from the host into the `/app` directory in the container - - `node:lts-alpine` - the image to use. Note that this is the base image for + - `node:24-alpine` - the image to use. Note that this is the base image for your app from the Dockerfile - - `sh -c "yarn install && yarn run dev"` - the command. You're starting a - shell using `sh` (alpine doesn't have `bash`) and running `yarn install` to - install packages and then running `yarn run dev` to start the development + - `sh -c "npm install && npm run dev"` - the command. You're starting a + shell using `sh` (alpine doesn't have `bash`) and running `npm install` to + install packages and then running `npm run dev` to start the development server. If you look in the `package.json`, you'll see that the `dev` script starts `nodemon`. diff --git a/content/get-started/workshop/07_multi_container.md b/content/get-started/workshop/07_multi_container.md index 3f7faf677dcb..8e13232c46de 100644 --- a/content/get-started/workshop/07_multi_container.md +++ b/content/get-started/workshop/07_multi_container.md @@ -218,8 +218,8 @@ You can now start your dev-ready container. -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ - node:lts-alpine \ - sh -c "yarn install && yarn run dev" + node:24-alpine \ + sh -c "npm install && npm run dev" ``` {{< /tab >}} @@ -234,8 +234,8 @@ You can now start your dev-ready container. -e MYSQL_USER=root ` -e MYSQL_PASSWORD=secret ` -e MYSQL_DB=todos ` - node:lts-alpine ` - sh -c "yarn install && yarn run dev" + node:24-alpine ` + sh -c "npm install && npm run dev" ``` {{< /tab >}} @@ -250,8 +250,8 @@ You can now start your dev-ready container. -e MYSQL_USER=root ^ -e MYSQL_PASSWORD=secret ^ -e MYSQL_DB=todos ^ - node:lts-alpine ^ - sh -c "yarn install && yarn run dev" + node:24-alpine ^ + sh -c "npm install && npm run dev" ``` {{< /tab >}} @@ -265,8 +265,8 @@ You can now start your dev-ready container. -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ - node:lts-alpine \ - sh -c "yarn install && yarn run dev" + node:24-alpine \ + sh -c "npm install && npm run dev" ``` {{< /tab >}} @@ -276,11 +276,13 @@ You can now start your dev-ready container. using the mysql database. ```console - $ nodemon src/index.js - [nodemon] 2.0.20 + [nodemon] 3.1.11 [nodemon] to restart at any time, enter `rs` - [nodemon] watching dir(s): *.* + [nodemon] watching path(s): *.* + [nodemon] watching extensions: js,mjs,cjs,json [nodemon] starting `node src/index.js` + Waiting for mysql:3306. + Connected! Connected to mysql db at host mysql Listening on port 3000 ``` diff --git a/content/get-started/workshop/08_using_compose.md b/content/get-started/workshop/08_using_compose.md index ab019410c3bb..a74d2172006d 100644 --- a/content/get-started/workshop/08_using_compose.md +++ b/content/get-started/workshop/08_using_compose.md @@ -29,9 +29,9 @@ In the `getting-started-app` directory, create a file named `compose.yaml`. │ ├── compose.yaml │ ├── node_modules/ │ ├── package.json +│ ├── package-lock.json │ ├── spec/ -│ ├── src/ -│ └── yarn.lock +│ └── src/ ``` ## Define the app service @@ -46,8 +46,8 @@ $ docker run -dp 127.0.0.1:3000:3000 \ -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ - node:lts-alpine \ - sh -c "yarn install && yarn run dev" + node:24-alpine \ + sh -c "npm install && npm run dev" ``` You'll now define this service in the `compose.yaml` file. @@ -58,7 +58,7 @@ You'll now define this service in the `compose.yaml` file. ```yaml services: app: - image: node:lts-alpine + image: node:24-alpine ``` 2. Typically, you will see `command` close to the `image` definition, although there is no requirement on ordering. Add the `command` to your `compose.yaml` file. @@ -66,8 +66,8 @@ You'll now define this service in the `compose.yaml` file. ```yaml services: app: - image: node:lts-alpine - command: sh -c "yarn install && yarn run dev" + image: node:24-alpine + command: sh -c "npm install && npm run dev" ``` 3. Now migrate the `-p 127.0.0.1:3000:3000` part of the command by defining the `ports` for the service. @@ -75,8 +75,8 @@ You'll now define this service in the `compose.yaml` file. ```yaml services: app: - image: node:lts-alpine - command: sh -c "yarn install && yarn run dev" + image: node:24-alpine + command: sh -c "npm install && npm run dev" ports: - 127.0.0.1:3000:3000 ``` @@ -89,8 +89,8 @@ You'll now define this service in the `compose.yaml` file. ```yaml services: app: - image: node:lts-alpine - command: sh -c "yarn install && yarn run dev" + image: node:24-alpine + command: sh -c "npm install && npm run dev" ports: - 127.0.0.1:3000:3000 working_dir: /app @@ -103,8 +103,8 @@ You'll now define this service in the `compose.yaml` file. ```yaml services: app: - image: node:lts-alpine - command: sh -c "yarn install && yarn run dev" + image: node:24-alpine + command: sh -c "npm install && npm run dev" ports: - 127.0.0.1:3000:3000 working_dir: /app @@ -185,8 +185,8 @@ At this point, your complete `compose.yaml` should look like this: ```yaml services: app: - image: node:lts-alpine - command: sh -c "yarn install && yarn run dev" + image: node:24-alpine + command: sh -c "npm install && npm run dev" ports: - 127.0.0.1:3000:3000 working_dir: /app diff --git a/content/get-started/workshop/09_image_best.md b/content/get-started/workshop/09_image_best.md index a84842d75f12..bcbf55de47da 100644 --- a/content/get-started/workshop/09_image_best.md +++ b/content/get-started/workshop/09_image_best.md @@ -27,14 +27,13 @@ to create each layer within an image. ```plaintext IMAGE CREATED CREATED BY SIZE COMMENT a78a40cbf866 18 seconds ago /bin/sh -c #(nop) CMD ["node" "src/index.j… 0B - f1d1808565d6 19 seconds ago /bin/sh -c yarn install --production 85.4MB + f1d1808565d6 19 seconds ago /bin/sh -c npm install --omit=dev 85.4MB a2c054d14948 36 seconds ago /bin/sh -c #(nop) COPY dir:5dc710ad87c789593… 198kB 9577ae713121 37 seconds ago /bin/sh -c #(nop) WORKDIR /app 0B b95baba1cfdb 13 days ago /bin/sh -c #(nop) CMD ["node"] 0B 13 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B 13 days ago /bin/sh -c #(nop) COPY file:238737301d473041… 116B 13 days ago /bin/sh -c apk add --no-cache --virtual .bui… 5.35MB - 13 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.21.1 0B 13 days ago /bin/sh -c addgroup -g 1000 node && addu… 74.3MB 13 days ago /bin/sh -c #(nop) ENV NODE_VERSION=12.14.1 0B 13 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B @@ -61,30 +60,31 @@ Look at the following Dockerfile you created for the getting started app. ```dockerfile # syntax=docker/dockerfile:1 -FROM node:lts-alpine +FROM node:24-alpine WORKDIR /app COPY . . -RUN yarn install --production +RUN npm install --omit=dev CMD ["node", "src/index.js"] +EXPOSE 3000 ``` Going back to the image history output, you see that each command in the Dockerfile becomes a new layer in the image. -You might remember that when you made a change to the image, the yarn dependencies had to be reinstalled. It doesn't make much sense to ship around the same dependencies every time you build. +You might remember that when you made a change to the image, the dependencies had to be reinstalled. It doesn't make much sense to ship around the same dependencies every time you build. To fix it, you need to restructure your Dockerfile to help support the caching of the dependencies. For Node-based applications, those dependencies are defined in the `package.json` file. You can copy only that file in first, install the -dependencies, and then copy in everything else. Then, you only recreate the yarn +dependencies, and then copy in everything else. Then, you only recreate the dependencies if there was a change to the `package.json`. 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:lts-alpine + FROM node:24-alpine WORKDIR /app - COPY package.json yarn.lock ./ - RUN yarn install --production + COPY package.json package-lock.json ./ + RUN npm install --omit=dev COPY . . CMD ["node", "src/index.js"] ``` @@ -103,13 +103,13 @@ dependencies if there was a change to the `package.json`. => => transferring dockerfile: 175B => [internal] load .dockerignore => => transferring context: 2B - => [internal] load metadata for docker.io/library/node:lts-alpine + => [internal] load metadata for docker.io/library/node:24-alpine => [internal] load build context => => transferring context: 53.37MB - => [1/5] FROM docker.io/library/node:lts-alpine + => [1/5] FROM docker.io/library/node:24-alpine => CACHED [2/5] WORKDIR /app - => [3/5] COPY package.json yarn.lock ./ - => [4/5] RUN yarn install --production + => [3/5] COPY package.json package-lock.json ./ + => [4/5] RUN npm install --omit=dev => [5/5] COPY . . => exporting to image => => exporting layers @@ -127,13 +127,13 @@ dependencies if there was a change to the `package.json`. => => transferring dockerfile: 37B => [internal] load .dockerignore => => transferring context: 2B - => [internal] load metadata for docker.io/library/node:lts-alpine + => [internal] load metadata for docker.io/library/node:24-alpine => [internal] load build context => => transferring context: 450.43kB - => [1/5] FROM docker.io/library/node:lts-alpine + => [1/5] FROM docker.io/library/node:24-alpine => CACHED [2/5] WORKDIR /app - => CACHED [3/5] COPY package.json yarn.lock ./ - => CACHED [4/5] RUN yarn install --production + => CACHED [3/5] COPY package.json package-lock.json ./ + => CACHED [4/5] RUN npm install => [5/5] COPY . . => exporting to image => => exporting layers @@ -182,21 +182,26 @@ for your production build. You can ship the static resources in a static nginx c ```dockerfile # syntax=docker/dockerfile:1 -FROM node:lts AS build +FROM node:24-alpine AS build WORKDIR /app -COPY package* yarn.lock ./ -RUN yarn install +COPY package* ./ +RUN npm install COPY public ./public COPY src ./src -RUN yarn run build +RUN npm run build FROM nginx:alpine COPY --from=build /app/build /usr/share/nginx/html ``` -In the previous Dockerfile example, it uses the `node:lts` image to perform the build (maximizing layer caching) and then copies the output +In the previous Dockerfile example, it uses the `node:24-alpine` image to perform the build (maximizing layer caching) and then copies the output into an nginx container. + + > [!Tips] + > This React example is for illustration purposes. The getting-started todo app is a `Node.js` backend application, not a React frontend. + + ## Summary In this section, you learned a few image building best practices, including layer caching and multi-stage builds. diff --git a/content/get-started/workshop/images/dashboard-two-containers.webp b/content/get-started/workshop/images/dashboard-two-containers.webp index 5e657ca56dad..89ca228f1fe4 100644 Binary files a/content/get-started/workshop/images/dashboard-two-containers.webp and b/content/get-started/workshop/images/dashboard-two-containers.webp differ diff --git a/content/get-started/workshop/images/pwd-add-new-instance.webp b/content/get-started/workshop/images/pwd-add-new-instance.webp deleted file mode 100644 index cfb827ead2b2..000000000000 Binary files a/content/get-started/workshop/images/pwd-add-new-instance.webp and /dev/null differ