全栈 dockerfile 的编写指南(前端 + 后端 nest)

Dockerfile 是一个文本文件,包含了一系列的指令和命令,这些指令和命令被 Docker 用来自动化构建 Docker 镜像的过程。Docker 镜像是轻量级、可执行的独立软件包,包含了运行一个应用所需的所有内容:代码、运行时环境、库、环境变量和配置文件。

Dockerfile 的主要用途如下:

  1. 定义环境:Dockerfile 允许你精确地定义运行你的应用所需要的操作系统环境、库以及依赖。
  2. 自动化构建:通过 Dockerfile,Docker 可以自动化构建镜像的过程,确保镜像的构建是可重复且一致的。
  3. 代码到部署的一致性:使用 Dockerfile 构建的镜像,可以确保开发、测试和生产环境中的运行环境一致性,减少了"在我机器上能运行"的问题。
  4. 版本控制和追踪:Dockerfile 可以像应用代码一样,被放在版本控制系统中,这样你可以追踪到环境的任何变更,以及回退到任何历史版本。
  5. 便捷的分发和部署:构建好的 Docker 镜像可以被推送到 Docker Hub 或其他容器镜像仓库,从而方便地被分发和部署到任何支持 Docker 的平台上。

一个基本的 Dockerfile 可能包含以下指令:

  • FROM:指定基础镜像。所有的 Dockerfile 都必须以一个 FROM 指令开始,它定义了将要构建的镜像的基础(父)镜像。
  • RUN:执行命令并创建新的镜像层,用于安装软件包和做其他配置。
  • COPY 和 ADD:从构建上下文中复制文件到镜像中。
  • CMD:提供容器默认执行的命令。Dockerfile 中只能有一个 CMD 指令。
  • ENTRYPOINT:配置容器启动时运行的命令,允许容器像应用程序一样运行。
  • EXPOSE:声明容器运行时监听的端口。
  • ENV:设置环境变量。
  • WORKDIR:设置工作目录,即容器内部的当前目录。
  • VOLUME:创建一个可以从本地主机或其他容器挂载的挂载点(或称为卷)。

使用 Dockerfile 构建镜像的命令是 docker build。这个过程包括读取 Dockerfile 的每一条指令,执行它们,最终创建一个可用的 Docker 镜像。

前端

在前端项目中使用 Docker 通常是为了确保一致的开发环境和简化部署流程。

Dockerfile 通常包含了构建前端项目所需的所有步骤,包括安装依赖、构建静态文件以及运行服务。以下是一个典型的前端项目的 Dockerfile 示例:

dockerfile 复制代码
# 使用多阶段构建,首先定义构建阶段
# 选择一个带有Node.js的轻量级基础镜像
FROM node:16-alpine as build-stage

# 设置工作目录
WORKDIR /app

# 复制package.json和package-lock.json(如果存在)
COPY package*.json ./

# 安装项目生产依赖
# 利用Docker缓存层,如果 package.json 没有变化,则不会重新安装node modules
RUN npm install --only=production

# 复制项目文件到工作目录
COPY . .

# 构建应用
RUN npm run build

# 阶段2:运行
# 使用 Nginx 镜像作为基础来提供前端静态文件服务
FROM nginx:stable-alpine as production-stage

# 定义环境变量,例如NODE_ENV
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /app

# 从构建阶段拷贝构建出的文件到Nginx目录
COPY --from=build-stage /app/dist /usr/share/nginx/html

# 配置nginx,如果有自定义的nginx配置可以取消注释并修改下面的行
# COPY nginx.conf /etc/nginx/conf.d/default.conf

# 暴露80端口
EXPOSE 80

# 启动Nginx服务器
CMD ["nginx", "-g", "daemon off;"]

这个 Dockerfile 包含了两个阶段:

  1. 构建阶段:这个阶段使用了一个带有 Node.js 的 Docker 镜像。在这个阶段中,首先设置了工作目录 /app,然后复制了 package.json 和 package-lock.json 文件,并执行了 npm install 来安装依赖。接下来,复制了所有项目文件,并执行了构建命令(例如 npm run build),这通常会生成一个 dist 文件夹,包含了所有静态文件。
  2. 运行阶段:这个阶段使用了一个轻量级的 Nginx 镜像来作为静态文件服务器。它从构建阶段复制了 dist 文件夹的内容到 Nginx 的服务目录。最后,暴露了端口 80,并设置了默认的启动命令来运行 Nginx。

当你运行这个 Docker 容器时,它会启动 Nginx 服务器并提供你的前端应用。

选择官方、经过验证的基础镜像,以确保安全性和可维护性。使用尽可能精简的基础镜像,比如 alpine,减少最终镜像的大小。

在这个 Dockerfile 中,我们使用了多阶段构建和利用 Docker 缓存层。这样做的好处是可以减小最终镜像的大小,因为构建阶段所需的依赖和工具在最终镜像中通常是不需要的。所以利用 Docker 的层次结构,将经常变动的指令放在 Dockerfile 的底部,这样可以充分利用缓存。

使用 ARG 和 ENV 来设置构建时和运行时的环境变量。这样可以在不修改 Dockerfile 的情况下重新构建镜像,更灵活。使用 ARG 声明的 NODE_ENV 构建参数,使用 ${NODE_ENV} 来取。花括号可加可不加。

利用多阶段构建可以将编译和运行环境分开,最终只将运行时必须的文件打包进镜像。

根据你的项目实际情况,可能还需要调整构建命令、复制的文件路径等。例如,如果你的构建命令不是 npm run build,就需要修改对应的 RUN 指令。如果你的静态文件输出目录不是 dist,也需要修改最后一个 COPY 指令中的路径。

此外,你可能还需要配置 Nginx,比如添加自定义的 nginx.conf 文件。这可以通过在第二个阶段添加一个 COPY 指令来实现,将你的配置文件复制到容器中的 Nginx 配置目录下。

确保在 Dockerfile 所在的目录结构中有你的前端代码和所有必要的文件,然后你可以通过以下命令来构建和运行你的Docker容器

dockerfile 复制代码
docker build -t my-frontend-app .
docker run -it -p 80:80 my-frontend-app

以上命令中 -t my-frontend-app 为构建出的镜像设置了一个标签,-p 80:80 将容器的 80 端口映射到宿主机的 80 端口。这样你就可以通过浏览器访问宿主机的 80 端口来访问你的前端应用了。

如果想要在服务器上运行,步骤如下:

例如运行 Ubuntu 的云服务器,以及一个在 Docker Hub 上公开可用的前端应用容器。

  1. 登录到你的服务器:使用 SSH 连接到你的服务器。
bash 复制代码
ssh [your-username]@[your-server-ip]

替换[your-username][your-server-ip]为你的服务器登录用户名和 IP 地址。

  1. 安装 Docker(如果尚未安装,安装了的):使用服务器的包管理器安装 Docker。
bash 复制代码
sudo apt update
sudo apt install docker.io
  1. 启动 Docker 服务(如果尚未运行):
bash 复制代码
sudo systemctl start docker

并确保 Docker 在每次启动时自动运行:

bash 复制代码
sudo systemctl enable docker
  1. 拉取 Docker 镜像:从 Docker Hub 或你的私有仓库拉取你的前端应用镜像。
bash 复制代码
sudo docker pull myusername/my-frontend-app:tag
  1. 运行 Docker 容器 :一旦镜像被拉取到服务器上,使用docker run命令来启动你的容器。
bash 复制代码
sudo docker run -d --name my-frontend-app-container -p 80:80 myusername/my-frontend-app:tag

这里的-d选项意味着"detached"模式,即在后台运行容器。--name给容器分配一个名称,以便于后续管理。

尽量为构建的镜像使用有意义的标签,并遵循版本控制最佳实践。
-p 80:80是端口映射,将容器内的 80 端口映射到主机的 80 端口上,这样外界访问服务器的 80 端口时,实际上是访问到了容器中运行的前端应用。

  1. 验证容器运行状态:检查容器是否正在运行。
bash 复制代码
sudo docker ps

现在,如果一切设置正确,你应该能够通过服务器的IP地址或绑定的域名在浏览器中访问你的前端应用了。

如果你需要查看应用的日志,可以使用:

bash 复制代码
sudo docker logs my-frontend-app-container

如果你需要停止容器,可以使用:

bash 复制代码
docker stop my-frontend-app-container

如果你需要重新启动容器,可以使用:

bash 复制代码
docker restart my-frontend-app-container

确保服务器的防火墙设置允许来自外部的 HTTP(端口80)和 HTTPS(端口 443,如果你使用 SSL)流量。

如果你需要配置 SSL 或设置一个更复杂的部署(如使用 Docker Compose,Kubernetes 等),这些步骤会更加复杂。

请注意,具体的命令可能会根据你的服务器配置、Docker 镜像设置以及你的需求有所不同。

后端 nest

在创建 Nest.js 项目的 Dockerfile 时,你需要考虑几个关键步骤:安装依赖项、构建应用程序和运行应用程序。以下是一个简单的 Dockerfile 示例,它将帮助你将 Nest.js 应用程序容器化:

dockerfile 复制代码
# Step 1: 使用带有Node.js的基础镜像
FROM node:16-alpine as builder

# 设置工作目录
WORKDIR /usr/src/app

# 复制package.json和package-lock.json(如果可用)
COPY package*.json ./

# 安装项目依赖
RUN npm install --only=production

# 复制所有文件到容器中
COPY . .

# 构建应用程序
RUN npm run build

# Step 2: 运行时使用更精简的基础镜像
FROM node:16-alpine

WORKDIR /usr/src/app

# 从builder阶段复制构建好的文件
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/node_modules ./node_modules

# 暴露3000端口
EXPOSE 3000

# 运行Nest.js应用程序
CMD ["node", "dist/main"]

这个 Dockerfile 分为两个步骤:

  1. 构建步骤(builder stage):使用 node:16-alpine 作为基础镜像,安装依赖,并构建 Nest.j s应用程序。这个阶段会生成构建好的应用程序代码。
  2. 运行时步骤(runtime stage):再次使用 node:16-alpine 作为基础镜像,但这次只是复制构建步骤中生成的 dist 目录和 node_modules 目录到新的工作目录中。这样做的目的是保持镜像尽可能的精简。因为构建步骤中的依赖、源代码和其他不必要的文件在运行时阶段是不需要的。

最后,通过 EXPOSE 指令声明容器运行时需要暴露的端口,然后通过 CMD 指令指定容器启动时运行的命令。

在你的实际项目中,你可能还需要设置环境变量、处理私有依赖库或者添加额外的配置文件。记得根据你项目的实际需求调整 Dockerfile。

gitlab 自动部署

下面是一个示例 .gitlab-ci.yml 文件,用于自动化构建和部署前端应用程序到 Docker 容器中:

yaml 复制代码
stages:
  - build
  - deploy

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t your-image-name .
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push your-image-name

deploy:
  stage: deploy
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker pull your-image-name
    - docker run -d --name your-container-name -p 80:80 your-image-name
  only:
    - master

上述示例中的.gitlab-ci.yml文件定义了两个阶段(stage):build 和 deploy。

在 build 阶段,使用 Docker 构建你的镜像,并将其推送到你的 Docker 注册表(registry)。

在 deploy 阶段,从 Docker 注册表中拉取镜像,并在容器中运行应用程序。

only 关键字指定了只有在推送到 master 分支时才会执行部署作业。

请注意,上述示例中使用了 GitLab CI/CD 提供的 CI/CD 变量(CI_REGISTRY_USER、CI_REGISTRY_PASSWORD 和 CI_REGISTRY),这些变量需要在 GitLab 项目的设置中进行配置。

你需要根据你的具体情况修改上述示例中的镜像名称、容器名称和端口映射等参数,以适应你的项目需求。此外,你还可以根据需要在 script 部分添加其他命令或步骤,例如运行测试、执行静态代码分析等。

完成后,将上述内容保存为 .gitlab-ci.yml 文件,并将其提交到你的 GitLab 代码仓库中。GitLab 将根据配置的 .gitlab-ci.yml 文件自动执行构建和部署操作。

docker 的环境变量

Docker 容器中的环境变量是一个关键特性,用于管理容器内的配置。环境变量可以在容器运行时传递给容器内部的应用程序,从而允许用户调整容器行为而无需修改应用程序代码。

在 Docker 中,你可以通过几种不同的方式设置环境变量:

  1. 使用 Dockerfile

Dockerfile 中,可以使用 ENV 指令来设置环境变量。这些变量在构建镜像时被设置,并且在运行容器时保持不变。

dockerfile 复制代码
ENV MY_VARIABLE my_value
  1. 在命令行中使用 docker run 命令:

当使用 docker run 命令启动容器时,可以通过 -e--env 选项来传递环境变量。

shell 复制代码
docker run -e "MY_VARIABLE=my_value" my_image

如果你有多个环境变量需要设置,可以多次使用 -e 选项。

  1. 使用环境变量文件:

如果你有许多环境变量需要设置,可以使用文件来存储这些变量,然后在使用 docker run 命令时通过 --env-file 选项指定该文件。

shell 复制代码
docker run --env-file my_env_file.txt my_image

其中 my_env_file.txt 文件包含了环境变量的键值对,每行一个,如下所示:

ini 复制代码
MY_VARIABLE=my_value
ANOTHER_VARIABLE=another_value
  1. docker-compose.yml 文件中设置:

如果你使用的是 Docker Compose,可以在 docker-compose.yml 文件中的 environment 部分设置环境变量。

yaml 复制代码
version: '3'
services:
  my_service:
    image: my_image
    environment:
      MY_VARIABLE: my_value

这些环境变量在容器启动时对容器内运行的应用程序可见,可以用于配置应用程序的行为,比如数据库连接信息、外部服务的 API 密钥等。

相关推荐
薛定谔的猫19828 分钟前
gradio学习代码部分
java·前端·javascript
Rust研习社17 分钟前
Reqwest 兼顾简洁与高性能的现代 HTTP 客户端
开发语言·网络·后端·http·rust
绿草在线17 分钟前
SpringBoot请求与响应全解析
spring boot·后端·lua
yqcoder19 分钟前
React 深度解析:类组件 (Class) vs 函数组件 (Function)
前端·javascript·react.js
HwJack2023 分钟前
HarmonyOS 开发中Web 组件渲染进程崩溃后的“起死回生”
前端·华为·harmonyos
HyaCinth23 分钟前
一人一周,用 Codex 渐进式迁移重构了一个材料学组件库
前端·javascript·css
心.c34 分钟前
大厂高频手写题
开发语言·前端·javascript
Victor3561 小时前
MongoDB(103)如何处理分片集群中的数据不一致?
后端
Victor3562 小时前
MongoDB(104)如何处理MongoDB中的磁盘空间不足问题?
后端
立莹Sir3 小时前
商品中台架构设计与技术落地实践——基于Spring Cloud微服务体系的完整解决方案
分布式·后端·spring cloud·docker·容器·架构·kubernetes