0%

首先将讲iml文件中的type属性值JAVA_MODULE其修改为PLUGIN_MODULE

1
<module type="PLUGIN_MODULE" version="4">

然后给项目选择合适的SDKalt

附上一段插件调试的启动命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@echo off
color 0A

set CONFIG_DIR=%cd%\sandbox
set JDK_DIR=D:\Program Files\Java\jdk1.8.0_191
set IDEA_DIR=D:\Program Files\JetBrains\IntelliJ IDEA 2020.1

java.exe ^
-Dvisualvm.id=434026973766400 ^
-Xmx512m -Xms256m -ea ^
-Didea.config.path=%CONFIG_DIR%\config ^
-Didea.system.path=%CONFIG_DIR%\system ^
-Didea.plugins.path=%CONFIG_DIR%\plugins ^
-Didea.classpath.index.enabled=false "-javaagent:%IDEA_DIR%\lib\idea_rt.jar=51116:%IDEA_DIR%\bin" ^
-Dfile.encoding=GBK ^
-classpath "%JDK_DIR%\lib\tools.jar;%IDEA_DIR%\lib\log4j.jar;%IDEA_DIR%\lib\jdom.jar;%IDEA_DIR%\lib\trove4j.jar;%IDEA_DIR%\lib\openapi.jar;%IDEA_DIR%\lib\util.jar;%IDEA_DIR%\lib\extensions.jar;%IDEA_DIR%\lib\bootstrap.jar;%IDEA_DIR%\lib\idea_rt.jar;%IDEA_DIR%\lib\idea.jar" ^
com.intellij.idea.Main

安装 Alpine

下载

  • Alpine Linux https://www.alpinelinux.org/downloads/,这里选择VIRTUAL,它与标准版相似、瘦下来的内核、针对虚拟系统进行了优化。
  • 下载得到的文件是 alpine-virt-3.12.0-x86_64.iso,大小 40m

安装

  • 引导虚拟光盘,进入 CDROM,登录界面输入账户 root 自动登录,执行 setup-alpine 进入安装程序,根据提示选项完成安装。最后重启进入硬盘系统。
  • 可参考:Alpine linux硬盘安装

配置 Alpine

开启 root 远程登录

  • 编辑 /etc/ssh/sshd_config 文件,配置 PermitRootLoginyes,重启 sshd 服务(/etc/init.d/sshd restart)。

修改 apk 包管理镜像

  • 编辑 /etc/apk/repositories 文件,将里面 dl-cdn.alpinelinux.org 改成 mirrors.aliyun.com,保存退出,然后执行 apk update 刷新缓存。
  • 可参考:https://developer.aliyun.com/mirror/alpine

安装 Docker

Docker

  • 开始安装 apk add docker
  • 要在引导时启动 Docker 守护程序,请运行:rc-update add docker boot
  • 然后手动启动 Docker 守护程序,运行:service docker start

Docker Compose

  • 开始安装 apk add docker-compose
  • 然后发现运行报错,缺失 python-dotenv 库,需要手动安装一下,先安装 pip 包管理器 apk add py-pip,在安装缺失库 pip install python-dotenv
  • 如果 pip 安装缓慢, 可以应用以下镜像加速设置:
    1
    2
    3
    4
    5
    6
    7
    mkdir -p ~/.pip
    tee ~/.pip/pip.conf <<-'EOF'
    [global]
    index-url = https://mirrors.aliyun.com/pypi/simple/
    [install]
    trusted-host=mirrors.aliyun.com
    EOF

Docker 加速镜像

1
2
3
4
5
6
7
8
9
mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://n031lwjl.mirror.aliyuncs.com",
"https://registry.docker-cn.com"
]
}
EOF

其他

Alpine 命令提示

  • Alpine 默认命令解释是 ash,需要换成 bash,再配合 bash-completion 就可以实现命令提示:apk add bash bash-doc bash-completion
  • 更换 ashbash,需要编辑 vi /etc/passwd

CentOS8 不像原来 CentOS7 存在一个 Mini 镜像了, 现在只有 bootDVD, DVD 动辄快10G大小了, 这谁受得了啊~!

boot 是一个纯粹的安装程序镜像, 不包含任何的软件安装源(DVD 版文件尺寸大也是因为自带了一个本地软件安装源), 所以 ISO 文件尺寸较小,只有500+MB。

但是安装的时候就很恼火了, 必须要提供一个有效软件安装源.

安装的时候有一个步骤是选择软件安装源(Software Installation Source)。
地址不正确,会提示“Error setting up base repository”。

这里要输入一个软件源地址, 可以是本地文件系统, 也可以是网络地址, 比如国内比较快的阿里云软件镜像源:
https://mirrors.aliyun.com/centos/8/BaseOS/x86_64/os/

alt

对了, 记得先打开网卡连接...

接下来的操作和 CentOS7 类似, 可以参见: https://6xyun.cn/article/77

注意镜像源地址系统版本是8不再是7!

安装脚手架

首先下载 node.js 要求版本在 8.9 以上

官网:https://nodejs.org/zh-cn/

下载完可检查在 Windows 任务命令行里输入

1
node -v

使用淘宝NPM镜像源下载比较快

1
npm config set registry https://registry.npm.taobao.org/

安装 vue-cli(全局安装vue-cli)

1
npm install vue-cli -g

如果出现 ``

检查环境是否安装上:

1
vue -V

如果在外部终端(比如 VSCode)执行失败, 需要对系统进行配置, 打开脚本执行权限 运行powershell(管理员身份) 输入 set-ExecutionPolicy RemoteSigned 然后根据情况选择对应策略, 建议选A

创建vue项目:

1
vue init webpack demo

 然后进入此文件夹:

1
cd demo

然后手动下载:

1
npm install

最后运行程序:

1
npm run dev

运行之后会导出网址:http://localhost:8080

在网页上输入上面的网址即可

IDEA调试

可以通过下面两个方法支持

  1. 浏览器安装 JetBrains IDE Support 插件支持
  2. IDEA 配置 JavaScript Dubug 运行选项支持

安装脚本(Install.bat)

1
2
3
4
5
6
7
8
9
10
11
12
13
@echo off
color 0a

DockerToolbox-19.03.1.exe /TASKS="modifypath,upgradevm,desktopicon" /TYPE="custom" /COMPONENTS="Docker,DockerMachine,DockerCompose,!VirtualBox,!Kitematic,!Git"

echo f | xcopy start.sh "%DOCKER_TOOLBOX_INSTALL_PATH%\start.sh" /y>nul
echo f | xcopy docker-machine-driver-vmwareworkstation.exe "%DOCKER_TOOLBOX_INSTALL_PATH%\docker-machine-driver-vm.exe" /y>nul
echo f | xcopy docker-machine-driver-vmwareworkstation.exe "%DOCKER_TOOLBOX_INSTALL_PATH%\docker-machine-driver-vmwareworkstation.exe" /y>nul

mkdir "%USERPROFILE%\.docker\machine\cache" >nul
echo f | xcopy boot2docker_*.iso "%USERPROFILE%\.docker\machine\cache\boot2docker.iso" /y>nul

pause

启动脚本(start.sh)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#!/bin/bash

export PATH="$PATH:/d/Program Files (x86)/VMware/VMware Workstation"

trap '[ "$?" -eq 0 ] || read -p "Looks like something went wrong in step ´$STEP´... Press any key to continue..."' EXIT

VM=${DOCKER_MACHINE_NAME-default}
DOCKER_MACHINE=./docker-machine.exe

BLUE='\033[1;34m'
GREEN='\033[0;32m'
NC='\033[0m'


if [ ! -f "${DOCKER_MACHINE}" ]; then
echo "Docker Machine is not installed. Please re-run the Toolbox Installer and try again."
exit 1
fi


vmrun.exe list | grep ${VM} &> /dev/null
VM_EXISTS_CODE=$?
if [ $VM_EXISTS_CODE -eq 1 ]; then
if [ -f ~/.docker/machine/machines/${VM}/${VM}.vmx ]; then
vmrun.exe -T ws start ~/.docker/machine/machines/${VM}/${VM}.vmx nogui
vmrun.exe list | grep ${VM} &> /dev/null
VM_EXISTS_CODE=$?
fi
fi

set -e

STEP="Checking if machine $VM exists"
if [ $VM_EXISTS_CODE -eq 1 ]; then
"${DOCKER_MACHINE}" rm -f "${VM}" &> /dev/null || :
rm -rf ~/.docker/machine/machines/"${VM}"
#set proxy variables if they exists
if [ -n ${HTTP_PROXY+x} ]; then
PROXY_ENV="$PROXY_ENV --engine-env HTTP_PROXY=$HTTP_PROXY"
fi
if [ -n ${HTTPS_PROXY+x} ]; then
PROXY_ENV="$PROXY_ENV --engine-env HTTPS_PROXY=$HTTPS_PROXY"
fi
if [ -n ${NO_PROXY+x} ]; then
PROXY_ENV="$PROXY_ENV --engine-env NO_PROXY=$NO_PROXY"
fi
"${DOCKER_MACHINE}" create -d vmwareworkstation $PROXY_ENV "${VM}"
fi

STEP="Checking status on $VM"
VM_STATUS="$(${DOCKER_MACHINE} status ${VM} 2>&1)"
if [ "${VM_STATUS}" != "Running" ]; then
"${DOCKER_MACHINE}" start "${VM}"
yes | "${DOCKER_MACHINE}" regenerate-certs "${VM}"
fi

STEP="Setting env"
eval "$("${DOCKER_MACHINE}" env --shell=bash --no-proxy "${VM}" | sed -e "s/export/SETX/g" | sed -e "s/=/ /g")" &> /dev/null #for persistent Environment Variables, available in next sessions
eval "$("${DOCKER_MACHINE}" env --shell=bash --no-proxy "${VM}")" #for transient Environment Variables, available in current session

STEP="Finalize"
clear
cat << EOF


## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\_______/

EOF
echo -e "${BLUE}docker${NC} is configured to use the ${GREEN}${VM}${NC} machine with IP ${GREEN}$(${DOCKER_MACHINE} ip ${VM})${NC}"
echo "For help getting started, check out the docs at https://docs.docker.com"
echo
cd

docker () {
MSYS_NO_PATHCONV=1 docker.exe "$@"
}
export -f docker

if [ $# -eq 0 ]; then
echo "Start interactive shell"
exec "$BASH" --login -i
else
echo "Start shell with command"
exec "$BASH" -c "$*"
fi

通常openvpn部署好以后,客户端连接VPN后会被配置一些路由,其客户端的路由会被修改为所有的流量都通过VPN来传输。但有时候,我们需要客户端的某些IP走VPN或者本地网关。这里有两种方法进行openvpn的路由配置

第一种方法: 客户端进行配置

在配置文件下增加如下内容:

1
2
3
4
route-nopull # 客户端连接openvpn后 不从服务端获取路由
max-routes 1000 # 设置路由的最大条数,默认是100,这里可以根据需求修改
route 192.168.0.0 255.255.255.0 net_gateway # 使192.168.0.0/24网段,不走vpn网关
route 192.168.1.0 255.255.255.0 vpn_gateway # 使192.168.1.0/24网段,走vpn网关

注意: 如果配置中有 redirect-gateway def1 则需要先移除

第二种方法: 在服务端进行配置

服务端和客户端的配置略有不同

1
2
push "route 192.168.0.0 255.255.255.0 net_gateway" # 将引号中的路由推送到客户端
push "route 192.168.1.0 255.255.255.0 vpn_gateway" # 将引号中的路由推送到客户端

注意: 如果配置中有 redirect-gateway 则需要先移除

配置完成后,需要重启openvpn服务。

两种方法选取其一即可。

Why

在多人协作项目开发中, 提交信息都是各式各样乱糟糟的, 虽然 Git 已经强制信息不能为空, 但是却因此产生了许多比空更无聊的记录信息. 所以,需要有一套规则来限制一些这些无理的行为.

于是, Commitizen 就火了起来, 下面来学习一下.

安装

Commitizen 是基于 NodeJS 的一个工具集合, 所以必要条件是先安装 NodeJS, 这点小事对于程序员来说不是问题, 就不详细展开了;

安装完 NodeJS 之后, 首先需要将目录变成一个 NodeJS 项目生成 package.json, 以便保存项目配置使工具生效:

1
2
cd ${project_dir}
npm init --yes

--yes 表示忽略交互说明, 根据项目自动生成

然后, 就可以使用 npm 安装 Commitizen 了:

1
npm install -g --save-dev commitizen

-g 表示全局安装, 如果只需要安装到项目目录, 则可以去掉 --save-dev 表示标记上述工具为项目开发依赖

安装完 Commitizen 我们也需要初始化它的配置, 并为它指定一个提交日志适配器, 这里选用的是 cz-conventional-changelogCommitizen 的适配器:

1
commitizen init cz-conventional-changelog --save-dev

另外还有一些其他的适配器, 比如 cz-customizable 可以让项目自定义模板

现在, 就可以使用 git cz 创建新的提交了!

扩展

光有了规范还不够, 有时候还得强制大家执行, 就需要对规则进行检测和拦截, 这里可以使用 husky 工具为本地仓库创建钩子, 把提交记录消息交给 commitlint 进行验证, 即可实现检测和拦截的功能.

安装钩子注册工具 husky、 提交检查工具 @commitlint/cli 及 提交检查规则 @commitlint/config-angular:

1
npm install --save-dev husky @commitlint/cli @commitlint/config-angular

然后创建 commitlint 的配置文件 .commitlintrc.js:

1
module.exports = {extends: ['@commitlint/config-angular']}

最后再启用 husky, 编辑 package.json 文件加入以下内容:

1
2
3
4
5
6
7
8
{
...,
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}

现在, 胡乱写的提交记录将会被拦截!

什么是 TLS

简单来说就是一种利用密码学证书使数据能够在互联网内加密安全传输的一种协议. 目前常见的应用场景就是客户端和服务器之间数据交互的加密(这里的客户端和服务端是一个相对概念, 我们通常把发起请求的一方认为是客户端, 把处理请求的一方认为是服务端), 比如浏览器使用的 https 协议就是基于这类协议的一个衍生协议, 目的就是防止非法数据传输和抵御中间人嗅探和篡改, 保证数据安全性. 另外 TLS 准确来说它是 传输层安全性协议 的一类, 由 SSL2.x 升级 SSL3.x 后改名而来, 本文用 TLS 泛指高版本的 传输层安全性协议. 更多有关 TLS 的介绍请参见:

TLS 的常见应用类型

常见的应用类型可以分为 单向认证双向认证 两类; SSL单向认证和双向认证的区别主要可以归纳为以下几点:

  1. 单向认证只要求站点部署了ssl证书就行,任何用户都可以去访问(IP被限制除外等),只是服务端提供了身份认证。而双向认证则是需要是服务端需要客户端提供身份认证,只能是服务端允许的客户能去访问,安全性相对于要高一些
  2. 双向认证SSL 协议的具体通讯过程,这种情况要求服务器和客户端双方都有证书。
  3. 单向认证SSL 协议不需要客户端拥有CA证书,以及在协商对称密码方案,对称通话密钥时,服务器发送给客户端的是没有加过密的(这并不影响SSL过程的安全性)密码方案。
  4. 如果有第三方攻击,获得的只是加密的数据,第三方要获得有用的信息,就需要对加密的数据进行解密,这时候的安全就依赖于密码方案的安全。而幸运的是,目前所用的密码方案,只要通讯密钥长度足够的长,就足够的安全。这也是我们强调要求使用128位加密通讯的原因。
  5. 一般Web应用都是采用单向认证的,原因很简单,用户数目广泛,且无需做在通讯层做用户身份验证,一般都在应用逻辑层来保证用户的合法登入。但如果是企业应用对接,情况就不一样,可能会要求对客户端(相对而言)做身份验证。这时就需要做双向认证。

TLS 加密流程

没时间解释了, 请自行百度, 注意区分单项加密和双向加密即可

TLS 证书制作

相关概念

在网上找如何签发证书的文章, 基本都是作者整理好的一梭子 Shell, 直接拿来用就可以了 (哈哈, 我自己写文章的时候也会这么干), 但是, 由于证书的事关安全的重要性, 如果对相关概念不清楚, 容易产生"为什么要这么生成, 一句命令不是更好"这种思想就可能会导致证书在使用过程中出现各种问题, 管理混乱. 所以, 如果对这些东西不熟悉或者不理解, 咱们就先来一起捋一捋签发相关的概念和流程:

  1. 证书是一份包含公钥与其他辅助内容的一种文件
  2. 除了CA证书外, 证书都被上一层证书的公钥签名
  3. 既然证书内包含公钥, 那么对应的就会有相应的私钥
  4. 证书颁发一般经过3个过程: a. 拥有一个私钥 b. 用私钥创建一个颁发证书的请求 c. 上级 CA 接收请求之后核验身份, 通过之后将请求文件中的内容取出生成证书并用自己的 CA 证书及私钥对生成的证书文件签名, 并在该证书当中留下自己 CA 的相关信息, 形成整条信任链
  5. X.509 证书包含三个文件:keycsrcrt。 a. key 是服务器上的私钥文件,用于对发送给客户端数据的加密,以及对从客户端接收到数据的解密 b. csr 是证书签名请求文件,用于提交给证书颁发机构(CA)对证书签名 c. crt 是由证书颁发机构(CA)签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有人的公钥,以及签署者的签名等信息

备注:在密码学中,X.509是一个标准,规范了公开秘钥认证、证书吊销列表、授权凭证、凭证路径验证算法等。

综上所述, 基本就概括和解释了网上流传的 openssl 命令的作用和理由, 同时也就解释了像 12306 这样自签证书的网站为什么安装了他的CA证书之后浏览器就不会报警了. 再啰嗦两句, 一般创建 HTTP 相关的证书时不建议加密码, 否则在某些 HTTP 服务器启动时会要求输入密码.

实际操作

这里还是以 openssl 生成一对双向认证证书为例子(默认情况下,openssl 输出格式为 PKCS#1-PEM)

CA 颁发

首先, 我们先颁发自己的 CA证书, 所以第一步, 就得先来一个 CA 对应的私钥:

1
openssl genrsa -out ca.key 2048

然后, 由于是CA证书, 不需要经过谁审核, 所以这里没有创建请求文件的步骤, 就直接颁发 CA证书(其中 -subj 非必要):

简码 全称 示例 含义
C Country Name (2 letter code) CN 国家代码
ST State or Province Name (full name) SiChuan 州或省份
L Locality Name (eg, city) ChengDu 城市
O Organization Name (eg, company) 6xyun 组织机构
OU Organizational Unit Name (eg, section) Master 机构部门
CN Common Name (eg, fully qualified host name) 6xyun CA 域名或证书使用者
emailAddress Email Address username@domain 邮箱地址
1
2
3
4
5
6
# 如果在 Windows 使用 Git Bash 出现错误
# name is expected to be in the format /type0=value0/type1=value1/type2=... where characters may be escaped by \. This name is not in that format: ...
# 则需要在命令前加上
# MSYS_NO_PATHCONV=1
# 例如 MSYS_NO_PATHCONV=1 openssl ...
openssl req -new -x509 -days 365 -key ca.key -out ca.crt -subj "/C=CN/ST=SiChuan/L=ChengDu/O=6xyun/OU=Master/CN=6xyun CA/emailAddress=username@domain"

当然, 上面只是为了表现正确的步骤, 如果你实在不耐烦, openssl 也支持两步合成一步, -newkey 可以让 openssl 先生成一个私钥再用这个私钥自动完成 CA 颁发:

1
openssl req -newkey rsa:2048 -nodes -keyout ca.key -x509 -days 3650 -out ca.crt

以上就完成了 CA 私钥生成以及证书颁发;

服务端颁发

接下来服务端证书, 同样也是先生成私钥:

1
openssl genrsa -out server.key 2048

由于服务端经常需要使用 DNS别名, 所以必须生成一个配置文件生成V3版证书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat << EOF > server.cnf
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req

[ req_distinguished_name ]

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = *.domain.com
DNS.2 = *.domain.cn
IP.1 = 10.0.0.1
IP.2 = 192.168.0.1
EOF

然后是创建请求文件(同样 -subj 非必要):

1
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=SiChuan/L=ChengDu/O=6xyun/OU=Master/CN=Server/emailAddress=username@domain" -config server.cnf

最后就是作为 CA 管理员签发这个服务端证书:

1
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt   -extensions v3_req -config openssl.cnf

经过上面的操作, 就得到了 server.crt 这个证书文件, 注意不要和请求文件 server.csr 混淆了.

客户端颁发

过程和服务器类似, 如果没有特殊需要颁发 V1版本即可:

1
2
3
4
5
6
# 生成私钥
openssl genrsa -out client.key 2048
# 生成请求文件
openssl req -new -key client.key -out client.csr -subj "/C=CN/ST=SiChuan/L=ChengDu/O=6xyun/OU=Master/CN=Client/emailAddress=username@domain"
# 生成证书
openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt

GUI 工具

如果你觉得上面的操作麻烦, 推荐一个极好的全平台支持的生成/管理证书的工具: X Certificate and Key management

引用

前言

由于目前就职的是一家做物联网相关产品的科技公司, 为了帮助公司解决后端发布更新时导致服务不可用问题及帮助项目实现未来业务扩张后集群运行等问题, 结合公司实际情况, 探索合适的技术栈来解决这些问题。最终决定使用 Jenkins + Swarm 来进行实施. 至于采用这套技术栈的原因, 大概可以总结为以下几点:

  • 公司业务需要保证服务高度可用, 发版/更新期间服务不被中断
  • 公司规模小, 提供的云上资源也同样很少, 需要轻量化的解决方案
  • 后端框架陈旧, 资源分布混乱, 需要一套合理的项目管理方法
  • 没有专职的运维人员, 要求配置和管理尽量简单, 降低使用者学习难度曲线

综上所述, 在容器编排选型上面就直接抛弃了 k8s 而选择轻量化的 swarm. 包括镜像管理这一块, 也是直接去掉了公共的存储仓库而选择直接在目标机器操作.

技术要点

  • 项目代码通过 Gitlab 管理, 并遵循工作流方式编写和提交
  • 编译打包通过 Jenkins + Git + Maven 实现, 要求项目能够参数化构建
  • 镜像生成使用 ssh 远程编译(由于不使用镜像仓库, 所以只打包不推送)
  • 集群方案使用 Swarm + docker-compose 方式管理, 使用 compose 的目的是因为公司的资源不多, 单台4C8G的 ECS 要跑4个 J2EE 项目服务以及还有各种中间件, 所以部分有状态的中间件之类的服务通过 compose 来跑尽量节省资源
  • 反向代理使用 Traefik, 减少 nginx 配置(主要原因还是没有 k8s 那样成熟的 ingeress)
  • 容器健康检查, 实现滚动更新时保证新版本服务可用之后才 down 掉旧服务
  • 容器通信使用 Swarm 模式原生方式, 为了保证 docker-compose 部署的服务和 Swarm 的服务能够相互连接, 需要使用自定义的 Swarm 网络
  • 统一日志收集, 集群化之后日志将变得分散, 需要使用有效手段来集中管理

实施过程

docker & swarm 安装

参见 Dokcer-CE & Docker Compose 安装

为了做到足够简单, 并没有去配置 Docker 的远程API, 后面统一使用 Jenkins 远程SSH操作

Traefik部署

参见 Traefik-V2.0 & Docker-Compose 最佳实践

项目使用的是 Swarm 模式, 所以上面文章的内容需要作适量改动, 详情参照官网 https://docs.traefik.io/v2.0/providers/docker/#swarmmode 项目因为只有单节点, 所以 Traefik 只是以 compose 部署的非集群方式运行

Jenkins部署

参见Jenkins + Git + Maven持续构建部署环境搭建

这次搭建过程处理上述的3个插件之外, 还使用到一个轻量的插件 Publish Over SSH, 通过它来实现传输文件和执行命令以及管理主机(不使用 scp & ssh 的原因是这个插件可以通过密码登录, 不用修改 sshd 服务) 另外赠送一篇记录解决 Jenkins 安装插件缓慢问题的文章 Jenkins for Docker 跳过插件安装及插件加速镜像设置 另外 Jenkins 需要的 Git Maven JDK 等组件我这边都是通过 Jenkins 内部安装的, 就不细讲了

ELK部署

日志收集分析使用的业界常用的 ELK 方案(找了很久没有找到合适的轻量替代方案), 搭配阿里的 log-pilot 收集器体验非常好, 为了减少系统采销, 这里使用的是 5.6.16 版本 实际实施发现 5.x 功能太弱 7.x 消耗太高, 最终使用的 6.8.5 版本 docker-compose.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
version: '3.7'
services:
# https://yq.aliyun.com/articles/674327
log-pilot:
image: registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.7-filebeat
restart: always
privileged: true
environment:
- PILOT_TYPE=filebeat
- LOGGING_OUTPUT=elasticsearch
- ELASTICSEARCH_HOSTS=elasticsearch:9200
# - ELASTICSEARCH_USER=
# - ELASTICSEARCH_PASSWORD=
volumes:
- "/:/host:ro"
- "/etc/localtime:/etc/localtime:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
# https://blog.csdn.net/qq_38906421/article/details/88644315
# https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html
elasticsearch:
image: elasticsearch:6.8.5
restart: always
environment:
- ES_JAVA_OPTS=-Xms256m -Xmx256m
- xpack.security.enabled=true
volumes:
- "{es_data_path}:/usr/share/elasticsearch/data/:rw"
# https://www.elastic.co/guide/en/kibana/current/docker.html
kibana:
image: kibana:6.8.5
restart: always
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- XPACK_MONITORING_ENABLED=true
- ELASTICSEARCH_USERNAME=kibana
- ELASTICSEARCH_PASSWORD={password}
ports:
- 5601:5601

综合实现

上面的部分基本就是环境准备, 主要的实现还是在 Jenkins 里面的脚本, 主要方案就是打包项目之后, 将项目发送到目标机器之后, 在通过 Docker 编译, 最终使用 docker stack deploy 命令发布服务到 Swarm 简单记录一下使用到的脚本配置(下面的脚本属于和项目绑定的关系, 不适用于任何其他项目, 仅作为参考)

KEY VALUE 备注
Source files module/target/app.war 传输的文件
Remove prefix module/target/ 忽略目录
Remote directory /root/docker-build/$JOB_BASE_NAME-$BUILD_NUMBER/ 存储路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 包名
PN=app.war
# 镜像名字
IN=domain/project:$BUILD_NUMBER
# 工作目录
WD=/root/docker-build/$JOB_BASE_NAME-$BUILD_NUMBER/

# 准备
cd $WD

# 构建
cat << EOF > Dockerfile
FROM tomcat:8.5.47-jdk8-openjdk

RUN \
echo 'tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}' >> /usr/local/tomcat/conf/catalina.properties && \
rm -rf /usr/local/tomcat/webapps/*

ENV TZ=Asia/Shanghai
ENV JAVA_OPTS="-Xmx256m -Djava.security.egd=file:/dev/urandom"

ADD $PN /usr/local/tomcat/webapps/$PN

HEALTHCHECK --interval=10s --timeout=3s --start-period=5m --retries=3 CMD curl --silent --fail http://localhost:8080/app/ || exit 1
EOF
docker build -t $IN .

# 发布
cat << EOF > docker-compose.yaml
version: '3.7'
services:
app:
image: $IN
networks:
- global
labels:
- "traefik.enable=true"
- "traefik.http.middlewares.gzip.compress=true"
- "traefik.http.middlewares.ssl.headers.sslRedirect=true"
- "traefik.http.routers.app_http.entrypoints=http"
- "traefik.http.routers.app_http.rule=Host(\`api.domain.org\`)"
- "traefik.http.routers.app_http.middlewares=gzip"
- "traefik.http.routers.app_https.entrypoints=https"
- "traefik.http.routers.app_https.rule=Host(\`api.domain.org\`)"
- "traefik.http.routers.app_https.middlewares=gzip"
- "traefik.http.routers.app_https.tls.certresolver=acme-resolver"
- "traefik.http.services.app.loadbalancer.server.port=8080"
# index_topic 实测不能出现下划线
- "aliyun.logs.${index_topic}=stdout"
deploy:
mode: replicated
replicas: 2
update_config:
parallelism: 1
order: start-first
networks:
global:
name: net-global
external: true
EOF
docker stack deploy --resolve-image never --orchestrator swarm -c docker-compose.yaml $JOB_BASE_NAME

之所以没有将 labels 放到 deploy 之下, 是因为 Traefik 是以非集群模式运行的 labelsdeploy 之下时 labels服务 的标签 labelsservice 之下时 labels容器 的标签

附录

  • 用于测试Api的脚本
1
2
3
4
5
6
7
8
#!/bin/sh
while true
do
current=`date "+%Y-%m-%d %H:%M:%S"`
timeStamp=`date -d "$current" +%s`
echo $timeStamp-`curl -I -m 1 -f http://domain:port/path -o /dev/null -s -w %{http_code}`
sleep 1
done

跳过初始化

dockre-compose.yaml

1
2
3
4
5
6
7
version: '3.7'
services:
jenkins:
image: jenkins/jenkins:2.204
restart: always
environment:
- JAVA_OPTS= -Djenkins.install.runSetupWizard=false
  • 重点就是 -Djenkins.install.runSetupWizard=false 这个参数

插件加速镜像设置(初始化)

dockre-compose.yaml

1
2
3
4
5
6
7
version: '3.7'
services:
jenkins:
image: jenkins/jenkins:2.204
restart: always
environment:
- JENKINS_UC_DOWNLOAD=https://mirrors.tuna.tsinghua.edu.cn/jenkins/

插件加速镜像设置(运行时)

下面是一些常用的加速地址

  1. https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
  2. http://mirror.esuni.jp/jenkins/updates/update-center.json
  3. http://mirror.xmission.com/jenkins/updates/update-center.json

插件最终下载加速

经过上面的设置, 最终下载还是一样的慢. 原因是缓存的下载地址还是原始地址, 所以需要黑科技一下:

1
2
sed -i 's/http:\/\/updates.jenkins-ci.org\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' /var/jenkins_home/updates/default.json
sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' /var/jenkins_home/updates/default.json

最终实验, 在第一次启动之后要求输入初始化密码时执行上面的命令, 然后重启容器, 就能很顺利的完成初始化。