前沿
起因是学习前端以来,一直没有拥有属于自己的博客网站。之前断断续续有过想法,但是都没有实践到底,连服务器都没有正经买过,各种学习笔记都记在语雀上。最近由于 Cursor 的爆火,平时工作学习中对它的附魔能力大为震撼,就利用 Cursor 写了一个博客网站,顺便记录下第一次用 Docker 在服务器部署项目的过程。
认识 Docker
什么是 Docker?
“加速您构建、共享和运行应用程序的方式。Docker 帮助开发人员在任何地方构建、共享、运行和验证应用程序——无需繁琐的环境配置或管理。” ——来自Docker 官网
“属于 Linux 容器的一种封装,提供简单易用的容器使用接口。 它是目前最流行的 Linux 容器解决方案。”——来自阮一峰的网络日志
简单来说,Docker 作为一个承载服务的容器,可以让开发者无需关注不同运行环境的差异,真正做到服务随处运行。
一、Docker相关概念
老规矩直接上图
首先需要明确三个概念:
Image镜像:Docker 根据 DockerFile 打包项目的代码、依赖和相关环境配置的镜像文件,可以理解为包含项目运行所需资源的打包产物;
Container容器:Image 运行产生的实例,各个容器之间相互独立,互不干扰;
Registry仓库: 集中存放镜像的仓库,常用的有 Docker Hub 、阿里云的容器镜像服务等
先串一下整体的流程:
- 本地项目开发完成后,编写 Dockerfile 文件;
- 尝试打包构建 docker 镜像并打 tag,构建成功后在本地运行容器完成自测;
- 将镜像push 到远程的镜像仓库;
- 在服务器上 pull 指定镜像,将容器运行在指定端口即可。
二、Docker 部署流程
2.1 安装Docker客户端&配置镜像源
在Docker 官网下载客户端,支持可视化界面操作。
在客户端设置 => Docker Engine => registry-mirrors 中配置仓库镜像源,否则build 时会因为获取不到相关资源(比如node:20.11.0-alpine)导致本地镜像生成失败,实测镜像源[0] 可用。
2.2 编写 DockerFile
DockerFile 的作用更像是一个命令集合,Docker 会根据文件每一行的关键字,分层进行打包堆叠,最终形成完整的镜像文件。 下面是我的博客项目用到的 DockerFile 文件,主要是指定node 和 yarn 版本。
# 1. 构建基础镜像
FROM node:20.11.0-alpine AS base
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV NODE_ENV=production
# 使用国内镜像源
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
# 启用并设置 Yarn Berry
RUN corepack enable \
&& corepack prepare yarn@3.6.3 --activate
# 2. 安装依赖
FROM base AS deps
# 复制所有 Yarn 相关文件
COPY package.json .
COPY yarn.lock .
COPY .yarnrc.yml .
COPY .yarn ./.yarn
# 初始化 Yarn
RUN yarn set version 3.6.3
# 安装依赖
RUN yarn install --immutable --inline-builds --network-timeout 100000
# 3. 构建应用
FROM base AS builder
# 复制依赖和配置
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/.yarn ./.yarn
COPY --from=deps /app/.yarnrc.yml .
COPY --from=deps /app/yarn.lock .
COPY --from=deps /app/package.json .
# 复制源代码,确保包含 content 目录
COPY content ./content
COPY . .
# 复制 next.config.js
COPY next.config.js .
# 构建应用
RUN yarn build
# 4. 生产环境
FROM base AS runner
# 复制所有必要文件
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.yarn ./.yarn
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/content ./content
COPY --from=builder /app/package.json .
COPY --from=builder /app/yarn.lock .
COPY --from=builder /app/.yarnrc.yml .
# 复制 next.config.js
COPY --from=builder /app/next.config.js .
# 暴露端口
EXPOSE 3000
# 启动应用
CMD ["yarn", "start"]
以及.gitignore文件(几乎可以通用)
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
2.3 build 本地镜像
# 构建镜像 Image
# -t用来指定镜像名称 .代表DockerFile 在当前路径
$ docker image build -t [image name] .
# 构建镜像成功后,实例化镜像,运行容器
# -d: 以后台方式运行容器 -p 将容器的 3000 端口映射到本机的 3000 端口上
$ docker run -d -p 3000:3000 [image name]
由于运行在本机的 3000 端口,通过http://localhost:3000/ 可以在本地访问容器运行的服务
2.4 镜像推送至远程仓库
给镜像打 tag 的目的是为了更好地区分不同镜像版本,可以类比 git 中给某次 commit 打上 tag 标识,一般上线服务都会选择使用 tag 而不是特定分支。我这边使用镜像仓库的阿里云的容器镜像服务。
# 登录阿里云Docker Registry
$ docker login --username=[阿里云用户名] [镜像仓库地址]
# 镜像打 tag
$ docker tag [ImageId] [镜像仓库地址]/[镜像名称]:[镜像版本号]
# 将镜像推送到Registry
$ docker push [镜像仓库地址]/[镜像名称]:[镜像版本号]
2.5 服务器上拉取镜像并运行容器
# 在服务器上同样登录阿里云镜像仓库
$ docker login --username=[阿里云用户名] [镜像仓库地址]
# 拉取指定镜像
$ docker pull [镜像仓库地址]/[镜像名称]:[镜像版本号]
# 镜像实例化 运行容器
$ sudo docker run -d --restart=always -p 3000:3000 [镜像仓库地址]/[镜像名称]:[镜像版本号]
三 docker 常用命令
# 查看所有镜像
$ docker images
# 查看所有正在运行的容器,命令后加上参数 -a 即为查看所有容器(包括停止运行的容器)
$ docker ps
# 停止运行指定容器
$ docker stop [容器id]
# 删除停止运行的容器 rm后加上参数-f 可以强制删除正在运行的容器
$ docker rm [容器id]
# 删除指定镜像
$ docker rmi [镜像id]
# 查看容器日志,主要排查问题用
$ docker logs [镜像id]
结语
希望对你有所帮助。