docker部署node服务类项目
本文在docker 用 nginx 部署静态项目基础上,继续利用 docker 环境,运行 node,部署服务器项目,从而更多理解docker
。
在网页里使用接口
上一个静态项目里,添加请求即可。接口会在服务端项目添加。
<body> 试试 <script src="https://unpkg.com/axios@0.27.2/dist/axios.min.js"></script> <script> axios .get('/api/json', { params: {}, }) .then((res) => { console.log(res); }) .catch((error) => { console.log(error); }); </script> </body>
创建服务器项目
建一个项目文件夹demo_server
,项目路径下运行npm init -y
初始化下
然后yarn add express
。
创建server.js
写接口
项目根目录下,创建server.js
。
const express = require('express'); const PORT = 8080; const HOST = '0.0.0.0'; const app = express(); app.get('/json', (req, res) => { res.json({ code: 0, data: 'This is message from node container', }); }); app.listen(PORT, HOST); console.log(`Running on http://${HOST}:${PORT}`);
拉取node
公共镜像
拉取node
公共镜像:
docker pull node
创建Dockerfile
文件
一般自定义构建镜像,是基于Dockerfile
文件构建。
项目根目录下,创建Dockerfile
文件
FROM node:latest WORKDIR /usr/src/app COPY package*.json ./ RUN npm install COPY . . EXPOSE 8080 CMD [ "npm", "start" ]
FROM
- 基于哪个镜像来实现WORKDIR
- 工作目录COPY
- 添加宿主机文件到容器里RUN
- 执行的命令EXPOSE
- 容器内应用可使用的端口CMD
- 容器启动后,所执行的程序,如果docker run
后面跟启动命令会被覆盖掉
创建.dockerignore
文件
构建镜像的时候 node_modules
的依赖直接通过 RUN npm install
来安装。 项目中创建一个 .dockerignore
文件来忽略一些直接跳过的文件:
node_modules npm-debug.log
自定义构建应用镜像
基于Dockerfile
文件,自定义构建服务器镜像:
docker build -t server_image .
-t
是给镜像命名.
是基于当前目录的Dockerfile
来构建镜像
基于镜像启动容器
用下面命令,启动容器,之后访问
docker run -p 5000:8080 -d --name server_container server_image
基于镜像,启动容器,来提供接口服务8080
端口,并映射宿主的5000
端口。
跨域转发
目前静态项目的端口是3333
,而服务的端口是5000
,这样请求就会跨域,所以需要将静态项目容器的请求转发到服务的容器上。
查看服务容器对应的 ip
查看服务容器对应的 ip:
docker inspect server_container | grep IPAddress
可以看到是172.17.0.3
,不同的可能不一样,记录这个值。
修改 nginx
配置
在静态项目的nginx/default.conf
,增加一条规则
location / { root /usr/share/nginx/html; index index.html index.htm; } # 增加这个 location /api/ { rewrite /api/(.*) /$1 break; proxy_pass http://172.17.0.3:8080; }
将 /api/{path}
转到目标服务的 /{path}
接口上,这里的172.17.0.3
就是服务容器对应的 IP。
配置负载均衡
后端服务一般都是双机或者多机以确保服务的稳定性。
我们可以再启动一个后端服务容器,并修改 nginx 的配置,来优化资源利用率,最大化吞吐量,减少延迟,确保容错配置。
先再启动一个服务容器,记录下其 IP
# 容器名和宿主的端口号 需要重命名 docker run -p 5001:8080 -d --name server_container2 server_image # 得到容器的IP,我这里是172.17.0.4 docker inspect server_container2 | grep IPAddress
修改一下 静态项目的nginx/default.conf
(新增 upstream
,修改 location /api/
中的 proxy_pass
)
server { # ... location /api/ { rewrite /api/(.*) /$1 break; # !!!修改这里 proxy_pass http://backend; } ; ... } # !!!upstream要与server同级 upstream backend { server 172.17.0.3:8080; server 172.17.0.4:8080; }
重启下静态项目的容器docker restart web_container
测试下 — 挂掉其中一个服务容器
docker stop server_container
刷新网页http://localhost:3333/,依旧正常~
启动容器失败 - 怎么查看原因
启动容器之后,docker ps
却没发现相应的容器,可以通过logs
诊断
# 比如 docker logs web_container docker logs [容器名 或者 容器id]
写负载均衡的时候,就碰到这种问题,原来我把upstream
写在server
内部了,logs就提示错误信息,然后根据错误提示google了下,因此找到原因~~
docker部署egg项目
成熟的项目,一般都会有框架,docker部署node服务项目,大同小异,这边示例下egg项目的部署
1. 项目新增Dockerfile文件
项目根目录下加Dockerfile
文件:
# 设置基础镜像,如果本地没有该镜像,会从Docker.io服务器pull镜像 FROM node:latest # 配置环境变量 ENV NODE_ENV production # 这个是容器中的文件目录 RUN mkdir -p /usr/src/app # 设置工作目录 WORKDIR /usr/src/app # 拷贝package.json文件到工作目录 # !!重要:package.json需要单独添加。 # Docker在构建镜像的时候,是一层一层构建的,仅当这一层有变化时,重新构建对应的层。 # 如果package.json和源代码一起添加到镜像,则每次修改源码都需要重新安装npm模块,这样木有必要。 # 所以,正确的顺序是: 添加package.json;安装npm模块;添加源代码。 COPY package.json /usr/src/app/package.json # 安装npm依赖(使用淘宝的镜像源) # 如果使用的境外服务器,无需使用淘宝的镜像源,即改为`RUN npm i`。 RUN npm i --production --registry=https://registry.npm.taobao.org # 拷贝所有源代码到工作目 COPY . /usr/src/app # 暴露容器端口 EXPOSE 9000 CMD npm start
2. 建立镜像,启动容器即可
之后建立镜像,然后启动容器即可
docker build -t egg_image ./ docker run -itd --net=host --name egg_container -p 8000:8000 egg_image
这样就可以访问http://localhost:8000
再次理解docker容器
- 镜像就像是一个安装包,每个docker容器类似新一个电脑下载了安装包然后启动
- 每个容器既然都是新电脑,那么就拥有自己独立的空间、网络。