昨天参加了一个技术沙龙,收获蛮多,在这里记录下其中的一个半自动化部署化工具Mina的使用方法。

Why

考虑到项目所处的阶段或者项目的规模,很多项目并没有精力和成本使用一套完善的CI/CD系统,但手动部署又过于琐碎和重复,简单的部署使用Shell脚本就够了,但Shell脚本的可读性差,处理复杂的场景也有点力不从心,比如管理脚本多个版本等。Mina使用Ruby实现DSL。相比起Shell,Mina更加容易阅读和维护,在运行时,mina会把Ruby文件转换成Bash脚本,运行速度也有保障。当然,没有最好的工具,只有适合自己的工具。

安装使用

官方的栗子已经很详细了,这里简单解析下使用MIna部署Vue的前端项目流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
require "mina/git"

# Basic settings:
# repository branch needed by mina/git

set :application_name, "foobar"
set :domain, "foobar.com"
set :deploy_to, "/var/www/foobar.com"
set :repository, "[email protected]:my/vue-project.git"
set :branch, "master"
set :image, "hub.yourdocker.com:latest"

# Optional settings:
set :user, "foobar" # Username in the server to SSH to.
set :port, "30000" # SSH port number.
set :forward_agent, true # SSH forward_agent.

# Shared dirs and files will be symlinked into the app-folder by the 'deploy:link_shared_paths' step.
# Some plugins already add folders to shared_dirs like `mina/rails` add `public/assets`, `vendor/bundle` and many more
# run `mina -d` to see all folders and files already included in `shared_dirs` and `shared_files`
# 共享node_modules 节约空间
set :shared_dirs, fetch(:shared_dirs, []).push("node_modules")
# set :shared_files, fetch(:shared_files, []).push('config/database.yml', 'config/secrets.yml')

# This task is the environment that is loaded for all remote run commands, such as
# `mina deploy` or `mina rake`.
task :remote_environment do
# 可以设置NodeJS 的版本、安装yarn等操作
# If you're using rbenv, use this to load the rbenv environment.
# Be sure to commit your .ruby-version or .rbenv-version to your repository.
# invoke :'rbenv:load'

# For those using RVM, use this to load an RVM version@gemset.
# invoke :'rvm:use', 'ruby-1.9.3-p125@default'
end

# Put any custom commands you need to run at setup
# All paths in `shared_dirs` and `shared_paths` will be created on their own.
task :setup do
# 安装Node.Js 的版本
# command %{rbenv install 2.3.0 --skip-existing}
end

desc "Deploys the current version to the server."
task :deploy do
# uncomment this line to make sure you pushed your local branch to the remote origin
invoke :'git:ensure_pushed'
deploy do
# Put things that will set up an empty directory into a fully set-up
# instance of your project.
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
command %{yarn install}
invoke :build
invoke :'deploy:cleanup'

on :launch do
in_path(fetch(:current_path)) do
command %{mkdir -p tmp/}
command %{touch tmp/restart.txt}
command %{docker run #{fetch :image}} # can also use compose pull
end
end
end

# you can use `run :local` to run tasks on local machine before of after the deploy scripts
# run(:local){ say 'done' }
end
# 构建 docker镜像
task :build do
command %(docker build -t #{fetch :image} .)
command %(docker push #{fetch :image})
end

# For help in making your deploy script, see the Mina documentation:
#
# - https://github.com/mina-deploy/mina/tree/master/docs

前端项目的Dockerfile

1
2
3
4
5
6
FROM nginx:latest

ADD dist /usr/share/nginx/html

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

在官方文档How Mina Works?中介绍了Mina在server上的目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.
├── current -> /home/deploy/www/example.com/releases/91
├── releases
│ ├── 86
│ ├── 87
│ ├── 88
│ ├── 89
│ ├── 90
│ └── 91
├── scm
├── shared
│ ├── bundle
│ ├── config
│ ├── log
│ ├── public
│ ├── tmp
│ └── vendor
└── tmp

其中current是指向当前版本的软连接;releases记录了每次部署的源码,为线上回退版本提供了方便,scm记录版本控制的信息,GIt就是项目下.git目录,shared包含在发布之间保存的文件夹和文件(日志、上传、配置,…) ), tmp用来放一些构建产生的临时文件。

Deploying描述了部署过程详细解析。

总结

使用下来,感觉mina对于脚本语言的项目管理考虑的十分到位,虽然JavaScript也有类似的工具,比如Shipit,但个人更喜欢Ruby的实现DSL的表达力。