作者:wiLdGoose
发布时间:August 27, 2019
分类:技术 Technology
可以看作是这篇文章的升级版本。这里感谢 druggo 留言提醒,让我决定抽时间折腾一下 lftp。
整个过程其实非常简单,照例自己马克一下。
安装 lftp 客户端:
yum -y install lftp
安装后使用过程中遇到报错:
Fatal Error: Certificate Verification: Not Trusted
原因是远端 FTP 服务器使用了自签发 SSL 证书,可以这样解决:
vim /etc/lftp.conf
在文件最后增加一行:
set ssl:verify-certificate no
改造后的脚本:
#!/bin/bash
date=`date +%Y%m%d`
deldate=`date -d -{NUMBER}day +%Y%m%d`
lftp ftp://{FTP_USER}:{FTP_PASSWD}@{HOST}:{PORT} <<!
cd /
lcd /data/backup
mrm *$deldate*
mput *$date*
exit
!
当然 lftp 还有一个更好用的指令:mirror。这里不用的原因是本地存储的时间与远端不一致。
依然是 3 点注意事项:
1、脚本中花括号内容替换为对应的变量;
2、cd 是远程的目录,lcd 是本地目录;
3、备份文件名需符合 date +%Y%m%d 这样的规则。
作者:wiLdGoose
发布时间:August 23, 2019
分类:技术 Technology
前阵子写了个小脚本,用于异地备份与自动删除过期文件。使用一段时间后发现异常,表现为:
ps aux | grep ftp
通过这个命令显示有大量的 FTP 客户端进程滞留内存。一开始以为传输未结束导致,经过文件比对后发现传输确实已结束。辣么为何 FTP 进程未能自动退出呢?
网上查询后有人说使用 passive 命令,但我的脚本中已经对 FTP 命令设置了 -inp 的参数,其中参数 p 即启用被动模式传输。若再使用 passive 命令,会使 FTP 从被动模式切换回默认的主动模式,导致连接与传输失败。
权宜之计是在 crontab 中,在定时备份的任务后若干小时再执行这个任务:
for i in `ps aux | grep "ftp -inp" | grep -v grep | awk '{ print $2 }'`; do kill -9 $i; done
确实挺 low 的,Google 了半天貌似没发现还有其他不幸遭遇的同志。若有更好的解决方案,跪求留言。
作者:wiLdGoose
发布时间:August 20, 2019
分类:技术 Technology
故事背景:
- 暂未启用 CI/CD 机制;
- JVM 以 Jar 包方式启动;
- 存在开发、测试、生产三套环境。
解决的痛点:
- 解放劳动力,使 Jar 包从开发、测试到生产环境的部署自动化;
- 友好且优雅地管理 JVM;
- 我要看到更详细的日志。嗯,配合 ELK 简直真香。
实现前提:
- Jar 包中配置文件(数据池、配置项等)剥离,每套环境独立,Jar 包重复利用;
- 各环境中已配置了 FTP 服务,且可访问到 Jar 包所在目录。
好,开始表演:
#!/bin/sh
# author: xuchao_dot_org
DATE_TIME=$(date "+%Y-%m-%d %H:%M:%S");
BASE_DIR=/data/java/jar/myproject
TMP_DIR=/data/java/tmp/myproject
PROJECT_NAME=myproject_name # 同样也是 Jar 包的文件名
CONFIG_FILE=/data/java/conf/myproject/application.yml
LOG_FILE=/data/java/logs/myproject/$(date +%Y_%m_%d_%H_%M_%S).log
DEV_IP=
DEV_PORT=
DEV_USERNAME=
DEV_PASSWD=
DEV_DIR=/jar/myproject
TEST_IP=
TEST_PORT=
TEST_USERNAME=
TEST_PASSWD=
TEST_DIR=/jar/myproject
export JAVA_HOME=/data/java/jdk/jdk1.8.0_191
export PATH=$JAVA_HOME/bin:$PATH
export JAVA=$JAVA_HOME/bin/java
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
function running() {
count=`ps aux | grep java | grep ${PROJECT_NAME}.jar | grep -v grep | wc -l`
procedure=`ps -ef | grep -w "${PROJECT_NAME}.jar" | grep -w "java" | grep -v "grep" | awk '{print $2}'`
if [ ${count} -eq 1 -o "${procedure}" != "" ]; then
return 0
else
return 1
fi
}
function start() {
if running; then
echo "${PROJECT_NAME} is running."
echo "${DATE_TIME} failed to start, ${PROJECT_NAME} is running." 2>&1 >> $LOG_FILE
exit 1
fi
echo "starting ${PROJECT_NAME}..."
echo "${DATE_TIME} starting ${PROJECT_NAME}..." >> $LOG_FILE
echo ${CLASSPATH} >> $LOG_FILE
exec nohup $JAVA -server -Xms128m -Xmx256m -Xss256k -Dspring.config.location=${CONFIG_FILE} -Djava.io.tmpdir=${TMP_DIR} -jar ${BASE_DIR}/${PROJECT_NAME}\.jar >> $LOG_FILE 2>&1 & # 需要调整 JVM 启动参数的选手请看这里
sleep 15
if running; then
echo "${PROJECT_NAME} is successfully started."
echo "${DATE_TIME} ${PROJECT_NAME} is successfully started." 2>&1 >> ${LOG_FILE}
else
echo "${PROJECT_NAME} is failed to start."
echo "${DATE_TIME} ${PROJECT_NAME} is failed to start." 2>&1 >> ${LOG_FILE}
fi
}
function stop() {
if ! running; then
echo "${PROJECT_NAME} is not running." | tee ${LOG_FILE}
echo "${DATE_TIME} failed to stop, ${PROJECT_NAME} is not running." 2>&1 >> ${LOG_FILE}
exit 1
fi
echo "stopping ${PROJECT_NAME}..."
echo "${DATE_TIME} stopping ${PROJECT_NAME}..." >> $LOG_FILE
pid=`ps -ef | grep -w "${PROJECT_NAME}.jar" | grep -w "java" | grep -v "grep" | awk '{print $2}'`
kill -15 ${pid}
sleep 5
if ! running; then
echo "${PROJECT_NAME} is successfully stoped."
echo "${DATE_TIME} ${PROJECT_NAME} is successfully stoped." 2>&1 >> ${LOG_FILE}
else
echo "${PROJECT_NAME} is failed to stop. trying again..."
echo "${DATE_TIME} ${PROJECT_NAME} is failed to stop. trying again..." 2>&1 >> ${LOG_FILE}
kill -15 ${pid}
sleep 5
if ! running; then
echo "${PROJECT_NAME} is successfully stoped. [2nd attempt]"
echo "${DATE_TIME} ${PROJECT_NAME} is successfully stoped. [2nd attempt]" 2>&1 >> ${LOG_FILE}
else
echo "${PROJECT_NAME} is failed to stop. [2nd attempt]"
echo "${DATE_TIME} ${PROJECT_NAME} is failed to stop. [2nd attempt]" 2>&1 >> ${LOG_FILE}
fi
fi
}
function status() {
if running; then
echo "${PROJECT_NAME} is running."
else
echo "${PROJECT_NAME} is stopped."
fi
}
function help() {
echo "script for jvm managerment. author: xuchao_dot_org"
echo "usage: $0 [start|stop|restart|status|get|help]"
echo " start: start the ${PROJECT_NAME} server"
echo " stop: stop the ${PROJECT_NAME} server"
echo " restart: restart the ${PROJECT_NAME} server"
echo " status: get ${PROJECT_NAME} current status, running or stopped"
echo " get: [dev|test] [version] get file from dev or test"
echo " help: show this message"
}
function get_dev() {
[ -z $1 ] && echo "params error. please see [ $0 help ] for more information." && exit 1
echo "geting file [${PROJECT_NAME}-$1.jar] from dev..."
echo "${DATE_TIME} geting file [${PROJECT_NAME}-$1.jar] from dev..." 2>&1 >> ${LOG_FILE}
ftp -inp 2>&1 >> ${LOG_FILE} <<EOF
open ${DEV_IP} ${DEV_PORT}
user ${DEV_USERNAME} ${DEV_PASSWD}
cd ${DEV_DIR}
lcd ${BASE_DIR}
get ${PROJECT_NAME}-$1.jar
close
bye
EOF
if [ -f "${BASE_DIR}/${PROJECT_NAME}-$1.jar" ]; then
echo "get file [${PROJECT_NAME}-$1.jar] from dev successfully."
echo "${DATE_TIME} get file [${PROJECT_NAME}-$1.jar] from dev successfully." 2>&1 >> ${LOG_FILE}
\cp -rf ${BASE_DIR}/${PROJECT_NAME}-$1.jar ${BASE_DIR}/${PROJECT_NAME}.jar
echo "copy file [${PROJECT_NAME}-$1.jar] into [${PROJECT_NAME}.jar] successfully."
echo "${DATE_TIME} copy file [${PROJECT_NAME}-$1.jar] into [${PROJECT_NAME}.jar] successfully." 2>&1 >> ${LOG_FILE}
else
echo "failed to get file [${PROJECT_NAME}-$1.jar] from dev."
echo "${DATE_TIME} failed to get file [${PROJECT_NAME}-$1.jar] from dev." 2>&1 >> ${LOG_FILE}
fi
}
function get_test() {
[ -z $1 ] && echo "params error. please see [ $0 help ] for more information." && exit 1
echo "geting file [${PROJECT_NAME}-$1.jar] from test..."
echo "${DATE_TIME} geting file [${PROJECT_NAME}-$1.jar] from test..." 2>&1 >> ${LOG_FILE}
ftp -inp 2>&1 >> ${LOG_FILE} <<EOF
open ${TEST_IP} ${TEST_PORT}
user ${TEST_USERNAME} ${TEST_PASSWD}
cd ${TEST_DIR}
lcd ${BASE_DIR}
get ${PROJECT_NAME}-$1.jar
close
bye
EOF
if [ -f "${BASE_DIR}/${PROJECT_NAME}-$1.jar" ]; then
echo "get file [${PROJECT_NAME}-$1.jar] from test successfully."
echo "${DATE_TIME} get file [${PROJECT_NAME}-$1.jar] from test successfully." 2>&1 >> ${LOG_FILE}
\cp -rf ${BASE_DIR}/${PROJECT_NAME}-$1.jar ${BASE_DIR}/${PROJECT_NAME}.jar
echo "copy file [${PROJECT_NAME}-$1.jar] into [${PROJECT_NAME}.jar] successfully."
echo "${DATE_TIME} copy file [${PROJECT_NAME}-$1.jar] into [${PROJECT_NAME}.jar] successfully." 2>&1 >> ${LOG_FILE}
else
echo "failed to get file [${PROJECT_NAME}-$1.jar] from test."
echo "${DATE_TIME} failed to get file [${PROJECT_NAME}-$1.jar] from test." 2>&1 >> ${LOG_FILE}
fi
}
function get_file() {
[[ -z $1 || -z $2 ]] && echo "params error. please see [ $0 help ] for more information." && exit 1
case "$1" in
dev)
get_dev $2
;;
test)
get_test $2
;;
*)
help
exit 1
;;
esac
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
$0 stop
sleep 5
$0 start
;;
status)
status
;;
help)
help
;;
get)
get_file $2 $3
;;
*)
help
exit 1
;;
esac
演出结束,下期再见。
作者:wiLdGoose
发布时间:August 8, 2019
分类:技术 Technology
JVM 的日志太多了,tail -f 累觉不爱,于是有搭建 ELK 堆栈的需求。
ELK 堆栈主要是 4 个组件:
- Elasticsearch:用于存储日志
- Kibana:用于搜索和可视化日志的Web界面
- Logstash:用于处理传入的日志
- Filebeat 代理:充当日志传送代理,利用伐木工具网络协议与 Logstash 进行通信
本次部署条件:ELK 部署在单机,操作系统为 CentOS 7。JVM 通过 Logstash 将日志写入 Elasticsearch,客户端通过 Kibana 实现可视化管理。ELK 组件均使用 7.3.0 版本。未使用 Filebeat 搜集 syslog,此项工作标记为 TODO。
开始。
1、准备工作
1.1、配置国内的 yum 源
增加一个 repo 文件:
vim /etc/yum.repos.d/elasticsearch.repo
添加清华镜像:
[elasticsearch-7.x]
name=Elasticsearch repository for 7.x packages
baseurl=https://mirrors.tuna.tsinghua.edu.cn/elasticstack/yum/elastic-7.x/
gpgcheck=0
enabled=1
强迫症患者来一波:
yum makecache
1.2、安装 Java 运行环境
这里我安装的是 OpenJDK(也可以是 Oracle Java),版本要求 1.8.0 及以上:
yum -y install java
确认 JDK 版本:
java -version
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-b10)
OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode)
若已安装 OpenJDK 且版本为 1.7,很有可能无法通过 yum 来升级。可先 yum remove java,再 yum -y install java。
2、安装与配置 ELK 组件
2.1、安装 ELK 组件
yum -y install elasticsearch kibana logstash
为 Elasticsearch 与 Logstash 创建数据存放与日志的目录:
mkdir -p /data/elasticsearch/{data,logs}
mkdir -p /data/logstash/{data,logs}
chown -R elasticsearch:elasticsearch /data/elasticsearch
chown -R logstash:logstash /data/logstash
2.2、配置 Elasticsearch
修改 Elasticsearch 配置文件:
vim /etc/elasticsearch/elasticsearch.yml
内容为:
cluster.name: test
node.name: node-1
path.data: /data/elasticsearch/data
path.logs: /data/elasticsearch/logs
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["127.0.0.1"]
cluster.initial_master_nodes: ["node-1"]
因为是单机部署,cluster.initial_master_nodes 中的节点名称要与 node.name 一致。其余都好理解。
启动 Elasticsearch:
service elasticsearch start
验证 Elasticsearch 是否成功启动:
ss -ntlup | grep -E "9200|9300"
tcp LISTEN 0 32768 :::9200 :::* users:(("java",pid=30466,fd=286))
tcp LISTEN 0 32768 :::9300 :::* users:(("java",pid=30466,fd=237))
2.3、配置 Kibana
修改 Kibana 配置文件:
vim /etc/kibana/kibana.yml
内容为:
server.port: 5601
server.host: "127.0.0.1"
elasticsearch.hosts: ["http://127.0.0.1:9200"]
kibana.index: ".kibana"
i18n.locale: "zh-CN"
这里值得注意的是,7.3.0 以前的版本中,elasticsearch.hosts 表现为 elasticsearch.url,需要注意甄别。
关于 Kibana 汉化:自 6.x 以来,官方自带汉化资源,7.x 的方法是通过修改配置文件 i18n.locale: "zh-CN" 即可。5.x 与 6.x 的汉化可参考这个项目。
启动 Kibana:
systemctl start kibana
验证 Kibana 是否成功启动:
ss -ntlup | grep 5601
tcp LISTEN 0 511 *:5601 *:* users:(("node",pid=31095,fd=18))
2.4、配置 Logstash
修改 Logstash 配置文件:
vim /etc/logstash/logstash.yml
内容为:
path.data: /data/logstash/data
path.logs: /data/logstash/logs
创建一个索引的配置文件:
vim /etc/logstash/conf.d/my-project-1.conf
这里是针对 JVM 的配置:
input {
tcp {
port => 5044
codec => json_lines
}
}
output {
elasticsearch {
hosts => ["127.0.0.1:9200"]
index => "test-myproject-1-%{type}"
}
stdout {
codec => rubydebug
}
}
配置文件的创建根据项目需要以此类推。
启动 Logstash:
systemctl start logstash
若启动失败,可执行:
/usr/share/logstash/bin/system-install /etc/logstash/startup.options systemd
验证 Logstash 是否成功启动:
ss -ntlup | grep 5044
tcp LISTEN 0 1024 :::5044 :::* users:(("java",pid=8747,fd=134))
2.5、反向代理
我的环境中,“本机”没有公网 80 或 443 端口权限。因此通过 nginx 反代至 ELK 所在主机的 Kibana 端口,即本文的:5601。
3、收尾
到这里为止若无报错,ELK 环境就搭建完毕了。收个尾:
systemctl daemon-reload # 重新加载所有配置文件
systemctl restart elasticsearch kibana logstash # 启动 ELK
systemctl enable elasticsearch kibana logstash # 将 ELK 加入开机启动
systemctl status elasticsearch kibana logstash # 查看 ELK 启动状态
现在,我们的 Java 项目可以通过 5044 端口直接将日志输出到 Logstash。其它诸如 MySQL、系统日志等的配置,敬请期待。
作者:wiLdGoose
发布时间:May 7, 2019
分类:技术 Technology
对于不能 SSH 的场景的确够麻烦,rsync 又嫌麻烦,只能 FTP 了。
以下是自己马克用的。
#!/bin/bash
date=`date +%Y%m%d`
deldate=`date -d -{NUMBER}day +%Y%m%d`
ftp -inp<<!
open {HOST} {PORT}
user {FTP_USER} {FTP_PASSWD}
cd /
lcd /data/backup
mdelete *$deldate*
mput *$date*
close
bye
!
注意几点:
1、脚本中花括号内容替换为对应的变量;
2、cd 是远程的目录,lcd 是本地目录;
3、备份文件名需符合 date +%Y%m%d 这样的规则。
- 1
- 2
- 3
- »