通過以上的學習,我們已經對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 [email protected]
[www@git-node1 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]
[www@git-node1 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]
[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