资讯专栏INFORMATION COLUMN

ThinkJS 项目构建 Docker 镜像

fuyi501 / 373人阅读

摘要:而项目代码会随着需求变更而经常变化。项目启动后一般会有两个目录会写入文件。

其实这个话题很简单,不是很想写这篇文章。不过的确还是有很多朋友在打包构建部署上存在一些问题,恰巧最近使用 Docker 部署了几个 ThinkJS 相关的项目,所以还是拿出来说说吧。需要提前说明的是本文并不是 Docker 的基础教程,默认大家都是了解 Docker 的。然后我会分享一下我觉得 ThinkJS 项目构建和部署过程中可能需要注意的点,我们先说说如何构建镜像,然后再说一下可能出现的问题。

构建镜像 基础镜像
FROM mhart/alpine-node:8.9.4

首先推荐大家基于 mhart/alpine-node 的镜像来构建,因为真的很小(20M左右)!不过因为小有很多的构建工具都不是很全,如果遇上一些安装时需要编译的模块时可能会缺少环境,这个时候你可以选择手动去安装环境,当然也可以选择使用 Docker 官方的 library/node 镜像(800M左右),为了省事推荐这种情况下还是使用后者比较好。

依赖安装
COPY package.json /animaris/package.json
RUN npm i --production --registry=https://registry.npm.taobao.org

选好了基础镜像之后下面就需要拷贝项目文件了。这里推荐大家先把 packge.json 文件 COPY 进来然后安装依赖。因为依赖的变化比较小,可以认为一段时间内这部分的镜像层都是稳定的。而项目代码会随着需求变更而经常变化。综上所述,我们选择让不变的镜像层优先打包保证镜像层的最大可复用性。

拷贝项目

一个正常的 ThinkJS 项目其实线上运行只需要以下几个文件:

app/:项目原始码文件夹,如果是未编译项目的话对应的是 src/ 目录

view/:前端模板文件夹

www/:前端静态资源文件夹

production.js:ThinkJS 启动脚本

所以只要按照顺序把这些文件 COPY 进去就可以了。

启动项目

大家平常都习惯使用 PM2 来启动 NodeJS 项目,它最大的好处就是能够帮我们守护项目进程,当进程被杀死的时候能帮我们自动重启。不过 Docker 本身就是有自动重启的特性的,所以在很多层面上 Docker 和 PM2 一块使用都有点冲突。其实因为 Docker 充当了守护的角色,我们完全可以直接使用 node production.js 命令去启动。以下是一个完整的 ThinkJS 项目 Dockerfile 示例:

FROM mhart/alpine-node:8.9.4

WORKDIR /animaris
COPY package.json /animaris/package.json
RUN npm i --production --registry=https://registry.npm.taobao.org

COPY src /animaris/src
COPY view /animaris/view
COPY www /animaris/www
COPY production.js /animaris/production.js

ENV DOCKER=true
EXPOSE 8360
CMD [ "node", "/animaris/production.js" ]

使用如下命令进行构建:

docker build -t lizheming/animaris ./Dockerfile

之后使用如下命令运行镜像,即可使用 http://localhost:8360 访问网站:

docker run -p 8360:8360 lizheming/animaris
注意事项 本地文件

使用 Docker 一定不能忘记的特性就是容器销毁后容器内的所有资源都是会被销毁的,下回会重新初始化,所以如果是需要持久化保存的应避免写到容器中,需要选择外部的持久化存储,例如 Volume 共享卷或者 S3 等相关服务。

ThinkJS 项目启动后一般会有 logs, runtime 两个目录会写入文件。其中 logs 是用来存储线上日志用的,这个最好使用共享卷的形式外载出来,方便之后排查问题。runtime 这个是 ThinkJS 运行时临时文件的存储地方,例如 cache 和 session 等。session 会话默认是使用文件类型存储的,如果使用 Docker 的话推荐选择 MySQL 等外部存储。其它的功能有相关需求也可以参考 session 服务。当然如果懒得用直接把 runtime 共享卷出来也是可以的。

还有就是如果有用户上传类的需求会上传到本地文件夹上也要记得共享出来,否则丢数据就完蛋叻!

静态资源

ThinkJS 一直主张在生产环境中使用 Nginx 来处理静态资源,这样不需要经过 Node 层直接 Server 转发性能会更高。不过这样就给镜像打包造成了一定的麻烦,因为静态资源也被打包到项目镜像里去了,而 Nginx 镜像正常是没办法跨镜像读取到文件的,所以就死解了。

在 ThinkJS 中是利用 think-resource 这个中间件来处理静态资源的访问的,然后它在线上环境的状态是 enabled: false。其实静态资源过一层 Node 并消耗不了多少资源,除非对性能有严苛要求的,我建议都可以直接把这个功能打开。这样所有的请求都统一成 Nginx 转发到 Node 镜像,解决了 Nginx 需要跨镜像读取文件的问题。具体可参考官方文档的“为什么上线后静态资源访问不了?”

module.exports = [
  ...
  {
    handle: "resource",
    enable: true // 始终开启,默认为 `enable: isDev` 表示只再开发环境下开启
  },
  ...
]

当然如果真的要在打包成一个镜像的情况下用 Nginx 处理静态资源也不是没有办法,我们可以利用共享卷来操作。Node 镜像通过将镜像文件共享卷的形式映射到本地,然后 Nginx 镜像通过共享卷的形式将之前本地映射的文件再映射到镜像中。这样通过本地的一层转发即可实现 Nginx 的跨镜像访问静态资源。

# ThinkJS 镜像
docker run 
    -v ./www:/app/www 
    -p 8360:8360 
lizheming/animaris

# Nginx 镜像
docker run 
    -v ./nginx.conf:/etc/nginx/conf.d/animaris.conf 
    -v ./www:/var/www/animaris/www 
    -p 80:80 
nginx
server {
    listen 80;
    server_name animaris.eming.li;
    root /var/www/animaris/www;

    location ~ /static/ {
        etag         on;
        expires      max;
    }
}
环境变量

平常我们是使用 development.jsproduction.js 来区分开发环境和生产环境的,然鹅这么打包之后发现都是生产环境了。这样有时候我们需要不同环境不同配置就不太好弄了。这里推荐大家使用环境变量来区分。

think.env = process.ENV.ENV;
docker run -e ENV=test -p 8360:8360 lizheming/animaris
终端日志

有用户提出疑问 #1106,说“网站500了为啥 logs 里没看到日志?”。这是因为虽然 ThinkJS 内部的日志都是用 think-logger 模块处理了,但是因为跨模块或者启动时机的问题,有一些日志没办法走日志模块记录并存储到文件,会直接打到终端里。所以当没有日志的时候我们可以考虑去 docker logs 中捞一下终端的日志,说不定有意外的惊喜。

后记

虽然文字写了很多,除了一些普适问题需要考虑一下之外,其实整体来看真个构建过程还是很简单的。同时也欢迎大家有什么问题和疑问留言交流。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/27305.html

相关文章

  • React搭建个人博客(二)consul-template+nginx+docker实现负载均衡

    摘要:前两个数据业务相关的服务即下图的,第三个项目就是的实现的负载均衡。这里后台,前台项目各启动了三个实例,用户访问的时候,就会根据配置的负载均衡的策略,访问其中一个。这一部分与之前我转发的实现服务发现及网关其实也只是差了个网关和负载均衡。 一.简介 上一篇只讲了博客的前端问题,这一篇讲一下后端的微服务搭建。项目的后端使用的thinkjs框架,在我之前的博客中已经写过,这里就不重点说明了。后...

    Coding01 评论0 收藏0
  • ThinkJS 3.0 正式版发布!

    摘要:是一款拥抱未来的框架,致力于集成项目最佳实践,规范项目让企业级团队开发变得更加简单,更加高效。中使用更优雅的解决异步问题,这个理念和是完全一致的。同时,提供一套进程之间通信的方式,如通知其他进程执行某个任务,代码在线更新后重启所有的子进程。 showImg(https://segmentfault.com/img/remote/1460000010996149); ThinkJS 是一...

    suosuopuo 评论0 收藏0
  • Jenkins集成Docker镜像实现自动发布

    摘要:集群环境的部署方式使用集群管理工具通过版本新增文件实现滚动升级容器等同于应用的部署 1. 思路&流程 Jenkins集成Docker镜像实现自动发布与Jenkins发布mavne项目思路一样总体流程 为:Jenkins 拉去远端源码 —— gitl实现应用打包 —— jenkins本地把应用程序打包进Docker镜像 ...

    defcon 评论0 收藏0
  • [手把手系列之]Docker 部署 vue 项目

    摘要:部署项目写在前面作为轻量级虚拟化技术,拥有持续集成版本控制可移植性隔离性和安全性等优势。容器可以被创建启动停止删除暂停等。重新运行应用容器直接基于镜像来启动容器,运行命令将宿主机的挂载到容器的目录上。Docker 部署 vue 项目 1.写在前面: Docker 作为轻量级虚拟化技术,拥有持续集成、版本控制、可移植性、隔离性和安全性等优势。本文使用Docker来部署一个vue的前端应用,并尽...

    VPointer 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<