通过以上的学习,我们已经对git非常熟悉了,可以设计一个自动化部署脚本:
q 约定:
1.已经有一个可以上线的代码在git仓库。
2.我们现在要做10个集群节点的一键部署,秒级回滚。
3.所有的web服务,都应该使用普通用户。(强烈建议)
4.所有的web服务都不应该监听80端口,除了负载均衡。
q 自动化部署思路大纲:
1.获取最新代码
2.编译(可选)
3.配置文件(软连接或者拷贝)。
4.打包(tar,加速传输)
5.文件分发(Scp Rsync Salt)(不需要密码验证)
6.将目标服务器移除集群(注释配置文件)
7.解压
8.防止webroot站点目录
9.scp差异文件(可能有一个节点配置文件不一样)
10.重启Web服务
11.测试
12.正常回退实践
13.紧急回退实践
q 1.环境说明
我这里使用1台负载均衡两台web来演示(salstack批量管理会有相关章节):
主机名 | IP地址 | 描述 | 部署服务 |
git-node | 192.168.56.11 | 部署机兼git仓库 | Gitlab |
lb-node1 | 192.168.56.100 | 负载均衡服务 | Nginx |
pre-node1 | 192.168.56.12 | Web测试节点 | Nginx+PHP |
Web-node1 | 192.168.56.13 | Web节点node1 | Nginx+PHP |
Web-node2 | 192.168.56.14 | Web节点node2 | Nginx+PHP |
q 2.创建相关用户(部署机以及web节点都需要操作)
useradd www && echo "123"|passwd --stdin www
q 3.部署机能登陆到web节点任意一台机器(部署机操作)
[www@git-node1 ~]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/www/.ssh/id_rsa):
Created directory '/home/www/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/www/.ssh/id_rsa.
Your public key has been saved in /home/www/.ssh/id_rsa.pub.
The key fingerprint is:
fc:75:01:96:47:27:d5:d4:e3:16:e7:15:3a:b1:a4:87 www@web-node1
The key's randomart image is:
+--[ RSA 2048]----+
| o=o+B|
| .=.===|
| E *o.=|
| .. .+.|
| S. o |
| . . .|
| .|
| |
| |
+-----------------+
//分发各个web节点,会提示输入密码
[www@git-node1 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub www@192.168.56.12
[www@git-node1 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub www@192.168.56.13
[www@git-node1 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub www@192.168.56.14
[www@git-node1 ~]# ssh 192.168.56.13 //测试一台
Last login: Mon Nov 7 03:45:08 2016 from 192.168.56.11
[www@web-node1 ~]$
q 4.gitlab服务部署(部署机操作)
安装配置依赖项
[root@git-node1 ~]# yum install curl openssh-server postfix
[root@git-node1 ~]# systemctl enable sshd postfix
[root@git-node1 ~]# systemctl start sshd postfix
[root@git-node1 ~]# firewall-cmd --permanent --add-service=http
[root@git-node1 ~]# systemctl reload firewalld
添加GitLab仓库,并安装到服务器上
[root@git-node1 ~]# curl -sS http://packages.gitlab.cc/install/gitlab-ce/script.rpm.sh | sudo bash
[root@git-node1 ~]# yum install gitlab-ce
启动GitLab
gitlab-ctl reconfigure
浏览到主机名和登录Browse to the hostname and login
首次访问GitLab,系统会让你重新设置管理员的密码,设置成功后会返回登录界面.
默认的管理员账号是root,如果你想更改默认管理员账号,请输入上面设置的新密码登录系统后修改帐号名.
q 5.创建项目相关目录(部署机操作)
项目代码仓库
mkdir -p /deploy/code/rainbow_pro
项目源代码仓库
mkdir -p /deploy/source/rainbow_pro
存放配置文件
mkdir -p /deploy/config/rainbow_pro/config
存放管理配置文件
mkdir -p /deploy/config/rainbow_pro/admin_config
mkdir -p /deploy/tmp
mkdir -p /deploy/logs
授权www用户
chown -R www:www /deploy
chown -R www:www /home/www/
查看目录结构
tree /deploy/
/deploy/
├── code
│ └── rainbow_pro
├── config
│ └── rainbow_pro
│ ├── admin_config
│ └── config
├── logs
├── source
│ └── rainbow_pro
└── tmp
q 6.web节点需要创建的项目目录(rainbow_pro是项目名称,如果修改了就需要修改脚本)
mkdir –p /deploy/tmp
mkdir –p /deploy/code/rainbow_pro
schown -R www.www /deploy/
q 7.负载均衡服务器配置
安装nginx
[root@lb-node1 ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@lb-node1 ~]# yum install -y nginx
配置nginx
[root@lb-node1 ~]# egrep -v '#|^$' /etc/nginx/nginx.conf.default > /etc/nginx/nginx.conf
[root@lb-node1 ~]# cat /etc/nginx/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream web_server {
server 192.168.56.13:8888;
server 192.168.56.14:8888;
}
server {
listen 80;
server_name linux.git.com;
location / {
root html;
index index.html index.htm;
proxy_pass http://web_server;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
启动负载均衡Nginx服务
[root@lb-node1 ~]# systemctl start nginx
q 8.sudo授权(所有web操作)
sed -i '98a www ALL=(ALL) NOPASSWD:ALL' /etc/sudoers
sed -i 's@Defaultsrequiretty@\#Defaultsrequiretty@g' /etc/sudoers
q 9.配置nginx(所有web操作)
安装nginx、php、php-fpm
[root@web-node1 ~]#wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@web-node1 ~]# yum install -y nginx php php-fpm
配置nginx
[root@web-node1 ~]# egrep -v '#|^$' /etc/nginx/nginx.conf.default > /etc/nginx/nginx.conf
[root@web-node1 ~]# cat /etc/nginx/nginx.conf
user www www;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8888;
server_name 192.168.56.13; #不同节点更改为不同节点IP
location / {
root /home/www/rainbow_pro;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
启动所有web节点Nginx+PHP
[root@web-node1 ~]# systemctl start nginx
[root@web-node1 ~]# systemctl start php-fpm
q 10.部署机切换www用户,添加远程项目,执行如下脚本(注意:如无法pull,请添加www用户key至gitlab)
[root@git-node1 ~]# echo "192.168.56.11 git-node1" >> /etc/hosts
[root@git-node1 ~]# su – www
//首次使用需要执行如下步骤
[www@git-node1 ~]$ cd /deploy/source/rainbow_pro/
[www@git-node1 rainbow_pro]$ git init
初始化空的 Git 版本库于 /deploy/source/rainbow_pro/.git/
[www@git-node1 rainbow_pro]$ git remote add origin git@git-node1:root/git_demo.git
[www@git-node1 rainbow_pro]$ git pull origin master
[www@git-node1 ~]$ cat deploy.sh
#!/bin/bash
#!/bin/bash
# 添加www用户,并且做好相关机器的sshkey认证(部署机能登陆到web节点任意一台机器)
# Nginx 权限必须让www用户可访问
#项目代码仓库
# mkdir -p /deploy/code/rainbow_pro
#项目源代码仓库
# mkdir -p /deploy/source/rainbow_pro
#存放配置文件
# mkdir -p /deploy/config/rainbow_pro/config
#存放管理配置文件
# mkdir -p /deploy/config/rainbow_pro/admin_config
# mkdir -p /deploy/tmp
# mkdir -p /deploy/logs
# web站点存放目录
# mkdir -p /home/www/
#授权www用户
# chown -R www:www /deploy
# chown -R www:www /home/www/
MSG(){
if [ $? -eq 0 ];then
echo "$1 OK"
else
echo "$1 FAIL"
shell_unlock;
exit 1
fi
}
# 定义代码变量
PRO_NAME="rainbow_pro"
CODE_DIR="/deploy/code/$PRO_NAME"
SOURCE_DIR="/deploy/source/$PRO_NAME"
CONFIG_DIR="/deploy/config/$PRO_NAME"
WEB_DIR="/home/www"
TMP_DIR="/deploy/tmp"
# web节点列表信息
PRE_LIST="192.168.56.12"
GROUP1_LIST="192.168.56.13 192.168.56.14"
ROLLBACK_LIST="192.168.90.13 192.168.56.14"
# Date/Time Veriables
CTIME=$(date "+%F-%H-%M")
# Shell Env
SHELL_NAME="deploy.sh"
SHELL_DIR="/home/www"
#日志定义
LOG_FILE="${SHELL_DIR}/${SHELL_NAME}".log
LOCK_FILE="/tmp/deploy.lock"
# 脚本锁
shell_lock(){
touch ${LOCK_FILE}
}
shell_unlock(){
rm -f ${LOCK_FILE}
}
# 测试URL
url_test(){
URL=$1
curl -s --head $URL |egrep '200|301|302'
}
# 日志记录
writelog(){
LOGINFO=$1
[ -f ${LOG_FILE} ] || touch ${LOG_FILE}
echo "${CTIME}: ${SHELL_NAME} : ${LOGINFO}" >> ${LOG_FILE}
}
# 获取代码
code_get(){
writelog "code_get"
cd $SOURCE_DIR && git pull origin $DEPLOY_METHOD
GIT_CID=$(git log|awk 'NR==1{print $2}'|cut -c 1-6)
PKG_VER="${CTIME}_${GIT_CID}"
PKG_NAME="${PRO_NAME}_${PKG_VER}"
cp -r ${SOURCE_DIR} ${TMP_DIR}/${PKG_NAME}
}
#代码编译过程(php没有编译过程)
code_bulid(){
echo code_bulid
}
#代码配置文件
code_config(){
writelog "code_config"
/bin/cp -r ${CONFIG_DIR}/config.php ${TMP_DIR}/${PKG_NAME}/config.php
}
#打包代码并去除.git目录
code_tar(){
writelog "code_tar"
cd ${TMP_DIR} && tar czf ${PKG_NAME}_tar.gz ${PKG_NAME} --exclude=.git --exclude=.gitignore
writelog "${PKG_NAME}_tar.gz"
}
#代码发送至各个节点
code_scp(){
writelog "code_scp"
for node in $PRE_LIST;do
scp ${TMP_DIR}/${PKG_NAME}_tar.gz $node:${TMP_DIR}/
done
for node in $GROUP1_LIST;do
scp ${TMP_DIR}/${PKG_NAME}_tar.gz $node:${TMP_DIR}/
done
}
#部署预生产环境代码
pre_deploy(){
writelog "Pre "$node" deploy code"
for node in ${PRE_LIST};do
ssh ${node} "cd ${TMP_DIR} && tar xf ${PKG_NAME}_tar.gz -C ${CODE_DIR}/"
ssh ${node} "rm -f $WEB_DIR/${PRO_NAME} && ln -s ${CODE_DIR}/${PKG_NAME} $WEB_DIR/${PRO_NAME}"
MSG "Pre "$node" deploy code"
done
}
#测试预生产环境代码
pre_test(){
for node in ${PRE_LIST};do
url_test "http://${node}:8888/index.html" >/dev/null
MSG "Pre "$node" Test_URL"
done
}
#分组部署代码
group1_deploy(){
writelog "Pro "$node" deploy code"
for node in $GROUP1_LIST;do
ssh $node "cd ${TMP_DIR} && tar xf ${PKG_NAME}_tar.gz -C ${CODE_DIR}/"
ssh $node "rm -f $WEB_DIR/${PRO_NAME} && ln -s ${CODE_DIR}/${PKG_NAME} $WEB_DIR/${PRO_NAME}"
MSG "Pro "$node" deploy code"
done
}
#分组测试代码
group1_test(){
for node in ${GROUP1_LIST};do
url_test "http://${node}:8888/index.html" >/dev/null
MSG "Pro "$node" Test_URL"
done
}
#重启php清理opcode缓存
code_reload(){
for node in $GROUP1_LIST;do
ssh $node "sudo systemctl restart php-fpm"
MSG "Pro "$node" php_fpm reload"
done
}
#列出web节点最近2天部署最新代码
rollback_list(){
for node in $GROUP1_LIST;do
ssh $node /usr/sbin/ifconfig eth0|awk 'NR==2 {print $2}'|sed -r 's#(.*)# echo "\=\=\=\1\=\=\="#g'|bash
ssh $node ls -l "$WEB_DIR"|grep "$PRO_NAME" &&\
ssh $node find "$CODE_DIR/" -maxdepth 1 -mtime -5|sed 1d|awk -F '/' '{print $5}'
ssh $node find "$CODE_DIR" -type d -name "$PRO_NAME*" -mtime +30|xargs rm -fr
done
}
rollback_fun(){
if [ -z $ROOLBACK ];then
shell_unlock;
echo "Please input rollback version" && exit;
else
for node in $ROLLBACK_LIST;do
ssh $node rm -f $WEB_DIR/${PRO_NAME} && \
ssh $node ln -s ${CODE_DIR}/$ROOLBACK $WEB_DIR/${PRO_NAME}
done
fi
}
main(){
if [ -f "$LOCK_FILE" ];then
echo "Deploy is Running" && exit;
fi
DEPLOY_METHOD="$1"
ROOLBACK="$2"
case $DEPLOY_METHOD in
deploy|master|dev) #可写多个分支
shell_lock;
code_get;
code_bulid;
code_config;
code_tar;
code_scp;
pre_deploy;
pre_test;
group1_deploy;
group1_test;
shell_unlock;
code_reload;
;;
list)
rollback_list;
;;
rollback)
shell_lock;
rollback_fun $ROLLBACK;
shell_unlock;
code_reload;
;;
*)
echo "$Usage:$0 [ branch | list | rollback ]"
esac
}
main $1 $2
部署master分支代码
[www@web-node1 ~]$ sh deploy.sh master
来自 git-node1:root/git_demo
* branch master -> FETCH_HEAD
Already up-to-date.
code_bulid
rainbow_pro_2016-11-07-03-27_194199_tar.gz 100% 2700.3KB/s 00:00
rainbow_pro_2016-11-07-03-27_194199_tar.gz 100%270 0.3KB/s 00:00
rainbow_pro_2016-11-07-03-27_194199_tar.gz 100% 2700.3KB/s 00:00
Pre 192.168.56.12 deploy code OK
Pre 192.168.56.12 Test_URL OK
Pro 192.168.56.13 deploy code OK
Pro 192.168.56.14 deploy code OK
Pro 192.168.56.13 Test_URL OK
Pro 192.168.56.14 Test_URL OK
Pro 192.168.56.13 php_fpm reload OK
Pro 192.168.56.14 php_fpm reload OK
查看当前版本
[www@web-node1 ~]$ sh deploy.sh list
===192.168.56.13===
lrwxrwxrwx 1 www www 60 11月 7 03:40 rainbow_pro -> /deploy/code/rainbow_pro/rainbow_pro_2016-11-07-03-40_194199
rainbow_pro_2016-11-07-04-10_194199
===192.168.56.14===
lrwxrwxrwx 1 www www 60 11月 7 05:09 rainbow_pro -> /deploy/code/rainbow_pro/rainbow_pro_2016-11-07-03-40_194199
rainbow_pro_2016-11-07-04-10_194199
测试访问服务器
[root@linux-node1 ~]# #echo "192.168.56.100 linux.git.com" >> /etc/hosts
[root@linux-node1 ~]# curl http://linux.git.com
hello boss
boos doubi
更新代码重新部署
[root@git-node1 demo]# echo "git + nginx + php" > index.html
[root@git-node1 demo]# git add index.html
[root@git-node1 demo]# git commit -m "new file"
[master 75c7d1f] new file
[root@git-node1 demo]# git push origin master
Counting objects: 10, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 575 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To git@git-node1:root/git_demo.git
1941990..75c7d1f master -> master
//重新部署
[www@web-node1 ~]$ sh deploy.sh master
//重新测试
[root@linux-node1 ~]# curl http://linux.git.com
git + nginx + php
执行回滚操作,回退上一个版本
[www@linux-node1 ~]$ sh deploy.sh rollback //不允许直接执行
Please input rollback version
//首先使用list查看想要回退的版本
[www@web-node1 ~]$ sh deploy.sh list
===192.168.56.11===
lrwxrwxrwx 1 www www 60 11月 7 04:17 rainbow_pro -> /deploy/code/rainbow_pro/rainbow_pro_2016-11-07-04-17_75c7d1
rainbow_pro_2016-11-07-04-10_194199
rainbow_pro_2016-11-07-04-17_75c7d1
===192.168.56.12===
lrwxrwxrwx 1 www www 60 11月 7 05:46 rainbow_pro -> /deploy/code/rainbow_pro/rainbow_pro_2016-11-07-04-17_75c7d1
rainbow_pro_2016-11-07-04-10_194199
rainbow_pro_2016-11-07-04-17_75c7d1
//回退至上一个版本
[www@web-node1 ~]$ sh deploy.sh rollback rainbow_pro_2016-11-07-04-10_194199
Pro 192.168.56.11 php_fpm reload OK
Pro 192.168.56.12 php_fpm reload OK
//再次查看,已经回退上一个版本
[root@linux-node1 ~]# curl http://linux.git.com
hello boss
boos doubi
查看代码部署日志
[www@web-node1 ~]$ cat deploy.sh.log
2016-11-07-04-10: deploy.sh : code_get
2016-11-07-04-10: deploy.sh : code_config
2016-11-07-04-10: deploy.sh : code_tar
2016-11-07-04-10: deploy.sh : rainbow_pro_2016-11-07-04-10_194199_tar.gz
2016-11-07-04-10: deploy.sh : code_scp
2016-11-07-04-10: deploy.sh : Pre 192.168.56.12 deploy code
2016-11-07-04-17: deploy.sh : Pro 192.168.56.13 deploy code
2016-11-07-04-10: deploy.sh : Pro 192.168.56.14 deploy code
2016-11-07-04-17: deploy.sh : code_get
2016-11-07-04-17: deploy.sh : code_config
2016-11-07-04-17: deploy.sh : code_tar
2016-11-07-04-17: deploy.sh : rainbow_pro_2016-11-07-04-17_75c7d1_tar.gz
2016-11-07-04-17: deploy.sh : code_scp
2016-11-07-04-17: deploy.sh : Pre 192.168.56.12 deploy code
2016-11-07-04-17: deploy.sh : Pro 192.168.56.13 deploy code
2016-11-07-04-17: deploy.sh : Pro 192.168.56.14 deploy code
徐亮伟, 江湖人称标杆徐。多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。擅长Web集群架构与自动化运维,曾负责国内某大型电商运维工作。
个人博客"[徐亮伟架构师之路](http://www.xuliangwei.com)"累计受益数万人。
笔者Q:552408925、572891887
架构师群:471443208