前端程序员如何学习 Dokcer (二)?

大家好,我是阿星,上一个文章:更适合前端的 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。

本地没有问题,我们就使用 Docker 来运行我们的服务。

容器化部署

使用上个文章的知识,我们现在把 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 文件(模板文件的各种指令含义可以参考Compose 模板文件 | Docker — 从入门到实践):

      # 使用 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
      

      我为每行添加了注释,使用的时候注释记得删除掉,不然会有问题,关于 yml 文件的基本知识,参考:通义 我们还需要之前编写的 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 编译的缘由,我们需要重启 Next.js 容器查看效果,我们先按 Ctrl + C 退出 Cli 界面,然后 Ctrl + D 退出交互终端,然后执行:

        # 查看 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 快捷操作)。如果忘记命令,这里推荐查阅这个中文文档,非常大而全的手册:前言 | Docker — 从入门到实践,很快能搜索到想要的代码

            一个人可以走得更快,但一群人才能走得更远。加入 【编程导航】知识星球和我一起学习进步吧!