因为工作需要需要制作deb安装包。
在老大的帮助下,终于搞成功了。
下面来看一下制作方法。我们以制作Nginx的deb安装包为例子。
首先看一下 脚本目录结构:
build.sh 打包deb脚本
nginx nginx控制脚本,用来在命令行执行 nginx start stop restar等命令。提取于nginx官方的deb包中。
post与pre则为打包成deb包后,安装deb包时所需要执行的脚步。
余下则是源码压缩包,都是nginx所需要的依赖。
#!/usr/bin/env bash
##############################
# 编译Nginx二进制安装包 #
##############################
################ 设置一些全局变量 #################
# 用来保存 打包后的 deb文件
WORKDIR="/home/zhai"
test -z $WORKDIR &&
echo -e "工作目录不存在,即将退出程序" &&
exit 1
# i386是32位的,amd64是64位
[[ `uname -m` == "x86_64" ]] && ARCH="x86_64" || ARCH="i386"
# 获得linux发行版,版本号,版本名称
LINUX=$(python -c "import platform;dist=platform.dist();print dist[0].lower(), dist[1], dist[2]")
LINUX_VERSION="$(echo $LINUX | cut -d' ' -f1)"
LINUX_VS_NUMBER="$(echo $LINUX | cut -d' ' -f2)"
LINUX_VS_NAME="$(echo $LINUX | cut -d' ' -f3)"
# 获得发行版+版本号 如 ubuntu14
ITERATION="$LINUX_VERSION$(echo $LINUX_VS_NUMBER | cut -d'.' -f1)"
# 版权信息
PACKAGE="nginx"
VERSION="1.12.0"
VENDOR="ZHAI"
URL="http://www.jianshu.com/u/4ffde5305e8f"
LICENSE="GPL"
# 临时文件,保存压缩文件解包后的内容
BUILD_PATH="/tmp/$PACKAGE"
# 程序安装路径
# 这个路径是为了方便后面打包deb,
# 要保证此路径下 只能有一个文件夹,就是你的安装程序。
# 每次打包前,都要清理次目录,保证打包环境的干净。
INSTALL_PATH="/opt/deb"
# 根据发行版选择对应的打包格式
case $LINUX_VERSION in
ubuntu|debian)
TARGET="deb"
;;
centos|redhat)
TARGET="rpm"
;;
*)
echo "不支持的平台."
exit 1
esac
################## 开始编译安装 ###################
# 清理旧的解包文件
# 清理打包环境,保证打包前 此路径下文件环境的干净。
rm -rf $BUILD_PATH
rm -rf $INSTALL_PATH
mkdir -p $BUILD_PATH
mkdir -p $INSTALL_PATH
# # ${var} 与 $var 是一个意思 都是引用(使用)一个变量
# # $var 引用$后面第一个变量的值,这个第一个变量指的是 从$开始 一直到遇到第一个空格,非英文字符或者另外一个$为止
# # 当你需要一个动态变量拼接字符的时候
# # 例如:var 是动态变量,num=$var-path 这时候 引用的是 var-path变量 而不是 var变量。
# # 怎么办呢 ?这时候就该${}出马了
# # ${var} 引用一个变量var ${}的作用范围只能在 {}以内。
# # num=${var}-path 引用的是 变量 var 而不是 var-path 因为${}的作用范围只能在{}以内
SOURCE_DIR=$(pwd)
# 安装依赖 zlib
tar xvf zlib-*.gz -C $BUILD_PATH
cd ${BUILD_PATH}/zlib*
./configure
make
make install
cd $SOURCE_DIR
# 安装依赖 openssl
tar xvf openssl-*.gz -C $BUILD_PATH
cd ${BUILD_PATH}/openssl**
./config
make
make install
cd $SOURCE_DIR
# 解包pcre源码
# pcre 依赖不需要安装,只需要把源码路径配置到 nginx编译参数中即可
tar xvf pcre-*.gz -C $BUILD_PATH
# 编译安装nginx
# # 配置编译参数
# prefix nginx安装路径
# 剩下两个参数表示,开启ssl,和pcre功能。
# 暂时只用设置这两个参数,可以根据需求进行添加。
tar xvf ${PACKAGE}-${VERSION}.tar.* -C $BUILD_PATH
cd ${BUILD_PATH}/${PACKAGE}-${VERSION}
PREFIX="nginx"
./auto/configure --prefix=$INSTALL_PATH/$PREFIX \
--with-http_ssl_module \
--with-pcre=$BUILD_PATH/pcre* # 配置prce源码文的路径
make
make install
cd $SOURCE_DIR
#################### 开始打包deb #####################
# FPM常用参数:
# -s:指定源类型
# -t:指定目标类型,即想要制作为什么包
# -n:指定包的名字
# -v:指定包的版本号
# -C:指定打包的相对路径
# -d:指定依赖于哪些包
# -f:第二次包时目录下如果有同名安装包存在,则覆盖它
# -p:输出的安装包的目录,不想放在当前目录下就需要指定
# --post-install:软件包安装完成之后所要运行的脚本;同--offer-install
# --pre-install:软件包安装之前所要运行的脚本;同--before-install
# --post-uninstall:软件包卸载完成之后所要运行的脚本;同--offer-remove
# --pre-uninstall:软件包卸载之前所要运行的脚本;同—before-remove
# 有几个地方需要说一下
# -C 值的就是 你的程序安装路径 例如 你程序安装在 /opt/deb/nginx
# 那么 -C 路径就是 /opt/deb 而不是 /opt/deb/niginx 切记
# 因为 fpm 会把 -C 路径下的 所有文件夹都打包了。
# 因此 要保证 -C 路径下 只能有一个你的软件目录,而不能有其他的。
# 列如:-C 为 /opt/deb 此时 deb下有三个文件目录 nginx apaceh php 那么
# fpm 会把这三个文件全部打包在一起。这不是我们想要,我们只想要单独的nginx。
# 所以要保证 nginx下只有一个文件目录。
# fpm打包后的deb,在安装的时候,安装路径为 -C 路径下的 文件目录为顶级安装路径。
# 也就是说 -C只是一个 打包时候所用的 寻找安装程序位置的路径。
# 安装deb时候 将 -C 转换为 / 也就是 根路径去安装
# 例如 -C /opt/deb
# 软件为 nginx 此时完整路径为 /opt/deb/nginx
# 安装的时候 其实是安装在 /nginx 直接安装在了根路径下,而不是 /opt/deb下。
# 怎么才能安装在 /opt/deb下呢?
# 这时候就需要 --prefix 参数了。
# --prefix 指定安装软件时候的 绝对路径。
# 例如 指定 --prefix 为 /home/wo/deb
# 那么软件就会安装在 /home/wo/deb/nginx 路径下。
# 还有一点
# 软件最终安装好时候的路径 一定要 与 软件编译安装时指定的安装路径一样,不然会有很多的路径错误。
# 这点很重要
# 例如 nginx 编译时候 指定的安装路径为 -prefix=/opt/deb/nginx
# 那么打包成deb 安装的时候 安装路径一定也要是 /opt/deb/nginx
SCRIPTS_PATH=$(pwd)
case $LINUX_VERSION in
ubuntu|debian)
fpm -f -s dir \
-t $TARGET \
-n $PACKAGE \
-v $VERSION \
-C $INSTALL_PATH \
-p $BUILD_PATH/ \
--prefix $INSTALL_PATH \
--iteration $ITERATION \
--license $LICENSE \
--vendor $VENDOR \
--url $URL \
--deb-no-default-config-files \
--pre-install $SCRIPTS_PATH/pre-install \
--post-install $SCRIPTS_PATH/post-install \
--pre-uninstall $SCRIPTS_PATH/pre-uninstall \
--post-uninstall $SCRIPTS_PATH/post-uninstall
;;
centos|redhat)
fpm -f -s dir \
-t $TARGET \
-n $PACKAGE \
-v $VERSION \
-C $INSTALL_PATH \
-p $BUILD_PATH/ \
--prefix $INSTALL_PATH
--iteration $ITERATION \
--license $LICENSE \
--vendor $VENDOR \
--url $URL \
--pre-install $SCRIPTS_PATH/pre-install \
--post-install $SCRIPTS_PATH/post-install \
--pre-uninstall $SCRIPTS_PATH/pre-uninstall \
--post-uninstall $SCRIPTS_PATH/post-uninstall
;;
*)
echo "Unsupport platform."
exit 1
esac
PACKDIR=${WORKDIR}/packages/LINUX_VERSION/`echo $LINUX_VS_NUMBER| cut -d'.' -f1`/${ARCH}
#cd -
# 移动deb到 工作目录
mkdir -p $PACKDIR
mv ${BUILD_PATH}/${PACKAGE}_${VERSION}-${ITERATION}_*.${TARGET} ${PACKDIR}/${PACKAGE}-${VERSION}.${ARCH}.${TARGET}
pre-install脚本
pre-install 运行期间为 软件安装之前,如果有Nginx旧版本在运行,那么就会结束Nginx旧版本进行,避免deb包的安装失败。
#!/usr/bin/env bash
#############################################
###### 软件包安装之前所要运行的脚本 #######
#############################################
# 结束 nginx进程,如果存在的话
retval=$(ps -A | grep nginx | grep -v grep | awk '{print $1}' | wc -l)
if [[ $retval -ne 0 ]]; then
ps -A | grep nginx | grep -v grep | awk '{print $1}' | xargs kill -9
fi
post-install脚本
post-install 运行期间为 软件安装完成之后,负责一些环境变量的设置。
#!/usr/bin/env bash
#############################################
##### 软件包安装完成后所要运行的脚本 #####
#############################################
LINUX=$(python -c "import platform;dist=platform.dist();print dist[0].lower(), dist[1], dist[2]")
LINUX_VERSION="$(echo $LINUX | cut -d' ' -f1)"
NGINX_PATH="/opt/deb/nginx/nginx"
NGINX_CONF="/opt/deb/nginx/conf/nginx.conf"
# pid 文件路径
PID_PATH="pid /opt/deb/nginx/logs/nginx.pid;"
# 将pid路径配置到 nginx配置文件中
case $LINUX_VERSION in
ubuntu|debian)
LN=$(grep -n "#pid" $NGINX_CONF | cut -d: -f1)
sed -i "$LN a\\$PID_PATH" $NGINX_CONF
# 删除旧nginx控制脚本,如果有的话,并将新nginx控制脚本移过去
test -f /etc/init.d/nginx && rm -rf /etc/init.d/nginx
cp $NGINX_PATH /etc/init.d/nginx
;;
centos|redhat)
LN=$(grep -n "#pid" $NGINX_CONF | cut -d: -f1)
sed -i "$LN a\\$PID_PATH" $NGINX_CONF
rm /etc/init.d/nginx -f
cp $NGINX_PATH /etc/init.d/nginx
;;
*)
;;
esac
pre-uninstall脚本
pre-uninstall脚本运行期间为 程序卸载删除之前,负责结束程序进程解除占用。
#!/usr/bin/env bash
#############################################
###### 软件包安装之前所要运行的脚本 ######
#############################################
# 结束 nginx进程,如果存在的话
retval=$(ps -A | grep nginx | grep -v grep | awk '{print $1}' | wc -l)
if [[ $retval -ne 0 ]]; then
ps -A | grep nginx | grep -v grep | awk '{print $1}' | xargs kill -9
fi
post-uninstall脚本
post-uninstall脚本运行期间为 程序卸载删除完成后,负责清理程序残留文件。
#!/usr/bin/env bash
#############################################
##### 软件包安装完成后所要运行的脚本 #####
#############################################
LINUX=$(python -c "import platform;dist=platform.dist();print dist[0].lower(), dist[1], dist[2]")
LINUX_VERSION="$(echo $LINUX | cut -d' ' -f1)"
INSTALL_PATH="/opt/deb"
NGINXDIR=$INSTALL_PATH/nginx
case $LINUX_VERSION in
ubuntu|debian)
test -d $NGINXDIR && rm -rf $NGINXDIR
;;
centos|redhat)
test -d $NGINXDIR && rm -rf $NGINXDIR
;;
*)
;;
esac
test -f /etc/init.d/nginx && rm -rf /etc/init.d/nginx
nginx为控制Nginx启动,停止,重启的脚本。提取与官方deb安装包中(安装官方deb包后,脚本路径为 /etc/init.d/nginx),有几个地方需要改一下就可以使用了。
#!/usr/bin/env bash
### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $local_fs $remote_fs $network $syslog $named
# Required-Stop: $local_fs $remote_fs $network $syslog $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the nginx web server
# Description: starts nginx using start-stop-daemon
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
################ 此处为修改的部分 ###########################
# nginx 执行文件路径
NGINX_PATH="/opt/deb/nginx/sbin/nginx"
# nginx配置文件路径
NGINX_CONF="/opt/deb/nginx/conf/nginx.conf"
# 开启日志输出
# 开启日志输入,一定不能写在这里,不然会被默认值给覆盖了。
# 一定要写在 最后面 case "$1" in 这一行的上面
# VERBOSE='yes'
#DAEMON=/usr/sbin/nginx
DAEMON=$NGINX_PATH
NAME=nginx
DESC=nginx
# Include nginx defaults if available
if [ -r /etc/default/nginx ]; then
. /etc/default/nginx
fi
test -x $DAEMON || exit 0
. /lib/init/vars.sh
. /lib/lsb/init-functions
# Try to extract nginx pidfile
PID="/opt/deb/nginx/logs/nginx.pid"
if [ -z "$PID" ]
then
PID=/run/nginx.pid
fi
################# 修改结束 ###################
# Check if the ULIMIT is set in /etc/default/nginx
# ulimit 用于限制 shell 启动进程所占用的资源
# nginx 默认是没有开启的
if [ -n "$ULIMIT" ]; then
# Set the ulimits
ulimit $ULIMIT
fi
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \
$DAEMON_OPTS 2>/dev/null \
|| return 2
}
test_nginx_config() {
$DAEMON -t $DAEMON_OPTS >/dev/null 2>&1
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PID --name $NAME
RETVAL="$?"
sleep 1
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --name $NAME
return 0
}
#
# Rotate log files
#
do_rotate() {
start-stop-daemon --stop --signal USR1 --quiet --pidfile $PID --name $NAME
return 0
}
#
# Online upgrade nginx executable
#
# "Upgrading Executable on the Fly"
# http://nginx.org/en/docs/control.html
#
do_upgrade() {
# Return
# 0 if nginx has been successfully upgraded
# 1 if nginx is not running
# 2 if the pid files were not created on time
# 3 if the old master could not be killed
if start-stop-daemon --stop --signal USR2 --quiet --pidfile $PID --name $NAME; then
# Wait for both old and new master to write their pid file
while [ ! -s "${PID}.oldbin" ] || [ ! -s "${PID}" ]; do
cnt=`expr $cnt + 1`
if [ $cnt -gt 10 ]; then
return 2
fi
sleep 1
done
# Everything is ready, gracefully stop the old master
if start-stop-daemon --stop --signal QUIT --quiet --pidfile "${PID}.oldbin" --name $NAME; then
return 0
else
return 3
fi
else
return 1
fi
}
# 开启日志输出
# 这个开启日志的 一定要写在这里,不然会被默认值给覆盖了。
VERBOSE='yes'
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
# Check configuration before stopping nginx
if ! test_nginx_config; then
log_end_msg 1 # Configuration error
exit 0
fi
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
reload|force-reload)
log_daemon_msg "Reloading $DESC configuration" "$NAME"
# Check configuration before reload nginx
#
# This is not entirely correct since the on-disk nginx binary
# may differ from the in-memory one, but that's not common.
# We prefer to check the configuration and return an error
# to the administrator.
if ! test_nginx_config; then
log_end_msg 1 # Configuration error
exit 0
fi
do_reload
log_end_msg $?
;;
configtest|testconfig)
log_daemon_msg "Testing $DESC configuration"
test_nginx_config
log_end_msg $?
;;
status)
status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $?
;;
upgrade)
log_daemon_msg "Upgrading binary" "$NAME"
do_upgrade
log_end_msg 0
;;
rotate)
log_daemon_msg "Re-opening $DESC log files" "$NAME"
do_rotate
log_end_msg $?
;;
*)
echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}" >&2
exit 3
;;
esac
:
打包后的deb 就保存在我们设置的工作目录中。
在看一下 deb的信息 :
我们看到确实安装在 /opt/deb/nginx路径之下
我们安装一下
安装成功 ,没毛病。运行一下,试一试控制命令:
ok 很完美!
ok 好了 完美运行