和@Leniy 说到利用git自动部署的事情,@Leniy 问了句,怎么配,既然有人有兴趣,我就扯几句。 git无疑是一个很赞的版本管理系统,简单易用不伤手。而如果在VPS上部署一个git,每次提交后,直接部署到环境中,不用ssh登进去顶着XXXms的延迟穷倒腾。 说明一下,我只是举个栗子,实现了"自动部署"的要求,看官可以做的远不止这些。

首先,环境要求:

  1. 要求客户端和服务端都有个git环境。
  2. 要求能push东西到服务端
  3. 要求能修改服务端的环境--ftp环境和ssh环境都可以接受其实,不过安全起见,最好让git的目录是web根目录的父目录

其次,原理:

git 的自动部署利用的是hooks (中文版),还可以参考这个.

简单说就是在服务端的hooks的文件夹中,有若干文件,在你执行各种操作的时候,会触发特定的bash,然后git会执行这些bash。

在GIT_DIR/hooks/中有各种xxx.sample文件,看官若有兴趣,可以研究下人家怎么写的。

因为是测试,我都在本地写,但其实一个是客户端,一个是服务端,两边都有操作。看官需要稍稍分辨下是在哪端。

好,废话不说,介绍个完整的耍耍。

1. 初始化服务端git

我在服务端~/目录下建立repo文件夹,初始化一个test.git

[yu@argcv-com ~]$ cd ~
[yu@argcv-com ~]$ mkdir repo
[yu@argcv-com ~]$ cd repo/
[yu@argcv-com repo]$ git init --bare test.git
Initialized empty Git repository in /home/yu/repo/test.git/
[yu@argcv-com repo]$ 

2. 初始化客户端

我在/tmp/client下建立个客户端

[yu@argcv-com ~]$ cd /tmp/
[yu@argcv-com tmp]$ mkdir client
[yu@argcv-com tmp]$ cd client/
[yu@argcv-com client]$ git init
Initialized empty Git repository in /tmp/client/.git/
[yu@argcv-com client]$ git remote add origin yu@localhost:repo/test.git
[yu@argcv-com client]$ touch README.md
[yu@argcv-com client]$ git add .
[yu@argcv-com client]$ git commit -m "init"
[master (root-commit) 00da9a9] init
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md
[yu@argcv-com client]$ git push origin --all 
Counting objects: 3, done.
Writing objects: 100% (3/3), 210 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To yu@localhost:repo/test.git
 * [new branch]      master -> master
[yu@argcv-com client]$ 

3. 初始化服务端待部署的环境

服务端部署在/tmp/deploy/下

[yu@argcv-com ~]$ cd /tmp/
[yu@argcv-com tmp]$ mkdir deploy
[yu@argcv-com tmp]$ cd deploy/
[yu@argcv-com deploy]$ git clone ~/repo/test.git .
Cloning into '.'...
done.

特别提醒下,此时在/tmp/deploy下有隐藏文件夹.git

安全起见,不要让你的web的/目录和它在同一层,而应该让它成为/tmp/deploy的一个子目录

4. 服务端添加hooks

刚才说过,服务端的git在 ~/repo/test.git/文件夹下

添加文件~/repo/test.git/hooks/post-receive,做出部署

注意,hooks下有好几个触发器,分别对应在不同阶段的时候采取的动作。

比如update,对应的其实是在更新前的动作,这个bash可以用来做backup,而如果用它来写自动部署,那么它每次部署都会慢一拍。。。这会很囧的

[yu@argcv-com repo]$ pwd
/home/yu/repo
[yu@argcv-com repo]$ cd test.git/
[yu@argcv-com test.git]$ cd hooks/
[yu@argcv-com hooks]$ vi post-receive
[yu@argcv-com hooks]$ cat post-receive
#!/bin/sh
unset GIT_DIR
NowPath=`pwd`
echo "now path is :"$NowPath
DeployPath="/tmp/deploy"
echo "deploy path is :"$DeployPath
cd $DeployPath
echo "cd deploy path"
git add . -A && git stash # remove local changes 
git pull origin master # pull data from master
# the follow line is also ok:
# git add . && git fetch origin && git reset --hard origin/master
echo "deploy done"
cd $NowPath
echo "fine"
# --- Finished
exit 0
[yu@argcv-com hooks]$ chmod +x post-receive
[yu@argcv-com hooks]$ 

hook其实就是个bash,不言自明,到待部署的目录,执行 git pull或者fetch && reset ,完成部署。之所以要"git add . -A"是因为你可能写一些untracked的文件,而这些文件若没有被add,那么版本控制可能无法对它进行修改。

当然,你也可以执行backup之类的动作。

注意和普通bash一样,加上可执行。

和Makefile一样,在每一行,目录都是固定的,需要解除环境变量GIT_DIR环境变量才能自由移动当前位置。

当然,其实我也可以用

(cd $DeployPath && git add . -A && git stash && git pull origin master )

来替换,如果比较简单的话。

5. 测试

首先,客户端添加文件,加入commit,并push

[yu@argcv-com client]$ touch newfile
[yu@argcv-com client]$ git add .
[yu@argcv-com client]$ git commit -m "add new file"
[master 68efdcf] add new file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 newfile
[yu@argcv-com client]$ git push origin
Counting objects: 4, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 384 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: now path is :/home/yu/repo/test.git
remote: deploy path is :/tmp/deploy
remote: cd deploy path
remote: From /home/yu/repo/test
remote:    6627165..5ac80ec  master     -> origin/master
remote: Updating 6627165..5ac80ec
remote: Fast-forward
remote:  newfile | 0
remote:  1 file changed, 0 insertions(+), 0 deletions(-)
remote:  create mode 100644 newfile
remote: deploy done
remote: fine
To yu@localhost:repo/test.git
   b3a32f2..5ac80ec  master -> master
[yu@argcv-com client]$ 

我们可以看到remote端提示

再查看服务端deploy的文件

[yu@argcv-com deploy]$ ls -al
total 0
drwxrwxr-x  3 yu   yu   100 Apr 26 17:29 .
drwxrwxrwt 22 root root 460 Apr 26 17:29 ..
drwxrwxr-x  8 yu   yu   280 Apr 26 17:29 .git
-rw-rw-r--  1 yu   yu     0 Apr 26 17:29 newfile
-rw-rw-r--  1 yu   yu     0 Apr 26 17:13 README.md

基本原理就是这样了。

6. 相关介绍

  • 用户权限问题

在实际使用的时候,有人反馈说会有 Permission Denied 之类的事情。

这种事情大多发生在你 Web Service 使用的用户和 git push 的用户不一致的情况下。一般解决思路是这样的。

首先把 git 所在用户加入 web 用户组, 比如命令

sudo usermod -a -G web git

若你把目标目录放在了 /var/www/html/your_git 下, 那么你要检查下 /var, /var/www, /var/www/your_git 三个目录的权限是否至少开到了 770 上. 然后还要考虑是否有 SELinux 在挡道, 一般修改权限后 sudo restorecon -R /var/www 下就可以了.

你也可以尝试以 git 的身份查看下 web 目录, 命令是

sudo -u git > /var/www/html/your_git/test_file

这个命令会尝试以 git 的身份在 /var/www/html/your_git/ 的目录下写个名为 test_file 的空文件.

  • 控制更新频率

一个比较好的实践是开多个 branch. 比如平时是 push 到远程的 master branch, 那么你可以开个名为 publish 的 branch. 前面的脚本改成使用 "origin/publish".

那么你执行

git push origin master

远程不会有任何更新. 而你执行

git push origin master:publish

那么远程的 publish 这时候会更新, 然后 hook 会把 publish 的最新结果放到运行环境.

来自的你,很高兴你能看到这儿。若本文对你有所用处,或者内容有什么不足之处,敬请毫不犹豫给个回复。谢谢!