ezdpl是easy deployment的简写,使用简单的ssh和shell脚本来部署、升级、回滚和重新配置linux服务器。

重要提示:
警告:这个项目还处于测试过程中,请仔细阅读说明,并且自己承担可能带来的风险。
最佳实践:根据自己的生产环境修改脚本,部署之前需要充分测试。

最新版本请关注我的github https://github.com/Panblack/ezdpl

为什么要写ezdpl?
现在很流行使用puppet之类的工具进行自动化的系统配置。puppet方便、高效而且可以在实际配置之前“预演”,日常工作可以简化为编写puppet脚本,剩下的让puppet自己去忙活就行了,可以轻松管理成百台的服务器。

不过有人就是对这个东西不感冒,也许没有那么多服务器,也许学习另一套“系统”来管理手头的系统是个负担。我就是这种情况,而且,我喜欢用“原始、简单”的方法,不要什么代理、插件、模块、剧本什么的。我必须始终知道服务器具体是怎么配置的,配置文件是咋写的。如果习惯了puppet之类的工具,哪天碰上没有这些工具的环境怎么干活呢?这可不是件令人欣慰的事情。

但是,自动化管理确实很必要。不用puppet之类的工具怎么管理一大堆服务器呢?对了,用shell脚本。只需要一台操作机(或者叫跳板机),保存着初始化或者升级系统用的脚本、配置文件和应用,所有文件都是“原始”形态,甚至目录结构都跟生产服务器一样。操作机具有对目标服务器的root免密码登录权限以便自动运行,所有工作仅仅需要一个脚本。

“等一等,哥们,ansible就是这么做的。你这不是重复造轮子吗?不明智!”你笑了吧?

没错,我是在重复造轮子,不过是个更简单的轮子,只为了好玩。而且呢,我不需要担心忘掉指令啊脚本啊这些“终极武器”,这很让人欣慰啊,呵呵呵。

用原始、简单的方式工作
ezdpl非常非常简单,仅仅用到如下技术:
* 精心组织的目录和文件(最关键的其实在这儿)
* scp(比如 'scp -r 目录 root@目标服务器:/ ')
* ssh(比如 'ssh root@目标服务器 指令 ')

基本的目录结构,有三个级别
级别0:ezdpl文件
级别1:应用名
级别2:版本
对某个应用服务器的任何变更或更新,都在“版本”级别建立一个新的目录来实现。
如果需要回滚,只需要在脚本参数里指定上一版本即可。

开始的设想比现在复杂得多,目录层次也很乱。现在的方案实实在在的说明了简单就是美。

场景:
* 所有服务器(操作机,目标服务器)都安装了 Centos6 x86_64
* 目标服务器仅配置了IP地址和主机名
* 操作机具有对所有目标服务器的root免密码登录权限,如果没有,则脚本运行时需要输入密码
* ezdpl部署在操作机 /home/ezdpl目录
* 操作机的ssh密钥最好有passphrase密码保护
* 所有目标服务器的应用都部署在/opt

置备应用目录
应用需要的目录和文件可以从头手工建立,或者从当前的生产服务器复制。以下指令会很有帮助:

[root@java_c-server /] mkdir -p /tmp/java_c
[root@java_c-server /] /bin/cp -r --parents /etc/logrotate.d/java_c /tmp/java_c
[root@java_c-server /] /bin/cp -r --parents /home/operuser/bin /tmp/java_c
[root@java_c-server /] /bin/cp -r --parents /opt/java_c /tmp/java_c
[root@java_c-server /] find /opt/logs/ -type d -exec mkdir -p /tmp/java_c/{} \;
[root@java_c-server /] scp -r /tmp/java_c/* root@operation-server:/home/ezdpl/apps/java_c/current/

说明:
ezdpl需要按照文件原始的目录结构保存每个文件,这样才能保证复制到目标服务器正确的路径下。cp -r --parent 可以带着父目录一起拷贝文件,正好满足这个需求。
有些生产环境需要准备一些空的目录结构,而生产服务器的目录里已经有了文件。上述的find 指令就可以从生产服务器里仅复制目录结构,忽略文件。

目录结构范例
级别 0,1

目录               描述
-------------- ------------------------------------------------
ezdpl
├── apps       [级别 ]
│ ├── common [级别 , common并不是真正的应用,只是所有服务器都需要的脚本和配置文件]
│ ├── web_a  [级别 , tomcat webapp a, 需要部署一台或多台]
│ ├── web_b  [级别 , tomcat webapp b, 需要部署一台或多台]
│ └── java_c   [级别 , java app c, 需要部署三台,每台需要配置多个IP地址]
├── ezdpl.sh   [级别 , 主脚本]
├── ezdpl_auto.sh [级别 , 主脚本, 静默模式]
└── README     [级别 , 不用解释吧? ;)]

级别 2

目录                                描述
------------------------------- ------------------------------------------------
common/
├──        [级别2, 版本20150720(暂时空)]
└── current         [级别2, 当前版本]
├── etc
│ ├── cron.daily
│ │ └── ntp_client.sh [ntp 时间同步脚本]
│ └── sysconfig
│ ├── iptables
│ └── static-routes
├── runme.sh        [初始化脚本]
└── tmp         [需要独立安装的软件包]
└── jdk-7u75-linux-x64.rpm web_a/
├──        [级别2, 版本20150406]
│ └── opt
│ └── tomcat-web_a
│ └── webapps [tomcat webapps]
└── current         [级别2, current version]
├── etc
│ └── logrotate.d
│ └── web_a
├── opt
│ ├── logs
│ │ └── web_a   [web_a 的日志目录(在tomcat-web_a/conf/logging.properties里设置]
│ └── tomcat-web_a
│ ├── bin
│ ├── conf
│ ├── lib
│ ├── LICENSE
│ ├── NOTICE
│ ├── RELEASE-NOTES
│ ├── RUNNING.txt
│ ├── temp
│ ├── webapps
│ └── work
└── root
└── bin   [web_a的管理脚本]
├── showlog
├── shutdown_web_a
└── start_web_a web_b/
(ommited) java_c/
├── current
│ ├── etc
│ │ └── logrotate.d
│ │ └── java_c
│ ├── home
│ │ └── operuser [java_c应用需要以普通用户执行]
│ │ └── bin  [java_c的管理脚本]
│ │ ├── showlog
│ │ ├── shutdown_java_c
│ │ └── start_java_c
│ ├── opt
│ │ ├── logs
│ │ │ └── java_c
│ │ └── java_c
│ │ ├── conf
│ │ ├── lib
│ │ ├── output
│ │ └── java_c.jar
│ └── runme.sh
├── java_c1
│ ├── etc
│ │ └── sysconfig
│ │ └── network-scripts [java_c 第一台服务器的ip配置文件若干]
│ └── runme.sh
├── java_c2
│ ├── etc
│ │ └── sysconfig
│ │ └── network-scripts
│ └── runme.sh
└── java_c3
├── etc
│ └── sysconfig
│ └── network-scripts
└── runme.sh

主脚本 ezdpl.sh 只需要做以下工作:
* 复制指定目录下的所有文件到目标服务器,比如 ./apps/app_name/version
* 远程执行初始化脚本,比如 ./apps/app_name/version/runme.sh
* 可以指定目标服务器的用户名,默认是root
* 可根据需要远程重启目标服务器,默认不重启

ezdpl_auto.sh 跟ezdpl.sh 几乎一样,只是去掉了交互确认部分,适合批量部署。

主脚本:
ezdpl/ezdpl.sh

#!/bin/bash
echo
echo "ezdpl does things in a raw and simple way."
echo "https://github.com/Panblack/ezdpl"
echo
echo "Will initialize a new target server."
echo "Or deploy an app to the target server."
echo "Or upgrade a running production server."
echo "Usage: ./ezdpl.sh <ip address> <app/version> [reboot Y/N(N)] [username(root)]"
echo "Init 10.1.1.1: ./ezdpl.sh 10.1.1.1 common/current"
echo "Deploy web_a to 10.1.1.1: ./ezdpl.sh 10.1.1.1 web_a/current Y root"
echo "Upgrade 10.1.1.2's app: ./ezdpl.sh 10.1.1.2 java_c/20150720 N"
echo "Upgrade 10.1.1.2's conf: ./ezdpl.sh 10.1.1.2 java_c/java_c2 N"
echo # Confirmation
read -p "Will overwrite configuration files or app on $1. Enter Y to continue: "
if [ "$REPLY" != "Y" ]; then
echo "Exit"
exit
fi
read -p "Are you sure? Enter Y to continue: "
if [ "$REPLY" != "Y" ]; then
echo "Exit"
exit
fi # variables
_ipaddress=$
_app_version=$
if [ -n "$3" ]; then
_reboot=$
fi
if [ -n "$4" ]; then
_username=$
else
_username="root"
fi # Check
if [ ! -d "./apps/$_app_version" ]; then
echo
echo "There is no $_app_version configured here !"
exit
fi chkaccess=`ssh $_username@$_ipaddress ls -d /opt`
if [ ! -n "$chkaccess" ]; then
echo
echo "$_ipaddress is not reachable. "
exit
fi # Start copy app/version
scp -r ./apps/$_app_version/* $_username@$_ipaddress:/
echo "./apps/$_app_version/* copied." # Run runme.sh on the target server
if [ -f "./apps/$_app_version/runme.sh" ]; then
ssh $_username@$_ipaddress sh /runme.sh
echo "$_username@$_ipaddress:/runme.sh executed."
#ssh $_username@$_ipaddress /bin/rm /runme.sh
#echo "$_username@$_ipaddress:/runme.sh deleted."
fi # Reboot target server.
if [ "$_reboot" = "Y" ]; then
echo
echo "Target server will reboot..."
echo
ssh $_username@$_ipaddress reboot
fi

runme.sh 范例
ezdpl/apps/common/current/runme.sh

#!/bin/bash
#make it your script
#set -e # /etc/profile
/bin/cp /etc/profile /etc/profile.bak
# Turn off mail check
chkmailcheck=$(cat /etc/profile |grep "unset MAILCHECK"|grep -v "#")
if [ ! -n "$chkmailcheck" ]; then
echo "unset MAILCHECK" >> /etc/profile
fi
# make vim default
chkvim=$(cat /etc/profile |grep "alias vi='vim'"|grep -v "#")
if [ ! -n "$chkvim" ]; then
echo "alias vi='vim'" >> /etc/profile
fi
# set LANG
chklang=$(cat /etc/profile |grep "export LANG=en_US.UTF-8"|grep -v "#")
if [ ! -n "$chklang" ]; then
echo "export LANG=en_US.UTF-8" >> /etc/profile
fi
echo
echo "/etc/profile modified."
echo # ll with long-iso date format
/bin/cp /etc/profile.d/colorls.sh /etc/profile.d/colorls.sh.bak
chkll=$(cat /etc/profile.d/colorls.sh |grep "alias ll='ls -l --color=auto --time-style=long-iso'"|grep -v "#")
if [ ! -n "$chkll" ]; then
echo "alias ll='ls -l --color=auto --time-style=long-iso' 2>/dev/null" >> /etc/profile.d/colorls.sh
fi
echo
echo "/etc/profile.d/colorls.sh modified."
echo # Selinux
sed 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config -i
echo
echo "/etc/selinux/config modified."
echo # disable cron mail
sed 's/MAILTO=root/MAILTO=""/g' /etc/crontab -i
echo
echo "/etc/crontab modified."
echo # install/reinstall jdk
for x in $(rpm -qa|egrep "jdk|jre"); do
rpm -e --nodeps $x
done
rpm -ivh /tmp/jdk-7u75-linux-x64.rpm
echo
echo "jdk installed/reinstalled."
echo # install necessary packages:
yum clean all
yum install zip unzip man vim tree ntpdate sysstat wget gcc tcpdump telnet bind-utils -y
echo
echo "necessary packages installed. "
echo # Finish
source /etc/profile
setenforce
chkconfig ip6tables off
chkconfig crond on
chkconfig iptables on
/etc/init.d/crond restart
/etc/init.d/iptables restart
/etc/init.d/network restart echo
echo "services restarted."
echo

ezdpl:完全依赖脚本和ssh的自动化部署方案的更多相关文章

  1. 自动化部署方案CICD

    自动化部署方案   由于来来也的时间不久,可能对现有的部署情况不是很了解,以下是个人对POC自动化部署的设计方案. 自动化部署优点 降低成本,提高生产力,高可用,更可靠,性能优化   与gitlab持 ...

  2. .NetCore基于Jenkins和Gogs的自动化部署方案

    准备工作 Jenkins和gogs的安装配置可以看前两篇:Jenkins安装.配置与说明  和 gogs安装与说明(docker) 此外,因为还要安装.net core的SDK和Git工具: 安装.n ...

  3. 前端自动化部署方案-实践(配合shell)

    以下实例项目为vue项目,其他项目当然也雷同咯 在项目中建一个这个么脚本文件 不说了,上代码 #!/bin/sh handle=$1; env=$2; # 远程部署机 webhook # 如果用远程机 ...

  4. 企业级自动化部署方案——ansible实现tomcat自动安装和配置

    共耗时10多个小时 思路一 总体设计 ansible-playbook目录结构 [root@ansible ~]# tree /etc/ansible/roles/tomcat /etc/ansibl ...

  5. Docker自动化部署方案

    一 概述 Docker发布版本应该与现有的版本发布尽量一致,参考jenkins的版本发布过程:我认为maven库和docker库有很多类似的地方,因此打包过程参考maven的打包过程:重点实现dock ...

  6. 基于 Jenkins Pipeline 自动化部署

    最近在公司推行Docker Swarm集群的过程中,需要用到Jenkins来做自动化部署,Jenkins实现自动化部署有很多种方案,可以直接在jenkins页面写Job,把一些操作和脚本都通过页面设置 ...

  7. [转]基于AWS的自动化部署实践

    作者 徐桂林 发布于 2014年1月22日 -------------------------------------------------------------------- 1. 背景 在过去 ...

  8. PXE自动化部署

    PXE 预启动执行环境,基于tftp条件下完成基于网络的自动化部署软件 原理: 网卡利用自身的tftp 请求dhcp 服务器获取ip和一个pxelinux.0的地址 在给定的tftp目录下存有ks的配 ...

  9. 使用GitHub Actions实现自动化部署

    前言 大家在工作中想必都是通过自动化部署来进行前端项目的部署的,也就是我们在开发完某个需求时,我们只需要将代码推送到某个分支,然后就能自动完成部署,我们一般不用关心项目是如何build以及如何depl ...

随机推荐

  1. IOS mac入门

    https://sarin.iteye.com/blog/1754920 注册Apple开发者平台账号 ## https://www.cnblogs.com/liuluoxing/p/6549725. ...

  2. 记一个SpringBoot中属性注入失败的问题Consider defining a bean of type ''' in your configuration

    今天遇到的一个问题: 代码检查了好几次,都没有错误,但是启动时就会报错Consider defining a bean of type ''' in your configuration. 启动类在c ...

  3. HDU - 3336 next运用+递推

    题目的匹配应该也要看成一个文本串与另一个模式串的匹配过程 Text是以当前i结尾的后缀来匹配Pattern的前缀(非真) 这里的Pattern肯定是可以匹配成功的,直接由next来保证(next总是当 ...

  4. 《STL详解》读书笔记

    vector 向量容器v.insert(v.begin(), num);//增加v.erase(v.begin(), v.end()); //擦除v.erase(v.begin());reverse( ...

  5. openstack、kvm、qemu-kvm、libvirt、xen的关系

    虚拟化技术—基础(1) 本文围绕下面3个问题进行对虚拟化技术展开讨论: 1.虚拟化技术实现方式有哪些?虚拟化技术分哪些? 2.请分别通过kvm.xen工具来实现虚拟化系统的部署? 3.请描述opens ...

  6. poj2718

    一.题意:给定一串数字,数字没有重复,个数为2~10个.求这些数字分为两份,组合成的两个数的差最小是多少 二.思路:首先可以肯定的是,将这n个数平均分成两份,所得到的最小差一定在其某个组合当中.因此可 ...

  7. java多线程-synchronized

    一.线程安全问题 多线程操作各自线程创建的资源的时候,不存在线程安全问题.但多线程操作同一个资源的时候就会出现线程安全问题.下例为两个线程操作同一个name资源时发生的问题. class TestSy ...

  8. 图解DTS和PTS

    由于把视频编码成I,B,P等帧,如下图   假设现在有I,B,P帧,那么要传输和显示呢??   如果按照显示顺序传输的话: 传输顺序就是I->B>P 当对B帧进行解码后,由于B帧无法单独显 ...

  9. 关于eclipse添加自动查找文件以及svn的插件

    1.添加自动查找当前文件位置的插件(如下图) 在百度搜索下载 OpenExplorer_1.5.0.v201108051313.jar,下载之后放入eclipse下面的plugin文件夹下面既可以 2 ...

  10. php中的$_GET如何获取带有“#”的参数

    <?php echo $_GET['key']; ?> 当url为http://test.com/c.php?key=999时,正常输出:999 当url为http://test.com/ ...