Docker 实践:构建 React 和 Express 项目
近期考虑了去学习如何部署自己的网站项目。根据网上的资料,决定先使用 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类型的网络是隔离的,即使是同一台宿主机上的容器也不能相互通信。...
剩余内容已隐藏