最近把ezdpl在生產環境中實施了,再加上這段時間的一些修改,一并介紹一下。
再次申明:
- ezdpl不是開箱即用的,需要根據自己的應用環境定制。對初學者來說使用起來反倒困難更多、風險更大。
- 它不是一個通用的項目,更多的是提供一種思路,將繁瑣的操作變得簡單易行,而且是用最原始的上傳文件、執行腳本的方式。但是要享受簡單,首先要做艱苦的準備和測試工作。
地址: https://github.com/Panblack/ezdpl
一、說明
1、組成
1) ezdpl.sh 主腳本,批量部署用的 auto.sh 腳本
2) 應用目錄,可以包含任意個版本目錄
3) 版本目錄,含files和可選的 pre.sh、fin.sh 腳本
2、工作目錄
├── 應用A │ ├── 版本1 │ │ ├── files │ │ │ ├── etc │ │ │ ├── usr │ │ │ └── opt │ │ ├── pre.sh │ │ └── fin.sh │ ├── 版本2 │ │ └── fin.sh │ ├── 版本3 │ │ └── files ├── 應用B │ └── 版本1 │ └── files ├──ezdpl.sh └──auto.sh
?
3、流程
首先操作機需要能無密碼登錄所有目標服務器,指令 ' ssh-keygen -t rsa ; ssh-copy-id 用戶名@目標服務器 ' 。
主腳本有2/3是判斷參數和目標服務器是否合法,真正的操作部分只有三處:
1) 如果有 pre.sh 腳本,就上傳并在目標服務器執行;
2) 復制 files 目錄下的所有文件到目標服務器的根目錄 /;
3) 如果有 fin.sh 腳本,就上傳并在目標服務器執行;
最后判斷是否要重啟目標服務器。
為什么要有兩個腳本呢?很簡單,復制文件之前可能需要對系統進行配置,有些系統配置需要特定文件上傳之后。
具體怎么用?看您心情。
4、用法
./ezdpl.sh <Silent Mode Y|N> <ip address>:[port] <app/version> [reboot Y|N(N)] [username(root)]
?主腳本共 5 個參數
<Silent Mode Y|N>?? ?? Y - 靜默模式, N - 需要手工確認,只能是 Y 或 N 。
<ip address>:[port]?? ?目標服務器 IP地址:端口,如果目標服務器sshd端口不是默認的 22,就需要指明。
<app/version>?? ??? ?? 工作目錄下的應用目錄和版本,比如 orders/20151012 。
[reboot Y|N(N)]?? ??? ?執行完 fin.sh 腳本后是否重啟,一般新裝系統往往需要重啟。默認為 N,可省略。
[username(root)]?? ??? 執行遠程登錄的用戶名,默認是 root,可省略。
5、最佳實踐
* 能用文件解決的就盡量不用腳本,除非你的sed、awk、正則表達式功底深厚并且愿意承擔風險。文件的好處是準確率高,便于事先審核、事后排錯。
* 在測試環境配置一套“干凈”的系統,然后將配好的文件復制到 ezdpl 工作目錄。記住文件需要帶完整路徑,用' cp --parents '就可以了。
* 如果有軟連接則必須壓縮打包再傳輸,因為 scp 會將軟連接復制成目標文件。
* 穩定版本保持不動,用' chattr +i ' 指令保護起來,或者打包備份,防止意外修改。微小變更也要建立新的版本并加以說明,以便跟蹤變更歷史,而且利于回退。
* 累積的小變更定期合并到新的穩定版。
cp --parents 示例,我們要從服務器 webserver1 上復制出 httpd 的配置文件:
[root@webserver1:/etc/httpd]# cp -r --parents ./conf /tmp/files
?結果會得到:
/tmp/files/etc/httpd/conf/ (含conf內的文件和目錄)
?
順便說一下,要讓 CentOS 在提示符顯示全路徑,在 /root/.bash_profile 末尾添上這一行:
PS1='[\u@\h:$PWD]# '
注意大小寫、單引號并且井號后面有個空格。
再執行
source /root/.bash_profile
?
如果要讓新建用戶也具備這個特性,在 /etc/skel/.bash_profile 末尾添上這一行:
PS1='[\u@\h:$PWD]$ '
?注意要用美元符 。
二、范例
1、場景
為新裝操作系統準備的應用服務器初始化環境,“應用/版本” 為 appserver/current 。
[root@operation:/opt/ezDpl]# ll total 28 -rw-r--r-- 1 root root 304 Oct 24 18:49 auto.sh -rw-r--r-- 1 root root 3383 Oct 24 18:57 ezdpl.sh drwxr-xr-x 3 root root 4096 Oct 24 13:48 appserver drwxr-xr-x 3 root root 4096 Oct 24 13:49 loadbalance drwxr-xr-x 3 root root 4096 Oct 24 13:49 orders drwxr-xr-x 3 root root 4096 Oct 24 13:49 stocks drwxr-xr-x 3 root root 4096 Oct 24 13:49 crm
?
2、appserver目錄結構
appserver/ ├── current │ ├── files │ │ │ ├── cron.daily │ │ │ │ └── ntpsync │ │ │ ├── profile │ │ │ ├── profile.d │ │ │ │ └── colorls.sh │ │ │ ├── security │ │ │ │ └── limits.conf │ │ │ ├── skel │ │ │ │ └── .bash_profile │ │ │ ├── sysconfig │ │ │ │ └── iptables │ │ │ └── sysctl.conf │ │ ├── opt │ │ │ ├── jdk1.8.0_65 │ │ │ │ └── bin │ │ │ ├── logs │ │ │ ├── packages │ │ │ └── tomcat8 │ │ │ ├── bin │ │ │ ├── conf │ │ │ ├── lib │ │ │ ├── temp │ │ │ ├── webapps │ │ │ └── work │ │ └── usr │ │ └── local │ │ └── bin │ ├── fin.sh │ └── pre.sh └──20151012└── fin.sh
?
上述目錄結構中,etc目錄下是已經改寫好的配置文件,分別為
etc/cron.daily/ntpsync | ntpdate 0.pool.ntp.org 1.pool.ntp.org 自動獲取網絡時間 |
etc/profile | 設置java環境變量 |
etc/profile.d/colorls.sh | 用 2015-10-12 09:00 的格式顯示文件日期時間,alias ll='ls -l --color=auto --time-style=long-iso' 2>/dev/null |
etc/security/limits.conf | 修改ulimits值以適應高并發 |
etc/skel/.bash_profile | 設定新建用戶的環境 |
etc/sysconfig/iptables | 防火墻設置 |
etc/sysctl.conf | 內核參數 |
opt里面是基礎應用,比如 jdk,調整好的 tomcat 等,上傳到目標服務器的 /opt 目錄。
usr/local/bin 里面是平常使用的服務器管理腳本,上傳到目標服務器的 /usr/local/bin 目錄。
3、pre.sh 腳本
作用:為無法連接外網的服務器配置內部 yum 源(源必須事先準備好,本例中源所在服務器為10.6.1.200),安裝必要的包,備份原始配置文件。
#!/bin/bash mkdir -p /etc/yum.repos.d/temp mv /etc/yum.repos.d/* /etc/yum.repos.d/templocal_repo=" [Local] name=Local repo baseurl=http://10.6.1.200/centos6 gpgcheck=0 enabled=1 " echo -e "$local_repo" > /etc/yum.repos.d/local.reponginx_repo=" [nginx] name=nginx repo baseurl=http://10.6.1.200/nginx gpgcheck=0 enabled=1 " echo -e "$nginx_repo" > /etc/yum.repos.d/nginx.repoyum -y install telnet man vim wget zip unzip ntpdate tree gcc iptraf tcpdump bind-utils yum -y install nginx echo echo "Packages installed..." echo /bin/cp /etc/sysconfig/iptables /etc/sysconfig/iptables.`date +%Y%m%d` /bin/cp /etc/security/limits.conf /etc/security/limits.conf.`date +%Y%m%d` /bin/cp /etc/sysctl.conf /etc/sysctl.conf.`date +%Y%m%d` #....(其余文件備份指令省略)
4、fin.sh?? ?創建用戶,并成為應用程序目錄所有者
#!/bin/bash useradd operuser && echo HisPassWord | passwd --stdin operuser chown -R operuser:operuser /opt
5、auto.sh?? ?
將appserver的current版本依次部署到10.6.1.x等五臺服務器上。簡單吧?
#!/bin/bash sh ezdpl.sh Y 10.6.1.11 appserver/current Y sh ezdpl.sh Y 10.6.1.12 appserver/current Y sh ezdpl.sh Y 10.6.1.13 appserver/current Y sh ezdpl.sh Y 10.6.1.14 appserver/current Y sh ezdpl.sh Y 10.6.1.15 appserver/current Y
為了達到并行的目的,auto.sh腳本可以稍加修改,比如:
sh ezdpl.sh Y 10.6.1.11 appserver/current Y > /tmp/10.6.1.11.log & sh ezdpl.sh Y 10.6.1.12 appserver/current Y > /tmp/10.6.1.12.log & sh ezdpl.sh Y 10.6.1.13 appserver/current Y > /tmp/10.6.1.13.log & ...
?
6、變更
服務器運行一段時間后需要統一修改 operuser 的密碼,并且將該用戶加入到 developing 組。
將指令寫入 fin.sh 腳本(pre.sh 也行,因為此項變更不涉及到上傳文件,所以沒有前后之分)。
#!/bin/bash echo HisNewPassWord | passwd --stdin operuser usermod -aG developing operuser
修改auto.sh腳本并執行。
#!/bin/bash sh ezdpl.sh Y 10.6.1.11 appserver/20151012 Y > /tmp/10.6.1.11.log & sh ezdpl.sh Y 10.6.1.12 appserver/20151012 Y > /tmp/10.6.1.12.log & sh ezdpl.sh Y 10.6.1.13 appserver/20151012 Y > /tmp/10.6.1.13.log & sh ezdpl.sh Y 10.6.1.14 appserver/20151012 Y > /tmp/10.6.1.14.log & sh ezdpl.sh Y 10.6.1.15 appserver/20151012 Y > /tmp/10.6.1.15.log &
也很簡單對吧?
?
7、回退
部署了應用orders的版本20151018,但是應用出現新的BUG,要回退到版本20151012。
部署:(部署時的 pre.sh 一般要包含刪除服務器當前版本的指令)
sh ezdpl.sh Y 10.6.1.11 orders/20151018 Y > /tmp/10.6.1.11.log & sh ezdpl.sh Y 10.6.1.12 orders/20151018 Y > /tmp/10.6.1.12.log & sh ezdpl.sh Y 10.6.1.13 orders/20151018 Y > /tmp/10.6.1.13.log & ...
?回退:(相當于部署上一個版本,當然,pre.sh 腳本會先刪除有BUG的 版本20151018。還是很簡單吧?)
sh ezdpl.sh Y 10.6.1.11 orders/20151012 Y > /tmp/10.6.1.11.log & sh ezdpl.sh Y 10.6.1.12 orders/20151012 Y > /tmp/10.6.1.12.log & sh ezdpl.sh Y 10.6.1.13 orders/20151012 Y > /tmp/10.6.1.13.log & ...
?
但是,依舊強烈建議在生產環境實施之前,要做充分的測試。
三、ezdpl.sh 版本1.1
?
#!/bin/bash # https://github.com/Panblack/ezdpl # Check Parameters #echo $1 #echo $2 #echo $3 #echo if [ -n "$1" ]; then_silent=$1if [ "$_silent" != "Y" ]; thenif [ "$_silent" != "N" ]; thenecho "The first parameter must be Y or N. Exit!"exit 1fifi elseecho "silent. Usage: ./ezdpl.sh <Silent Mode Y|N> <ip address>:[port] <app/version> [reboot Y|N(N)] [username(root)]"exit 1 fiif [ -n "$2" ]; then#Detailed param check will be needed._ipaddress=$(echo $2|awk -F':' '{print $1}')_port=$(echo $2|awk -F':' '{print $2}')if [ ${#_port} -eq 0 ]; then_port="22"fi elseecho "ipaddress:port. Usage: ./ezdpl.sh <Silent Mode Y|N> <ip address>:[port] <app/version> [reboot Y|N(N)] [username(root)]"exit 1 fiif [ -n "$3" ]; then_app_version=$3 elseecho "app/version. Usage: ./ezdpl.sh <Silent Mode Y|N> <ip address>:[port] <app/version> [reboot Y|N(N)] [username(root)]"exit 1 fi# Optional parameters if [ -n "$4" ]; then_reboot=$4 else_reboot="N" fi if [ -n "$5" ]; then_username=$5 else_username="root" fi# Silent mode or not if [ "$_silent" != "Y" ]; thenechoecho "Ezdpl does things in a raw and simple way."echo "https://github.com/Panblack/ezdpl"echoecho "Will initialize a new server, or deploy apps to a certain server, or upgrade a production server."echo "Usage: ./ezdpl.sh <Silent Mode Y|N> <ip address>:[port] <app/version> [reboot Y|N(N)] [username(root)]"echo "Manually Initialize 10.1.1.1: ./ezdpl.sh N 10.1.1.1 common/current Y"echo "Silently Deploy app_a to 10.1.1.1: ./ezdpl.sh Y 10.1.1.1:22 app_a/current Y root"echo "Silently Upgrade 10.1.1.2's app_a: ./ezdpl.sh Y 10.1.1.2:2222 app_a/20150720"echo "Manually Upgrade 10.1.1.2's conf: ./ezdpl.sh N 10.1.1.2:2222 app_a/2015-10-12"echo# Confirmationread -p "Will overwrite configuration files or apps on $_ipaddress. Enter Y to continue: "if [ "$REPLY" != "Y" ]; thenecho "Exit"exit 0fi# Confirmation againread -p "Are you sure? Enter Y to continue: "if [ "$REPLY" != "Y" ]; thenecho "Exit"exit 0fi fi# Check echo "Target Server: ${_ipaddress}..." ssh -p $_port $_username@$_ipaddress uname > /dev/null if [ "$?" != "0" ]; thenechoecho "$_ipaddress is not reachable. "exit 1 fiif [ ! -d "./$_app_version" ]; thenechoecho "There is no $_app_version configured here !"exit 1 fi# Everything seems OK. Go! # Run pre.sh on the target server if [ -f "./$_app_version/pre.sh" ]; thenscp -P $_port ./$_app_version/pre.sh $_username@$_ipaddress:/tmp/ssh -p $_port $_username@$_ipaddress sh /tmp/pre.shecho "$_username@$_ipaddress:/tmp/pre.sh executed." fi# Start copy app/version/files/* if [ -d ./$_app_version/files ]; thenscp -P $_port -r ./$_app_version/files/* $_username@$_ipaddress:/echo "./$_app_version/files/* copied." fi# Run fin.sh on the target server if [ -f "./$_app_version/fin.sh" ]; thenscp -P $_port ./$_app_version/fin.sh $_username@$_ipaddress:/tmp/ssh -p $_port $_username@$_ipaddress sh /tmp/fin.shecho "$_username@$_ipaddress:/tmp/fin.sh executed." fi# Reboot target server. if [ "$_reboot" = "Y" ]; thenechoecho "Target server will reboot..."echossh -p $_port $_username@$_ipaddress reboot fi echo "Target Server: ${_ipaddress} done!"; echo # End of ezdpl.sh
?