前端程序员学习 Docker 的笔记 (二)

5 分钟
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 项目 容器化部署

  1. 拉取 Redis 镜像:docker pull redis
  2. 关闭本地命令行启动的 Redis ,使用 Docker 启动 Redis:docker run -p 6379:6379 redis redis-server 关闭本地的Redis 后,我们的项目刷新是会报错的: 使用 Docker 启动 Redis 后,就恢复了
  3. 我们完成了 Redis 容器化,我们现在把 Next.js 项目也新开一个容器进行部署,但是每个容器都是一个单独的隔离空间。这时候即将容器化部署的 Next.js 服务就访问不到我们的 Redis 服务。那么我们就需要将两个容器服务连接起来,Docker 可以将容器加入自定义的 Docker 网络的方式来连接多个容器。

Docker 支持自定义网络,这里我们使用桥接网络来连接多个容器,容器间就可以通信了!

  1. 创建一个 自定义 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 .创建镜像

  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 提供的一种用于在容器和主机之间共享数据的机制。数据卷可以独立于容器的生命周期存在,这意味着即使容器删除了,数据卷中的数据也不会丢失。它的特点:

  1. 持久性:数据卷的数据会一直保留,直到明确删除,即使容器删除,数据仍然存在。
  2. 共享和重用:数据卷可以在多个容器之间共享和重用。
  3. 备份和恢复:可以轻松地对数据卷进行备份和恢复操作。
  4. 高效:数据卷的性能优于直接在容器内存储数据。

上面部署的项目,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

到这里,本节就结束了,但是这次部署,依然有两个问题?

  1. 打包后的 next.js 镜像很大, 我们需要优化
  2. 目前依然只能本地访问,我们部署是为了让所有人都可以访问,但是没有做到。

下一节,我们深度使用 Docker,解决这两个问题,到这里,我发现 Docker 命令还是很多的,如果不是经常使用,根本就记不住(ps:虽然可以使用 Docker desktop 快捷操作)。如果忘记命令,这里推荐查阅这个中文文档,非常大而全的手册:https://yeasy.gitbook.io/docker_practice,很快能搜索到想要的代码