前端程序员学习 Docker 的笔记 (二)
如何使用 Docker Compose 来部署一个包含 Next.js 和 Redis 的项目。文章首先演示了本地启动项目的步骤,然后讲解了如何使用 Docker 容器化部署服务。接着介绍了 Docker Compose 的概念和使用方法,通过编写 docker-compose.yml 文件来管理多个容器。最后,文章还讨论了数据卷的概念和使用,以实现数据持久化。整篇文章通过实际操作步骤,深入浅出地讲解了 Docker 在前端项目中的应用,适合想要学习 Docker 的前端开发者阅读
大家好,我是阿星,上一个文章:更适合前端的 Docker 教程(一),我分享了 Docker 基础,如何通过 Docker 容器化部署一个纯前端的项目,但是真实项目,肯定是离不开后端的,下面就以一个需要 nextjs + redis 技术的项目为例,来入门一下 Docker Compose
本地启动项目
我们先本地启动一下需要这些技术的项目,这里直接 git 拉取仓库就好:
git clone -b day1 git@github.com:mqyqingfeng/next-react-notes-demo.git
本地运行
cd next-react-notes-demo && npm i && npm run dev
由于这个项目需要 Redis 服务,我们新开一个命令行运行:redis-server
,如果没有安装 redis,需要先安装一下 Redis。
容器化部署
使用上个文章的知识,我们现在把 Redis 和 Next.js 项目 容器化部署
- 拉取 Redis 镜像:
docker pull redis
- 关闭本地命令行启动的 Redis ,使用 Docker 启动 Redis:
docker run -p 6379:6379 redis redis-server
关闭本地的Redis 后,我们的项目刷新是会报错的: 使用 Docker 启动 Redis 后,就恢复了 - 我们完成了 Redis 容器化,我们现在把 Next.js 项目也新开一个容器进行部署,但是每个容器都是一个单独的隔离空间。这时候即将容器化部署的 Next.js 服务就访问不到我们的 Redis 服务。那么我们就需要将两个容器服务连接起来,Docker 可以将容器加入自定义的 Docker 网络的方式来连接多个容器。
Docker 支持自定义网络,这里我们使用桥接网络来连接多个容器,容器间就可以通信了!
- 创建一个 自定义 Docker 网络:
docker network create -d bridge react-notes
- -d bridge :参数指定 Docker 网络类型,有 bridge、overlay。
- react-notes 为我们的自定义网络的名字
5)暂停或者删除之前开启的 Redis 容器,运行一个新的 Redis 容器并连接到新建的 react-notes 网络:
docker run -p 6379:6379 --network react-notes redis redis-server
6)查找 redis 容器的 IP 地址:docker network inspect react-notes
得到 IPv4Address 的 host 将其设置为 lib/redis.js 的 Redis host
7) 为 Next.js 项目创建 Dockerfile,上篇文章讲了每行命令的含义
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install --registry=https://registry.npmmirror.com
CMD npm run build && npm start
EXPOSE 3000
8)有了 Dockerfile ,我们就可以为项目打包镜像。执行:docker image build -t next-react-notes-demo:0.0.1 .
创建镜像
- 运行打包好的镜像,生成一个实例:
docker run -p 4000:3000 --network react-notes next-react-notes-demo:0.0.1
本地访问 http://localhost:4000 我们应该可以看到页面,但是我们这样设置网络,设置每个容器,是不是很麻烦?没错我们可以使用** Docker Compose 来帮我们管理多个容器**
Docker Compose
Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过编写一个 docker-compose.yml 文件,你可以在其中定义所有服务、网络和卷,然后使用一个命令就能启动和管理这些服务。
三个核心概念:
- 服务:也就是容器,例如上面的前端网页服务和 Redis 服务
- 网络:Docker Compose 会创建一个默认网络让所有服务互相连接,你也可以定义自定义网络
- 卷:用于在服务(容器)之间共享和持久化数据。可以在开头创建数据卷,给服务使用
先查看是否安装了 Docker Compose,如果是安装 Docker 客户端,是会默认安装上的
docker-compose --version
# 应该能看到:Docker Compose version v2.27.0-desktop.2 这样的返回
我们编写 docker-compose.yml 文件(模板文件的各种指令含义可以参考 https://yeasy.gitbook.io/docker_practice/compose/compose_file):
# 使用 Docker Compose 文件格式版本 3.8。这版本支持新的功能和属性,同时确保与 Docker 引擎的兼容性。
version: '3.8'
# 定义了应用的所有服务,每个服务对应一个容器。
services:
# 服务名称,可以任意字符串,这里是 Redis 服务
redis:
# 指定镜像,从 Docker Hub 下载该镜像
image: redis
# 将容器的 6379 端口映射到主机的 6379 端口。这使得在本地可以通过端口 6379 访问 Redis 服务。 ports:
- '6379:6379'
# 指定容器启动时执行的命令,这里是 redis-server,即启动 Redis 服务器
command: redis-server
# nextjs 前端网页服务
nextapp:
# 构建镜像时使用当前目录,nextapp 服务的镜像将根据当前目录中的 Dockerfile 构建。
build: .
# 将容器的 3000 端口映射到主机的 4000 端口。
ports:
- '4000:3000'
# nextapp 服务依赖 redis 服务。这意味着在启动 nextapp 服务之前,Docker Compose 会先启动 redis 服务。 depends_on:
- redis
我为每行添加了注释,使用的时候注释记得删除掉,不然会有问题,我们还需要之前编写的 Dockerfile ,我们还需要修改 redis.js
const redis = new Redis({
host: 'redis'
});
我们这时候命令行运行 Docker Compose 脚本:docker-compose up
docker-compose up 会尝试自动完成包括
- 构建镜像
- (重新)创建服务
- 启动服务
- 关联服务相关容器的一系列操作
现在我们的项目应该启动了,依然是:http://localhost:4000,我们会看到: 之前我们做的事情,现在只需要一行命令就都帮我们搞定了 真好,加入每次部署使用 Docker Compose 节省时间是 1 分钟(应该肯定不止吧!),那帮所有使用 Docker 的人节省的时间那得是多少呀!这就是技术的魅力,省下来的时间玩游戏多香呀! 现在还有一个问题,就是 Redis 中的数据都在容器中,如果容器销毁,产生的数据也都随之消失,那如果是线上,这肯定不行的呀,数据才是最重要的,得要把容器的数据同步备份到主机中,即使容器销毁,数据也还在,那就需要数据卷 VOLUME 了,让我们继续往下看
数据卷
数据卷(Volumes)是 Docker 提供的一种用于在容器和主机之间共享数据的机制。数据卷可以独立于容器的生命周期存在,这意味着即使容器删除了,数据卷中的数据也不会丢失。它的特点:
- 持久性:数据卷的数据会一直保留,直到明确删除,即使容器删除,数据仍然存在。
- 共享和重用:数据卷可以在多个容器之间共享和重用。
- 备份和恢复:可以轻松地对数据卷进行备份和恢复操作。
- 高效:数据卷的性能优于直接在容器内存储数据。
上面部署的项目,Redis 的数据是没有存储到主机的,我们验证一下:
# 查看 redis 容器的 container id
docker container ls
# 进入想要进入的容器 (这里是进入 Redis 容器)
docker exec -it 6ddf48e06645 bash
终端效果: 我们执行一些 Redis 命令,来删除一条数据
redis-cli
keys *
hgetAll notes
hdel notes 1702459182837
hgetAll notes
# 查看 Next.js 项目的 container id
docker container ls
# 重启 Next.js 容器
docker container restart 74776b12c032
我们能看到: 这时候,如果我们把所有容器都删除(Redis 容器也删除了),重新运行: 先执行:docker-compose down
,再执行docker-compose up
我们会发现数据又恢复到了三条,那我现在希望我删除能够一直生效,就需要使用到 数据卷(volumes)的功能,它会将数据存在主机文件系统的某个区域,现在我们在项目的根目录下建立一个名为 redis 的文件夹,修改 docker-compose.yml如下:
version: '3.8'
services:
redis:
image: redis
ports:
- '6379:6379'
command: redis-server
volumes:
- ./redis:/data
nextapp:
build: .
ports:
- '4000:3000'
depends_on:
- redis
./redis:/data 是通过 : 进行分割,左边是主机的地址,右边是 容器的地址,我们会发现很多地方都是这样,左边是宿主机的信息,右边是容器的。 我们配置这个后,容器中的数据就会同步到主机,也就实现了数据持久化。 我们删除掉之前的镜像,再重新构建的镜像,因为数据做了持久化,再重复一遍刚才的操作再次打开地址的时候,数据如果是两条,那么数据卷就生效,我们动手试一下吧!
# 查看镜像
docker image ls
# 删除镜像
docker rmi next-react-demo-nextapp
# 再次查看
docker image ls
# 重新执行 docker compose
docker-compose up
去删数据:
重新启动 nextapp 容器,然后刷新 http://localhost:4000
我们删除关闭所有容器,然后重启。会看到数据依然是两条。那数据持久化就完成啦! 如果细心的小伙伴,可以看到我们主机的 Redis 目录下 多了一个 文件
这个文件是二进制文件,正是 Redis 数据的全量备份。运行 docker-compose up
的时候,redis 又会读取加载这个文件,也就实现了数据持久化。 dump.rdb 这个文件就是 Redis 的持久化机制的体现。Redis 的持久化机制有两种,
- 一种是 RDB(Redis Database),RDB 是一次快照,也是默认值
- 一种是 AOF(Append Only File)。开启 Redis 容器时的 --appendonly 参数开启那就是 AOF。
数据卷的其他命令
- 创建:
docker volume create my-vol
- 查看:
docker volume ls
- 查看数据卷对应主机位置:
docker volume inspect my-vol
在 docker-compose.yml 创建数据卷
version: '3.8'
# 创建名为 redis-data 数据卷
volumes:
redis-data:
services:
redis:
image: redis
ports:
- '6379:6379'
command: redis-server
# 挂载 到容器
volumes:
- redis-data:/data
nextapp:
build: .
ports:
- '4000:3000'
depends_on:
- redis
到这里,本节就结束了,但是这次部署,依然有两个问题?
- 打包后的 next.js 镜像很大, 我们需要优化
- 目前依然只能本地访问,我们部署是为了让所有人都可以访问,但是没有做到。
下一节,我们深度使用 Docker,解决这两个问题,到这里,我发现 Docker 命令还是很多的,如果不是经常使用,根本就记不住(ps:虽然可以使用 Docker desktop 快捷操作)。如果忘记命令,这里推荐查阅这个中文文档,非常大而全的手册:https://yeasy.gitbook.io/docker_practice,很快能搜索到想要的代码