Cytrogen 的个人博客

万圣节恶魔的领地

马上订阅 Cytrogen 的个人博客 RSS 更新: https://cytrogen.icu/atom.xml

Docker 实践:构建 React 和 Express 项目

2024年3月1日 14:11

近期考虑了去学习如何部署自己的网站项目。根据网上的资料,决定先使用 Docker + Nginx 的组合来部署到本地上,之后再考虑部署到云端。

项目结构

我的项目前端是 React,后端是 Express、使用了 Socket.io 来实现实时通信、使用了 MongoDB 来存储数据。

Docker 容器的话需要为每个服务创建一个容器,所以我需要创建三个容器:前端、后端、数据库。同时还要创建一个 Nginx 容器来作为反向代理。

Nginx 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。

那么反向代理是什么意思呢?通常情况下我们如果访问一个网站,浏览器会直接向服务器发送请求,服务器再返回数据给浏览器。而反向代理是指,浏览器发送请求给 Nginx,Nginx 再将请求转发给服务器,服务器返回数据给 Nginx,Nginx 再返回数据给浏览器。

这么做的目的是为了隐藏服务器的真实 IP 地址,提高安全性。因为用户只能向 Nginx 发送请求,而不能直接向服务器发送请求。

Dockerfile

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

为什么要使用 Docker 呢?因为 Docker 可以让开发者摆脱「在我的机器上可以运行」的问题。

应用能够在任何地方运行,而不用担心环境问题。这样就可以避免因为环境问题导致的 bug,也可以避免因为环境问题导致的部署问题。

Docker 的两个重要概念为镜像和容器。

镜像是一个只读的模板,可以想象为一个菜谱、详细列出了如何制作一道菜的步骤。就像你无法在菜谱上做菜一样,你也无法在镜像上做任何操作。

容器是镜像的一个实例,可以想象为一道菜。Docker(厨师)会根据镜像(菜谱)制作出容器(菜),并且可以对容器进行操作。

Dockerfile 则是编写菜谱的过程。它是一个文本文件,包含了一条条的指令,每一条指令构建一层,从而构建出一个完整的镜像。

每个服务都需要一个 Dockerfile 来构建镜像。我的项目结构中暂且只有前端和后端,所以我需要创建两个 Dockerfile、存放在这两个目录下。

# client/Dockerfile# 使用node:20.11.0-alpine作为基础镜像# alpine代表着这是一个轻量级的镜像、体积更小FROM node:20.11.0-alpine# 设置工作目录# 工作目录是容器中的一个目录,用来存放项目文件WORKDIR /app# 复制package.json到工作目录COPY package.json .# 安装依赖RUN npm install# 复制所有文件到工作目录COPY . .# 启动项目CMD ["npm", "start"]
FROM node:20.11.0-alpineWORKDIR /appCOPY package.json .RUN npm installCOPY . .# EXPOSE指令通知了Docker、容器在运行时监听的端口# 这个指令并不会让容器的端口映射到宿主机的端口,如果需要映射,还需要在运行容器时使用-p参数EXPOSE 4000CMD ["node", "./bin/www"]

宿主机是指安装了 Docker 的机器,也就是我们的电脑。

Docker Compose

Docker Compose 是一个用来定义和运行多容器 Docker 应用的工具。通过一个单独的 docker-compose.yml 配置文件来配置应用的服务,然后使用 docker-compose up 命令来从配置文件中构建、启动、管理整个应用。

因为我需要创建多个容器,所以我需要一个 docker-compose.yml 文件来更好地管理这些容器。

在整个项目的根目录下创建一个 docker-compose.yml 文件:

# docker-compose.ymlversion: '3'# 定义服务services:  # 定义nginx服务  nginx:    image: nginx:alpine    ports: # 将容器的80端口映射到宿主机的80端口      - "80:80"    depends_on: # 依赖于client和server服务      - client      - server    volumes: # 将宿主机的nginx.conf文件映射到容器的/etc/nginx/conf.d/default.conf文件      # 这里的nginx.conf文件之后会提到      - ./nginx.conf:/etc/nginx/conf.d/default.conf    networks: # 将nginx服务加入到app-network网络中      - app-network  client:    build: ./client # 使用client目录下的Dockerfile构建镜像    ports:      - "3000:3000"    networks:      - app-network  server:    build: ./server    ports:      - "4000:4000"    networks:      - app-network  # 定义mongodb服务  mongodb:    image: mongo    ports: # 将容器的27017端口映射到宿主机的28017端口,之后会提到为什么端口号不一样      - "28017:27017"    volumes: # 将mongodb_data卷挂载到/data/db目录      - mongodb_data:/data/db    networks:      - app-networkvolumes: # 定义mongodb_data卷  mongodb_data:networks:  app-network: # 定义app-network网络    driver: bridge

卷是一种数据持久化和数据共享的机制。它可以将宿主机的目录挂载到容器中,这样容器中的数据就可以持久化到宿主机上了。 即使容器被删除,宿主机上的数据也不会丢失。

网络定义了容器之间如何相互通信。每个网络都代表了一个独立的虚拟网络,容器可以连接到这个网络上,从而实现容器之间的通信。bridge 类型会给容器分配一个 IP 地址,这样容器之间就可以通过 IP 地址相互通信。不同 bridge 类型的网络是隔离的,即使是同一台宿主机上的容器也不能相互通信。...

剩余内容已隐藏

查看完整文章以阅读更多