自动化部署脚本
⑴ neo4j怎么编写自动化脚本
1、shell脚本自动化部署neo4j
#!/bin/bash
host_name=(
wyl01
wyl02
wyl03
)
host_ip=(
10.10.252.151
10.10.252.112
10.10.252.127
)
#neo4j部分信息
NEO4J_VERSION='neo4j-community-3.1.4' #neo4j的版本,注意这里不.tar.gz
NEO4J_INSTALL_PATH='/opt' #noe4j的安装路径
#1 步骤输出 紫色
#2 正确输出 绿色
#3 错误输出 红色
#4 提示输出 蓝色
#5 警告输出 黄色
#根据不同的颜色打印出提示信息
function echo_fun(){
if [ $# -ge 2 ];then
params_num=$1
shift 1
params_mes=$@
else
echo_fun 3 请至少输入两个参数 echo_fun ..
exit
fi
case $params_num in
1)
echo -e "\n\033[35;40;1m ****************************** ${params_mes} ******************************\033[0m\r\n"
;;
2)
echo -e "\033[32;40;1m ${params_mes}\033[0m\r\n"
;;
3)
echo -e "\n\033[31;40;1m ${params_mes}\033[0m\r\n"
;;
4)
echo -e "\033[36;40;1m ${params_mes}\033[0m\r\n"
;;
5)
echo -e "\033[33;40;1m ${params_mes} \033[0m\r\n"
;;
*)
echo_fun 3 参数异常第一个参数应为1,2,3,4,5
;;
esac
}
function check_ok(){
if [ $? -ne 0 ];then
echo_fun 3 执行失败,请查看日志
exit 1
fi
}
function adser(){
useradd -d /home/$1 $1
expect -c"
spawn passwd $1
expect {
\"*yes/no*\" {send \"yes\r\";exp_continue}
\"*New password:*\" {send \"$2\r\";exp_continue}
\"*Retype new password:*\" {send \"$2\r\";exp_continue}
}"
}
function check_exist_user(){
num_user=`cat /etc/passwd|grep -w $1|wc -l`
if [ ${num_user} -eq 1 ];then
echo_fun 2 该机器已经创建了$1用户,无需再创建,继续下一步
num_user=`echo 1`
else
echo_fun 4 创建$1用户
num_user=`echo 0`
fi
}
function check_catalog_exist(){
test -d $1 && echo_fun 5 $1的安装目录已经存在,请检查 && exit
}
#检查环境变量---单台
function check_etc_profile(){
echo_fun 4 配置环境变量
#检查环境变量中是否已经配置了
path_num=`cat /etc/profile |grep -w $1 |wc -l `
if [ ${path_num} -ge 1 ];then
echo_fun 5 该机器环境变量中已经配置,请检查准确性
#回滚,删除之前的软连接和安装包
rm -rf $1
rm -rf $2
exit
fi
}
#解压neo4j的二进制包,并设置软连接,和改变其属组属性。
function extract_neo4j(){
cd $SOFTWARE_PATH
echo_fun 4 解压neo4j二进制包,并创建软连接
tar -xf ${NEO4J_VERSION}-unix.tar.gz -C ${NEO4J_INSTALL_PATH}
check_ok
ln -s ${NEO4J_INSTALL_PATH}/${NEO4J_VERSION} ${NEO4J_INSTALL_PATH}/neo4j
cd ${NEO4J_INSTALL_PATH}
chown -R $1:$1 ${NEO4J_INSTALL_PATH}/neo4j*
sleep 1s
}
function alter_neo4j_config(){
echo_fun 4 修改neo4j配置文件内容
sleep 1s
cd /${NEO4J_INSTALL_PATH}/neo4j/conf/
sed -i '/#dbms.directories.data/s/^#//' neo4j.conf
sed -i '/#dbms.directories.plugins/s/^#//' neo4j.conf
sed -i '/#dbms.directories.certificates/s/^#//' neo4j.conf
sed -i '/#dbms.directories.logs/s/^#//' neo4j.conf
sed -i '/#dbms.directories.lib/s/^#//' neo4j.conf
sed -i '/#dbms.directories.run/s/^#//' neo4j.conf
sed -i '/#dbms.connectors.default_listen_address/s/^#//' neo4j.conf
sed -i '/#dbms.connector.bolt.tls_leve/s/^#//' neo4j.conf
sed -i 's/#dbms.connector.bolt.listen_address=:7687/dbms.connector.bolt.listen_address='"${host_ip[0]}"':7687/g' neo4j.conf
sed -i 's/#dbms.connector.http.listen_address=:7474/dbms.connector.http.listen_address='"${host_ip[0]}"':7474/g' neo4j.conf
sed -i 's/#dbms.connector.https.listen_address=:7473/dbms.connector.https.listen_address='"${host_ip[0]}"':7473/g' neo4j.conf
sed -i '/#dbms.security.allow_csv_import_from_file_urls=true/s/^#//' neo4j.conf
sed -i '/#dbms.shell.enabled/s/^#//' neo4j.conf
sed -i 's/#dbms.shell.host=127.0.0.1/dbms.shell.host='"${host_ip[0]}"'/g' neo4j.conf
sed -i '/#dbms.shell.port=1337/s/^#//' neo4j.conf
}
function start_neo4j(){
echo_fun 4 启动neo4j进程
su - ${NEO4J_USER} <<EOF
cd /opt/neo4j
nohup ./bin/neo4j start >/home/${NEO4J_USER}/neo4j.log 2>&1
EOF
}
function step_fun_4(){
echo_fun 4 检查neo4j安装目录是否存在
check_catalog_exist ${NEO4J_INSTALL_PATH}/${NEO4J_VERSION}
echo_fun 5 请输入neo4j的属组用户
read -p "neo4j_user=" neo4j_user
check_exist_user ${neo4j_user}
if [ $num_user -eq 0 ]; then
echo_fun 4 给neo4j用户输入密码
read -p "neo4j_password=" neo4j_passwd
adser ${neo4j_user} ${neo4j_passwd}
fi
#解压,设置软连接
extract_neo4j ${neo4j_user}
#修改配置文件
alter_neo4j_config
#检查环境变量
echo_fun 4 检查环境变量
check_etc_profile ${NEO4J_INSTALL_PATH}/neo4j ${NEO4J_INSTALL_PATH}/${NEO4J_VERSION}
#配置环境变量
if [ ${path_num} -lt 1 ];then
echo -e '\nexport NEO4J_HOME='${NEO4J_INSTALL_PATH}'/neo4j\nexport PATH=${NEO4J_HOME}/bin:$PATH'>> /etc/profile
source /etc/profile
fi
#启动neo4j
start_neo4j
neo4j_pid_num=`ps -ef |grep neo4j|grep -v grep|wc -l`
if [ ${neo4j_pid_num} -eq 1 ];then
echo_fun 2 neo4j has started....
echo_fun 2 在浏览器输入${host_ip[0]}:7474
else
echo_fun 3 启动失败,请查看错误日志信息/home/${NEO4J_USER}/neo4j.log
fi
}
step_fun_4
执行结果如下:
⑵ openstack自动化部署有哪些工具
Fuel比较值得推荐
这是Mirantis出品的部署安装工具,2013年10月份,推出他的3.2版本,让人很震撼,基本算是把Openstack所有的部署都web化,你可以太多的选择:尤其是网络,存储。这基本都是大家学习的榜样。已经敢和企业签订SLA。
目前国内杜玉杰,九州云,在做该项目的汉化工作和考虑日后的技术支持。
Devstack
这应该算是Openstack最早的安装脚本,他是通过直接git源码,进行安装,目的是让开发者可以快速搭建一个环境。目前这套脚本可以在Ubuntu和Fedora下跑的很好。
如果你想了解新版本的功能,通过Devstack是一个最佳选择。我第一次成功安装Openstack,就是用这个。
Diablo安装脚本
这算是我知道的第一个基于ubuntu 源的安装脚本,当时Diablo发布的时候,有bug,keystone和Horizon无法一起工作,ubuntu 11.10源里的包也是有相同的bug,并且Ubuntu 官方并不打算维护和升级。这位作者个人身份,维护了Diablo 版本后来的升级,打包,并且写了一套脚本。
这套脚本,其实写的非常好,我同事改造了一下,就可以安装Essex版本。目前正在改进,用来安装Folsom版本。
Stackops
Openstack起步的时候,官方的安装文档,其实基本都是来自Stackops,当时他们提供Openstack的部署服务。你下载一个iso,装完一个ubuntu系统后,就会转到他们的web,你填写相关的配置参数,通过chef进行部署。
Stackops在Chef上是比较强悍的,包括Dell的Crowbar,也是和他合作,才搞定Chef。不过我的理解,他的Chef 脚本是没有开源的。
Stackops通过商业定制,进行收费。目前感觉有点走下坡路,业内的声音比较小了。对openstack的新版本支持比较慢,Essex支持,到9月份才支持。目前还没有任何关于Folsom 的支持。
Crowbar
在Openstack上,Dell是有点郁闷,中国的那句老话,起个大早,赶个晚集。Openstack刚发布的时候,Dell就号称要做一个工具部署,不过毕竟是硬件公司,对软件还是比较外行。
Dell的Crowbar,是一个iso,装完后,你可以在web界面进行Openstack的部署,底下其实也是使用Chef。集成了Nagios监控,安装OS是dell自己开发的,有点创新。
以前Dell的Crowbar一个大的特色就是可以远程设置机器的Raid和Bios,这是从Dell,我才知道可以这样做。不过也正因为这个特色,导致厂商绑定,还有Dell自己如何定位Crowbar,当初还想搞一个商业的版本,结果后来放弃。
Crowbar目前的开发,其实应该是遇到问题,任何做部署工具的,应该都是一个很好的失败的案例。后续的发展,维护,更新都是问题。
Maas+Juju
这是Canonical推出的部署工具,可以用在Openstack的部署,类似Puppet,Chef 的部署工具。Maas是用于安装ubuntu,juju是用于部署应用。
⑶ 如何用脚本自动化部署xml文件
仅仅需要按下按钮
最近,我的岳父向我展示了他的T系列福特,我发现自己开始着迷于他坚持练习的启动汽车的方式。首先调整火花塞,打开汽油活塞,像主气缸中加入一些汽油,挂空挡,拉起手刹,之后在汽车前面摇动发动机点火。这辆车就又可以继续行驶了。
在看到我的岳父需要扳动变速箱并脚踩踏板才能保持汽车行驶的时候,我对于自己的汽车的简单进入方式和一键启动有了新的认识。
而对于规模不断增长、相当复杂的物理和虚拟数据中心,不论是本地的还是远程的,你都不能依靠某个人来记住如何启动T系列福特,而是需要通过执行重复的任务来保证其正常运行。
自动化可以帮助解决下面的一系列问题:
? 重复。如果需要多次执行同一个任务,并且需要保持可靠性和一致性,就需要对任务进行自动化和计划执行了。
? 人为失误。避免人为失误,特别是在危机处理过程中,对于大规模环境的成功运营是至关重要的。即便是一个好的管理员,面对关键业务中断的巨大压力,也可能犯下错误使得情况变得更加糟糕。
? 文档问题。 自动化对于特定的步骤和预期的、可审计的结果都提供了清晰的文档。需要确保你的IT部门已经制定了灾难恢复文档,并且已经已经实现了自动化。
自动化面临新的挑战
当然,自动化不能神奇地解决所有IT问题。一个良好的自动化策略需要理解哪些地方怎样使用恰当的工具,并且在最大程度上发挥这些工具的作用。
可以使用不同的方式来实现自动化。让我们从可以提供图形用户界面(GUI)的工具开始,它们可以让IT管理员重复执行任务,并且实现对任务的大规模管理。
Windows Server
2012中的图形化服务器管理器和系统中心虚拟机管理器(SCVMM)等这些工具提供了一些自动化和扩展特性,比如向多个服务器部署角色,或者同时管理多
台虚拟机,但是不要错误地认为这是一种灵活的自动化环境。没有一种GUI能够理解你的特殊业务需求。
为了实现能够业务部门所有需求的灵活性,需要一个由向导和有限的按钮组成的、能够顺利执行的工具。最好的自动化策略应该提供一个对所有可能性和控制权限都开放的环境——而这也意味着可能导致彻底的失败。并没有一种简易的、使用于所有环境的自动化方式。
高效的自动化工具都是十分复杂的,需要IT专家在培训和特定技术领域进行大量投入。学习自动化技能能够促进职业发展、增加跨平台的潜力和为公司盈利。
除此之外,IT部门必须和业务部门协同合作,来为自动化制定一个有价值的方案。关于这一点,IT专家和CIO都应该阅读由Gene
Kim、Kevin Behr和George Spafford共同编写的The Phoenix
Project。这本书介绍了IT部门应该如何成为业务的一部分,而不是现在的敌对关系。
⑷ java web 项目如何做自动部署
环境
由于工作中使用的IDE是eclipse,使用Maven进行包管理,所以,以下所涉及的都是基于Java 1.8,Maven 3.3.9,Elicpse Luna 4.4.2。
历程
TOP1 Maven插件配置
项目中如果有使用Maven的话,则可以很方便的以tomcat7为例,在pom文件中配置一个插件即可轻松实现
TOP2 Linux脚本部署
tomcat虽然部署麻烦,但是就稳定性来说,比起其它的web容器还是稍微好一丢丢。略过tomcat的坑不说,服务器中往往会有多个tomcat存在,特别是在测试的服务器上,我司放了有10多个tomcat,这样,作为一个 没有专门部署人员的小团队来说,简直是个噩梦,部署几个应用需要执行一大丢命令。
会偷懒的人往往就会写好一些常用的部署脚步,避免重复性的工作,在这里,我只简单介绍自己所使用的一种脚本,就是根据tomcat容器所在路径找到所在进程,杀死进程,然后进行重启。
TOP3 Windows与Linux架桥互通
由于是在windows中进行开发工作,服务器使用的是cent os,所以又会多出许多重复性的工作。所以我选择使用putty,在windows与Linux之间架一个桥,从而能够方便的上传文件以及对远程服务器进行操作。
整体思路是,本地打包maven项目,打包完成之后上传到服务器WEB容器对应目录,然后调用我们上一步的服务器脚本,完成整体项目部署流程。
⑸ 如何在Ubuntu 14.04服务器上自动化部署Spring Boot的应用
1. 在Ubuntu中安装Java8
以下是一个简单的安装方法。
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer
$ java -version
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
2. 本地使用Gradle发布Spring Boot应用
我这里使用Jetty9作为内置的服务器。
// ...
bootRun {
systemProperties = System.properties
}
configurations {
compile.exclude mole: "spring-boot-starter-tomcat"
}
dependencies {
// spring boot
compile "org.springframework.boot:spring-boot-starter-web:1.3.0.M5"
compile "org.springframework.boot:spring-boot-starter-jetty"
// ...
}
//...
在本地运行默认使用 src/main/resources/application.yml
作为配置文件,而在proction环境中我们系统它支持提供外部的配置文件 application-proction.yml
。
./gradlew bootRun # 开发环境下默认使用项目里的application.yml
# 在本地测试使用外部配置文件
./gradlew bootRun -Dspring.config.location=/path/to/application-proction.yml
# 发布
./gradlew build
# 运行
java -jar build/libs/SpringBlog-0.1.jar # 默认使用jar包里面的application.yml配置文件
# 使用外部配置文件
java -jar build/libs/SpringBlog-0.1.jar --spring.config.location=/path/to/application-proction.yml
3. 在Ubuntu服务器上部署Spring Boot应用
# 上传SpringBlog-0.1.jar到服务器
scp build/libs/SpringBlog-0.1.jar root@your_server_ip:/root/spring-blog/current
# 在服务器上配置生产环境的配置文件
scp application-proction.yml root@your_server_ip:/root/spring-blog/current
然后 SSH
登录服务器,修改配置文件 application-proction.yml
,试运行应用。
ssh root@your_server_ip
cd spring-blog/current
java -jar SpringBlog-0.1.jar --spring.config.location=application-proction.yml
# application-proction.yml
server:
address: raysmond.com # 使用域名或者IP,启动之后就可以这个域名或IP访问网站了
port: 80
contextPath:
spring:
profiles:
active: proction
thymeleaf:
cache: true
jade4j:
caching: true
dataSource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1/spring_blog
username: root
password:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
hbm2ddl.auto: update
show_sql: false
redis:
host: localhost
port: 6379
4. 如何在Ubuntu中后台运行Spring Boot应用?
推荐使用 nohup
这个命令。
cd /root/spring-blog/current
nohup java -jar SpringBlog-0.1.jar --spring.config.location=application-proction.yml \
> ../logs/proction.log 2> ../logs/proction.err &
在Ubuntu还可以 /etc/init.d
目录下新建一个脚本,把SpringBlog作为service来运行,这样不用每次都打这么繁琐的命令了。新建一个 /etc/init.d/spring_blog
文件,内容如下:
#!/bin/sh
SERVICE_NAME=spring_blog
HOME=/root/spring-blog
PATH_TO_JAR=$HOME/current/SpringBlog-0.1.jar
PID_PATH_NAME=/tmp/spring_blog.pid
LOG=$HOME/logs/proction.log
ERROR_LOG=$HOME/logs/proction.err
CONFIG=$HOME/application-proction.yml
case $1 in
start)
echo "Starting $SERVICE_NAME ..."
if [ ! -f $PID_PATH_NAME ]; then
cd $HOME/current
nohup java -jar $PATH_TO_JAR --spring.config.location=application-proction.yml > $LOG 2> $ERROR_LOG &
echo $! > $PID_PATH_NAME
echo "$SERVICE_NAME started ..."
else
echo "$SERVICE_NAME is already running ..."
fi
;;
stop)
if [ -f $PID_PATH_NAME ]; then
PID=$(cat $PID_PATH_NAME);
echo "$SERVICE_NAME stoping ..."
kill $PID;
echo "$SERVICE_NAME stopped ..."
rm $PID_PATH_NAME
else
echo "$SERVICE_NAME is not running ..."
fi
;;
restart)
if [ -f $PID_PATH_NAME ]; then
PID=$(cat $PID_PATH_NAME);
echo "$SERVICE_NAME stopping ...";
kill $PID;
echo "$SERVICE_NAME stopped ...";
rm $PID_PATH_NAME
echo "$SERVICE_NAME starting ..."
cd $HOME/current
nohup java -jar $PATH_TO_JAR --spring.config.location=application-proction.yml > $LOG 2> $ERROR_LOG &
echo $! > $PID_PATH_NAME
echo "$SERVICE_NAME started ..."
else
echo "$SERVICE_NAME is not running ..."
fi
;;
esac
现在就可以使用service的方式来运行网站了。
sudo service spring_blog start
sudo service spring_blog stop
sudo service spring_blog restart
5. 在本地自动化部署网站到远程服务器
在本地我用了一个shell脚本和一个python脚本来配合自动化部署。
deploy.sh
使用 gradle
的命令发布jar包,使用 scp
命令吧jar包上传到服务器上;
deploy.py
使用 SSH
远程登录服务器,并在服务器上执行部署命令。
# deploy.sh
#!/bin/bash
SERVER="your_server_ip"
JAR="build/libs/SpringBlog-0.1.jar"
echo "Building $JAR..."
./gradlew build
echo "Upload $JAR to server $SERVER..."
scp $JAR root@$SERVER:/root/spring-blog/
python deploy.py
deploy.py
主要使用了一个 paramiko
库,用于 SSH
远程登录服务器,并执行命令。这个脚本会把服务器上 /root/spring-blog/current/SpringBlog-0.1.jar
备份到 /root/spring-blog/releases
中,并把新发布的jar包放到 /root/spring-blog/current
中,然后重启 spring_blog
服务。
#!/usr/bin/python
import paramiko
import threading
import time
ip = 'your_server_ip'
user = 'root'
password = ''
jar = 'SpringBlog-0.1.jar'
home='/root/spring-blog'
current=home+"/current"
releases=home+"/releases"
def execute_cmds(ip, user, passwd, cmd):
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip,22,user,passwd,timeout=5)
for m in cmd:
print m
stdin, stdout, stderr = ssh.exec_command(m)
# stdin.write("Y")
out = stdout.readlines()
for o in out:
print o,
print '%s\tOK\n'%(ip)
ssh.close()
except :
print '%s\tError\n'%(ip)
if __name__=='__main__':
print 'Start deploying %s to server %s'%(jar, ip)
now = time.strftime("%Y%m%d%H%M%S")
cmd = [
'echo Stop spring_blog service... && service spring_blog stop',
'echo Flush all redis cache data... && redis-cli -r 1 flushall',
'echo Stop redis server... && service redis_6379 stop',
'echo Use new jar... ' + \
' && mv ' + current + '/' + jar + ' ' + releases + '/' + now + '_' + jar ,
'mv ' + home + '/' + jar + ' ' + current + '/' + jar,
'echo Stop redis... && service redis_6379 start',
'echo Start spring_blog service... && service spring_blog start ' + \
' && echo All done.'
]
a=threading.Thread(target=execute_cmds, args=(ip,user,password,cmd))
a.start()
配置完以后,在本地写完代码就可以运行 ./deploy.sh
一键部署到远程服务器了。
⑹ Linux 自动化部署,打包和传输
# file tt.tar.bz2
tt.tar.bz2: bzip2 compressed data, block size = 900k
可以查看文件类型是不是已经好了
[root@localhost init]# tar cjf tt.tar.bz2 tt
^C
[root@localhost init]# file tt.tar.bz2
tt.tar.bz2: empty
中断是empty类型
⑺ 如何使用SHELL批量或自动化部署软件
思路如下
shell 非交互安装软件
变量+软件目录
循环体
安装软件命令
结束。
⑻ 如何使用powershell写自动化脚本
仅仅需要按下按钮
最近,我的岳父向我展示了他的T系列福特,我发现自己开始着迷于他坚持练习的启动汽车的方式。首先调整火花塞,打开汽油活塞,像主气缸中加入一些汽油,挂空挡,拉起手刹,之后在汽车前面摇动发动机点火。这辆车就又可以继续行驶了。
在看到我的岳父需要扳动变速箱并脚踩踏板才能保持汽车行驶的时候,我对于自己的汽车的简单进入方式和一键启动有了新的认识。
而对于规模不断增长、相当复杂的物理和虚拟数据中心,不论是本地的还是远程的,你都不能依靠某个人来记住如何启动T系列福特,而是需要通过执行重复的任务来保证其正常运行。
自动化可以帮助解决下面的一系列问题:
• 重复。如果需要多次执行同一个任务,并且需要保持可靠性和一致性,就需要对任务进行自动化和计划执行了。
• 人为失误。避免人为失误,特别是在危机处理过程中,对于大规模环境的成功运营是至关重要的。即便是一个好的管理员,面对关键业务中断的巨大压力,也可能犯下错误使得情况变得更加糟糕。
• 文档问题。 自动化对于特定的步骤和预期的、可审计的结果都提供了清晰的文档。需要确保你的IT部门已经制定了灾难恢复文档,并且已经已经实现了自动化。
自动化面临新的挑战
当然,自动化不能神奇地解决所有IT问题。一个良好的自动化策略需要理解哪些地方怎样使用恰当的工具,并且在最大程度上发挥这些工具的作用。
可以使用不同的方式来实现自动化。让我们从可以提供图形用户界面(GUI)的工具开始,它们可以让IT管理员重复执行任务,并且实现对任务的大规模管理。
Windows Server
2012中的图形化服务器管理器和系统中心虚拟机管理器(SCVMM)等这些工具提供了一些自动化和扩展特性,比如向多个服务器部署角色,或者同时管理多
台虚拟机,但是不要错误地认为这是一种灵活的自动化环境。没有一种GUI能够理解你的特殊业务需求。
为了实现能够业务部门所有需求的灵活性,需要一个由向导和有限的按钮组成的、能够顺利执行的工具。最好的自动化策略应该提供一个对所有可能性和控制权限都开放的环境——而这也意味着可能导致彻底的失败。并没有一种简易的、使用于所有环境的自动化方式。
高效的自动化工具都是十分复杂的,需要IT专家在培训和特定技术领域进行大量投入。学习自动化技能能够促进职业发展、增加跨平台的潜力和为公司盈利。
除此之外,IT部门必须和业务部门协同合作,来为自动化制定一个有价值的方案。关于这一点,IT专家和CIO都应该阅读由Gene
Kim、Kevin Behr和George Spafford共同编写的The Phoenix
Project。这本书介绍了IT部门应该如何成为业务的一部分,而不是现在的敌对关系。