前言

极客营企业级Java应用(实践星)现代运维系统架构实战(简称现代运维实战)是极客营Linux全栈运维课程的综合实践部分。

现代运维实战让新人在三个月内掌握IT团队和IT产品的运维工作大致脉络和部分细节,涉及企业级Java应用、Linux系统和软件的常用配置、系统和网络安全加固、源代码版本控制(Git/GitLab)、自动化构建系统(Jenkins)、数据缓存集群(Redis)、数据库主从和集群(MySQL)、分布式文件存储(Ceph)、数据备份系统、系统监控和风险预警,全方位覆盖IT团队对产品研发、数字运营、数据安全、流量支撑等方面的需求。

现代运维实战假设读者已经完全掌握 《Pro Linux之基础快速上手 》 ,具备搭建LAMP/LNMP、Tomcat等常见Web环境的能力。

现代运维实战内容上只强调操作过程,对Linux系统原理和操作原则、软件工作原理、系统框架和数据系统设计等理论方面都未涉及。

理论方面是全栈运维工作的重点,虽然有很多参考书籍和技术资料。但,技术路线跨度大、学科交叉多,一般情况很难突破,所以本文未考虑理论方面内容。

1. 综述

1.1. 服务器描述

1.1.1. 生产服务器A

主机名

wan_servera

SSH远程端口

37856

操作系统

CentOS 7

IP地址

192.168.1.121

用途

MySQL Master/用户访问入口/Redis Slave/Ceph Slave

1.1.2. 生产服务器B

主机名

wan_serverb

SSH远程端口

37856

操作系统

CentOS 7

IP地址

192.168.1.122

用途

MySQL Slave/Redis Master/Ceph Slave

1.1.3. 生产服务器C

主机名

wan_serverc

SSH远程端口

37856

操作系统

CentOS 7

IP地址

192.168.1.123

用途

MySQL Slave/备份服务器/Redis Slave/Redis Sentinel/Ceph Master

1.1.4. 内网服务器

主机名

lan_server

SSH远程端口

22

操作系统

CentOS 7

IP地址

192.168.1.124

内存

8G(GitLab+Jenkins)

用途

GitLab/Jenkins

1.1.5. 开发工作机

主机名

devel_machine

1.2. 系统要求

1.2.1. 生产服务器

CentOS 7

2 Ghz 双核处理器或更高
4 GB 系统内存
30 GB 硬盘空间
互联网接入

1.2.2. 内网服务器

CentOS 7

2 Ghz 双核处理器或更高
8 GB 系统内存
30 GB 硬盘空间
互联网接入

1.2.3. 开发工作机

Ubuntu 22.04.1 LTS

2 Ghz 双核处理器或更高
4 GB 系统内存
30 GB 硬盘空间
互联网接入

1.2.4. Linux发行版 ISO 下载地址

  1. CentOS 7 ISO(迷你安装)

    • )下载地址一:清华大学

    • )下载地址二:网易

    • )下载地址三:阿里云

  2. Ubuntu 22.04.1 LTS 桌面版 ISO(DVD)

    • )下载地址一:清华大学

    • )下载地址二:网易

    • )下载地址三:阿里云

2. 初始化配置CentOS 7

操作位置
  • 内网服务器

  • 生产服务器A

  • 生产服务器B

  • 生产服务器C

2.1. 配置YUM

2.1.1. 仅安装64位软件包

位置:内网服务器&生产服务器A/B/C,用户:root
echo "exclude=*.i386 *.i586 *.i686" >> /etc/yum.conf

2.1.2. 强制YUM使用IPv4

位置:内网服务器&生产服务器A/B/C,用户:root
echo 'ip_resolve=4' >> /etc/yum.conf

个别时候,YUM会出现IPv4的网络,意外使用IPv6而导致报错。比如:

终端输出
Downloading packages:
jenkins-2.375.2-1.1.noarch.rpm FAILED
http://pkg.jenkins.io/redhat-stable/jenkins-2.375.2-1.1.noarch.rpm: [Errno 14] curl#7 - "Failed to connect to 2a04:4e42:1a::645: Network is unreachable"
Trying other mirror.

2.2. 更新系统

更新软件包至最新状态是系统安装完成后,第一件事情,也是最重要的事情

保持CentOS 7软件包都是最新状态,可以避免出现一些未知问题

位置:内网服务器&生产服务器A/B/C,用户:root
yum update -y

比如 curl 访问URL时,提示HTTPS证书不可信,而不能访问。

实际上更新这几个包基本都能解决问题: yum update -y ca-certificates nss curl

怎么分析出问题原因的?

  1. -Iv 参数访问有问题的URL:

    位置:内网服务器&生产服务器A/B/C,用户:root
    curl -Iv https://modern-sys-arch-action.cdgeekcamp.com/
    终端输出
    * About to connect() to modern-sys-arch-action.cdgeekcamp.com port 443 (#0)
    *   Trying 47.103.57.228...
    * Connected to modern-sys-arch-action.cdgeekcamp.com (47.103.57.228) port 443 (#0)
    * Initializing NSS with certpath: sql:/etc/pki/nssdb (1)
    *   CAfile: /etc/pki/tls/certs/ca-bundle.crt (2)
      CApath: none
    * SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    * Server certificate:
    * 	subject: CN=*.cdgeekcamp.com
    * 	start date: Dec 29 15:04:25 2022 GMT
    * 	expire date: Mar 29 15:04:24 2023 GMT
    * 	common name: *.cdgeekcamp.com
    * 	issuer: CN=R3,O=Let's Encrypt,C=US
    > HEAD / HTTP/1.1
    > User-Agent: curl/7.29.0
    > Host: modern-sys-arch-action.cdgeekcamp.com
    ......
    ......
    1 出现 NSS 关键字
    2 NSS 看起来又依赖 /etc/pki/tls/certs/ca-bundle.crt 文件
  2. 查询 /etc/pki/tls/certs/ca-bundle.crt 文件属于哪个软件包

    位置:内网服务器&生产服务器A/B/C,用户:root
    yum provides /etc/pki/tls/certs/ca-bundle.crt
    终端输出
    Loaded plugins: fastestmirror
    Loading mirror speeds from cached hostfile
     * base: mirrors.huaweicloud.com
     * extras: mirrors.huaweicloud.com
     * updates: mirrors.huaweicloud.com
    ......
    ......
    ......
    ca-certificates-2022.2.54-74.el7_9.noarch : The Mozilla CA root certificate bundle (1)
    Repo        : @updates
    Matched from:
    Filename    : /etc/pki/tls/certs/ca-bundle.crt
    1 软件包全名:ca-certificates-2022.2.54-74.el7_9.noarch
  3. 查询软件包(短)名称

    位置:内网服务器&生产服务器A/B/C,用户:root
    yum info ca-certificates-2022.2.54-74.el7_9.noarch | egrep 'Name\s+:'
    终端输出
    Name        : ca-certificates

2.3. EPEL镜像设置

  1. 安装EPEL软件包

    位置:内网服务器&生产服务器A/B/C,用户:root
    yum install -y epel-release
  2. 设置国内镜像服务器,加速EPEL软件包下载

    位置:内网服务器&生产服务器A/B/C,用户:root
    sed -e 's!^metalink=!#metalink=!g' \
        -e 's!^#baseurl=!baseurl=!g' \
        -e 's!//download\.fedoraproject\.org/pub!//mirrors.tuna.tsinghua.edu.cn!g' \
        -e 's!//download\.example/pub!//mirrors.tuna.tsinghua.edu.cn!g' \
        -e 's!http://mirrors!https://mirrors!g' \
        -i /etc/yum.repos.d/epel*.repo
    更加详细的EPEL镜像设置说明,请访问 清华大学EPEL镜像使用帮助
  3. 创建 yum 缓存,减少网络请求

    位置:内网服务器&生产服务器A/B/C,用户:root
    yum makecache
    终端输出
    Loaded plugins: fastestmirror
    Loading mirror speeds from cached hostfile
     * base: mirrors.tuna.tsinghua.edu.cn
     * extras: mirrors.ustc.edu.cn
     * updates: mirrors.ustc.edu.cn
    base                                                     | 3.6 kB     00:00
    epel                                                     | 4.7 kB     00:00
    extras                                                   | 2.9 kB     00:00
    updates                                                  | 2.9 kB     00:00
    Metadata Cache Created

2.4. 安装常用软件

位置:内网服务器&生产服务器A/B/C,用户:root
yum install -y vim-enhanced wget curl yum-utils tree pwgen unzip expect
yum-utils 包含下文会用到的 yum-config-manageryumdownloader

2.5. 安装Python3.8+

系统管理使用的Python程序依赖Python 3.8+,Yum源中只有Python 3.6,故使用压缩包安装较新的版本

2.5.1. 安装依赖:OpenSSL 1.1+

安装OpenSSL 1.1+软件包
位置:内网服务器&生产服务器A/B/C,用户:root
pkg_name=openssl
pkg_ver=1.1.1n
pkg_rel=1
pkg=${pkg_name}-${pkg_ver}
tar_name=${pkg_name}-${pkg_ver}-${pkg_rel}.el7.x86_64.tar.gz
url=http://dl.cdgeekcamp.com/centos/7/${pkg_name}/${pkg_ver}/${tar_name}
prefix=/usr/local/${pkg}

test -d ${prefix} || (wget ${url} -O /tmp/${tar_name} && tar xf /tmp/${tar_name} -C $(dirname ${prefix}))

rm -f /tmp/${tar_name}
更新动态链接库配置
位置:内网服务器&生产服务器A/B/C,用户:root
egrep "^${prefix}/lib" /etc/ld.so.conf || (echo "${prefix}/lib" >> /etc/ld.so.conf && ldconfig)

确认配置:

位置:内网服务器&生产服务器A/B/C,用户:root
ldconfig -p | grep ${prefix}
终端输出
	libssl.so.1.1 (libc6,x86-64) => /usr/local/openssl-1.1.1n/lib/libssl.so.1.1
	libssl.so (libc6,x86-64) => /usr/local/openssl-1.1.1n/lib/libssl.so
	libcrypto.so.1.1 (libc6,x86-64) => /usr/local/openssl-1.1.1n/lib/libcrypto.so.1.1
	libcrypto.so (libc6,x86-64) => /usr/local/openssl-1.1.1n/lib/libcrypto.so

2.5.2. 安装Python3.8+

设置Python3.8+安装变量
位置:内网服务器&生产服务器A/B/C,用户:root
pkg_name=python
pkg_ver=3.10.5
pkg_short_num=3
pkg_ver_num=310
pkg_rel=1
pkg=${pkg_name}-${pkg_ver}
tar_name=${pkg_name}-${pkg_ver}-${pkg_rel}.el7.x86_64.tar.gz
url=http://dl.cdgeekcamp.com/centos/7/python/3/${tar_name}
prefix=/usr/local/${pkg}
bin_dir=${prefix}/bin
安装Python3.8+软件包
位置:内网服务器&生产服务器A/B/C,用户:root
test -d ${prefix} || (wget ${url} -O /tmp/${tar_name} && tar xf /tmp/${tar_name} -C $(dirname ${prefix}))

rm -f /tmp/${tar_name}
设置Python3.8+的环境变量
位置:内网服务器&生产服务器A/B/C,用户:root
echo "export PATH=${bin_dir}:${PATH}" > /etc/profile.d/python${pkg_short_num}.sh
. /etc/profile

确认设置:

位置:内网服务器&生产服务器A/B/C,用户:root
which python${pkg_short_num} | grep ${prefix}
终端输出
/usr/local/python-3.10.5/bin/python3 (1)
1 注意看版本号 3.10.5
设置Python3.8+的软链接
位置:内网服务器&生产服务器A/B/C,用户:root
cd ${bin_dir}
test -L pip${pkg_ver_num} || ln -v -s pip${pkg_short_num} pip${pkg_ver_num}
test -L python${pkg_ver_num} || ln -v -s python${pkg_short_num} python${pkg_ver_num}

echo -e "PIP软链接:pip${pkg_ver_num}\nPython3软链接:python${pkg_ver_num}"

测试PIP命令:

位置:内网服务器&生产服务器A/B/C,用户:root
echo -e "执行命令:pip${pkg_ver_num} list\n" && pip${pkg_ver_num} list
终端输出
执行命令:pip310 list (1)

Package    Version
---------- -------
pip        22.3.1
setuptools 58.1.0
1 pip310是新建的PIP软链接
设置PIP国内镜像

系统全局设置,系统有效

位置:内网服务器&生产服务器A/B/C,用户:root
pip${pkg_ver_num} config set --global global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
终端输出
Writing to /etc/pip.conf
更新最新版PIP
位置:内网服务器&生产服务器A/B/C,用户:root
pip${pkg_ver_num} install --root-user-action=ignore -U pip

pip${pkg_ver_num} 只是安装时使用,日常请用类似 pip310 代替

同样的,执行Python程序,也请用类似 python310 代替

2.6. 安装命令行编辑工具

2.6.1. XML编辑工具

位置:内网服务器&生产服务器A/B/C,用户:root
yum install -y xmlstarlet
操作示例

准备测试XML文件:

位置:不限,用户:不限
cat << EOF > test_xmlstarlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <mirrors>
    <mirror>
      <id>aliyun</id>
      <name>Aliyun Central</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
</settings>
EOF

查看默认名称空间下的 /settings/mirrors/mirror 节点:

位置:不限,用户:不限
xmlstarlet sel -t -m '/_:settings/_:mirrors/_:mirror' -c . -n test_xmlstarlet.xml
终端输出
<mirror xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <id>aliyun</id>
      <name>Aliyun Central</name>
      <url>https:/maven.aliyun.com/repository/public</url>
      <mirrorOf>*</mirrorOf>
</mirror>

查看默认名称空间下的 /settings/mirrors/mirror/id 的值:

位置:不限,用户:不限
xmlstarlet sel -t -m '/_:settings/_:mirrors/_:mirror' -v '//_:id' -n test_xmlstarlet.xml
终端输出
aliyun

删除默认名称空间下的 /settings/mirrors/mirror 节点,添加一个新的 /settings/mirrors/mirror 节点(相当于覆盖同名节点):

位置:不限,用户:不限
xmlstarlet ed \
    -d '/_:settings/_:mirrors/_:mirror' \
    -s '/_:settings/_:mirrors' -t elem -n 'mirror' \
    -s '/_:settings/_:mirrors/mirror' -t elem -n id -v 'aliyun2' \
    -s '/_:settings/_:mirrors/mirror' -t elem -n name -v 'Aliyun Central2' \
    -s '/_:settings/_:mirrors/mirror' -t elem -n url -v 'https:/maven.aliyun.com/repository/public' \
    -s '/_:settings/_:mirrors/mirror' -t elem -n mirrorOf -v '*' \
    test_xmlstarlet.xml
类似 xmlstarlet ed -L 可以直接编辑保存
终端输出
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <mirrors>
    <mirror>
      <id>aliyun2</id>
      <name>Aliyun Central2</name>
      <url>https:/maven.aliyun.com/repository/public</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
</settings>

列出XML文档的结构:

位置:不限,用户:不限
xmlstarlet el test_xmlstarlet.xml
终端输出
settings
settings/mirrors
settings/mirrors/mirror
settings/mirrors/mirror/id
settings/mirrors/mirror/name
settings/mirrors/mirror/url
settings/mirrors/mirror/mirrorOf

2.6.2. INI和Java Properties文件编辑工具

位置:内网服务器&生产服务器A/B/C,用户:root
pip310 install --root-user-action=ignore -U crudini
详细使用说明,请访问 crudini
操作示例

准备测试Properties文件:

位置:不限,用户:不限
cat << EOF > test_crudini.properties
server.port=8080
spring.redis.client-name=ShiJianXing
spring.redis.host=localhost
spring.redis.password=
spring.redis.port=6379
spring.redis.database=0
spring.redis.connect-timeout=1000
spring.redis.timeout=1000
spring.redis.maxRetries=5
spring.redis.maxTotalRetriesSeconds=10
application.config.user-photo-save-dir=/var/lib/sjx/images
logging.config=classpath:logback.xml
spring.thymeleaf.check-template-location=false
EOF

读取指定字段:

位置:不限,用户:不限
crudini --get --existing test_crudini.properties "" application.config.user-photo-save-dir
"" 表示 application.config.user-photo-save-dir 字段没有在 [section] 下,也就是空section
终端输出
/var/lib/sjx/images

设置指定字段:

位置:不限,用户:不限
crudini --set --existing test_crudini.properties "" application.config.user-photo-save-dir /tmp

确认设置:

位置:不限,用户:不限
crudini --get --existing test_crudini.properties "" application.config.user-photo-save-dir
终端输出
/tmp

2.6.3. JSON编辑工具(可选安装)

位置:内网服务器&生产服务器A/B/C,用户:root
wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 -O /usr/local/bin/jq
chmod 755 /usr/local/bin/jq
jq 没有类似 sed -i 的编辑保存功能,需要临时文件转存
详细使用说明,请访问 jq Tutorialcrudini
操作示例

准备测试JSON文件:

位置:不限,用户:不限
cat << EOF > test_jq.json
{
  "property1": true,
  "list": [
    {
      "id": 1,
      "name": "APP1"
    },
    {
      "id": 2,
      "name": "APP2"
    }
  ],
  "property2": false
}
EOF

删除列表中的 APP1 对象:

位置:不限,用户:不限
jq 'del(.list[] | select(.name=="APP1"))' test_jq.json
{
  "property1": true,
  "list": [
    {
      "id": 2,
      "name": "APP2"
    }
  ],
  "property2": false
}

name 来自Shell变量,则使用 --arg 参数传递给 jq

位置:不限,用户:不限
name=APP2
jq --arg name "${name}" 'del(.list[] | select(.name==$name))' test_jq.json
{
  "property1": true,
  "list": [
    {
      "id": 1,
      "name": "APP1"
    }
  ],
  "property2": false
}

2.6.4. YAML编辑工具(可选安装)

位置:内网服务器&生产服务器A/B/C,用户:root
pip310 install --root-user-action=ignore -U yq
详细使用说明,请访问 yq
操作示例

准备测试YAML文件:

位置:不限,用户:不限
cat << EOF > test_yq.yaml
name: "antlr4"
version: "4.9.0"
release_number: 1
package: false
description: "New Dart runtime for ANTLR4."
homepage: "https://github.com/antlr/antlr4"
license: "BSD-3-Clause"
dependencies:
  logging: ^0.11.4
  collection: ^1.14.12
dev_dependencies:
  pedantic: ^1.0.0

environment:
  sdk: ">=2.7.0 <3.0.0"
EOF

修改指定字段为数字类型:

位置:不限,用户:不限
yq -y .release_number=2 test_yq.yaml
终端输出
name: antlr4
version: 4.9.0
release_number: 2 (1)
package: false
description: New Dart runtime for ANTLR4.
homepage: https://github.com/antlr/antlr4
license: BSD-3-Clause
dependencies:
  logging: ^0.11.4
  collection: ^1.14.12
dev_dependencies:
  pedantic: ^1.0.0
environment:
  sdk: '>=2.7.0 <3.0.0'
1 release_number

修改指定字段为布尔类型:

位置:不限,用户:不限
yq -y .package=true test_yq.yaml
终端输出
name: antlr4
version: 4.9.0
release_number: 1
package: true (1)
description: New Dart runtime for ANTLR4.
homepage: https://github.com/antlr/antlr4
license: BSD-3-Clause
dependencies:
  logging: ^0.11.4
  collection: ^1.14.12
dev_dependencies:
  pedantic: ^1.0.0
environment:
  sdk: '>=2.7.0 <3.0.0'
1 package

修改指定字段为字符串类型:

位置:不限,用户:不限
yq -y .version='"4.9.100"' test_yq.yaml
终端输出
name: antlr4
version: 4.9.100 (1)
release_number: 1
package: false
description: New Dart runtime for ANTLR4.
homepage: https://github.com/antlr/antlr4
license: BSD-3-Clause
dependencies:
  logging: ^0.11.4
  collection: ^1.14.12
dev_dependencies:
  pedantic: ^1.0.0
environment:
  sdk: '>=2.7.0 <3.0.0'
1 version
类似 yq -yi 可以直接编辑保存

2.6.5. TOML编辑工具(可选安装)

位置:内网服务器&生产服务器A/B/C,用户:root
pip310 install --root-user-action=ignore -U toml-cli
详细使用说明,请访问 toml-cli
操作示例

准备测试TOML文件:

位置:不限,用户:不限
cat << EOF > test_toml_cli.toml
[tool.poetry]
name = "toml-cli"
version = "0.3.1"
description = "Command line interface to read and write keys/values to/from toml files"
authors = ["Marc Rijken"]
license = "MIT"
repository = "https://github.com/mrijken/toml-cli"
readme = "README.md"
packages = [
    { include = "toml_cli" },
]
EOF

读取指定字段的值:

位置:不限,用户:不限
toml get --toml-path test_toml_cli.toml tool.poetry.name
终端输出
toml-cli

设置指定字段的值:

位置:不限,用户:不限
toml set --toml-path test_toml_cli.toml tool.poetry.version 0.2.0
test_toml_cli.toml文件内容
[tool.poetry]
name = "toml-cli"
version = "0.2.0" (1)
description = "Command line interface to read and write keys/values to/from toml files"
authors = ["Marc Rijken"]
license = "MIT"
repository = "https://github.com/mrijken/toml-cli"
readme = "README.md"
packages = [
    { include = "toml_cli" },
]
1 tool.poetry.version
位置:不限,用户:不限
toml add_section --toml-path test_toml_cli.toml tool.poetry.new_section
test_toml_cli.toml文件内容
[tool.poetry]
name = "toml-cli"
version = "0.2.0"
description = "Command line interface to read and write keys/values to/from toml files"
authors = ["Marc Rijken"]
license = "MIT"
repository = "https://github.com/mrijken/toml-cli"
readme = "README.md"
packages = [
    { include = "toml_cli" },
]

[tool.poetry.new_section] (1)
1 tool.poetry.new_section

移除指定字段:

位置:不限,用户:不限
toml unset --toml-path test_toml_cli.toml tool.poetry.version
test_toml_cli.toml文件内容
[tool.poetry]
name = "toml-cli" (1)
description = "Command line interface to read and write keys/values to/from toml files"
authors = ["Marc Rijken"]
license = "MIT"
repository = "https://github.com/mrijken/toml-cli"
readme = "README.md"
packages = [
    { include = "toml_cli" },
]
[tool.poetry.new_section]
1 以前的 version = "0.2.0" 已经不存在了

2.7. 设置系统时区

当前系统时区设置为 中国上海

位置:内网服务器&生产服务器A/B/C,用户:root
yes|cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

查看系统时间:

位置:内网服务器&生产服务器A/B/C,用户:root
date
终端输出
Wed Jan 11 01:14:32 CST 2023

查看格式化的系统时间:

位置:内网服务器&生产服务器A/B/C,用户:root
date "+%Y-%m-%d %H:%M:%S %Z %z"
终端输出
2023-01-11 01:11:49 CST +0800
CST(China Standard Time) 表示中国标准时间(UTC +8)

2.8. 安装时间同步服务

安装时间同步软件 chrony

位置:内网服务器&生产服务器A/B/C,用户:root
yum install -y chrony

设置开机启动:

位置:内网服务器&生产服务器A/B/C,用户:root
systemctl enable chronyd

启动时间同步服务:

位置:内网服务器&生产服务器A/B/C,用户:root
systemctl start chronyd

最新版的Systemd已经集成网络时间同步服务(systemd-timesyncd),可以直接使用:

位置:内网服务器&生产服务器A/B/C,用户:root
systemctl enable systemd-timesyncd
systemctl start systemd-timesyncd

CentOS 7使用的老版Systemd(Systemd v219),不带网络时间同步服务

2.9. 禁用IPv6

永久禁用IPv6,重启后生效:

位置:内网服务器&生产服务器A/B/C,用户:root
echo "net.ipv6.conf.all.disable_ipv6 = 1" >>  /etc/sysctl.conf
echo "net.ipv6.conf.default.disable_ipv6 = 1" >>  /etc/sysctl.conf

2.10. 删除安装日志

位置:内网服务器&生产服务器A/B/C,用户:root
rm -f ~/anaconda-ks.cfg  ~/install.log  ~/install.log.syslog

2.11. 禁用SELINUX

设置SELINUX配置文件,重启后生效:

位置:内网服务器&生产服务器A/B/C,用户:root
echo SELINUX=disabled>/etc/selinux/config
echo SELINUXTYPE=targeted>>/etc/selinux/config
SELINUX在日常Linux使用经验中,并没有发挥安全方面的初衷。相反和Linux本身的系统机制和功能有很多重叠部分。Linux系统安全来自多个方面,有没有启用SELINUX,对企业级的Linux系统管理并没有实际影响

2.12. 设置最大文件句柄

也许你听过,"Linux下,一切皆文件"文件句柄 种类很多,常见的有普通文件句柄(C语言中 open() 函数返回的就是文件句柄)、网络相关句柄等

永久设置打开文件(句柄)的最大数量,重启后生效:

位置:内网服务器&生产服务器A/B/C,用户:root
echo "*               soft   nofile            65535" >> /etc/security/limits.conf
echo "*               hard   nofile            65535" >> /etc/security/limits.conf

临时有效的方法,退出当前Shell后失效:

位置:内网服务器&生产服务器A/B/C,用户:root
ulimit -n 65535

重启后,可以这样确认设置:

位置:内网服务器&生产服务器A/B/C,用户:root
ulimit -n
终端输出
65535

2.13. 优化SSH服务

  1. 修改SSH服务端配置文件(sshd_config) ,加速登录速度、设置连接保持等

    位置:内网服务器&生产服务器A/B/C,用户:root
    # SSH连接时,服务端会将客户端IP反向解析为域名,导致登录过程缓慢
    sed -i "s/#UseDNS yes/UseDNS no/" /etc/ssh/sshd_config
    sed -i "s/GSSAPIAuthentication yes/GSSAPIAuthentication no/" /etc/ssh/sshd_config
    sed -i "s/GSSAPICleanupCredentials yes/GSSAPICleanupCredentials no/" /etc/ssh/sshd_config
    # 登录失败最大尝试次数
    sed -i "s/#MaxAuthTries 6/MaxAuthTries 10/" /etc/ssh/sshd_config
    # 连接保持:服务端每隔N秒发送一次请求给客户端,客户端响应后完成一次连接保持检查
    sed -i "s/#ClientAliveInterval 0/ClientAliveInterval 30/" /etc/ssh/sshd_config
    # 连接保持最大重试次数:服务端发出请求后,超过N次没有收到客户端响应,服务端则断开客户端连接
    sed -i "s/#ClientAliveCountMax 3/ClientAliveCountMax 10/" /etc/ssh/sshd_config
  2. 重载SSH服务端配置

    位置:内网服务器&生产服务器A/B/C,用户:root
    systemctl reload sshd
  3. 退出当前Shell,重新登录SSH后,新配置生效

2.14. 设置主机名

2.14.1. 初始化本地解析设置

位置:内网服务器&生产服务器A/B/C,用户:root
echo '127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4' > /etc/hosts

如果 /etc/hosts 文件中的存在:

::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

个别软件会直接使用 ::1 导致出现网络问题。建议在IPv4的系统中,直接删除IPv6的 ::1 配置。

2.14.2. 修改主机名

  1. 修改主机名

    位置:生产服务器A,用户:root
    hostnamectl set-hostname wan_servera
    位置:生产服务器B,用户:root
    hostnamectl set-hostname wan_serverb
    位置:生产服务器C,用户:root
    hostnamectl set-hostname wan_serverc
    位置:内网服务器,用户:root
    hostnamectl set-hostname lan_server
  2. 查看新的主机名

    位置:生产服务器A/B/C及内网服务器,用户:root
    hostnamectl status | grep 'Static hostname:'
  3. 查看主机名:hostname

    位置:内网服务器&生产服务器A/B/C,用户:root
    env | grep HOSTNAME
    终端输出
    HOSTNAME=wan_servera
    或
    HOSTNAME=wan_serverb
    或
    HOSTNAME=wan_serverc
    或
    HOSTNAME=lan_server

    环境变量更新后,可以使用变量 ${HOSTNAME} 来代替主机名,比如:

    位置:内网服务器&生产服务器A/B/C,用户:root
    echo "当前系统主机名:${HOSTNAME}"
  4. 更新本地解析

    位置:生产服务器A/B/C及内网服务器,用户:root
    echo "127.0.1.1 ${HOSTNAME}" >> /etc/hosts

2.14.3. 更新开发工作机的本地解析设置

将生产服务器A/B/C及内网服务器的主机名写入 开发工作机/etc/hosts 文件,下文将用主机名代替IP地址指代各服务器

位置:开发工作机
sudo bash -c 'cat >> /etc/hosts' << EOF
192.168.1.121 wan_servera www.sjx.com
192.168.1.122 wan_serverb
192.168.1.123 wan_serverc
192.168.1.124 lan_server test.sjx.com gitlab.sjx.com jenkins.sjx.com
EOF

这里,还为了测试 sjx.com 域名增加了本地解析

如果你购买了自己的域名,请根据需要在域名服务商系统设置解析记录

实践星正式域名

www.sjx.com

实践星测试域名

test.sjx.com

内网GitLab域名

gitlab.sjx.com

内网Jenkins域名

jenkins.sjx.com

2.14.4. 开发工作机测试本地解析

位置:开发工作机
cat << EOF > ping_test.sh
ping -c 5 wan_servera
ping -c 5 wan_serverb
ping -c 5 wan_serverc
ping -c 5 lan_server
ping -c 5 www.sjx.com
ping -c 5 test.sjx.com
ping -c 5 gitlab.sjx.com
ping -c 5 jenkins.sjx.com
EOF

sh -x ping_test.sh && rm -f ping_test.sh
终端输出
+ ping -c 5 wan_servera
PING wan_servera (192.168.1.121) 56(84) bytes of data.
64 bytes from wan_servera (192.168.1.121): icmp_seq=1 ttl=64 time=8.50 ms
64 bytes from wan_servera (192.168.1.121): icmp_seq=1 ttl=64 time=8.50 ms
64 bytes from wan_servera (192.168.1.121): icmp_seq=1 ttl=64 time=8.50 ms
64 bytes from wan_servera (192.168.1.121): icmp_seq=1 ttl=64 time=8.50 ms
64 bytes from wan_servera (192.168.1.121): icmp_seq=1 ttl=64 time=8.50 ms
--- wan_servera ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4008ms
rtt min/avg/max/mdev = 1.317/2.843/8.502/2.830 ms
...省略的内容...
...省略的内容...
...省略的内容...
+ ping -c 5 jenkins.sjx.com
PING jenkins.sjx.com (192.168.1.124) 56(84) bytes of data.
64 bytes from lan_server (192.168.1.124): icmp_seq=1 ttl=64 time=1.39 ms
64 bytes from lan_server (192.168.1.124): icmp_seq=1 ttl=64 time=1.39 ms
64 bytes from lan_server (192.168.1.124): icmp_seq=1 ttl=64 time=1.39 ms
64 bytes from lan_server (192.168.1.124): icmp_seq=1 ttl=64 time=1.39 ms
64 bytes from lan_server (192.168.1.124): icmp_seq=1 ttl=64 time=1.39 ms
--- jenkins.sjx.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 1.194/2.462/7.177/2.358 ms

3. 本地SSH无密码登录服务器

SSH远程、Shell命令和管道让Linux操作变得灵活高效,手动输入密码明显降低了体验

SSH无密码登录(SSH证书登录)是企业级Linux系统管理的标配。这不仅是方便日常管理操作,更是会提升Linux系统的安全性

具体到本文中,开发者和系统管理员需要在开发工作机(又称 本地 )频繁SSH远程连接内网服务器和生产服务器A/B/C

也就是说,本地用钥匙(私钥)打开服务器的门锁(公钥),进入服务器

3.1. 本地生成SSH密钥对

SSH密钥对包括私钥文件(Private Key)和公钥文件(Public Key)

私钥文件

像门钥匙,作为登录Linux服务器的关键。 这个文件必须保护好,不能外泄。

公钥文件

像门锁,只要有钥匙就可以打开。公钥文件可以发布到任何地方,不需要保密。

3.1.1. 生成服务器管理的密钥对

生成密钥对并指定密钥文件名的前缀和文件路径:

位置:开发工作机
ssh-keygen -N ""  -f ~/.ssh/root@sjx.server -C "root@sjx.server"
终端输出
Generating public/private rsa key pair.
Your identification has been saved in /home/lyuqiang/.ssh/root@sjx.server
Your public key has been saved in /home/lyuqiang/.ssh/root@sjx.server.pub
The key fingerprint is:
SHA256:+l66ltFwbh5/+9Ybxt6BE/i1wMyY0AqZgwLxNUQCRbc root@sjx.server
The key's randomart image is:
+---[RSA 3072]----+
| +=+o*           |
|  o + + o .      |
|   o E = . .     |
|    .   + + B    |
|        S* + * . |
|       .. = . * .|
|      .  =.o + *.|
|       .oo. . = *|
|       o=.   ..*+|
+----[SHA256]-----+
ssh-keygen 命令生成了两个文件
私钥文件

~/.ssh/root@sjx.server

公钥文件

~/.ssh/root@sjx.server.pub

3.1.2. 生成GitLab超级管理员的密钥对

位置:开发工作机
ssh-keygen -N ""  -f ~/.ssh/root@gitlab.sjx.com -C "root@gitlab.sjx.com"
ssh-keygen 命令生成了两个文件
私钥文件

~/.ssh/root@gitlab.sjx.com

公钥文件

~/.ssh/root@gitlab.sjx.com.pub

3.1.3. 生成Jenkins访问GitLab仓库的密钥对

位置:开发工作机
ssh-keygen -N ""  -f ~/.ssh/jenkins@gitlab.sjx.com -C "jenkins@gitlab.sjx.com"
ssh-keygen 命令生成了两个文件
私钥文件

~/.ssh/jenkins@gitlab.sjx.com

公钥文件

~/.ssh/jenkins@gitlab.sjx.com.pub

3.2. 本地启用SSH私钥

SSH配置文件

~/.ssh/config

SSH服务器私钥文件

~/.ssh/root@sjx.server

GitLab超级管理员私钥文件

~/.ssh/root@gitlab.sjx.com

SSH配置文件对权限特别敏感,请一定注意!
  1. 创建 ~/.ssh 目录和配置文件

    位置:开发工作机
    mkdir -p ~/.ssh && touch ~/.ssh/config
  2. 允许修改配置文件

    位置:开发工作机
    chmod 700 ~/.ssh/config
  3. 添加主机设置,连接内网服务器和生产服务器A/B/C时,默认使用 root 用户和私钥 ~/.ssh/root@sjx.server 登录

    位置:开发工作机
    cat << EOF >> ~/.ssh/config
    Host lan_server wan_server*
        User root
        IdentityFile ~/.ssh/root@sjx.server
    
    Host gitlab.sjx.com
        User git
        IdentityFile ~/.ssh/root@gitlab.sjx.com
    EOF
    完整的SSH远程命令:ssh root@IP地址,设置 User root 参数后可以简化为:ssh IP地址

    常见的另一个配置方式:

    ~/.ssh/config
    IdentityFile ~/.ssh/foobar_server_private (1)
    
    Host lan_server wan_server*
        User root
        IdentityFile ~/.ssh/root@sjx.server
    1 表示密钥配置对所有主机生效,不限制主机范围
  4. 关闭配置文件写入权限

    位置:开发工作机
    chmod 400 ~/.ssh/config
    为什么关闭 ~/.ssh/config 文件的写入权限,请阅读 无密码登录配置的疑难杂症

3.3. 上传SSH公钥到服务器

3.3.1. 上传SSH公钥到内网服务器

  1. 上传SSH公钥到 lan_server

    位置:开发工作机
    ssh-copy-id -i ~/.ssh/root@sjx.server.pub lan_server
  2. 接着输入Linux系统密码

  3. 密码正确的话,将会看到和下面类似的结果

    终端输出
    /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/lyuqiang/.ssh/root@sjx.server.pub"
    The authenticity of host 'lan_server (192.168.1.124)' can't be established.
    ED25519 key fingerprint is SHA256:Kko4tfRp+SyBFul945EaOYuwZl36yTmyjbSgnNhNkN0.
    This host key is known by the following other names/addresses:
        ~/.ssh/known_hosts:1: [hashed name]
    Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
    root@lan_server's password:
    
    Number of key(s) added: 1 (1)
    
    Now try logging into the machine, with:   "ssh 'lan_server'"
    and check to make sure that only the key(s) you wanted were added.
    1 表示上传公钥成功
  4. SSH远程登录测试

位置:开发工作机
ssh lan_server
终端输出
lyuqiang@ubuntu2204:~$ ssh lan_server
Last login: Wed Jan 11 22:36:49 2023 from 192.168.1.20
[root@lan_server ~]#

3.3.2. 上传SSH公钥到生产服务器

位置:开发工作机
ssh-copy-id -i ~/.ssh/root@sjx.server.pub wan_servera

ssh-copy-id -i ~/.ssh/root@sjx.server.pub wan_serverb

ssh-copy-id -i ~/.ssh/root@sjx.server.pub wan_serverc

3.3.3. 上传GitLab超级管理员公钥

3.4. 无密码登录配置的疑难杂症

保证以下关键点正确无误,就可以100%配置成功

3.4.1. 本地用户SSH配置目录

~/.ssh/ 目录只能被当前用户写入,也就是下图标记的 drwx------ 中第一个 w 必须存在

终端输出
$ ls -dl ~/.ssh/
drwx------ 2 lyuqiang lyuqiang 4096  1月 11 22:38 /home/lyuqiang/.ssh/
修复方法
目录用户组设置

chown -R ${USER}:${USER} ~/.ssh/

注意带 -R 递归修改目录及目录下所有文件
目录读写权限

chmod 700 ~/.ssh/

仅修改目录,不修改目录下文件权限

3.4.2. 本地用户SSH配置文件

~/.ssh/config 文件的权限必须是 400-r--------

终端输出
$ ll ~/.ssh/config
-r-------- 1 lyuqiang lyuqiang 75  1月 11 22:38 /home/lyuqiang/.ssh/config

3.4.3. 服务器用户SSH配置目录

3.4.4. 服务器用户公钥配置文件

~/.ssh/authorized_keys 文件同样只能被当前用户写入,权限可以是 400-r------- )或者 600-rw-------

终端输出
$ ll ~/.ssh/authorized_keys
-r-------- 1 root root 574 1月  11 23:43 /root/.ssh/authorized_keys

authorized_keys 中每一行都是一把 (公钥)

终端输出
$ cat -n ~/.ssh/authorized_keys
    1	ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCyvwx84ddU+k82F2XDIWTNH3BqDjIfUcS7M4HUwKtahHYxYrglUo7JCA0deUpM53SzMw0b6pGsUrus1VEgsYjPPboB8wdOb9tY3psGIRu9zo5vUS08bLDKEbOiJVbKoI8QWk1Oy10rzzAqn071pOxOHj5JiNYQuPqu4GD1x7HJKmajWaqv3wiYFCVX1fhy5P3vYQVTMSRR0emxin3Y7NlngQUDJzvoy1PjOHXHS6FeD4z6Cc7RVE36+fgC/BionqSudNPvVVxLviDtnxKZQkfa04RjUXLYcN9h7hdTmChIoxH9y3Ni8wxvJThzmHI+n0mmlF9t00Kys7ALX/zTyLyjgl/Tpc/cG4O8z/pK0oSb0kwO0fCCknM2CiUPKTOh7G+eA+4/idLg+xE93lQWeNi4YPFj49TCvxcFEd768NdZkFa7M5/L9n/N7W4TPc6i7kmHjw0AzebdLEdtzKypqjXNlObt0msCkcNxPH02gTkmcHbTHhcK7rbsQXofmOV+/7c= root@sjx.server
    2	ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLGJVJI1Cqr59VH1NVQgPs08n7e/HRc2Q8AUpOWGoJpVzIgjO+ipjqwnxh3eiBd806eXIIa5OFwRm0fYfMFxBOdo3l5qGtBe82PwTotdtpcacP5Dkrn+HZ1kG+cf0BNSF5oXbTCTrqY12/T8h4035BXyRw7+MuVPiCUhydYs3RgsODA47ZR3owgjvPsayUd5MrD8gidGqv1zdyW9nQXnXB7m9Sn9Mg8rk6qBxQUbtMN9ez0BFrUGhXCkW562zhJjP5j4RLVfvL2N1bWT9EoFTCjk55pv58j+PTNEGUmu8PrU8mtgf6zQO871whTD8/H6brzaMwuB5Rd5OYkVir0BXj lyuqiang@archlinux

3.4.5. 查看SSH命令详细日志

日志是诊断SSH登录问题最重要的手段:

  • Linux系统的登录日志

  • ssh 命令打印的调试日志

这里,看看第二种日志: ssh root@lan_server -v

可以看到最后一张图有以下信息:

终端输出
debug1: Offering public key: /home/lyuqiang/.ssh/root@sjx.server RSA SHA256:+l66ltFwbh5/+9Ybxt6BE/i1wMyY0AqZgwLxNUQCRbc explicit

debug1: Server accepts key: /home/lyuqiang/.ssh/root@sjx.server RSA SHA256:+l66ltFwbh5/+9Ybxt6BE/i1wMyY0AqZgwLxNUQCRbc explicit

这意味着,~/.ssh/config 中的配置生效了

如果日志中没有你的密钥文件,请检查 config 配置

ch02.s4 1
ch02.s4 2

3.5. 写在最后

这次用最简单的方式配置了证书登录,你也可以SCP上传公钥或者全手动配置公钥

请千万牢记几个关键配置的权限问题

4. 内网环境搭建一:GitLab

操作位置
  • 内网服务器

4.1. 新增GitLab Yum仓库

4.1.1. 添加Yum仓库

位置:内网服务器,用户:root
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash

4.1.2. 列出GitLab的仓库列表

位置:内网服务器,用户:root
yum repolist all | grep gitlab
终端输出
gitlab_gitlab-ce/x86_64             gitlab_gitlab-ce        enabled:         853
!gitlab_gitlab-ce-source            gitlab_gitlab-ce-source disabled

4.1.3. 查看可用的GitLab软件包

  1. 列出软件包列表

    位置:内网服务器,用户:root
    yum repo-pkgs gitlab_gitlab-ce list
    终端输出
    Loaded plugins: fastestmirror
    Loading mirror speeds from cached hostfile
     * base: mirrors.bfsu.edu.cn
     * extras: mirrors.bfsu.edu.cn
     * updates: mirrors.cqu.edu.cn
    Available Packages
    gitlab-ce.x86_64                                 15.7.3-ce.0.el7                                 gitlab_gitlab-ce
  2. 查看软件包描述

位置:内网服务器,用户:root
yum --disablerepo=\* --enablerepo=gitlab_gitlab-ce info gitlab-ce
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.bfsu.edu.cn
 * extras: mirrors.bfsu.edu.cn
 * updates: mirrors.cqu.edu.cn
Available Packages
Name        : gitlab-ce
Arch        : x86_64
Version     : 15.7.3
Release     : ce.0.el7
Size        : 1.1 G (1)
Repo        : gitlab_gitlab-ce/x86_64
Summary     : GitLab Community Edition (including NGINX, Postgres, Redis)
URL         : https://about.gitlab.com/
License     : MIT
Description : GitLab Community Edition (including NGINX, Postgres, Redis)
1 GitLab的软件包比较大,安装过程时间较长

4.1.4. 默认禁用GitLab仓库

按需单独启用GitLab仓库

GitLab仓库(服务器位于国外)经常无法访问或者速度慢,影响Yum使用,默认禁用之

位置:内网服务器,用户:root
yum-config-manager --disable gitlab_gitlab-ce | egrep '(\[gitlab_gitlab-ce\])|enabled'
终端输出
[gitlab_gitlab-ce]
enabled = 0 或 False

4.2. 安装GitLab软件包

4.2.1. 安装GitLab依赖

运行 gitlab-ctl 命令时,会出现警告:

终端输出
ffi-libarchive could not be loaded, libarchive is probably not installed on system, archive_file will not be available

安装 libarchive 包,消除警告:

位置:内网服务器,用户:root
yum install -y libarchive

4.2.2. 预设GitLab运行参数

预设GitLab访问URL
位置:内网服务器,用户:root
EXTERNAL_URL="http://gitlab.sjx.com:8181"
预设GitLab默认密码
位置:内网服务器,用户:root
GITLAB_ROOT_PASSWORD=$(pwgen -s 20|head -n 1)
echo -e "GitLab默认用户:root\nGitLab默认密码:${GITLAB_ROOT_PASSWORD}"
终端输出
GitLab默认用户:root
GitLab默认密码:QJQCM3fAFXKYpYUeSL5e
预设GitLab访问域名
位置:内网服务器,用户:root
egrep '^127.0.0.1 gitlab.sjx.com$' /etc/hosts > /dev/null || echo '127.0.0.1 gitlab.sjx.com' >> /etc/hosts
127.0.0.1 gitlab.sjx.com 只为安装,无其它实质作用

域名 gitlab.sjx.com 必须能解析出IP地址,EXTERNAL_URL参数才有效

这样安装后,不再需要手动修改 /etc/gitlab/gitlab.rb 文件中的 external_url 参数

4.2.3. 网络安装GitLab

位置:内网服务器,用户:root
EXTERNAL_URL=${EXTERNAL_URL} GITLAB_ROOT_PASSWORD=${GITLAB_ROOT_PASSWORD} yum --disablerepo=\* --enablerepo=gitlab_gitlab-ce install -y gitlab-ce
单独启用GitLab仓库( gitlab_gitlab-ce ),安装GitLab

没用 export 设置当前Shell环境变量,是担心 yum install 之前忘记设置预设参数,可能会重新安装浪费时间

位置:内网服务器,用户:root
export EXTERNAL_URL=${EXTERNAL_URL}
export GITLAB_ROOT_PASSWORD=${GITLAB_ROOT_PASSWORD}
GitLab的软件包有1.1G。而GitLab仓库又可能出现网络问题,安装多次都不成功。强烈建议采用 手动安装GitLab
终端输出
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.bfsu.edu.cn
 * extras: mirrors.bfsu.edu.cn
 * updates: mirrors.bfsu.edu.cn
Resolving Dependencies
--> Running transaction check
---> Package gitlab-ce.x86_64 0:15.6.3-ce.0.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

==============================================================================================
 Package        Arch        Version              Repository                               Size
==============================================================================================
Installing:
 gitlab-ce      x86_64      15.6.3-ce.0.el7      gitlab_gitlab-ce                        1.1 G

Transaction Summary
==============================================================================================
Install  1 Package

Total size: 1.1 G
Installed size: 2.4 G
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : gitlab-ce-15.6.3-ce.0.el7.x86_64                                           1/1
...省略的内容...
...省略的内容...
...省略的内容...
[2023-01-13T16:34:34+08:00] INFO: Cinc Client Run complete in 243.10249991 seconds

Running handlers:
[2023-01-13T16:34:34+08:00] INFO: Running report handlers
Running handlers complete
[2023-01-13T16:34:34+08:00] INFO: Report handlers complete
Infra Phase complete, 555/1531 resources updated in 04 minutes 05 seconds

NNotes:
Default admin account has been configured with following details:
Username: root
Password: You didn't opt-in to print initial root password to STDOUT.

NOTE: Because these credentials might be present in your log files in plain text, it is highly recommended to reset the password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password.

gitlab Reconfigured!

       *.                  *.
      ***                 ***
     *****               *****
    .******             *******
    ********            ********
   ,,,,,,,,,***********,,,,,,,,,
  ,,,,,,,,,,,*********,,,,,,,,,,,
  .,,,,,,,,,,,*******,,,,,,,,,,,,
      ,,,,,,,,,*****,,,,,,,,,.
         ,,,,,,,****,,,,,,
            .,,,***,,,,
                ,*,.



     _______ __  __          __
    / ____(_) /_/ /   ____ _/ /_
   / / __/ / __/ /   / __ `/ __ \
  / /_/ / / /_/ /___/ /_/ / /_/ /
  \____/_/\__/_____/\__,_/_.___/


Thank you for installing GitLab!
GitLab should be available at http://gitlab.sjx.com

For a comprehensive list of configuration options please see the Omnibus GitLab readme
https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md

Help us improve the installation experience, let us know how we did with a 1 minute survey:
https://gitlab.fra1.qualtrics.com/jfe/form/SV_6kVqZANThUQ1bZb?installation=omnibus&release=15-6

  Verifying  : gitlab-ce-15.6.3-ce.0.el7.x86_64                                             1/1

Installed:
  gitlab-ce.x86_64 0:15.6.3-ce.0.el7

Complete!

4.2.4. 手动安装GitLab

GitLab仓库网络中断,yum install 多次都不成功,主要是YUM每次重新下载文件导致

先断点续传下载 .rpm 文件,再从文件安装GitLab就能安装成功

yumdownloader 也能手动下载,但不支持断点续传
  1. 打印GitLab软件包URL

    位置:内网服务器,用户:root
    GITLAB_RPM_URL=$(yumdownloader --disablerepo=\* --enablerepo=gitlab_gitlab-ce --urls gitlab-ce | egrep '^https://.+\.rpm$')
    GITLAB_RPM_FILE=/tmp/$(basename ${GITLAB_RPM_URL})
    
    echo -e "GitLab软件包文件:\n\t${GITLAB_RPM_FILE}\n GitLab软件包URL:\n\t${GITLAB_RPM_URL}"
    终端输出
    GitLab软件包文件:
    	/tmp/gitlab-ce-15.7.3-ce.0.el7.x86_64.rpm
     GitLab软件包URL:
    	https://packages.gitlab.com/gitlab/gitlab-ce/el/7/x86_64/gitlab-ce-15.7.3-ce.0.el7.x86_64.rpm
  2. 下载软件包

    位置:内网服务器,用户:root
    wget -c ${GITLAB_RPM_URL} -O ${GITLAB_RPM_FILE}
    wget 因为带了 -c 参数,下载速度慢时,可以中断下载,多次重新运行上面的命令
  3. 安装GitLab

    位置:内网服务器,用户:root
    EXTERNAL_URL=${EXTERNAL_URL} GITLAB_ROOT_PASSWORD=${GITLAB_ROOT_PASSWORD} yum install -y ${GITLAB_RPM_FILE}
    GitLab安装成功后,请手动删除下载的文件:rm -f /tmp/gitlab-ce*.rpm

    (安装时,终端输出类似 网络安装GitLab

    终端输出
    Loaded plugins: fastestmirror
    Examining /tmp/gitlab-ce-15.6.3-ce.0.el7.x86_64.rpm: gitlab-ce-15.6.3-ce.0.el7.x86_64
    Marking /tmp/gitlab-ce-15.6.3-ce.0.el7.x86_64.rpm to be installed
    Resolving Dependencies
    --> Running transaction check
    ---> Package gitlab-ce.x86_64 0:15.6.3-ce.0.el7 will be installed
    --> Finished Dependency Resolution
    
    Dependencies Resolved
    
    ==============================================================================================
     Package        Arch        Version              Repository                               Size
    ==============================================================================================
    Installing:
     gitlab-ce      x86_64      15.6.3-ce.0.el7      /gitlab-ce-15.6.3-ce.0.el7.x86_64      2.4 G
    
    Transaction Summary
    ==============================================================================================
    Install  1 Package
    
    Total size: 2.4 G
    Installed size: 2.4 G
    ...内容同上...
    ...内容同上...
    ...内容同上...

4.3. 查看GitLab服务状态

4.3.1. 查看GitLab的系统服务状态

位置:内网服务器,用户:root
systemctl status gitlab-runsvdir
终端输出
● gitlab-runsvdir.service - GitLab Runit supervision process
   Loaded: loaded (/usr/lib/systemd/system/gitlab-runsvdir.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2023-01-14 00:30:42 CST; 11h ago
 Main PID: 10912 (runsvdir)
   CGroup: /system.slice/gitlab-runsvdir.service
           ├─10912 runsvdir -P /opt/gitlab/service log: .............................................
           ├─10914 runsv logrotate
           ├─10924 svlogd -tt /var/log/gitlab/logrotate
           ├─10930 runsv redis
           ├─10932 /opt/gitlab/embedded/bin/redis-server unixsocket:/var/opt/gitlab/redis/redis.socket
           ├─10940 svlogd -tt /var/log/gitlab/redis
           ├─10950 runsv gitaly
           ├─10975 svlogd /var/log/gitlab/gitaly
           ├─11079 runsv postgresql
...省略的内容...
...省略的内容...
...省略的内容...
Jan 14 00:30:42 lan_server systemd[1]: Started GitLab Runit supervision process.

4.3.2. 查看GitLab的所有组件状态

位置:内网服务器,用户:root
gitlab-ctl status
终端输出
run: alertmanager: (pid 11869) 39866s; run: log: (pid 11659) 39907s
run: gitaly: (pid 11728) 39876s; run: log: (pid 10975) 40076s
run: gitlab-exporter: (pid 11845) 39868s; run: log: (pid 11451) 39925s
run: gitlab-kas: (pid 11817) 39870s; run: log: (pid 11225) 40017s
run: gitlab-workhorse: (pid 11829) 39869s; run: log: (pid 11371) 39942s
run: logrotate: (pid 25010) 488s; run: log: (pid 10924) 40088s
run: nginx: (pid 11399) 39940s; run: log: (pid 11415) 39938s
run: node-exporter: (pid 11839) 39869s; run: log: (pid 11443) 39931s
run: postgres-exporter: (pid 11879) 39866s; run: log: (pid 11682) 39903s
run: postgresql: (pid 11081) 40037s; run: log: (pid 11125) 40034s
run: prometheus: (pid 11854) 39867s; run: log: (pid 11630) 39914s
run: puma: (pid 11287) 39958s; run: log: (pid 11294) 39957s
run: redis: (pid 10932) 40084s; run: log: (pid 10940) 40082s
run: redis-exporter: (pid 11847) 39868s; run: log: (pid 11469) 39919s
run: sidekiq: (pid 11303) 39952s; run: log: (pid 11320) 39950s

4.3.3. 查看GitLab默认HTTP端口

位置:内网服务器,用户:root
gitlab-ctl show-config 2>/dev/null | grep '"external-url":'
终端输出
    "external-url": "http://gitlab.sjx.com:8181", (1)
1 GitLab访问URL:http://gitlab.sjx.com:8181

4.4. 为GitLab新增防火墙规则

  1. 增加防火墙放行规则

    位置:内网服务器,用户:root
    GITLAB_PORT=8181
    PERM="--permanent"
    SERV_NAME=GITLAB_${GITLAB_PORT}
    SERV="${PERM} --service=${SERV_NAME}"
    
    firewall-cmd ${PERM} --new-service=${SERV_NAME}
    firewall-cmd ${SERV} --set-short="GitLab ports"
    firewall-cmd ${SERV} --set-description="GitLab port exceptions"
    firewall-cmd ${SERV} --add-port=${GITLAB_PORT}/tcp
    firewall-cmd ${PERM} --add-service=${SERV_NAME}
    参数说明
    GITLAB_PORT

    GITLAB运行端口注意和 预设GitLab访问URL 的端口保持一致

  2. 重载防火墙配置

    位置:内网服务器,用户:root
    firewall-cmd --reload
  3. 查看防火墙规则

    位置:内网服务器,用户:root
    firewall-cmd --list-all
    终端输出
    public (active)
      target: default
      icmp-block-inversion: no
      interfaces: enp0s3
      sources:
      services: GITLAB_8181 dhcpv6-client ssh (1)
      ports:
      protocols:
      masquerade: no
      forward-ports:
      source-ports:
      icmp-blocks:
      rich rules:
    1 放行的规则中必须有 GITLAB_8181 服务

现在,可以去试一试 第一次访问GitLab Web

如果你对 gitlab-ctl 命令用法更感兴趣,请继续阅读 GitLab管理维护:gitlab-ctl

4.5. GitLab管理维护:gitlab-ctl

4.5.1. gitlab-ctl 命令参数

位置:内网服务器,用户:root
gitlab-ctl --help
终端输出
omnibus-ctl: command (subcommand)
check-config
  Check if there are any configuration in gitlab.rb that is removed in specified version
deploy-page
  Put up the deploy page
diff-config
  Compare the user configuration with package available configuration
get-redis-master
  Get connection details to Redis master
remove-accounts
  Delete *all* users and groups used by this package
reset-grafana
  Reset Grafana instance to its initial state by removing the data directory
set-grafana-password
  Reset admin password for Grafana
upgrade
  Run migrations after a package upgrade
upgrade-check
  Check if the upgrade is acceptable
General Commands:
  cleanse
    Delete *all* gitlab data, and start from scratch.
  help
    Print this help message.
  reconfigure
    Reconfigure the application.
  show-config
    Show the configuration that would be generated by reconfigure.
  uninstall
    Kill all processes and uninstall the process supervisor (data will be preserved).
Service Management Commands:
  graceful-kill
    Attempt a graceful stop, then SIGKILL the entire process group.
  hup
    Send the services a HUP.
  int
    Send the services an INT.
  kill
    Send the services a KILL.
  once
    Start the services if they are down. Do not restart them if they stop.
  restart
    Stop the services if they are running, then start them again.
  service-list
    List all the services (enabled services appear with a *.)
  start
    Start services if they are down, and restart them if they stop.
  status
    Show the status of all the services.
  stop
    Stop the services, and do not restart them.
  tail
    Watch the service logs of all enabled services.
  term
    Send the services a TERM.
  usr1
    Send the services a USR1.
  usr2
    Send the services a USR2.
Backup Commands:
  backup-etc
    Backup GitLab configuration [options]
Let's Encrypt Commands:
  renew-le-certs
    Renew the existing Let's Encrypt certificates
Database Commands:
  pg-password-md5
    Generate MD5 Hash of user password in PostgreSQL format
  pg-upgrade
    Upgrade the PostgreSQL DB to the latest supported version
  revert-pg-upgrade
    Run this to revert to the previous version of the database
  set-replication-password
    Set database replication password
Gitaly Commands:
  praefect
    Interact with Gitaly cluster
Container Registry Commands:
  registry-garbage-collect
    Run Container Registry garbage collection.

4.5.2. 列表GitLab组件状态

位置:内网服务器,用户:root
gitlab-ctl status

4.5.3. 管理GitLab所有组件状态

位置:内网服务器,用户:root
gitlab-ctl start

gitlab-ctl stop

gitlab-ctl restart

4.5.4. 管理GitLab指定组件状态

位置:内网服务器,用户:root
gitlab-ctl start nginx

gitlab-ctl stop nginx

gitlab-ctl restart nginx

gitlab-ctl hup nginx

4.5.5. 滚动输出GitLab日志

位置:内网服务器,用户:root
tree /var/log/gitlab/
终端输出
/var/log/gitlab/
├── gitlab-rails
│   ├── api_json.log
│   ├── application_json.log
│   ├── application.log
│   ├── auth.log
│   ├── database_load_balancing.log
│   ├── exceptions_json.log
│   ├── gitlab-rails-db-migrate-2023-01-13-16-32-01.log
│   ├── grpc.log
│   ├── production_json.log
│   ├── production.log
│   ├── service_measurement.log
│   └── sidekiq_client.log
├── gitlab-shell
├── gitlab-workhorse
├── logrotate
├── nginx
│   ├── access.log
│   ├── config -> /opt/gitlab/sv/nginx/log/config
│   ├── current
│   ├── error.log
│   ├── gitlab_access.log
│   ├── gitlab_error.log
│   └── lock
...省略的内容...
...省略的内容...
...省略的内容...

18 directories, 66 files
滚动输出日志
  • 输出所有日志

    位置:内网服务器,用户:root
    gitlab-ctl tail
  • 输出指定目录下的所有日志,如 /var/log/gitlab/nginx

    位置:内网服务器,用户:root
    gitlab-ctl tail nginx
  • 输出指定文件的日志,如 /var/log/gitlab/gitlab-rails/production.log

    位置:内网服务器,用户:root
    gitlab-ctl tail gitlab-rails/production.log
滚动输出日志并保存到文件
位置:内网服务器,用户:root
gitlab-ctl tail | tee --append /tmp/gitlab_tail.log

4.5.6. 打印GitLab配置清单

位置:内网服务器,用户:root
gitlab-ctl show-config
终端输出
...省略的内容...
Installing cookbook gem dependencies:
Compiling cookbooks...
Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!
Top level ::Parts is deprecated, require 'multipart/post' and use `Multipart::Post::Parts` instead!
Loading Cinc Auditor profile files:
Loading Cinc Auditor input files:
Loading Cinc Auditor waiver files:
{
    "gitlab": {
        "gitlab-rails": {
            "initial_root_password": "...省略的内容...",
            "store_initial_root_password": true,
            "db_username": "gitlab",
            "db_host": "/var/opt/gitlab/postgresql",
            "db_port": 5432,
            "databases": {
                "main": {
                    "enable": true,
                    "db_adapter": "postgresql",
                    "db_encoding": "unicode",
                    "db_database": "gitlabhq_production",
                    "db_username": "gitlab",
                    "db_load_balancing": {
                        "hosts": []
                    },
                    "db_host": "/var/opt/gitlab/postgresql",
                    "db_port": 5432,
                    "db_sslcompression": 0,
                    "db_prepared_statements": false,
                    "db_database_tasks": true,
                    "db_statements_limit": 1000
                }
            },
            "gitlab_url": "http://gitlab.sjx.com:8181",
            "gitlab_host": "gitlab.sjx.com",
            "gitlab_email_from": "gitlab@gitlab.sjx.com",
            "gitlab_https": false,
            "gitlab_port": 8181,
        },
        "external-url": "http://gitlab.sjx.com:8181",
        "user": {
            "home": "/var/opt/gitlab",
            "git_user_email": "gitlab@gitlab.sjx.com"
        },
        "nginx": {
            "proxy_set_headers": {
                "Host": "$http_host_with_default",
                "X-Real-IP": "$remote_addr",
                "X-Forwarded-For": "$proxy_add_x_forwarded_for",
                "Upgrade": "$http_upgrade",
                "Connection": "$connection_upgrade",
                "X-Forwarded-Proto": "http"
            },
            "real_ip_trusted_addresses": [],
            "listen_port": 8181
        }
    },
    "letsencrypt": {
        "auto_enabled": false,
        "enable": false
    },
    "postgresql": {
        "internal_certificate": "...省略的内容...",
        "internal_key": "...省略的内容...",
        "connect_port": 5432,
        "wal_keep_size": 160
    },
...省略的内容...
}

Converging 0 resources

Running handlers:
Running handlers complete
Infra Phase complete, 0/0 resources updated in 06 seconds

4.5.7. 打印自定义的配置

位置:内网服务器,用户:root
gitlab-ctl diff-config
终端输出
warning: core.fsyncObjectFiles is deprecated; use core.fsync instead
diff --git a/etc/gitlab/gitlab.rb b/opt/gitlab/etc/gitlab.rb.template
index b9a6d1b..b5d9523 100644
--- a/etc/gitlab/gitlab.rb
+++ b/opt/gitlab/etc/gitlab.rb.template
@@ -29,7 +29,7 @@
 ##! On AWS EC2 instances, we also attempt to fetch the public hostname/IP
 ##! address from AWS. For more details, see:
 ##! https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
-external_url 'http://gitlab.sjx.com'
+external_url 'GENERATED_EXTERNAL_URL'

 ## Roles for multi-instance GitLab
 ##! The default is to have no roles enabled, which results in GitLab running as an all-in-one instance.

4.6. 第一次访问GitLab Web

访问 http://gitlab.sjx.com:8181 ,完成第一次登录操作

  1. GitLab Web登录页面

    ch04.s7 1
  2. 输入默认帐号和密码

    默认帐号和密码见 预设GitLab默认密码
    ch04.s7 2
  3. GitLab Web首页

    ch04.s7 3

4.7. 配置GitLab Web

4.7.1. 禁用Auto DevOps

URL入口

http://gitlab.sjx.com:8181/admin/application_settings/ci_cd#js-ci-cd-settings

操作
菜单

Admin  Settings  CI/CD  Continuous Integration and Deployment

输入
  1. 取消选项 "Default to Auto DevOps pipeline for all projects" 的勾选状态(

  2. 取消选项 "Enable shared runners for new projects" 的勾选状态(

  3. 取消选项 "Enable pipeline suggestion banner" 的勾选状态(

完成

点击下方 Save Changes 按钮,保存设置

操作示例如下:

ch04.s8 1

4.8. 添加GitLab超级管理员的SSH公钥

4.8.1. 打印GitLab超级管理员的SSH公钥

位置:开发工作机
cat ~/.ssh/root@gitlab.sjx.com.pub
终端输出
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj5fKDvY0VBhD850bN/qLjNfxhZwWThfRSjN2EFdhHyzUp+/K2ISBHi8ufua9kPu9OJoixR0fo4gAsDsZA+eWlziy/CQyQQ+Fk9a29F2jKTSycgOV/CBrkz3Ng+pM3dYFDgPz5WGxTm1u5IOolQRy/c6CglVtscD6MiX/empD+lFAXEskxZKj0T8BYMAkvAZxQ1KB9/M9XZJVIDtuHziHfj1nGzm5TQdcycq+JBGzNWME9mk0TyUV5yzFRxq4GYIBVKfuGPlncRwVx3bm26CxDhWoyUrZOUUVfG0jTjwkDLA7b+Woh8v9fDIGYGD0fGBbQ5FGAAlb8j1bfj69/PJSOlC8fb338GkcPqPxhjBe1Js+mOzOxBL/qrJtpNBsK0KIRRXdzxp6/JNCaq1M8ByGRpK2dEIDjUlBc2zfuj8O1Mr9bYoApe3RekOgPFKGowdzakTBVWGHO9zezzQYco+xLnqSBAboTMlhBrPQn/AzOXk+mVTb20cowJq4RHG3WYcE= root@gitlab.sjx.com

4.8.2. 填写SSH公钥内容

URL入口

http://gitlab.sjx.com:8181/-/profile/keys

操作
菜单

User Settings  SSH Keys  Add an SSH key

输入
"Key"

(填写刚才打印的SSH公钥)

"Title"

自动填写

"Expiration date"

清除日期,保留空白

完成

点击下方 Add key 按钮,添加SSH公钥

操作示例如下:

ch04.s9 1

4.8.3. 测试GitLab管理员的密钥

位置:开发工作机
ssh git@gitlab.sjx.com
终端输出
PTY allocation request failed on channel 0
Welcome to GitLab, @root!
Connection to gitlab.sjx.com closed.

出现GitLab的欢迎提示,表示GitLab Web上用户添加的SSH公钥匹配 ~/.ssh/config 文件中设置的私钥

如果没有出现GitLab的欢迎提示,加上 -v 参数,打印SSH命令的调试检查:

位置:开发工作机
ssh git@gitlab.sjx.com -v
终端输出
OpenSSH_8.9p1 Ubuntu-3, OpenSSL 3.0.2 15 Mar 2022
debug1: Reading configuration data /home/lyuqiang/.ssh/config
debug1: /home/lyuqiang/.ssh/config line 5: Applying options for gitlab.sjx.com (1)
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to gitlab.sjx.com [192.168.1.124] port 22.
debug1: Connection established.
debug1: identity file /home/lyuqiang/.ssh/root@gitlab.sjx.com type 0
debug1: identity file /home/lyuqiang/.ssh/root@gitlab.sjx.com-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_8.9p1 Ubuntu-3
...省略的内容...
debug1: Will attempt key: /home/lyuqiang/.ssh/root@gitlab.sjx.com RSA SHA256:oYdOQqLmqVmzcnWI+x3z4tVtaE86rifdeL9sm3SMLpg explicit agent (2)
debug1: Will attempt key: lyuqiang@ubuntu2204 RSA SHA256:+l66ltFwbh5/+9Ybxt6BE/i1wMyY0AqZgwLxNUQCRbc agent (3)
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<rsa-sha2-256,rsa-sha2-512>
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey,password
debug1: Next authentication method: publickey
debug1: Offering public key: /home/lyuqiang/.ssh/root@gitlab.sjx.com RSA SHA256:oYdOQqLmqVmzcnWI+x3z4tVtaE86rifdeL9sm3SMLpg explicit agent
debug1: Server accepts key: /home/lyuqiang/.ssh/root@gitlab.sjx.com RSA SHA256:oYdOQqLmqVmzcnWI+x3z4tVtaE86rifdeL9sm3SMLpg explicit agent
Authenticated to gitlab.sjx.com ([192.168.1.124]:22) using "publickey".
...省略的内容...
PTY allocation request failed on channel 0
Welcome to GitLab, @root!
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: client_input_channel_req: channel 0 rtype eow@openssh.com reply 0
debug1: channel 0: free: client-session, nchannels 1
Connection to gitlab.sjx.com closed.
Transferred: sent 4048, received 3244 bytes, in 0.2 seconds
Bytes per second: sent 26883.3, received 21543.8
debug1: Exit status 0
1 git@gitlab.sjx.com 中的 gitlab.sjx.com 匹配上了 ~/.ssh/config 文件中设置的 Host gitlab.sjx.com
2 ~/.ssh/config 文件中设置的SSH私钥路径: IdentityFile ~/.ssh/root@gitlab.sjx.com
3 GitLab Web用户配置的SSH公钥匹配上了指定的SSH私钥(~/.ssh/root@gitlab.sjx.com

4.9. 添加Jenkins的SSH公钥

按照 添加GitLab超级管理员的SSH公钥 中的操作步骤,将Jenkins公钥文件 ~/.ssh/jenkins@gitlab.sjx.com.pub 的内容添加到GitLab。

操作示例如下:

ch04.s10 1

4.10. 安全加固GitLab

4.10.1. 关闭GitLab注册功能

  • 企业内部使用GitLab通常是由负责人或管理员,手动创建新用户

  • 如果GitLab运行在公网,更加要关闭注册功能

    URL入口

    http://gitlab.sjx.com:8181/admin/application_settings/general#js-signup-settings

    操作
    菜单

    Admin Area  General  Sign-up restrictions

    输入

    取消选项 "Sign-up enabled" 的勾选状态(

    完成

    点击下方 Save Changes 按钮,保存设置

操作示例如下:

ch04.s11 1
确认注册功能已经禁用
  • 退出登录

ch04.s11 2
  • 确认登录页面正下方,已经没有注册功能入口

ch04.s11 3

4.10.2. GitLab仓库的可见等级和访问限制

创建Git仓库时,GitLab默认可见级别是 Visibility Level  Public

ch04.s11 4

Public级别的仓库不需要身份认证就能访问,如 http://gitlab.sjx.com:8181/root/my-first-git-repo

ch04.s11 5

无登录访问 http://gitlab.sjx.com:8181/root/my-first-git-repo

ch04.s11 6

右上角出现:

Sign in

可以说明当前并没有登录就能访问仓库

取消非root用户的Public可选级别

root用户以外的所有用户,取消创建仓库可见级别下的 Visibility Level  Public 选项

URL入口

http://gitlab.sjx.com:8181/admin/application_settings/general#js-signup-settings

操作
菜单

Admin  Settings  General  Visibility and access controls  Restricted visibility levels

输入

勾选 "Public"(

完成

点击下方 Save Changes 按钮,保存设置

操作示例如下:

ch04.s11 7

新建一个GitLab用户,创建仓库,Visibility Level  Public 选项不见了:

ch04.s11 8
启用HTTP身份验证

启用HTTP身份验证,给GitLab Web的最后一道功能安全屏障

所有GitLab Web访问必须通过HTTP身份验证,root用户也不例外

  1. 生成HTTP身份验证密码

    位置:内网服务器,用户:root
    yum install -y httpd-tools
    
    BASIC_AUTH_PASSWORD=`pwgen -s 20|head -n 1`
    echo -e "GitLab Web HTTP身份验证用户名:git\n  GitLab Web HTTP身份验证密码:${BASIC_AUTH_PASSWORD}"
    终端输出
    GitLab Web HTTP身份验证用户名:git
      GitLab Web HTTP身份验证密码:iuuLDzvb9g6rv96bQCBR
  2. 生成HTTP身份验证文件

    位置:内网服务器,用户:root
    echo ${BASIC_AUTH_PASSWORD} | htpasswd -i -c /etc/gitlab/gitlab_htpasswd git
    终端输出
    Adding password for user git
  3. 修改GitLab配置,新增Nginx自定义配置,启用启用HTTP身份验证

    位置:内网服务器,用户:root
    grep "^nginx\['custom_gitlab_server_config'\]" /etc/gitlab/gitlab.rb \
        || echo "nginx['custom_gitlab_server_config'] = \"auth_basic 'Restricted';\n  auth_basic_user_file /etc/gitlab/gitlab_htpasswd;\n\"" \
            >> /etc/gitlab/gitlab.rb
  4. 重载GitLab配置

    位置:内网服务器,用户:root
    gitlab-ctl reconfigure
    终端输出
    [2023-01-14T20:38:50+08:00] INFO: Started Cinc Zero at chefzero://localhost:1 with repository at /opt/gitlab/embedded (One version per cookbook)
    Cinc Client, version 17.10.0
    Patents: https://www.chef.io/patents
    Infra Phase starting
    [2023-01-14T20:38:50+08:00] INFO: *** Cinc Client 17.10.0 ***
    [2023-01-14T20:38:50+08:00] INFO: Platform: x86_64-linux
    [2023-01-14T20:38:50+08:00] INFO: Cinc-client pid: 6135
    [2023-01-14T20:38:52+08:00] INFO: Setting the run_list to ["recipe[gitlab]"] from CLI options
    [2023-01-14T20:38:52+08:00] INFO: Run List is [recipe[gitlab]]
    [2023-01-14T20:38:52+08:00] INFO: Run List expands to [gitlab]
    [2023-01-14T20:38:52+08:00] INFO: Starting Cinc Client Run for lan_server
    [2023-01-14T20:38:52+08:00] INFO: Running start handlers
    [2023-01-14T20:38:52+08:00] INFO: Start handlers complete.
    Resolving cookbooks for run list: ["gitlab"]
    ...省略的内容...
    ...省略的内容...
    ...省略的内容...
    Running handlers:
    [2023-01-14T20:39:07+08:00] INFO: Running report handlers
    Running handlers complete
    [2023-01-14T20:39:07+08:00] INFO: Report handlers complete
    Infra Phase complete, 3/781 resources updated in 16 seconds
    gitlab Reconfigured!
  5. 确认GitLab配置

    位置:内网服务器,用户:root
    gitlab-ctl show-config | grep custom_gitlab_server_config
    终端输出
          "custom_gitlab_server_config": "auth_basic 'Restricted';\n  auth_basic_user_file /etc/gitlab/gitlab_htpasswd;\n",
  6. 确认GitLab内置Nginx配置文件已经更新

    位置:内网服务器,用户:root
    grep auth_basic /var/opt/gitlab/nginx/conf/gitlab-http.conf
    终端输出
      auth_basic 'Restricted';
      auth_basic_user_file /etc/gitlab/gitlab_htpasswd;
  7. 测试访问,确认GitLab Web的HTTP身份验证功能已经开启生效

    位置:内网服务器,用户:root
    curl -v http://gitlab.sjx.com:8181/
    终端输出
    *   Trying 192.168.1.124:8181...
    * Connected to gitlab.sjx.com (192.168.1.124) port 8181 (#0)
    > GET / HTTP/1.1
    > Host: gitlab.sjx.com:8181
    > User-Agent: curl/7.81.0
    > Accept: */*
    >
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 401 Unauthorized (1)
    < Server: nginx
    < Date: Sat, 14 Jan 2023 13:06:28 GMT
    < Content-Type: text/html
    < Content-Length: 172
    < Connection: keep-alive
    < WWW-Authenticate: Basic realm="Restricted" (2)
    <
    <html>
    <head><title>401 Authorization Required</title></head>
    <body>
    <center><h1>401 Authorization Required</h1></center>
    <hr><center>nginx</center>
    </body>
    </html>
    * Connection #0 to host gitlab.sjx.com left intact
    1 访问没提供HTTP身份验证,HTTP返回代码就是401
    2 "Restricted" 和Nginx的配置相同
  8. 浏览器访问测试,提示输入用户名和密码

ch04.s11 9

4.11. 导入实践星源代码到GitLab

URL入口

http://gitlab.sjx.com:8181/projects/new#import_project

操作
菜单

Projects  Import pproject  Repository by URL

输入
"Git repository URL"

https://gitee.com/fifilyu/shi-jian-xing.git

"Project name"

实践星

"Project URL"

http://gitlab.sjx.com:8181/root

"Project slug"

shi-jian-xing

"Project description (optional)"

Spring Boot + Spring MVC + Spring Security + Thymeleaf + Redis + MySQL的Java示例性项目,用快速上手SpringBoot框架

"Visibility Level"

Private

完成

点击下方 Create project 按钮,创建Git仓库

操作示例如下:

ch04.s12 1

4.11.1. 实践星源代码正在导入

ch04.s12 2

4.11.2. 实践星源代码导入完成

ch04.s12 3
ch04.s12 4
ch04.s12 5

5. 运行实践星一:开发工作机(本地)

操作位置
  • 开发工作机

5.1. 配置本地开发环境

配置本地开发环境,像 程序员 一样在 本地 运行Java代码

5.1.1. 安装命令行编辑工具

开发工作机
sudo apt install -y xmlstarlet crudini

5.1.2. 安装Git

  1. 安装软件包

    开发工作机
    sudo apt install -y git
  2. 查看版本

    开发工作机
    git --version
    终端输出
    git version 2.34.1

5.1.3. 安装Java 11

  1. 安装软件包

    开发工作机
    sudo apt install -y openjdk-11-jdk
  2. 查看当前默认 Java 版本

    开发工作机
    java -version
    终端输出
    openjdk version "11.0.17" 2022-10-18
    OpenJDK Runtime Environment (build 11.0.17+8-post-Ubuntu-1ubuntu222.04)
    OpenJDK 64-Bit Server VM (build 11.0.17+8-post-Ubuntu-1ubuntu222.04, mixed mode, sharing)

    设置Java11为默认版本:

    开发工作机
    sudo update-alternatives --set java /usr/lib/jvm/java-11-openjdk-amd64/bin/java
    sudo update-alternatives --set javac /usr/lib/jvm/java-11-openjdk-amd64/bin/javac

5.1.4. 安装配置MySQL

安装软件包
开发工作机
sudo apt install -y mysql-server
查看服务状态
开发工作机
systemctl status mysql
终端输出
● mysql.service - MySQL Community Server
     Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2023-01-06 21:08:13 CST; 1 day 18h ago
   Main PID: 5896 (mysqld)
     Status: "Server is operational"
      Tasks: 47 (limit: 4628)
     Memory: 276.1M
        CPU: 9min 42.638s
     CGroup: /system.slice/mysql.service
             └─5896 /usr/sbin/mysqld

1月 06 21:08:11 ubuntu2204 systemd[1]: Starting MySQL Community Server...
1月 06 21:08:13 ubuntu2204 systemd[1]: Started MySQL Community Server.
本机无密码登录设置
  1. 查看MySQL默认用户和密码

    开发工作机
    sudo cat /etc/mysql/debian.cnf
    终端输出
    # Automatically generated for Debian scripts. DO NOT TOUCH!
    [client]
    host     = localhost
    user     = debian-sys-maint
    password = 随机生成的用户密码
    socket   = /var/run/mysqld/mysqld.sock
    [mysql_upgrade]
    host     = localhost
    user     = debian-sys-maint
    password = 随机生成的用户密码
    socket   = /var/run/mysqld/mysqld.sock
  2. 脚本无人化配置(自动输入密码)

    1. 安装依赖包

      开发工作机
      sudo apt install -y expect
    2. 自动读取默认用户和密码

      开发工作机
      MYSQL_USER_NAME=`sudo grep -E 'user[ ]+=' /etc/mysql/debian.cnf|head -n 1|cut -d ' ' -f 7`
      MYSQL_USER_PASSWORD=`sudo grep -E 'password[ ]+=' /etc/mysql/debian.cnf|head -n 1|cut -d ' ' -f 3`
      
      echo "MySQL默认设置->用户名称:${MYSQL_USER_NAME} 用户密码:${MYSQL_USER_PASSWORD}"
      终端输出
      MySQL默认设置->用户名称:debian-sys-maint 用户密码:随机生成的用户密码
    3. 执行脚本

      开发工作机
      unbuffer expect -c "
      spawn mysql_config_editor set --skip-warn --login-path=client --host=localhost --user=debian-sys-maint --password
      expect -nocase \"Enter password:\" {send \"${MYSQL_USER_PASSWORD}\n\"; interact}
      "
      终端输出
      spawn mysql_config_editor set --skip-warn --login-path=client --host=localhost --user=debian-sys-maint --password (1)
      Enter password:
      1 注意 --login-path 参数值为 client,表示可以这样(mysql --login-path=client)无密码执行MySQL命令
    4. 查看MySQL无密码配置清单

      开发工作机
      mysql_config_editor print --all
      终端输出
      [client] (1)
      user = "debian-sys-maint"
      password = *****
      host = "localhost"
      1 此处的名称必须与前文的 --login-path=client 一致,都是 client
无密码登录测试

现在,可以不带用户名称及密码在终端执行 mysql 命令了。比如,查看数据库列表:

开发工作机
mysql -e "show databases;"
终端输出
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

--login-path 值为 client 时,可以省略此参数。 不省略时,完整命令如下:

mysql --login-path=client -e "show databases;"

5.1.5. 安装配置Redis

  1. 安装软件包

    开发工作机
    sudo apt install -y redis
  2. 查看服务状态

    开发工作机
    systemctl status redis
    终端输出
    ● redis-server.service - Advanced key-value store
         Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled)
         Active: active (running) since Fri 2023-01-06 21:08:15 CST; 1 day 19h ago
           Docs: http://redis.io/documentation,
                 man:redis-server(1)
       Main PID: 6045 (redis-server)
         Status: "Ready to accept connections"
          Tasks: 5 (limit: 4628)
         Memory: 3.7M
            CPU: 2min 2.950s
         CGroup: /system.slice/redis-server.service
                 └─6045 "/usr/bin/redis-server 127.0.0.1:6379"
    
    1月 06 21:08:15 ubuntu2204 systemd[1]: Starting Advanced key-value store...
    1月 06 21:08:15 ubuntu2204 systemd[1]: Started Advanced key-value store.
  3. 测试Redis连接

    开发工作机
    redis-cli ping
    终端输出
    PONG

5.1.6. 安装配置Maven

  1. 安装软件包

    开发工作机
    sudo apt install -y maven
  2. 查看版本

    开发工作机
    mvn --version
    终端输出
    Apache Maven 3.6.3
    Maven home: /usr/share/maven
    Java version: 11.0.17, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64 (1)
    Default locale: zh_CN, platform encoding: UTF-8
    OS name: "linux", version: "5.15.0-43-generic", arch: "amd64", family: "unix"
    1 输出中的 Java version 必须是Java 11
  3. 配置Maven仓库,将会从国内镜像(阿里云)服务器下载各种实践星依赖包

    开发工作机
    mkdir -p ~/.m2
    
    cat << EOF > ~/.m2/settings.xml
    <settings>
      <mirrors>
        <mirror>
          <id>aliyun</id>
          <name>Aliyun Central</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
          <mirrorOf>central</mirrorOf>
        </mirror>
      </mirrors>
    </settings>
    EOF

5.2. 本地Git克隆实践星源代码

从GitLab仓库克隆(下载)实践星源代码到 ~/workspace/shi-jian-xing 目录:

开发工作机
git clone git@gitlab.sjx.com:root/shi-jian-xing.git ~/workspace/shi-jian-xing
终端输出
正克隆到 '/home/lyuqiang/workspace/shi-jian-xing'...
remote: Enumerating objects: 437, done.
remote: Counting objects: 100% (437/437), done.
remote: Compressing objects: 100% (353/353), done.
remote: Total 437 (delta 189), reused 0 (delta 0), pack-reused 0
接收对象中: 100% (437/437), 143.24 KiB | 299.00 KiB/s, 完成.
处理 delta 中: 100% (189/189), 完成.

访问 http://gitlab.sjx.com:8181/root/shi-jian-xing,点击 Clone 按钮,"Clone with SSH" 文本框中的就是实践星的GitLab仓库地址。如下图所示:

ch05.s2 1

查看克隆的实践星源代码:

开发工作机
ll ~/workspace/shi-jian-xing/
终端输出
总用量 64
drwxrwxr-x  4 lyuqiang lyuqiang  4096  1月 10 14:44 ./
drwxr-x--- 20 lyuqiang lyuqiang  4096  1月 10 14:44 ../
drwxrwxr-x  8 lyuqiang lyuqiang  4096  1月 10 14:44 .git/ (1)
-rw-rw-r--  1 lyuqiang lyuqiang    50  1月 10 14:44 .gitignore
-rw-rw-r--  1 lyuqiang lyuqiang 35149  1月 10 14:44 LICENSE
-rw-rw-r--  1 lyuqiang lyuqiang  3034  1月 10 14:44 pom.xml
-rw-rw-r--  1 lyuqiang lyuqiang  2677  1月 10 14:44 README.md
drwxrwxr-x  3 lyuqiang lyuqiang  4096  1月 10 14:44 src/
1 Git仓库数据目录,包括Git数据和仓库配置

悄悄试一试Git命令,软件系统可还有代码以外的东西哦~

  • 查看仓库工作区状态

开发工作机
cd ~/workspace/shi-jian-xing
git status
终端输出
位于分支 master
您的分支与上游分支 'origin/master' 一致。

无文件要提交,干净的工作区
  • 查看仓库远程地址

开发工作机
cd ~/workspace/shi-jian-xing
git remote -v
终端输出
origin	git@gitlab.sjx.com:root/shi-jian-xing.git (fetch) (1)
origin	git@gitlab.sjx.com:root/shi-jian-xing.git (push)
1 git clone 时的URL相同

如果你对Git感兴趣,可以看看Git官方编写的 Pro Git第二版(简体中文)

5.3. 实践星目录结构说明

安装 tree 软件包:

开发工作机
sudo apt-get install -y tree

tree 命令打印目录树:

开发工作机
tree ~/workspace/shi-jian-xing/
终端输出
/home/lyuqiang/workspace/shi-jian-xing/
├── LICENSE (1)
├── pom.xml (2)
├── README.md (3)
└── src
    └── main
        ├── java (4)
        │   └── com
        │       └── cdgeekcamp
        │           └── demo (5)
        │               ├── Application.java (6)
        │               ├── config (7)
        │               │   ├── ApplicationConfig.java
        │               │   ├── MvcConfig.java
        │               │   └── SecurityConfiguration.java
        │               ├── dto (8)
        │               │   └── UserDto.java
        │               ├── model (9)
        │               │   ├── Role.java
        │               │   └── User.java
        │               ├── repository (10)
        │               │   ├── RoleRepository.java
        │               │   └── UserRepository.java
        │               ├── service (11)
        │               │   ├── CustomUserDetailsService.java
        │               │   ├── UserServiceImpl.java
        │               │   └── UserService.java
        │               ├── util (12)
        │               │   ├── LoginUrlAuthenticationSuccessHandler.java
        │               │   ├── TbConstants.java
        │               │   └── UtilTool.java
        │               └── web (13)
        │                   ├── HomeController.java
        │                   └── RegistrationController.java
        └── resources (14)
            ├── application.properties.sample (15)
            ├── logback.xml.sample (16)
            └── thymeleaf (17)
                ├── css
                │   └── style.css
                ├── images
                │   ├── favicon.ico
                │   ├── signin.jpg
                │   └── signup.jpg
                ├── js
                │   └── jquery.min.js
                └── templates
                    ├── index.html (18)
                    ├── login.html (19)
                    └── registration.html (20)

18 directories, 30 files

了解完目录结构,阅读 配置本地实践星运行参数,为运行实践星提前做一些配置工作

如果你还想了解目录结构的具体细节,熟悉Java项目的组织原则和(项目)工程结构,请阅读以下内容:

1 LICENSE:实践星源代码使用哪种许可证进行开源
2 pom.xml:实践星的Maven配置文件。pom.xml 会告诉Maven:
  1. 项目名称、项目版本号、打包文件名称等等

  2. 项目构建时使用的文件编码、依赖的Java版本等等

  3. 项目依赖哪些Jar包,并从默认或 ~/.m2/settings.xml 中配置的网络仓库下载

  4. 如何构建Java源代码(foobar.javafoobar.class

  5. 如何打包Jar文件(java -jar foobar.jar

  6. 如何打包War文件(foobar.war 放到指定的 Tomcat 目录下即可访问Web页面

  7. Java运行入口(main 函数)在哪个Java类

  8. …​…​等等

3 README.md:开发者对实践星系统的一些说明和描述
4 src/main/java/:Java官方规定的源代码根目录(src/main/java 下的 目录,开发者沟通时,会称做
5 com/cdgeekcamp/demo/:开发者创建的Java ,用于存放源代码
6 Application.java:实践星的运行入口(main 函数位置)
7 config:动态配置SpringBoot框架行为的代码
8 dto:临时保存网站用户数据的Java对象
9 model:数据库表和Java类之间的映射,比如把数据库中的 user 表映射成Java的 User
10 repository:Java操作数据库的工具类
11 service:对数据库的增删改查操作代码,开发者会统一放到 service 包下,方便维护管理
12 util:开发者编写的一些辅助工具代码
13 web:实践星Web相关代码,包括URL、HTTP返回代码、HTTP请求方法、HTTP请求参数等等
14 src/main/resources/:Java官方规定的项目资源(图片、网页、文件模板等等)和配置文件(如 `application.properties`)存放目录
15 application.properties.sample:开发者自定义的系统配置文件模板。运行项目时,会复制成 application.properties
16 logback.xml.sample:开发者自定义的日志配置文件模板。运行项目时,会复制成 logback.xml
17 thymeleaf:静态网页模板引擎 thymeleaf 相关静态资源文件(css、js、图片等等)以及HTML模板(实践星运行时,HTML模板会被Java代码动态解析后,转换成HTML网页
18 index.html:默认首页的HTML模板
19 login.html:登录页面的HTML模板
20 registration.html:注册页面的HTML模板

5.4. 配置本地实践星运行参数

5.4.1. 切换工作目录

开发工作机
cd ~/workspace/shi-jian-xing

5.4.2. 从模板创建配置文件

开发工作机
cp src/main/resources/application.properties.sample src/main/resources/application.properties
cp src/main/resources/logback.xml.sample src/main/resources/logback.xml

5.4.3. 设置用户头像目录

设置用户头像目录至当前目录下的 var/lib

开发工作机
crudini --set --existing src/main/resources/application.properties "" application.config.user-photo-save-dir 'var/lib/sjx/images/avatar'

5.4.4. 设置MySQL登录认证

自动从MySQL配置文件获取登录信息,然后写入配置文件

开发工作机
MYSQL_USER_NAME=`sudo grep -E 'user[ ]+=' /etc/mysql/debian.cnf|head -n 1|cut -d ' ' -f 7`
MYSQL_USER_PASSWORD=`sudo grep -E 'password[ ]+=' /etc/mysql/debian.cnf|head -n 1|cut -d ' ' -f 3`

echo "MySQL默认设置->用户名称:${MYSQL_USER_NAME} 用户密码:${MYSQL_USER_PASSWORD}"

crudini --set --existing src/main/resources/application.properties "" spring.datasource.username "${MYSQL_USER_NAME}"

crudini --set --existing src/main/resources/application.properties "" spring.datasource.password "${MYSQL_USER_PASSWORD}"

5.4.5. 设置Redis登录认证

Redis安装后,默认空密码,不需要更新配置文件

5.4.6. 设置日志文件路径

设置日志文件路径至当前目录下的 var/log

开发工作机
xmlstarlet ed -L -u '/configuration/appender[@name="FILE"]/file' -v var/log/error.log src/main/resources/logback.xml

5.4.7. 确认参数设置

查看MySQL登录信息、用户头像目录和日志文件路径设置

开发工作机
egrep -nh \
    'spring\.datasource\.(username|password)|user-photo-save-dir|<file>' \
    src/main/resources/application.properties \
    src/main/resources/logback.xml
终端输出
31:spring.datasource.username=debian-sys-maint
33:spring.datasource.password=随机生成的用户密码
65:application.config.user-photo-save-dir=var/lib/sjx/images/avatar
3:        <file>var/log/error.log</file>

5.5. 本地启动实践星

5.5.1. 切换工作目录

开发工作机
cd ~/workspace/shi-jian-xing

5.5.2. 本地启动实践星

开发工作机
mvn spring-boot:run

5.5.3. 启动过程说明

这可能是你第一次用Maven构建Java项目,看到终端在 跳动 不用惊讶和陌生~

Maven会优先从网络仓库下载开发者在 pom.xml 中设置的Java依赖,依赖越多下载时间越长

另外,看到下面截图中的 maven.aliyun.com 是不是很熟悉?对的,这是在 安装配置Maven 中手动设置的国内镜像服务器

  1. 正在下载编译相关的依赖包

    ch05.s4 1
  2. 正在将 .java 文件编译成 .class 文件

    ch05.s4 2
  3. 正在运行相关的依赖包

    ch05.s4 3
  4. Maven启动SpringBoot框架内置的Tomcat

    1. Tomcat启动中…​…​

      ch05.s4 4
    2. Tomcat启动完成,监听HTTP端口:8080

      ch05.s4 5

5.6. 本地访问实践星

访问 http://localhost:8080,第一次看到实践星的Web页面

5.6.1. 实践星功能展示

  1. 登录页面

    ch05.s6 1
  2. 注册页面

    ch05.s6 2
  3. 创建帐户

    ch05.s6 3
  4. 注册成功

    ch05.s6 4
  5. 登录网站

    ch05.s6 5.1
  6. 登录失败

    ch05.s6 5.2
  7. 登录成功,显示用户主页

    ch05.s6 6
  8. 退出登录

    ch05.s6 7

5.6.2. 实践星功能说明

实践星是一个用于演示的Java项目。所以,每次启动时都会重建数据库表,清空原有用户数据。

5.7. 本地打包实践星

为方便在内网服务器运行实践星,提前准备好Jar文件

构建实践星,生成Jar文件:

位置:开发工作机
mvn -f ~/workspace/shi-jian-xing package
终端输出
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------< com.cdgeekcamp:shi-jian-xing >--------------------
[INFO] Building ShiJianXing 0.0.1
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ shi-jian-xing ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 10 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ shi-jian-xing ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 17 source files to /home/lyuqiang/workspace/shi-jian-xing/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ shi-jian-xing ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] skip non existing resourceDirectory /home/lyuqiang/workspace/shi-jian-xing/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.10.1:testCompile (default-testCompile) @ shi-jian-xing ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ shi-jian-xing ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:3.2.2:jar (default-jar) @ shi-jian-xing ---
[INFO] Building jar: /home/lyuqiang/workspace/shi-jian-xing/target/shi-jian-xing-0.0.1.jar (1)
[INFO]
[INFO] --- spring-boot-maven-plugin:2.7.6:repackage (repackage) @ shi-jian-xing ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.712 s
[INFO] Finished at: 2023-01-18T14:41:50+08:00
[INFO] ------------------------------------------------------------------------
1 Jar文件的保存路径

6. 运行实践星二:内网服务器

操作位置
  • 内网服务器

6.1. 内网环境说明

在内网服务器配置测试环境,为后续GitLab+Jenkins自动发布做运行环境准备

内网服务器运行的实践星是用于软件研发部门(产品经理、程序员和软件测试)完成 系统集成、功能联调和软件测试工作

6.2. 配置内网环境一:安装Git

6.2.1. 安装软件包

位置:内网服务器,用户:root
yum install -y git

6.2.2. 查看版本

位置:内网服务器,用户:root
git --version
终端输出
git version 1.8.3.1

6.3. 配置内网环境二:安装Java 11

6.3.1. 安装软件包

位置:内网服务器,用户:root
yum install -y java-11-openjdk java-11-openjdk-devel java-11-openjdk-headless

6.3.2. 查看当前默认 Java 版本

位置:内网服务器,用户:root
java -version
终端输出
openjdk version "11.0.17" 2022-10-18 LTS
OpenJDK Runtime Environment (Red_Hat-11.0.17.0.8-2.el7_9) (build 11.0.17+8-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-11.0.17.0.8-2.el7_9) (build 11.0.17+8-LTS, mixed mode, sharing)

6.3.3. 设置Java11为默认版本

位置:内网服务器,用户:root
alternatives --set java `ls /usr/lib/jvm/java-11-openjdk-*/bin/java`
alternatives --set javac `ls /usr/lib/jvm/java-11-openjdk-*/bin/javac`
alternatives --set jre_openjdk `ls -d /usr/lib/jvm/java-11-openjdk-*`
alternatives --set java_sdk_openjdk `ls -d /usr/lib/jvm/java-11-openjdk-*`

+ IMPORTANT: 同时设置四个选项后,/etc/alternatives/java_sdk 会自动链接到 /usr/lib/jvm/java-11-openjdk-*,最终 JAVA_HOME 才是期望的Java 11

6.4. 配置内网环境三:安装Maven

实践星依赖Maven 3.5+

6.4.1. 安装软件包

位置:内网服务器,用户:root
wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo

yum install -y apache-maven
Maven软件包默认依赖Java 8。以下Java相关软件包将被自动安装
  • java-1.8.0-openjdk

  • java-1.8.0-openjdk-devel

  • java-1.8.0-openjdk-headless

注意设置默认Java版本,详情见 设置Java11为默认版本

6.4.2. 查看版本

位置:内网服务器,用户:root
mvn -v
终端输出
Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T15:58:13+08:00)
Maven home: /usr/share/apache-maven
Java version: 11.0.17, vendor: Red Hat, Inc. (1)
Java home: /usr/lib/jvm/java-11-openjdk-11.0.17.0.8-2.el7_9.x86_64
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1160.81.1.el7.x86_64", arch: "amd64", family: "unix"
1 输出中的 Java version 必须是Java 11

6.4.3. 配置全局Maven仓库

配置全局Maven仓库,将会从国内镜像(阿里云)服务器下载各种实践星依赖包

位置:内网服务器,用户:root
xmlstarlet ed -L \
    -d '/_:settings/_:mirrors/_:mirror' \
    -s '/_:settings/_:mirrors' -t elem -n 'mirror' \
    -s '/_:settings/_:mirrors/mirror' -t elem -n id -v 'aliyunmavenmirror' \
    -s '/_:settings/_:mirrors/mirror' -t elem -n name -v '阿里云Maven公共仓库镜像' \
    -s '/_:settings/_:mirrors/mirror' -t elem -n url -v 'https://maven.aliyun.com/repository/public' \
    -s '/_:settings/_:mirrors/mirror' -t elem -n mirrorOf -v '*' \
    /etc/maven/settings.xml

确认镜像添加成功:

位置:内网服务器,用户:root
xmlstarlet el /etc/maven/settings.xml | grep settings/mirrors/mirror
终端输出
settings/mirrors/mirror
settings/mirrors/mirror/id
settings/mirrors/mirror/name
settings/mirrors/mirror/url
settings/mirrors/mirror/mirrorOf

6.5. 配置内网环境四:安装MySQL

6.5.1. 新增MySQL官方Yum仓库

安装MySQL仓库
位置:内网服务器,用户:root
yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm
导入MySQL仓库公钥
位置:内网服务器,用户:root
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-mysql*
打印所有MySQL仓库列表
位置:内网服务器,用户:root
yum repolist all | grep mysql
终端输出
[root@lan_server ~]# yum repolist all | grep mysql
mysql-cluster-7.5-community/x86_64           MySQL Cluster  disabled
mysql-cluster-7.5-community-source           MySQL Cluster  disabled
mysql-cluster-7.6-community/x86_64           MySQL Cluster  disabled
mysql-cluster-7.6-community-source           MySQL Cluster  disabled
mysql-cluster-8.0-community/x86_64           MySQL Cluster  disabled
mysql-cluster-8.0-community-debuginfo/x86_64 MySQL Cluster  disabled
mysql-cluster-8.0-community-source           MySQL Cluster  disabled
mysql-connectors-community/x86_64            MySQL Connecto enabled:         206
mysql-connectors-community-debuginfo/x86_64  MySQL Connecto disabled
mysql-connectors-community-source            MySQL Connecto disabled
mysql-tools-community/x86_64                 MySQL Tools Co enabled:          94
mysql-tools-community-debuginfo/x86_64       MySQL Tools Co disabled
mysql-tools-community-source                 MySQL Tools Co disabled
mysql-tools-preview/x86_64                   MySQL Tools Pr disabled
mysql-tools-preview-source                   MySQL Tools Pr disabled
mysql57-community/x86_64                     MySQL 5.7 Comm disabled
mysql57-community-source                     MySQL 5.7 Comm disabled
mysql80-community/x86_64                     MySQL 8.0 Comm enabled:     229+138
mysql80-community-debuginfo/x86_64           MySQL 8.0 Comm disabled
mysql80-community-source                     MySQL 8.0 Comm disabled
打印启用的MySQL仓库列表
位置:内网服务器,用户:root
yum repolist | grep mysql
终端输出
mysql-connectors-community/x86_64 MySQL Connectors Community                 206
mysql-tools-community/x86_64      MySQL Tools Community                       94
mysql80-community/x86_64          MySQL 8.0 Community Server             229+138
打印MySQL8.0仓库的软件包列表
位置:内网服务器,用户:root
yum repo-pkgs mysql80-community list
终端输出
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.cqu.edu.cn
 * extras: mirrors.cqu.edu.cn
 * updates: mirrors.cqu.edu.cn
Available Packages
mysql-community-client.x86_64                    8.0.31-1.el7       mysql80-community
mysql-community-client-plugins.x86_64            8.0.31-1.el7       mysql80-community
mysql-community-common.x86_64                    8.0.31-1.el7       mysql80-community
mysql-community-devel.x86_64                     8.0.31-1.el7       mysql80-community
mysql-community-embedded-compat.x86_64           8.0.31-1.el7       mysql80-community
mysql-community-icu-data-files.x86_64            8.0.31-1.el7       mysql80-community
mysql-community-libs.x86_64                      8.0.31-1.el7       mysql80-community
mysql-community-libs-compat.x86_64               8.0.31-1.el7       mysql80-community
mysql-community-server.x86_64                    8.0.31-1.el7       mysql80-community
mysql-community-server-debug.x86_64              8.0.31-1.el7       mysql80-community
mysql-community-test.x86_64                      8.0.31-1.el7       mysql80-community
mysql-ref-manual-8.0-en-html-chapter.noarch      1-20220914         mysql80-community
mysql-ref-manual-8.0-en-pdf.noarch               1-20220914         mysql80-community
默认禁用MySQL仓库

按需单独启用MySQL仓库

位置:内网服务器,用户:root
yum-config-manager --disable mysql-connectors-community | egrep '(\[mysql-connectors-community\])|enabled'
yum-config-manager --disable mysql-tools-community | egrep '(\[mysql-tools-community\])|enabled'
yum-config-manager --disable mysql80-community | egrep '(\[mysql80-community\])|enabled'

6.5.2. 安装MySQL软件包

位置:内网服务器,用户:root
yum --enablerepo=mysql80-community install -y mysql-community-server

6.5.3. 配置MySQL

设置MySQL数据目录(可选)
位置:内网服务器,用户:root
mkdir -p /data/mysql

crudini --set --existing /etc/my.cnf "mysqld" datadir /data/mysql
MySQL 8.0第一次启动,会初始化数据目录并设置权限
关闭MySQL X插件(端口33060)
位置:内网服务器,用户:root
echo mysqlx=0 >> /etc/my.cnf
允许外部访问MySQL运行端口(3306)

为方便开发者在内网测试或调试程序,对外开放MySQL端口

位置:内网服务器,用户:root
# MySQL监听系统所有IP,外部可连接3306
echo bind-address=0.0.0.0 >> /etc/my.cnf

# 增加防火墙规则
firewall-cmd --permanent --add-service=mysql
firewall-cmd --reload
firewall-cmd --list-all
终端输出
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources:
  services: GITLAB_8181 dhcpv6-client mysql ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
开机启动MySQL
位置:内网服务器,用户:root
systemctl enable mysqld

6.5.4. 启动MySQL

启动MySQL服务:

位置:内网服务器,用户:root
systemctl start mysqld

查看MySQL服务状态:

位置:内网服务器,用户:root
systemctl status mysqld
终端输出
● mysqld.service - MySQL Server
   Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2023-01-17 12:52:17 CST; 4min 42s ago
     Docs: man:mysqld(8)
           http://dev.mysql.com/doc/refman/en/using-systemd.html
  Process: 22188 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
 Main PID: 22216 (mysqld)
   Status: "Server is operational"
   CGroup: /system.slice/mysqld.service
           └─22216 /usr/sbin/mysqld

Jan 17 12:52:09 lan_server systemd[1]: Starting MySQL Server...
Jan 17 12:52:17 lan_server systemd[1]: Started MySQL Server.

6.5.5. 修改密码

临时密码有不常用的特殊字符,不便日常管理。不降低安全性的前提性,更改MySQL密码

位置:内网服务器,用户:root
MYSQL_TMP_ROOT_PASSWORD=$(grep 'A temporary password' /var/log/mysqld.log | tail -n 1 | awk '{print $NF}')

export SJX_MYSQL_ROOT_PASSWORD=$(pwgen -csnB 10)_$(pwgen -csnB 10)
# 永久保存临时配置(重新登录或重启都有效)
sed -i '/export SJX_/d' ~/.bash_profile && env | grep SJX_ | awk '{print "export "$1}' >> ~/.bash_profile

echo -e "  MySQL用户名:root\nMySQL临时密码:${MYSQL_TMP_ROOT_PASSWORD}\n  MySQL新密码:${SJX_MYSQL_ROOT_PASSWORD}"

mysqladmin -uroot -p"${MYSQL_TMP_ROOT_PASSWORD}" password ${SJX_MYSQL_ROOT_PASSWORD}
MySQL 8.0默认安装了密码有效性插件(validate_password),无法设置 123456 之类的简单用户密码
终端输出
  MySQL用户名:root
MySQL临时密码:j)_gsXV2>bBP
  MySQL新密码:9fhWR33qq9_MJvEH4oYvw

6.5.6. 本机无密码登录设置

脚本无人化配置(自动输入密码)
  1. 执行脚本

    位置:内网服务器,用户:root
    unbuffer expect -c "
    spawn mysql_config_editor set --skip-warn --login-path=client --host=localhost --user=root --password
    expect -nocase \"Enter password:\" {send \"${SJX_MYSQL_ROOT_PASSWORD}\n\"; interact}
    "
    终端输出
    spawn mysql_config_editor set --skip-warn --login-path=client --host=localhost --user=root --password (1)
    Enter password:
    1 注意 --login-path 参数值为 client,表示可以这样(mysql --login-path=client)无密码执行MySQL命令
  2. 查看MySQL无密码配置清单

    位置:内网服务器,用户:root
    mysql_config_editor print --all
    终端输出
    [client] (1)
    user = "root"
    password = *****
    host = "localhost"
    1 此处的名称必须与前文的 --login-path=client 一致,都是 client

6.5.7. 无密码登录测试

现在,可以不带用户名称及密码在终端执行 mysql 命令了。比如,查看数据库列表:

位置:内网服务器,用户:root
mysql -e "show databases;"
终端输出
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

--login-path 值为 client 时,可以省略此参数。 不省略时,完整命令如下:

mysql --login-path=client -e "show databases;"

6.5.8. 创建内网实践星的MySQL用户

创建MySQL用户
位置:内网服务器,用户:root
export SJX_MYSQL_HOST=localhost
export SJX_MYSQL_DB=sjx
export SJX_MYSQL_USER_NAME=sjx
export SJX_MYSQL_USER=${SJX_MYSQL_USER_NAME}@${SJX_MYSQL_HOST}
export SJX_MYSQL_PASSWORD=$(pwgen -s 10)_$(pwgen -s 10)

# 永久保存临时配置(重新登录或重启都有效)
sed -i '/export SJX_/d' ~/.bash_profile && env | grep SJX_ | awk '{print "export "$1}' >> ~/.bash_profile

mysql -e "DROP DATABASE IF EXISTS ${SJX_MYSQL_DB}; CREATE DATABASE ${SJX_MYSQL_DB};"
mysql -e "DROP USER IF EXISTS ${SJX_MYSQL_USER}; CREATE USER ${SJX_MYSQL_USER} IDENTIFIED BY '${SJX_MYSQL_PASSWORD}';"
mysql -e "GRANT ALL ON sjx.* TO ${SJX_MYSQL_USER} WITH GRANT OPTION;"

echo -e "实践星MySQL连接参数:\n\tMySQL主机地址:${SJX_MYSQL_HOST}\n\tMySQL主机端口:3306\n\tMySQL数据库名:${SJX_MYSQL_DB}\n\tMySQL用户名称:${SJX_MYSQL_USER_NAME}\n\tMySQL用户密码:${SJX_MYSQL_PASSWORD}"
终端输出
实践星MySQL连接参数:
	MySQL主机地址:localhost
	MySQL主机端口:3306
	MySQL数据库名:sjx
	MySQL用户名称:sjx
	MySQL用户密码:7fpBAmAaGK_VVGKD16ACj
设置实践星MySQL用户无密码登录,方便日常管理
位置:内网服务器,用户:root
unbuffer expect -c "
spawn mysql_config_editor set --skip-warn --login-path=${SJX_MYSQL_USER_NAME} --host=${SJX_MYSQL_HOST} --user=${SJX_MYSQL_USER_NAME} --password
expect -nocase \"Enter password:\" {send \"${SJX_MYSQL_PASSWORD}\n\"; interact}
"
终端输出
spawn mysql_config_editor set --skip-warn --login-path=sjx --host=localhost --user=sjx --password
Enter password:
测试实践星MySQL用户连接
位置:内网服务器,用户:root
mysql --login-path=sjx sjx -e 'select user();'
终端输出
+---------------+
| user()        |
+---------------+
| sjx@localhost |
+---------------+

6.6. 配置内网环境五:安装Redis

Yum和EPEL源的Redis版本比较老

6.6.1. 新增Yum仓库

安装Remi仓库
位置:内网服务器,用户:root
yum install -y http://rpms.remirepo.net/enterprise/remi-release-7.rpm
导入Remi仓库公钥
位置:内网服务器,用户:root
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-remi
打印所有Remi仓库列表
位置:内网服务器,用户:root
yum repolist all | grep -i remi
终端输出
 * remi-safe: ftp.riken.jp
remi                                         Remi's RPM rep disabled
...省略的内容...
...省略的内容...
...省略的内容...
remi-php82                                   Remi's PHP 8.2 disabled
remi-php82-debuginfo/x86_64                  Remi's PHP 8.2 disabled
remi-php82-test                              Remi's PHP 8.2 disabled
remi-php82-test-debuginfo/x86_64             Remi's PHP 8.2 disabled
remi-safe                                    Safe Remi's RP enabled:       5,098
remi-safe-debuginfo/x86_64                   Remi's RPM rep disabled
remi-test                                    Remi's test RP disabled
remi-test-debuginfo/x86_64                   Remi's test RP disabled
默认禁用Remi仓库
位置:内网服务器,用户:root
yum-config-manager --disable remi-safe | egrep '(\[remi-safe\])|enabled'
终端输出
[remi-safe]
enabled = 0
查看Redis软件包名
位置:内网服务器,用户:root
yum repo-pkgs remi list | grep '^redis'
终端输出
redis.x86_64                                  7.0.7-1.el7.remi              remi
redis-devel.x86_64                            7.0.7-1.el7.remi              remi
redis-doc.noarch                              7.0.7-1.el7.remi              remi
redis-trib.noarch                             7.0.7-1.el7.remi              remi
确认Redis软件包来源

打印Redis软件包信息:

位置:内网服务器,用户:root
yum --disablerepo=\* --enablerepo=remi info redis
终端输出
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.cqu.edu.cn
 * extras: mirrors.cqu.edu.cn
 * remi: ftp.riken.jp
 * remi-safe: ftp.riken.jp
 * updates: mirrors.cqu.edu.cn
Available Packages
Name        : redis
Arch        : x86_64
Version     : 7.0.7 (1)
Release     : 1.el7.remi (2)
Size        : 1.5 M
Repo        : remi
Summary     : A persistent key-value database
URL         : http://redis.io
License     : BSD
Description : Redis is an advanced key-value store. It is often referred to as a data
            : structure server since keys can contain strings, hashes, lists, sets
            : and sorted sets.
            :
            : You can run atomic operations on these types, like appending to a
            : string; incrementing the value in a hash; pushing to a list; computing
            : set intersection, union and difference; or getting the member with
            : highest ranking in a sorted set.
            :
            : In order to achieve its outstanding performance, Redis works with an
            : in-memory dataset. Depending on your use case, you can persist it
            : either by dumping the dataset to disk every once in a while, or by
            : appending each command to a log.
            :
            : Redis also supports trivial-to-setup master-slave replication, with
            : very fast non-blocking first synchronization, auto-reconnection on net
            : split and so forth.
            :
            : Other features include Transactions, Pub/Sub, Lua scripting, Keys with
            : a limited time-to-live, and configuration settings to make Redis behave
            : like a cache.
            :
            : You can use Redis from most programming languages also.
1 Redis最新版本
2 remi 仓库

6.6.2. 安装Redis软件包

位置:内网服务器,用户:root
yum --disablerepo=\* --enablerepo=remi install -y redis

6.6.3. 配置Redis

允许外部访问Redis运行端口(6379)

为方便开发者在内网测试或调试程序,对外开放Redis端口

  1. Redis监听系统所有IP,外部可连接6379

    位置:内网服务器,用户:root
    sed -i "s/^bind.*/bind \*/" /etc/redis/redis.conf
  2. 增加防火墙规则

    位置:内网服务器,用户:root
    REDIS_PORT=6379
    PERM="--permanent"
    SERV_NAME=REDIS_${REDIS_PORT}
    SERV="${PERM} --service=${SERV_NAME}"
    
    firewall-cmd ${PERM} --new-service=${SERV_NAME}
    firewall-cmd ${SERV} --set-short="Redis ports"
    firewall-cmd ${SERV} --set-description="Redis port exceptions"
    firewall-cmd ${SERV} --add-port=${REDIS_PORT}/tcp
    firewall-cmd ${PERM} --add-service=${SERV_NAME}
    
    firewall-cmd --reload
    firewall-cmd --list-all
    终端输出
    public (active)
      target: default
      icmp-block-inversion: no
      interfaces: enp0s3
      sources:
      services: GITLAB_8181 REDIS_6379 dhcpv6-client mysql ssh
      ports:
      protocols:
      masquerade: no
      forward-ports:
      source-ports:
      icmp-blocks:
      rich rules:
开机启动Redis
位置:内网服务器,用户:root
systemctl enable redis
设置Redis默认用户密码
位置:内网服务器,用户:root
export SJX_REDIS_PASSWORD=$(pwgen -s 20)

# 永久保存临时配置(重新登录或重启都有效)
sed -i '/export SJX_/d' ~/.bash_profile && env | grep SJX_ | awk '{print "export "$1}' >> ~/.bash_profile

echo "Redis默认用户密码:${SJX_REDIS_PASSWORD}"

sed -i "s/# requirepass .*/requirepass ${SJX_REDIS_PASSWORD}/" /etc/redis/redis.conf
终端输出
Redis默认用户密码:xuSslt3myQyi2EYKXV84

6.6.4. 启动Redis

启动Redis服务:

位置:内网服务器,用户:root
systemctl start redis

查看Redis服务状态:

位置:内网服务器,用户:root
systemctl status redis
终端输出
● redis.service - Redis persistent key-value database
   Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/redis.service.d
           └─limit.conf
   Active: active (running) since Tue 2023-01-17 23:34:49 CST; 2s ago
 Main PID: 5009 (redis-server)
   Status: "Ready to accept connections"
   CGroup: /system.slice/redis.service
           └─5009 /usr/bin/redis-server *:6379

Jan 17 23:34:49 lan_server systemd[1]: Starting Redis persistent key-value database...
Jan 17 23:34:49 lan_server systemd[1]: Started Redis persistent key-value database.
注意 /etc/systemd/system/redis.service.d/limit.conf 对Redis服务的资源限制或优化参数

6.6.5. 测试Redis连接

位置:内网服务器,用户:root
export REDISCLI_AUTH=${SJX_REDIS_PASSWORD}
redis-cli ping
终端输出
PONG
用环境变量 REDISCLI_AUTH 可以消除 redis-cli 的安全提示,比每次连接密码都明文显示在终端中安全些

6.7. 配置内网环境六:配置实践星

6.7.1. 创建内网实践星用户

创建用户 sjx

位置:内网服务器,用户:root
useradd --home-dir /var/lib/sjx --create-home --shell /sbin/nologin --comment "Shi Jian Xing" sjx

查看 sjx 用户:

位置:内网服务器,用户:root
getent passwd sjx
终端输出
sjx:x:1000:1000:Shi Jian Xing:/var/lib/sjx:/sbin/nologin

6.7.2. 创建内网实践星目录和文件

位置:内网服务器,用户:root
mkdir -p /opt/sjx/bin /etc/sjx /var/lib/sjx/images/avatar /var/log/sjx
touch /var/log/sjx/error.log
chown -R sjx:sjx /var/lib/sjx /var/log/sjx

6.7.3. 上传实践星Jar文件和配置模板到内网服务器

位置:开发工作机
cd ~/workspace/shi-jian-xing

scp target/shi-jian-xing-0.0.1.jar lan_server:/opt/sjx/bin/
scp src/main/resources/application.properties.sample lan_server:/etc/sjx/application.properties
scp src/main/resources/logback.xml.sample lan_server:/etc/sjx/logback.xml
终端输出
shi-jian-xing-0.0.1.jar                                           100%   69MB  10.9MB/s   00:06
application.properties.sample                                     100% 3176   234.2KB/s   00:00
logback.xml.sample                                                100%  724   336.9KB/s   00:00

6.7.4. 生成内网实践星主配置

位置:内网服务器,用户:root
crudini --set --existing /etc/sjx/application.properties "" server.address '0.0.0.0'

crudini --set --existing /etc/sjx/application.properties "" server.port '8383'

crudini --set --existing /etc/sjx/application.properties "" spring.datasource.url 'jdbc:mysql://'${SJX_MYSQL_HOST}'/'${SJX_MYSQL_DB}'?createDatabaseIfNotExist=True&serverTimezone=Asia/Shanghai&useUnicode=yes&characterEncoding=UTF-8'

crudini --set --existing /etc/sjx/application.properties "" spring.datasource.username "${SJX_MYSQL_USER_NAME}"

crudini --set --existing /etc/sjx/application.properties "" spring.datasource.password "${SJX_MYSQL_PASSWORD}"

crudini --set --existing /etc/sjx/application.properties "" spring.redis.password "${SJX_REDIS_PASSWORD}"

crudini --set --existing /etc/sjx/application.properties "" logging.config '/etc/sjx/logback.xml'

确认修改后的实践星主配置:

位置:内网服务器,用户:root
egrep -nh '(server\..*)|(spring\.datasource\.(url|username|password))|(spring\.redis\.password)|(logging\.config)' /etc/sjx/application.properties
终端输出
7:server.address=0.0.0.0
9:server.port=8383
33:spring.datasource.url=jdbc:mysql://localhost/sjx?createDatabaseIfNotExist=True&serverTimezone=Asia/Shanghai&useUnicode=yes&characterEncoding=UTF-8
35:spring.datasource.username=sjx
37:spring.datasource.password=3ecxC2nO1q_knfKJs5ZBx
48:server.servlet.session.timeout=30m
54:spring.redis.password=xuSslt3myQyi2EYKXV84
72:logging.config=/etc/sjx/logback.xml

6.7.5. 生成内网实践星日志配置

使用实践星默认的日志配置,没什么参数需要修改

确认实践星日志配置:

位置:内网服务器,用户:root
grep '<file>' /etc/sjx/logback.xml
终端输出
        <file>/var/log/sjx/error.log</file>

6.7.6. 创建内网实践星系统服务

更新内网实践星的Jar文件软链接
位置:内网服务器,用户:root
cd /opt/sjx/bin/
ln -fs shi-jian-xing-0.0.1.jar sjx.jar

ll 命令,确认软件链接:

ch06.s7 1
创建内网实践星的Service文件
位置:内网服务器,用户:root
cat << EOF > /usr/lib/systemd/system/sjx.service
[Unit]
Description=ShiJianXing: A Spring Boot Demo
After=network.target network-online.target nss-lookup.target

[Service]
Type=simple
Environment="CONFIG_FILE=/etc/sjx/application.properties"
Environment="JAR_FILE=/opt/sjx/bin/sjx.jar"

User=sjx
Group=sjx
WorkingDirectory=/var/lib/sjx
ExecStart=/usr/bin/java -Dspring.config.location=\${CONFIG_FILE} -jar \${JAR_FILE}
SuccessExitStatus=143
StandardOutput=null
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF
开机启动内网实践星
位置:内网服务器,用户:root
systemctl enable sjx

6.8. 配置内网环境七:运行实践星

6.8.1. 启动内网实践星

位置:内网服务器,用户:root
systemctl start sjx
位置:内网服务器,用户:root
systemctl status sjx
终端输出
● sjx.service - ShiJianXing: A Spring Boot Demo
   Loaded: loaded (/usr/lib/systemd/system/sjx.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2023-01-18 23:05:12 CST; 4min 43s ago
 Main PID: 7984 (java)
   CGroup: /system.slice/sjx.service
           └─7984 /usr/bin/java -Dspring.config.location=/etc/sjx/application.properties -jar /opt/sjx/bin/sjx.jar

Jan 18 23:05:12 lan_server systemd[1]: Started ShiJianXing: A Spring Boot Demo.
位置:内网服务器,用户:root
tail -f /var/log/sjx/error.log
终端输出
2023-01-18 23:05:14.968 main  INFO  com.cdgeekcamp.shijianxing.Application --- Starting Application v0.0.1 using Java 11.0.17 on lan_server with PID 7984 (/opt/sjx/bin/shi-jian-xing-0.0.1.jar started by sjx in /var/lib/sjx) (1)
2023-01-18 23:05:14.973 main  INFO  com.cdgeekcamp.shijianxing.Application --- No active profile set, falling back to 1 default profile: "default"
2023-01-18 23:05:16.161 main  INFO  org.springframework.data.repository.config.RepositoryConfigurationDelegate --- Multiple Spring Data modules found, entering strict repository configuration mode
2023-01-18 23:05:16.165 main  INFO  org.springframework.data.repository.config.RepositoryConfigurationDelegate --- Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-01-18 23:05:16.532 main  INFO  org.springframework.data.repository.config.RepositoryConfigurationDelegate --- Finished Spring Data repository scanning in 355 ms. Found 2 JPA repository interfaces.
2023-01-18 23:05:17.417 main  INFO  org.springframework.boot.web.embedded.tomcat.TomcatWebServer --- Tomcat initialized with port(s): 8383 (http)
2023-01-18 23:05:17.434 main  INFO  org.apache.coyote.http11.Http11NioProtocol --- Initializing ProtocolHandler ["http-nio-0.0.0.0-8383"] (2)
2023-01-18 23:05:17.435 main  INFO  org.apache.catalina.core.StandardService --- Starting service [Tomcat]
2023-01-18 23:05:17.435 main  INFO  org.apache.catalina.core.StandardEngine --- Starting Servlet engine: [Apache Tomcat/9.0.69]
2023-01-18 23:05:17.643 main  INFO  org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/] --- Initializing Spring embedded WebApplicationContext
2023-01-18 23:05:17.643 main  INFO  org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext --- Root WebApplicationContext: initialization completed in 2601 ms
2023-01-18 23:05:18.369 main  INFO  com.zaxxer.hikari.HikariDataSource --- ShiJianXingHikariConnPool - Starting...
2023-01-18 23:05:19.027 main  INFO  com.zaxxer.hikari.HikariDataSource --- ShiJianXingHikariConnPool - Start completed.
2023-01-18 23:05:31.400 main  INFO  org.hibernate.jpa.internal.util.LogHelper --- HHH000204: Processing PersistenceUnitInfo [name: default]
2023-01-18 23:05:31.475 main  INFO  org.hibernate.Version --- HHH000412: Hibernate ORM core version 5.6.14.Final
2023-01-18 23:05:31.649 main  INFO  org.hibernate.annotations.common.Version --- HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2023-01-18 23:05:31.802 main  INFO  org.hibernate.dialect.Dialect --- HHH000400: Using dialect: org.hibernate.dialect.MySQL5InnoDBDialect
2023-01-18 23:05:32.517 main  INFO  org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator --- HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-01-18 23:05:32.527 main  INFO  org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean --- Initialized JPA EntityManagerFactory for persistence unit 'default'
...省略的内容...
...省略的内容...
...省略的内容...
2023-01-18 23:05:36.227 main  INFO  org.apache.coyote.http11.Http11NioProtocol --- Starting ProtocolHandler ["http-nio-0.0.0.0-8383"]
2023-01-18 23:05:36.371 main  INFO  org.springframework.boot.web.embedded.tomcat.TomcatWebServer --- Tomcat started on port(s): 8383 (http) with context path ''
2023-01-18 23:05:36.527 main  INFO  org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor --- No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2023-01-18 23:05:36.597 main  INFO  com.cdgeekcamp.shijianxing.Application --- Started Application in 22.675 seconds (JVM running for 24.051)
2023-01-18 23:05:44.117 http-nio-0.0.0.0-8383-exec-1 INFO  org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/] --- Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-01-18 23:05:44.117 http-nio-0.0.0.0-8383-exec-1 INFO  org.springframework.web.servlet.DispatcherServlet --- Initializing Servlet 'dispatcherServlet'
2023-01-18 23:05:44.119 http-nio-0.0.0.0-8383-exec-1 INFO  org.springframework.web.servlet.DispatcherServlet --- Completed initialization in 2 ms
1 sjx.service 中定义的 /opt/sjx/bin/sjx.jar/opt/sjx/bin/shi-jian-xing-0.0.1.jar 的软链接。by sjx in /var/lib/sjx 表示实践星是在 sjx 用户权限下运行,实践星当前的工作目录:/var/lib/sjx
2 实践星运行在 0.0.0.0:8383 可以被外部访问,比如开发工作机

6.8.2. 临时增加防火墙放行规则

位置:内网服务器,用户:root
SJX_PORT=8383
PERM="--permanent"
SERV_NAME=SJX_${SJX_PORT}
SERV="${PERM} --service=${SERV_NAME}"

firewall-cmd ${PERM} --new-service=${SERV_NAME}
firewall-cmd ${SERV} --set-short="ShiJianXing ports"
firewall-cmd ${SERV} --set-description="ShiJianXing port exceptions"
firewall-cmd ${SERV} --add-port=${SJX_PORT}/tcp
firewall-cmd ${PERM} --add-service=${SERV_NAME}

firewall-cmd --reload
firewall-cmd --list-all
终端输出
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources:
  services: GITLAB_8181 REDIS_6379 SJX_8383 dhcpv6-client mysql ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

6.8.3. 开发工作机访问内网实践星(临时地址)

操作位置:开发工作机

确认内网实践星Web功能是否可用,内网实践星临时访问URL: http://test.sjx.com:8383

6.9. 配置内网环境八:实践星的Nginx反向代理

实际工作中,直接将Spring Boot启动的程序端口:http://test.sjx.com:8383 暴露到 公共网络 是不可控、不安全的。

内网实践星当前运行在局域网,也不应该轻视安全问题。

内网实践星的系统配置要和生产环境基本保持一致(比如,给实践星加上Nginx反向代理,控制访问行为等等)。

6.9.1. 安装Nginx

增加Nginx官方源
位置:内网服务器,用户:root
cat << EOF > /etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF

rpm --import https://nginx.org/keys/nginx_signing.key

yum makecache

确认软件包来自Nginx官方Yum源:

位置:内网服务器,用户:root
yum info nginx
终端输出
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.bfsu.edu.cn
 * extras: mirrors.aliyun.com
 * updates: mirrors.bfsu.edu.cn
Available Packages
Name        : nginx
Arch        : x86_64
Epoch       : 1
Version     : 1.22.1
Release     : 1.el7.ngx
Size        : 797 k
Repo        : nginx-stable/7/x86_64 (1)
Summary     : High performance web server
URL         : https://nginx.org/
License     : 2-clause BSD-like license
Description : nginx [engine x] is an HTTP and reverse proxy server, as well as
            : a mail proxy server.
1 nginx-stable 表示Nginx官方Yum源
安装Nginx软件包
位置:内网服务器,用户:root
yum install -y nginx
初始化Nginx配置
  1. 修改Nginx主配置

    位置:内网服务器,用户:root
    test -f /etc/nginx/nginx.conf.init || cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.init
    
    cat << EOF > /etc/nginx/nginx.conf
    # For more information on configuration, see:
    #   * Official English Documentation: http://nginx.org/en/docs/
    #   * Official Russian Documentation: http://nginx.org/ru/docs/
    
    user nginx;
    worker_processes auto;
    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;
    
    worker_rlimit_nofile 65535;
    
    events {
        worker_connections 65535;
    }
    
    http {
        include             /etc/nginx/mime.types;
        default_type        application/octet-stream;
    
        log_format  main  '\$host \$server_port \$remote_addr - \$remote_user [\$time_local] "\$request" '
                          '\$status \$request_time \$body_bytes_sent "\$http_referer" '
                          '"\$http_user_agent" "\$http_x_forwarded_for"';
    
        access_log  /var/log/nginx/access.log  main;
    
        sendfile            on;
        tcp_nopush          on;
        tcp_nodelay         on;
        keepalive_timeout   65;
        types_hash_max_size 2048;
    
        server_names_hash_bucket_size 128;
        server_name_in_redirect off;
        client_header_buffer_size 32k;
        large_client_header_buffers 4 32k;
    
        client_header_timeout  3m;
        client_body_timeout    3m;
        client_max_body_size 50m;
        client_body_buffer_size 256k;
        send_timeout           3m;
    
        gzip  on;
        gzip_min_length  1k;
        gzip_buffers     4 16k;
        gzip_http_version 1.0;
        gzip_comp_level 2;
        gzip_types image/svg+xml application/x-font-wof text/plain text/xml text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript text/javascript;
        gzip_vary on;
    
        proxy_redirect off;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header REMOTE-HOST \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_connect_timeout 60;
        proxy_send_timeout 60;
        proxy_read_timeout 60;
        proxy_buffer_size 256k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;
        proxy_temp_file_write_size 256k;
        proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
        proxy_max_temp_file_size 128m;
        #让代理服务端不要主动关闭客户端的连接,协助处理499返回代码问题
        proxy_ignore_client_abort on;
    
        fastcgi_buffer_size 64k;
        fastcgi_buffers 4 64k;
        fastcgi_busy_buffers_size 128k;
    
        index index.html index.htm index.php default.html default.htm default.php;
    
        # Load modular configuration files from the /etc/nginx/conf.d directory.
        # See http://nginx.org/en/docs/ngx_core_module.html#include
        # for more information.
        include /etc/nginx/conf.d/*.conf;
    }
    EOF
  2. 增加默认 Host

    位置:内网服务器,用户:root
    mkdir -p /etc/nginx/conf.d
    
    test -f /etc/nginx/conf.d/default.conf && (test -f /etc/nginx/conf.d/default.conf.init || cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.init)
    
    cat << EOF > /etc/nginx/conf.d/default.conf
    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;
    
        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
    
        location / {
        }
    
        error_page 404 /404.html;
            location = /40x.html {
        }
    
        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
    EOF
  3. 测试Nginx配置

    位置:内网服务器,用户:root
    nginx -t && (test -s /var/run/nginx.pid || rm -f /var/run/nginx.pid)
test -s 检测不是空文件时返回代码 0
为什么删除 /var/run/nginx.pid

nginx -t 之后,/var/run/nginx.pid 空文件会一直被保留,而 nginx.service 并不能处理 PIDFile 为空的情况,导致启动失败

6.9.2. 添加内网实践星的Nginx配置

位置:内网服务器,用户:root
cat << EOF > /etc/nginx/conf.d/test.sjx.com.conf
server {
    listen       80;
    server_name  test.sjx.com;
    root         /var/lib/sjx;

    location / {
        proxy_set_header Host \$http_host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-Proto http;

        proxy_pass http://127.0.0.1:8383;
    }
}
EOF

nginx -t && (test -s /var/run/nginx.pid || rm -f /var/run/nginx.pid)

6.9.3. 启动Nginx

位置:内网服务器,用户:root
systemctl enable nginx
位置:内网服务器,用户:root
systemctl start nginx
位置:内网服务器,用户:root
systemctl status nginx
终端输出
● nginx.service - nginx - high performance web server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since Thu 2023-01-19 00:00:23 CST; 3s ago
     Docs: http://nginx.org/en/docs/
  Process: 9617 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
 Main PID: 9618 (nginx)
   CGroup: /system.slice/nginx.service
           ├─9618 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
           ├─9619 nginx: worker process
           ├─9620 nginx: worker process
           ├─9621 nginx: worker process
           └─9622 nginx: worker process

Jan 19 00:00:23 lan_server systemd[1]: Starting nginx - high performance web server...
Jan 19 00:00:23 lan_server systemd[1]: Started nginx - high performance web server.

6.9.4. 移除内网实践星的临时设置

内网实践星不对外暴露端口

修改实践星主配置文件:

位置:内网服务器,用户:root
crudini --set --existing /etc/sjx/application.properties "" server.address '127.0.0.1'

确认修改后的实践星主配置:

位置:内网服务器,用户:root
egrep -nh 'server\.address' /etc/sjx/application.properties
终端输出
7:server.address=127.0.0.1

重启实践星服务:

位置:内网服务器,用户:root
systemctl restart sjx
移除内网实践星临时防火墙规则
位置:内网服务器,用户:root
# 删除放行规则和服务定义
firewall-cmd --permanent --delete-service=SJX_8383
firewall-cmd --reload

6.9.5. 增加内网Nginx的防火墙放行规则

位置:内网服务器,用户:root
firewall-cmd --permanent --add-service=http

firewall-cmd --reload
firewall-cmd --list-all
终端输出
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources:
  services: GITLAB_8181 REDIS_6379 dhcpv6-client http mysql ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

6.9.6. 开发工作机访问内网实践星(正式地址)

操作位置:开发工作机

内网实践星正式访问URL: http://test.sjx.com/

6.10. 移除内网环境安装过程中的临时环境变量

位置:内网服务器,用户:root
sed -i '/export SJX_/d' ~/.bash_profile
source ~/.bash_profile

7. 内网环境搭建二:Jenkins

操作位置
  • 开发工作机

  • 内网服务器

7.1. 新增Jenkins Yum仓库

7.1.1. 下载仓库文件

位置:内网服务器,用户:root
wget \
  --inet4-only \
  -O /etc/yum.repos.d/jenkins.repo \
  https://pkg.jenkins.io/redhat-stable/jenkins.repo
wget参数说明
--inet4-only

表示仅使用IPv4下载文件。pkg.jenkins.io 使用了IPv6解析域名,可能导致下载失败

-O

下载文件保存到指定位置

7.1.2. 导入仓库密钥

  1. 下载密钥文件

    位置:内网服务器,用户:root
    wget \
      --inet4-only \
      -O /tmp/jenkins.io.key \
      https://pkg.jenkins.io/redhat-stable/jenkins.io.key
  2. 导入密钥

    rpm --import /tmp/jenkins.io.key
  3. 删除临时文件

    位置:内网服务器,用户:root
    rm -f /tmp/jenkins.io.key

7.1.3. 默认禁用Jenkins仓库

按需单独启用Jenkins仓库

Jenkins仓库(服务器位于国外)经常无法访问或者速度慢,影响Yum使用,默认禁用之

位置:内网服务器,用户:root
yum-config-manager --disable jenkins | egrep '(\[jenkins\])|enabled'
终端输出
[jenkins]
enabled = 0 或 False

7.2. 安装Jenkins软件包

7.2.1. 安装依赖

  1. 安装Java11

    位置:内网服务器,用户:root
    yum install -y java-11-openjdk java-11-openjdk-devel java-11-openjdk-headless
  2. 设置系统默认Java

    位置:内网服务器,用户:root
    alternatives --set java `ls /usr/lib/jvm/java-11-openjdk-*/bin/java`
    alternatives --set javac `ls /usr/lib/jvm/java-11-openjdk-*/bin/javac`
    alternatives --set jre_openjdk `ls -d /usr/lib/jvm/java-11-openjdk-*`
    alternatives --set java_sdk_openjdk `ls -d /usr/lib/jvm/java-11-openjdk-*`
同时设置四个选项后,/etc/alternatives/java_sdk 会自动链接到 /usr/lib/jvm/java-11-openjdk-*,最终 JAVA_HOME 才是期望的Java 11
  1. 查看默认Java版本

    位置:内网服务器,用户:root
    java -version
    终端输出
    openjdk version "11.0.6" 2020-01-14 LTS
    OpenJDK Runtime Environment 18.9 (build 11.0.6+10-LTS)
    OpenJDK 64-Bit Server VM 18.9 (build 11.0.6+10-LTS, mixed mode, sharing)

7.2.2. 安装Jenkins

位置:内网服务器,用户:root
yum --disablerepo=\* --enablerepo=jenkins install jenkins
单独启用Jenkins仓库( jenkins ),安装Jenkins

7.3. 配置Jenkins服务

7.3.1. 开机启动Jenkins

位置:内网服务器,用户:root
systemctl enable jenkins

7.3.2. 设置Jenkins启动参数

  1. 创建Systemd配置文件

    位置:内网服务器,用户:root
    mkdir -p /etc/systemd/system/jenkins.service.d/
    
    cat << EOF > /etc/systemd/system/jenkins.service.d/override.conf
    [Service]
    Environment="JENKINS_PORT=8282"
    # 默认配置文件中的 %C 实际并未生效,而是出现了错误的 "/var/lib/jenkins/%C/jenkins/"
    Environment="JENKINS_WEBROOT=/var/cache/jenkins/war"
    Environment="JENKINS_LOG=/var/log/jenkins/jenkins.log"
    EOF
    参数说明
    JENKINS_PORT

    Jenkins运行端口。默认端口是8080,和之前安装的GitLab端口(同为8080)冲突,所以设置为 8282

    JENKINS_WEBROOT

    Jenkins缓存目录

    JENKINS_LOG

    Jenkins日志文件

    JENKINS_DEBUG_LEVEL 参数无任何效果(官方文档未说明)

  2. 重载Systemd配置

    位置:内网服务器,用户:root
    systemctl daemon-reload

7.4. 为Jenkins新增防火墙规则

  1. 增加防火墙放行规则

    位置:内网服务器,用户:root
    JENKINS_PORT=8282
    PERM="--permanent"
    SERV_NAME=jenkins_${JENKINS_PORT}
    SERV="${PERM} --service=${SERV_NAME}"
    
    firewall-cmd ${PERM} --new-service=${SERV_NAME}
    firewall-cmd ${SERV} --set-short="Jenkins ports"
    firewall-cmd ${SERV} --set-description="Jenkins port exceptions"
    firewall-cmd ${SERV} --add-port=${JENKINS_PORT}/tcp
    firewall-cmd ${PERM} --add-service=${SERV_NAME}
    参数说明
    JENKINS_PORT

    Jenkins运行端口注意和 设置Jenkins启动参数 保持一致

  2. 重载防火墙配置

    位置:内网服务器,用户:root
    firewall-cmd --reload
  3. 查看防火墙规则

    位置:内网服务器,用户:root
    firewall-cmd --list-all
    终端输出
    public (active)
      target: default
      icmp-block-inversion: no
      interfaces: enp0s3
      sources:
      services: dhcpv6-client http https jenkins_8282 ssh (1)
      ports:
      protocols:
      masquerade: no
      forward-ports:
      source-ports:
      icmp-blocks:
      rich rules:
    1 放行的规则中必须有 jenkins_8282 服务

7.5. 启动Jenkins

  1. 启动Jenkins

    位置:内网服务器,用户:root
    systemctl start jenkins
  2. 查看Jenkins状态

    位置:内网服务器,用户:root
    systemctl status jenkins
    终端输出
    ● jenkins.service - Jenkins Continuous Integration Server
       Loaded: loaded (/usr/lib/systemd/system/jenkins.service; enabled; vendor preset: disabled)
      Drop-In: /etc/systemd/system/jenkins.service.d
               └─override.conf
       Active: active (running) since Mon 2023-01-02 16:18:45 CST; 9h ago (1)
    ......
    ......
    1 输出中有 active (running) 表示Jenkins已经启动
    Jenkins Web暂时无法访问,详情见下文
  3. 等待Jenkins启动完成

    查看日志文件: /var/log/jenkins/jenkins.log,确认Jenkins启动完成

    日志文件内容
    1	Running from: /usr/share/java/jenkins.war
    2	2023-01-03 14:34:54.190+0000 [id=1]	INFO	winstone.Logger#logInternal: Beginning extraction from war file
    3	2023-01-03 14:34:54.243+0000 [id=1]	WARNING	o.e.j.s.handler.ContextHandler#setContextPath: Empty contextPath
    4	2023-01-03 14:34:54.310+0000 [id=1]	INFO	org.eclipse.jetty.server.Server#doStart: jetty-10.0.12; built: 2022-09-14T01:54:40.076Z; git: 408d0139887e27a57b54ed52e2d92a36731a7e88; jvm 11.0.17+8-LTS
    ......
    ......
    12	2023-01-03 14:34:56.463+0000 [id=31]	INFO	jenkins.InitReactorRunner$1#onAttained: Started initialization
    13	2023-01-03 14:34:56.740+0000 [id=36]	INFO	jenkins.InitReactorRunner$1#onAttained: Listed all plugins
    14	2023-01-03 14:35:00.167+0000 [id=35]	INFO	jenkins.InitReactorRunner$1#onAttained: Prepared all plugins
    15	2023-01-03 14:35:00.207+0000 [id=32]	INFO	jenkins.InitReactorRunner$1#onAttained: Started all plugins
    16	2023-01-03 14:35:00.227+0000 [id=30]	INFO	jenkins.InitReactorRunner$1#onAttained: Augmented all extensions
    17	2023-01-03 14:35:00.926+0000 [id=35]	INFO	h.p.b.g.GlobalTimeOutConfiguration#load: global timeout not set
    18	2023-01-03 14:35:01.596+0000 [id=32]	INFO	jenkins.InitReactorRunner$1#onAttained: System config loaded
    19	2023-01-03 14:35:01.597+0000 [id=38]	INFO	jenkins.InitReactorRunner$1#onAttained: System config adapted
    20	2023-01-03 14:35:01.626+0000 [id=31]	INFO	jenkins.InitReactorRunner$1#onAttained: Loaded all jobs
    21	2023-01-03 14:35:01.639+0000 [id=32]	INFO	jenkins.InitReactorRunner$1#onAttained: Configuration for all jobs updated
    22	2023-01-03 14:35:01.981+0000 [id=30]	INFO	jenkins.InitReactorRunner$1#onAttained: Completed initialization
    23	2023-01-03 14:35:02.107+0000 [id=23]	INFO	hudson.lifecycle.Lifecycle#onReady: Jenkins is fully up and running (1)
    1 注意此行日志

    未见以下日志消息,必须长时间等待(半小时甚至更久):

    Jenkins is fully up and running

如果你更关心 "为什么Jenkins启动慢?",请访问 为什么Jenkins启动慢,反之跳过

现在,你可以访问 第一次访问Jenkins Web 继续完成Jenkins安装操作

7.5.1. 为什么Jenkins启动慢

Jenkins某些时候(比如第一次)启动会访问升级服务( https://updates.jenkins.io/ )检查是否有新版本。

因网络原因无法访问升级服务,Jenkins会反复重试,导致一直无法完成启动。

日志文件会见到以下内容:

2022-12-26 14:05:48.312+0000 [id=30]    WARNING hudson.model.UpdateCenter#updateDefaultSite: Upgrading Jenkins. Failed to update the default Update Site 'default'. Plugin upgrades may fail.
java.io.EOFException: SSL peer shut down incorrectly
        at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:483)
        at java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:472)
        at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:160)
        at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:111)
        at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506)
......
......
2022-12-26 14:06:08.238+0000 [id=47]    INFO    hudson.util.Retrier#start: Calling the listener of the allowed exception 'connect timed out' at the attempt #1 to do the action check updates server
2022-12-26 14:06:08.240+0000 [id=47]    INFO    hudson.util.Retrier#start: Attempted the action check updates server for 1 time(s) with no success
2022-12-26 14:06:08.242+0000 [id=47]    SEVERE  hudson.PluginManager#doCheckUpdatesServer: Error checking update sites for 1 attempt(s). Last exception was: SocketTimeoutException: connect timed out

这个时候访问Jenkins Web会见到以下界面反复刷新,直到看到 Jenkins启动完成的日志提示 中提及的日志消息

Jenkins启动界面

7.6. 第一次访问Jenkins Web

7.6.1. 获取初始管理员密码

查看密码

位置:内网服务器,用户:root
cat /var/lib/jenkins/secrets/initialAdminPassword
终端输出
35e10761f3e44cddbac0ecc586c17639 (1)
1 每次安装Jenkins生成的密码都不同
本文展示的密码仅作示例用途,请勿直接使用

7.6.2. 访问Jenkins Web

操作位置
  • 开发工作机

访问Jenkins Web地址

http://jenkins.sjx.com:8282

注意端口是 8282,具体设置见 设置Jenkins启动参数

7.6.3. 解锁Jenkins

输入 获取初始管理员密码 看到的密码,解锁Jenkins

解锁Jenkins

7.6.4. 选择插件

选择 自定义Jenkins  安装推荐的插件

自定义Jenkins

7.6.5. 安装插件

网络安装选择的插件,需要更多的耐心等待下载完成…​…​
安装推荐的插件

7.6.6. 创建第一个管理员用户

建议选择 创建第一个管理员用户  使用admin账户继续 (跳过创建),熟悉Jenkins后再创建新的管理员
创建第一个管理员用户

7.6.7. 配置URL

输入 http://jenkins.sjx.com:8282

配置Jenkins URL

7.6.8. 配置完成

Jenkins安装已完成

7.6.9. Jenkins首页

Jenkins首页

8. Jenkins任务一:自动发布实践星到内网服务器

操作位置
  • 内网服务器

8.1. 初始化Jenkins终端配置

8.1.1. 启用Jenkins终端登录

修改登录终端:

位置:内网服务器,用户:root
usermod --shell /bin/bash jenkins

查看登录终端设置:

位置:内网服务器,用户:root
getent passwd jenkins | awk -F ':' '{print "用户名称:"$1"\n用户编号:"$3"\n  组编号:"$4"\n用户说明:"$5"\n用户目录:"$6"\n登录终端:"$7}'
终端输出
用户名称:jenkins
用户编号:992
  组编号:989
用户说明:Jenkins Automation Server
用户目录:/var/lib/jenkins
登录终端:/bin/bash (1)
1 /bin/false 修改为 /bin/bash 之后,才能使用 jenkins 用户执行命令

8.1.2. 创建Jenkins终端环境配置文件

位置:内网服务器,用户:root
runuser -l jenkins -c 'cat > /var/lib/jenkins/.bashrc' << EOF
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

# Source global definitions
test -f /etc/bashrc && source /etc/bashrc

cd ~
EOF

8.1.3. 初始化Jenkins终端SSH客户端配置

位置:内网服务器,用户:root
runuser -l jenkins -c 'mkdir -p ~/.ssh'
runuser -l jenkins -c 'chmod 700 ~/.ssh'

runuser -l jenkins -c 'touch /var/lib/jenkins/.ssh/config'
runuser -l jenkins -c 'chmod 600 ~/.ssh/config'

runuser -l jenkins -c 'cat > ~/.ssh/config' << EOF
Host lan_server wan_server*
    User root
    IdentityFile ~/.ssh/root@sjx.server

Host gitlab.sjx.com
    User git
    IdentityFile ~/.ssh/jenkins@gitlab.sjx.com
EOF

runuser -l jenkins -c 'chmod 400 ~/.ssh/config'
runuser -l jenkins -c 'chmod 600 ~/.ssh/known_hosts'

8.1.4. 添加服务器主机名的本地解析

位置:内网服务器,用户:root
cat << EOF >> /etc/hosts
127.0.0.1 gitlab.sjx.com
192.168.1.121 wan_servera
192.168.1.122 wan_serverb
192.168.1.123 wan_serverc
EOF

8.1.5. 为Jenkins终端更新服务器公钥文件

为Jenkins终端更新服务器公钥文件:

位置:内网服务器,用户:root
runuser -l jenkins -c 'ssh-keyscan gitlab.sjx.com lan_server wan_servera wan_serverb wan_serverc > ~/.ssh/known_hosts'

以后 jenkins 用户使用 ssh 命令第一次连接服务器时,不用再回答 yes

8.1.6. 初始化Jenkins使用的私钥文件权限

位置:内网服务器,用户:root
runuser -l jenkins -c 'touch ~/.ssh/root@sjx.server ~/.ssh/jenkins@gitlab.sjx.com'
runuser -l jenkins -c 'chmod 400 ~/.ssh/root@sjx.server ~/.ssh/jenkins@gitlab.sjx.com'

确认权限:

位置:内网服务器,用户:root
runuser -l jenkins -c 'ls -l ~/.ssh'
终端输出
total 8
-r-------- 1 jenkins jenkins   82 Jan 24 23:38 config
-r-------- 1 jenkins jenkins    0 Jan 24 23:40 jenkins@gitlab.sjx.com (1)
-rw------- 1 jenkins jenkins 3301 Jan 24 23:40 known_hosts
-r-------- 1 jenkins jenkins    0 Jan 24 23:40 root@sjx.server (1)
1 文件权限为 -r--------,用户和组是 jenkins

8.1.7. 上传Jenkins使用的私钥文件

开发工作机
scp ~/.ssh/root@sjx.server ~/.ssh/jenkins@gitlab.sjx.com root@lan_server:/var/lib/jenkins/.ssh/
终端输出
root@sjx.server                                                               100% 2610     1.0MB/s   00:00
jenkins@gitlab.sjx.com                                                        100% 2610     1.0MB/s   00:00

root@lan_server 指定了 root 用户上传SSH私钥

不过,私钥文件的权限(-r--------)、用户组(jenkins)不会被覆盖

8.1.8. 测试Jenkins私钥

测试Jenkins使用的GitLab私钥
位置:内网服务器,用户:root
runuser -l jenkins -c 'ssh git@gitlab.sjx.com'
终端输出
PTY allocation request failed on channel 0
Welcome to GitLab, @root!
Connection to gitlab.sjx.com closed.
测试Jenkins使用的服务器私钥
位置:内网服务器,用户:root
runuser -l jenkins -c 'ssh lan_server hostnamectl'
终端输出
   Static hostname: lan_server
         Icon name: computer-vm
           Chassis: vm
        Machine ID: 749ca85581ae2140b4032d82bfd8aae2
           Boot ID: c840bbcc353e421ea77e9c6a09115640
    Virtualization: kvm
  Operating System: CentOS Linux 7 (Core)
       CPE OS Name: cpe:/o:centos:centos:7
            Kernel: Linux 3.10.0-1160.81.1.el7.x86_64
      Architecture: x86-64

8.2. 安装rain-shell-scripter

rain-shell-scripter是用来代替Shell脚本的Python工具,

编写CSV格式文件即可完美解决脚本中的返回值、数值运算、错误处理、流程控制难题,

让脚本逻辑更加 清晰 可控

更多介绍请访问 rain-shell-scripter

8.2.1. PIP安装rain-shell-scripter

PIP安装软件包:

位置:内网服务器,用户:root
pip310 install --root-user-action=ignore -U rain-shell-scripter

查看执行文件环境变量:

位置:内网服务器,用户:root
which rain_shell_scripter
终端输出
/usr/local/python-3.10.5/bin/rain_shell_scripter

8.2.2. rain-shell-scripter入门

下载示例规则文件 hello.csv

位置:内网服务器,用户:root
wget https://raw.githubusercontent.com/fifilyu/rain-shell-scripter/master/examples/hello.csv -O hello.csv

运行规则文件 hello.csv

位置:内网服务器,用户:root
rain_shell_scripter -f hello.csv
终端输出
2023-01-25 00:13:34 1473 [INFO] 行号:2 -> 设置项目名称...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:3 -> 开始......构建项目——hello
2023-01-25 00:13:34 1473 [INFO] 行号:4 -> 获取当前工作目录...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:5 -> 设置环境变量"WORK_DIR"...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:6 -> 模拟->Maven构建项目——hello...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:7 -> 模拟->确认构建后存在target文件(JAR包)...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:8 -> 模拟->获取target文件名称...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:9 -> 模拟->本地环境-生成JAR包的哈希值...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:10 -> 模拟->目标环境-生成JAR包的哈希值...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:11 -> 期望的哈希值...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:12 -> 模拟->对比JAR包的哈希值...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:13 -> 模拟->对比JAR包的哈希值...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:14 -> 模拟->对比JAR包的哈希值...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:15 -> 复制文件测试1...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:16 -> 复制文件测试2...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:17 -> 删除文件...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:18 -> 复制文件测试3...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:19 -> 模拟->目标环境-删除构建目录...[ OK ]
2023-01-25 00:13:34 1473 [INFO] 行号:20 -> 忽略不存在的变量
2023-01-25 00:13:34 1473 [INFO] 行号:21 -> 结束......构建项目——hello

或者以调试模式运行,可以看到更多的执行过程日志(比如Shell命令的执行日志):

位置:内网服务器,用户:root
rain_shell_scripter -f hello.csv -l 5
终端输出
2023-01-25 00:14:57 1521 [TRACE] Enter function: to_csv_row_obj
2023-01-25 00:14:57 1521 [TRACE] var->row=['CONST', 'NULL', 'NULL', 'STR', 'hello', 'NULL', 'PROJECT', '设置项目名称']
2023-01-25 00:14:57 1521 [TRACE] Exit function: to_csv_row_obj
2023-01-25 00:14:57 1521 [TRACE] Enter function: const_handler
2023-01-25 00:14:57 1521 [TRACE] var->row=<rain_shell_scripter.CsvRow object at 0x7fba2d7914b0>
2023-01-25 00:14:57 1521 [TRACE] var->message=设置项目名称
2023-01-25 00:14:57 1521 [TRACE] var->var_name=PROJECT
2023-01-25 00:14:57 1521 [TRACE] var->var_value=hello
2023-01-25 00:16:55 1607 [INFO] 行号:2 -> 设置项目名称...[ OK ]
...省略的内容...
2023-01-25 00:16:55 1607 [INFO] 行号:3 -> 开始......构建项目——hello
2023-01-25 00:16:55 1607 [TRACE] Exit function: message_handler
2023-01-25 00:16:55 1607 [TRACE] Enter function: to_csv_row_obj
2023-01-25 00:16:55 1607 [TRACE] var->row=['RUN', 'pwd', '0', 'STR', 'NULL', 'NULL', 'pwd', '获取当前工作目录']
2023-01-25 00:16:55 1607 [TRACE] Exit function: to_csv_row_obj
2023-01-25 00:16:55 1607 [TRACE] Enter function: run_handler
2023-01-25 00:16:55 1607 [TRACE] var->row=<rain_shell_scripter.CsvRow object at 0x7f45ff7654b0>
2023-01-25 00:16:55 1607 [TRACE] var->message=获取当前工作目录
2023-01-25 00:16:55 1607 [TRACE] var->expr_line=pwd
2023-01-25 00:16:55 1607 [TRACE] var->expected_return_code=0
2023-01-25 00:16:55 1607 [TRACE] var->expected_return_type=ReturnType.STR
2023-01-25 00:16:55 1607 [TRACE] var->return_filter=NULL
2023-01-25 00:16:55 1607 [TRACE] var->save_var_name=pwd
2023-01-25 00:16:55 1607 [TRACE] Enter function: execute_cmd
2023-01-25 00:16:55 1607 [DEBUG] cmd=pwd (1)
2023-01-25 00:16:55 1607 [DEBUG] result=/root (2)
2023-01-25 00:16:55 1607 [TRACE] Exit function: execute_cmd
2023-01-25 00:16:55 1607 [TRACE] var->return_code=0 (3)
2023-01-25 00:16:55 1607 [TRACE] var->result=/root
2023-01-25 00:16:55 1607 [INFO] 行号:4 -> 获取当前工作目录...[ OK ]
...省略的内容...
2023-01-25 00:14:58 1521 [INFO] 行号:21 -> 结束......构建项目——hello
2023-01-25 00:14:58 1521 [TRACE] Exit function: message_handler
2023-01-25 00:14:58 1521 [DEBUG] 变量暂存区:
{
    "PROJECT": "hello",
    "expected_hash": "e0aa021e21dddbd6d8cecec71e9cf564",
    "local_hash": "e0aa021e21dddbd6d8cecec71e9cf564",
    "pwd": "/root",
    "remote_hash": "e0aa021e21dddbd6d8cecec71e9cf564",
    "ret_val1": 1,
    "ret_val2": "a",
    "target_file": "hello-1.0.0.jar"
}
1 执行的Shell命令:pwd
2 命令执行结果:/root
3 命令返回代码:0

8.3. 进入Jenkins终端

之前一直用root用户利用 runuser -l jenkins 执行Jenkins相关的操作。从现在起,所有操作都在Jenkins终端环境中完成,直到本章完结

进入Jenkins终端环境命令:

位置:内网服务器,用户:root
su jenkins

查看当前终端:

位置:内网服务器,用户:jenkins
id
终端输出
uid=992(jenkins) gid=989(jenkins) groups=989(jenkins)

查看当前工作目录:

位置:内网服务器,用户:jenkins
pwd
终端输出
/var/lib/jenkins

8.4. 在Jenkins终端测试实践星构建过程

8.4.1. 人工构建实践星过程详解

  1. 检查Java设置之系统默认Java命令(类似JAVA_HOME)

    JAVA_HOME有什么用?

    主要为了方便找到 JAVA_HOME/bin 目录下的 javajavac 等可执行文件路径

    大部分Linux发行版都有类似 alternatives 的命令,可以直接在多个Java版本中选择一个默认版本,不再需要JAVA_HOME

    当然,有些Java程序还是会依赖JAVA_HOME查找文件,这时的JAVA_HOME只是为了兼容而已

    位置:内网服务器,用户:jenkins
    alternatives --display java | grep -v slave
    终端输出
    java - status is manual.
     link currently points to /usr/lib/jvm/java-11-openjdk-11.0.17.0.8-2.el7_9.x86_64/bin/java (1)
    /usr/lib/jvm/java-11-openjdk-11.0.17.0.8-2.el7_9.x86_64/bin/java - family java-11-openjdk.x86_64 priority 1
    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.352.b08-2.el7_9.x86_64/jre/bin/java - family java-1.8.0-openjdk.x86_64 priority 1800352
    Current `best' version is /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.352.b08-2.el7_9.x86_64/jre/bin/java.
    1 因为之前手动设置的缘故,java 命令被强制指向了 /usr/lib/jvm/java-11-openjdk-11.0.17.0.8-2.el7_9.x86_64/bin/java (Java 11)
  2. 检查Java设置之Java版本

    位置:内网服务器,用户:jenkins
    java -version
    终端输出
    openjdk version "11.0.17" 2022-10-18 LTS (1)
    OpenJDK Runtime Environment (Red_Hat-11.0.17.0.8-2.el7_9) (build 11.0.17+8-LTS)
    OpenJDK 64-Bit Server VM (Red_Hat-11.0.17.0.8-2.el7_9) (build 11.0.17+8-LTS, mixed mode, sharing)
    1 Java版本号:11.0.17
  3. 确认Maven使用的Java 11

    位置:内网服务器,用户:jenkins
    mvn -v
    终端输出
    Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T15:58:13+08:00)
    Maven home: /usr/share/apache-maven
    Java version: 11.0.17, vendor: Red Hat, Inc. (1)
    Java home: /usr/lib/jvm/java-11-openjdk-11.0.17.0.8-2.el7_9.x86_64
    Default locale: en_US, platform encoding: UTF-8
    OS name: "linux", version: "3.10.0-1160.81.1.el7.x86_64", arch: "amd64", family: "unix"
    1 Java版本号:11.0.17
  4. 克隆实践星源代码

    位置:内网服务器,用户:jenkins
    git clone git@gitlab.sjx.com:root/shi-jian-xing.git ~/workspace/shi-jian-xing
    终端输出
    Cloning into '/var/lib/jenkins/workspace/shi-jian-xing'...
    remote: Enumerating objects: 464, done.
    remote: Counting objects: 100% (27/27), done.
    remote: Compressing objects: 100% (24/24), done.
    remote: Total 464 (delta 10), reused 0 (delta 0), pack-reused 437
    Receiving objects: 100% (464/464), 149.24 KiB | 0 bytes/s, done.
    Resolving deltas: 100% (198/198), done.
  5. pom.xml 文件获取Jar文件路径

    位置:内网服务器,用户:jenkins
    JAR_FILENAME=$(xmlstarlet sel -t -v '/_:project/_:artifactId' -o '-' -v '/_:project/_:version' -o '.jar' -n ~/workspace/shi-jian-xing/pom.xml)
    JAR_FILE=~/workspace/shi-jian-xing/target/${JAR_FILENAME}
    
    echo "Jar文件:${JAR_FILE}"
    终端输出
    Jar文件:/var/lib/jenkins/workspace/shi-jian-xing/target/shi-jian-xing-0.0.1.jar
  6. 删除构建缓存和临时文件

    位置:内网服务器,用户:jenkins
    rm -rf ~/workspace/shi-jian-xing/target/

    理论上 mvn clean 也可以清除 target 目录及其子文件,范围仅局限在当前Java源代码

    如果移除了某些Java包或Java类,clean 指令不会同步移除对应的 .class 文件,造成Jar文件运行后的行为和实际代码不同

    故,直接删除 target 目录才是最好的选择

  7. 构建实践星,生成Jar文件

    位置:内网服务器,用户:jenkins
    mvn -f ~/workspace/shi-jian-xing package
    终端输出
    [INFO] Scanning for projects...
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] Building ShiJianXing 0.0.1
    [INFO] ------------------------------------------------------------------------
    ...省略的内容...
    [INFO] --- maven-jar-plugin:3.3.0:jar (default-jar) @ shi-jian-xing ---
    [INFO] Building jar: /var/lib/jenkins/workspace/shi-jian-xing/target/shi-jian-xing-0.0.1.jar (1)
    [INFO]
    [INFO] --- spring-boot-maven-plugin:2.7.6:repackage (repackage) @ shi-jian-xing ---
    [INFO] Replacing main artifact with repackaged archive
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 3.958 s
    [INFO] Finished at: 2023-01-25T22:47:54+08:00
    [INFO] Final Memory: 33M/120M
    [INFO] ------------------------------------------------------------------------
    1 生成的Jar文件路径
  8. 检查Jar文件是否已经生成

    位置:内网服务器,用户:jenkins
    test -f ${JAR_FILE} && echo 找到实践星的Jar文件
    终端输出
    找到实践星的Jar文件
  9. 测试解压ZIP压缩包,验证Jar文件有效性

    位置:内网服务器,用户:jenkins
    unzip -t ${JAR_FILE}
    Jar文件是一个ZIP格式的压缩包
    终端输出
    Archive:  /var/lib/jenkins/workspace/shi-jian-xing/target/shi-jian-xing-0.0.1.jar
        testing: META-INF/                OK
        testing: META-INF/MANIFEST.MF     OK
        testing: org/                     OK
        testing: org/springframework/     OK
        testing: org/springframework/boot/   OK
        testing: org/springframework/boot/loader/   OK
        testing: org/springframework/boot/loader/ClassPathIndexFile.class   OK
        testing: org/springframework/boot/loader/ExecutableArchiveLauncher.class   OK
        testing: org/springframework/boot/loader/JarLauncher.class   OK
        ...省略的内容...
        ...省略的内容...
        ...省略的内容...
        testing: BOOT-INF/lib/filters-2.0.235-1.jar   OK
        testing: BOOT-INF/lib/avatar-generator-smiley-1.1.0.jar   OK
        testing: BOOT-INF/lib/reflections-0.9.10.jar   OK
        testing: BOOT-INF/lib/guava-15.0.jar   OK
        testing: BOOT-INF/lib/javassist-3.19.0-GA.jar   OK
        testing: BOOT-INF/lib/annotations-2.0.1.jar   OK
        testing: BOOT-INF/lib/avatar-generator-8bit-1.1.0.jar   OK
        testing: BOOT-INF/lib/avatar-generator-cat-1.1.0.jar   OK
        testing: BOOT-INF/lib/happy-java-0.0.4.jar   OK
        testing: BOOT-INF/lib/gson-2.9.1.jar   OK
        testing: BOOT-INF/lib/spring-boot-jarmode-layertools-2.7.6.jar   OK
        testing: BOOT-INF/classpath.idx   OK
        testing: BOOT-INF/layers.idx      OK
    No errors detected in compressed data of /var/lib/jenkins/workspace/shi-jian-xing/target/shi-jian-xing-0.0.1.jar. (1)
1 解压过程没有出现错误,Jar文件有效

8.4.2. 为什么这次构建实践星前后过程这么繁琐?

以后Jenkins会成百上千次的构建实践星,
重复整个构建过程,除非实践星内容(代码、数据库、配置等)永不更新,
否则不能保证每次构建实践星都能顺利完成或生成Jar文件,
必须考虑各种意外情况

8.5. 基于rain-shell-scripter半自动构建实践星

rain-shell-scripter 的规则文件代替Shell脚本,没有大量 if 语句和换行,获取命令执行结果或者状态码简单,让逻辑更精简紧凑

更重要的是,规则文件是CSV格式的文本文件,可以用Git像管理代码一样,将文件放入Git仓库统一管理

8.5.1. CVS文件编辑方法

方法一:Visual Studio Code(推荐)

推荐使用微软的 Visual Studio Code 编辑CSV文件,配合插件轻松编辑

ch08.s4 1
ch08.s4 2
显示自带的CSV表头,启用参数 Read options  Has header
方法二:WPS表格
ch08.s4 3
WPS表格自动调整列宽方法

WPS表格技巧—自动调整列宽

方法三:Office Excel
Office Excel 自动调整列宽方法

访问 更改列宽和行高 ,查看 更改列宽以自动适合内容(自动调整) 中的内容

8.5.2. 创建第一版实践星规则文件

一句话创建第一版规则文件:

位置:内网服务器,用户:jenkins
mkdir -p ~/scripts

cat << EOF > ~/scripts/build_sjx.csv
模式,表达式,返回代码,返回类型,返回值,过滤器,变量名,提示信息
CONST,NULL,NULL,STR,实践星,NULL,PROJECT,设置项目名称
CONST,NULL,NULL,STR,shi-jian-xing,NULL,PROJECT_NAME,设置项目英文名称——\${PROJECT}
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,当前工作目录:\${PWD}
CONST,NULL,NULL,STR,workspace/\${PROJECT_NAME},NULL,PROJECT_ROOT_DIR,设置项目根目录
ENV,NULL,NULL,NULL,/etc/alternatives/java_sdk_11_openjdk,NULL,JAVA_HOME,"设置Java环境变量""JAVA_HOME"""
RUN,"java -version 2>&1 | head -n 1 | awk '{print \$3}' | tr -d '""'",0,STR,NULL,NULL,JAVA_FULL_VERSION,获取Java(完整)版本号
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,Java(完整)版本号:\${JAVA_FULL_VERSION}
RUN,echo \${JAVA_FULL_VERSION} | awk -F '.' '{print \$1}',0,STR,NULL,NULL,JAVA_MAJOR_VERSION,获取Java主版本号
STATEMENT,'\${JAVA_MAJOR_VERSION}' == '11',NULL,INT,1,NULL,NULL,确认Java主版本号为11
RUN,"mvn -v | grep 'Java version:' | awk '{print \$3}' | tr -d ','",0,STR,NULL,NULL,MAVEN_JAVA_VERSION,获取Maven使用的Java版本号
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,Maven使用的Java版本号:\${MAVEN_JAVA_VERSION}
STATEMENT,'\${MAVEN_JAVA_VERSION}' == '\${JAVA_FULL_VERSION}',NULL,INT,1,NULL,NULL,确认Maven使用的Java11
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,开始......构建项目——\${PROJECT}
RUN,rm -rf \${PROJECT_ROOT_DIR} && git clone git@gitlab.sjx.com:root/shi-jian-xing.git \${PROJECT_ROOT_DIR},0,NULL,NULL,NULL,NULL,克隆实践星源代码
RUN,xmlstarlet sel -t -v '/_:project/_:artifactId' -o '-' -v '/_:project/_:version' -o '.jar' -n \${PROJECT_ROOT_DIR}/pom.xml,0,STR,NULL,NULL,JAR_FILENAME,从pom.xml文件获取Jar文件名称
CONST,NULL,NULL,STR,\${PROJECT_ROOT_DIR}/target/\${JAR_FILENAME},NULL,JAR_FILE,设置Jar文件路径
RUN,rm -rf \${PROJECT_ROOT_DIR}/target/,0,NULL,NULL,NULL,NULL,删除构建缓存和临时文件
RUN,mvn -f \${PROJECT_ROOT_DIR} package,0,NULL,NULL,NULL,NULL,Maven构建\${PROJECT}
RUN,test -f \${JAR_FILE},0,NULL,NULL,NULL,NULL,确认构建后存在Jar文件
RUN,unzip -t \${JAR_FILE} > /dev/null,0,NULL,NULL,NULL,NULL,测试解压ZIP压缩包,验证Jar文件有效性
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,结束......构建项目——\${PROJECT}
EOF
rm -rf 使用带变量的绝对路径非常危险,如果变量值为空,可能变成 rm -rf /。所以,rm -rf 操作时,使用的带变量的相对路径

8.5.3. 运行第一版实践星规则文件

位置:内网服务器,用户:jenkins
rain_shell_scripter -f ~/scripts/build_sjx.csv
终端输出
2023-01-26 01:49:04 4038 [INFO] 行号:2 -> 设置项目名称...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:3 -> 设置项目英文名称——实践星...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:4 -> 当前工作目录:/var/lib/jenkins/test
2023-01-26 01:49:04 4038 [INFO] 行号:5 -> 设置项目根目录...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:6 -> 设置Java环境变量"JAVA_HOME"...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:7 -> 获取Java(完整)版本号...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:8 -> Java(完整)版本号:11.0.17
2023-01-26 01:49:04 4038 [INFO] 行号:9 -> 获取Java主版本号...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:10 -> 确认Java主版本号为11...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:11 -> 获取Maven使用的Java版本号...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:12 -> Maven使用的Java版本号:11.0.17
2023-01-26 01:49:04 4038 [INFO] 行号:13 -> 确认Maven使用的Java11...[ OK ]
2023-01-26 01:49:04 4038 [INFO] 行号:14 -> 开始......构建项目——实践星
2023-01-26 01:49:05 4038 [INFO] 行号:15 -> 克隆实践星源代码...[ OK ]
2023-01-26 01:49:05 4038 [INFO] 行号:16 -> 从pom.xml文件获取Jar文件名称...[ OK ]
2023-01-26 01:49:05 4038 [INFO] 行号:17 -> 设置Jar文件路径...[ OK ]
2023-01-26 01:49:05 4038 [INFO] 行号:18 -> 删除构建缓存和临时文件...[ OK ]
2023-01-26 01:49:10 4038 [INFO] 行号:19 -> Maven构建实践星...[ OK ]
2023-01-26 01:49:10 4038 [INFO] 行号:20 -> 确认构建后存在Jar文件...[ OK ]
2023-01-26 01:49:11 4038 [INFO] 行号:21 -> 测试解压ZIP压缩包,验证Jar文件有效性...[ OK ]
2023-01-26 01:49:11 4038 [INFO] 行号:22 -> 结束......构建项目——实践星

8.5.4. 调试模式运行第一版实践星规则文件

以调试模式运行时,构建过程更加清晰明确,方便调整规则:

位置:内网服务器,用户:jenkins
rain_shell_scripter -f ~/scripts/build_sjx.csv -l 4
终端输出
2023-01-26 01:52:34 4391 [INFO] 行号:2 -> 设置项目名称...[ OK ]
2023-01-26 01:52:34 4391 [INFO] 行号:3 -> 设置项目英文名称——实践星...[ OK ]
2023-01-26 01:52:34 4391 [INFO] 行号:4 -> 当前工作目录:/var/lib/jenkins/test
2023-01-26 01:52:34 4391 [INFO] 行号:5 -> 设置项目根目录...[ OK ]
2023-01-26 01:52:34 4391 [INFO] 行号:6 -> 设置Java环境变量"JAVA_HOME"...[ OK ]
2023-01-26 01:52:34 4391 [DEBUG] cmd=java -version 2>&1 | head -n 1 | awk '{print $3}' | tr -d '"'
2023-01-26 01:52:34 4391 [DEBUG] result=11.0.17
2023-01-26 01:52:34 4391 [INFO] 行号:7 -> 获取Java(完整)版本号...[ OK ]
2023-01-26 01:52:34 4391 [INFO] 行号:8 -> Java(完整)版本号:11.0.17
...省略的内容...
2023-01-26 01:52:35 4391 [DEBUG] cmd=mvn -f workspace/shi-jian-xing package
2023-01-26 01:52:40 4391 [DEBUG] result=[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building ShiJianXing 0.0.1
[INFO] ------------------------------------------------------------------------
...省略的内容...
[INFO] --- maven-jar-plugin:3.3.0:jar (default-jar) @ shi-jian-xing ---
[INFO] Building jar: /var/lib/jenkins/test/workspace/shi-jian-xing/target/shi-jian-xing-0.0.1.jar
...省略的内容...
2023-01-26 01:52:40 4391 [INFO] 行号:19 -> Maven构建实践星...[ OK ]
...省略的内容...
2023-01-26 01:52:41 4391 [INFO] 行号:21 -> 测试解压ZIP压缩包,验证Jar文件有效性...[ OK ]
2023-01-26 01:52:41 4391 [INFO] 行号:22 -> 结束......构建项目——实践星
2023-01-26 01:52:41 4391 [DEBUG] 变量暂存区:
{
    "JAR_FILE": "workspace/shi-jian-xing/target/shi-jian-xing-0.0.1.jar",
    "JAR_FILENAME": "shi-jian-xing-0.0.1.jar",
    "JAVA_FULL_VERSION": "11.0.17",
    "JAVA_MAJOR_VERSION": "11",
    "MAVEN_JAVA_VERSION": "11.0.17",
    "PROJECT": "实践星",
    "PROJECT_NAME": "shi-jian-xing",
    "PROJECT_ROOT_DIR": "workspace/shi-jian-xing"
}

8.6. Jenkins任务自动构建实践星

8.6.1. 移除Jenkins终端的临时SSH密钥配置

移除 Host gitlab.sjx.com 配置,使用Jenkins内置的密钥功能代替

位置:内网服务器,用户:jenkins
chmod 600 ~/.ssh/config

cat << EOF > ~/.ssh/config
Host lan_server wan_server*
    User root
    IdentityFile ~/.ssh/root@sjx.server
EOF

chmod 400 ~/.ssh/config

8.6.2. 创建第二版实践星规则文件

一句话创建第二版规则文件:

位置:内网服务器,用户:jenkins
cat << EOF > ~/scripts/build_sjx.csv
模式,表达式,返回代码,返回类型,返回值,过滤器,变量名,提示信息
CONST,NULL,NULL,STR,实践星,NULL,PROJECT,设置项目名称
CONST,NULL,NULL,STR,shi-jian-xing,NULL,PROJECT_NAME,设置项目英文名称——\${PROJECT}
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,当前工作目录:\${PWD}
ENV,NULL,NULL,NULL,/etc/alternatives/java_sdk_11_openjdk,NULL,JAVA_HOME,"设置Java环境变量""JAVA_HOME"""
RUN,"java -version 2>&1 | head -n 1 | awk '{print \$3}' | tr -d '""'",0,STR,NULL,NULL,JAVA_FULL_VERSION,获取Java(完整)版本号
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,Java(完整)版本号:\${JAVA_FULL_VERSION}
RUN,echo \${JAVA_FULL_VERSION} | awk -F '.' '{print \$1}',0,STR,NULL,NULL,JAVA_MAJOR_VERSION,获取Java主版本号
STATEMENT,'\${JAVA_MAJOR_VERSION}' == '11',NULL,INT,1,NULL,NULL,确认Java主版本号为11
RUN,"mvn -v | grep 'Java version:' | awk '{print \$3}' | tr -d ','",0,STR,NULL,NULL,MAVEN_JAVA_VERSION,获取Maven使用的Java版本号
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,Maven使用的Java版本号:\${MAVEN_JAVA_VERSION}
STATEMENT,'\${MAVEN_JAVA_VERSION}' == '\${JAVA_FULL_VERSION}',NULL,INT,1,NULL,NULL,确认Maven使用的Java11
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,开始......构建项目——\${PROJECT}
RUN,xmlstarlet sel -t -v '/_:project/_:artifactId' -o '-' -v '/_:project/_:version' -o '.jar' -n pom.xml,0,STR,NULL,NULL,JAR_FILENAME,从pom.xml文件获取Jar文件名称
CONST,NULL,NULL,STR,target/\${JAR_FILENAME},NULL,JAR_FILE,设置Jar文件路径
RUN,rm -rf target/,0,NULL,NULL,NULL,NULL,删除构建缓存和临时文件
RUN,mvn package,0,NULL,NULL,NULL,NULL,Maven构建\${PROJECT}
RUN,test -f \${JAR_FILE},0,NULL,NULL,NULL,NULL,确认构建后存在Jar文件
RUN,unzip -t \${JAR_FILE} > /dev/null,0,NULL,NULL,NULL,NULL,测试解压ZIP压缩包,验证Jar文件有效性
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,结束......构建项目——\${PROJECT}
EOF

第一版第二版 规则文件的变化,总体上是移除 Git仓库克隆命令、移除 项目根目录 和修改 项目根目录相关 的内容

下文 配置内网实践星的Jenkins任务 完成后,
每次构建实践星时,
Jenkins会自动克隆或拉取最新代码,
还会切换到实践星代码目录(工作目录)

变更详情
移除第一版的第5行

CONST,NULL,NULL,STR,workspace/${PROJECT_NAME},NULL,PROJECT_ROOT_DIR,设置项目根目录

移除第一版的第15行

RUN,rm -rf ${PROJECT_ROOT_DIR} && git clone git@gitlab.sjx.com:root/shi-jian-xing.git ${PROJECT_ROOT_DIR},0,NULL,NULL,NULL,NULL,克隆实践星源代码

修改第一版的第16行

${PROJECT_ROOT_DIR}/pom.xml 修改为 pom.xml

最终效果如下

RUN,xmlstarlet sel -t -v '/:project/:artifactId' -o '-' -v '/:project/:version' -o '.jar' -n pom.xml,0,STR,NULL,NULL,JAR_FILENAME,从pom.xml文件获取Jar文件名称

修改第一版的第17行

${PROJECT_ROOT_DIR}/target/${JAR_FILENAME} 修改为 target/${JAR_FILENAME}

最终效果如下

CONST,NULL,NULL,STR,target/${JAR_FILENAME},NULL,JAR_FILE,设置Jar文件路径

修改第一版的第18行

${PROJECT_ROOT_DIR}/target/ 修改为 target/

最终效果如下

RUN,rm -rf target/,0,NULL,NULL,NULL,NULL,删除构建缓存和临时文件

修改第一版的第19行

mvn -f ${PROJECT_ROOT_DIR} package 修改为 mvn package

最终效果如下

RUN,mvn package,0,NULL,NULL,NULL,NULL,Maven构建${PROJECT}

8.6.3. 在Jenkins中新增SSH密钥

URL入口

http://jenkins.sjx.com:8282/manage/credentials/store/system/domain/_/newCredentials

操作
菜单

Dashboard  Manage Jenkins  凭据  系统  全局凭据 (unrestricted)

输入
"类型"

SSH Username with private key

"范围"

全局 (Jenkins, nodes, items, all child items, etc)

"ID"

jenkins_gitlab_sjx_com

"描述"

jenkins@gitlab.sjx.com

"Username"

(留空)

"Private Key"

选中 Enter directly
填入 开发工作机~/.ssh/jenkins@gitlab.sjx.com 的文件内容(用于Jenkins访问GitLab仓库的密钥)

完成

点击下方 Create 按钮,创建Jenkins密钥

操作示例如下:

ch08.s6 1
新增SSH密钥(1)
ch08.s6 2
新增SSH密钥(2)
ch08.s6 3
新增SSH密钥(3)

8.6.4. 创建内网实践星的Jenkins任务

URL入口

http://jenkins.sjx.com:8282/newJob

操作
菜单

Dashboard  Create a job

输入
"任务名称"

内网实践星

"任务类型"

Freestyle project

完成

点击下方 确定 按钮,创建一个Jenkins任务

操作示例如下:

ch08.s6 4

8.6.5. 配置内网实践星的Jenkins任务

URL入口

http://jenkins.sjx.com:8282/job/内网实践星/configure

操作
菜单

Dashboard  内网实践星  配置

输入
"源码管理"

选择 Git

"Repository URL"

git@gitlab.sjx.com:root/shi-jian-xing.git

"Credentials"

jenkins (jenkins@gitlab.sjx.com)

"Build Steps"

选择 执行 shell

"执行 shell"

REMOTE_PORT=22 REMOTE_USER=root REMOTE_HOST=lan_server /usr/local/python-3.10.5/bin/rain_shell_scripter -f ${JENKINS_HOME}/scripts/build_sjx.csv -l 4

完成

点击下方 保存 按钮,保存内网实践星的Jenkins任务配置

提前设置的 REMOTE_PORT=22 REMOTE_USER=root REMOTE_HOST=lan_server 三个环境变量只对当前命令行有效,在最终版规则文件时才会使用

操作示例如下:

ch08.s6 5
保存内网实践星的Jenkins任务配置(1)
ch08.s6 6
保存内网实践星的Jenkins任务配置(2)

8.6.6. 运行内网实践星的Jenkins任务

URL入口

http://jenkins.sjx.com:8282/job/内网实践星/

操作
菜单

Dashboard  内网实践星

完成

点击左边 立即构建 按钮,运行内网实践星的Jenkins任务

操作示例如下:

ch08.s6 7
查看内网实践星的构建日志

点击左下角的构建序号(比如,#3),查看内网实践星的构建日志:

ch08.s6 8

点击 控制台输出

ch08.s6 9
控制台输出(1)
ch08.s6 10
控制台输出(2)
ch08.s6 11
控制台输出(3)

8.6.7. 总结

在构建失败时,控制台输出的日志信息可以帮助查找原因

rain-shell-scripter 输出的命令执行日志规范清晰,让整个构建过程一目了然,更快解决构建问题

现在,Jenkins可以代替人工完成内网实践星的构建了

Jenkins也只是生成了实践星的Jar文件,离真正自动发布内网实践星还差几步:

  • 上传Jar文件到内网服务器

  • 更新内网实践星配置

  • 启动或重启内网实践星系统服务

8.7. Jenkins任务自动发布实践星到内网服务器

8.7.1. 创建最终版实践星规则文件

一句话创建最终版规则文件:

位置:内网服务器,用户:jenkins
cat << EOF > ~/scripts/build_sjx.csv
模式,表达式,返回代码,返回类型,返回值,过滤器,变量名,提示信息
CONST,NULL,NULL,STR,实践星,NULL,PROJECT,设置项目名称
CONST,NULL,NULL,STR,shi-jian-xing,NULL,PROJECT_NAME,设置项目英文名称——\${PROJECT}
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,当前工作目录:\${PWD}
ENV,NULL,NULL,NULL,/etc/alternatives/java_sdk_11_openjdk,NULL,JAVA_HOME,"设置Java环境变量""JAVA_HOME"""
RUN,"java -version 2>&1 | head -n 1 | awk '{print \$3}' | tr -d '""'",0,STR,NULL,NULL,JAVA_FULL_VERSION,获取Java(完整)版本号
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,Java(完整)版本号:\${JAVA_FULL_VERSION}
RUN,echo \${JAVA_FULL_VERSION} | awk -F '.' '{print \$1}',0,STR,NULL,NULL,JAVA_MAJOR_VERSION,获取Java主版本号
STATEMENT,'\${JAVA_MAJOR_VERSION}' == '11',NULL,INT,1,NULL,NULL,确认Java主版本号为11
RUN,"mvn -v | grep 'Java version:' | awk '{print \$3}' | tr -d ','",0,STR,NULL,NULL,MAVEN_JAVA_VERSION,获取Maven使用的Java版本号
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,Maven使用的Java版本号:\${MAVEN_JAVA_VERSION}
STATEMENT,'\${MAVEN_JAVA_VERSION}' == '\${JAVA_FULL_VERSION}',NULL,INT,1,NULL,NULL,确认Maven使用的Java11
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,开始......构建项目——\${PROJECT}
RUN,xmlstarlet sel -t -v '/_:project/_:artifactId' -o '-' -v '/_:project/_:version' -o '.jar' -n pom.xml,0,STR,NULL,NULL,JAR_FILENAME,从pom.xml文件获取Jar文件名称
CONST,NULL,NULL,STR,target/\${JAR_FILENAME},NULL,JAR_FILE,设置Jar文件路径
RUN,rm -rf target/,0,NULL,NULL,NULL,NULL,删除构建缓存和临时文件
RUN,mvn package,0,NULL,NULL,NULL,NULL,Maven构建\${PROJECT}
RUN,test -f \${JAR_FILE},0,NULL,NULL,NULL,NULL,确认构建后存在Jar文件
RUN,unzip -t \${JAR_FILE} > /dev/null,0,NULL,NULL,NULL,NULL,测试解压ZIP压缩包,验证Jar文件有效性
RUN,sha1sum \${JAR_FILE},0,NULL,NULL,^([\d\w]+) .*$,local_hash,Jenkins环境-生成Jar文件的哈希值
CONST,NULL,NULL,STR,/opt/sjx/bin/\${JAR_FILENAME},NULL,SJX_BIN,设置变量->内网服务器实践星执行路径
CONST,NULL,NULL,STR,ssh -p \${REMOTE_PORT} \${REMOTE_USER}@\${REMOTE_HOST},NULL,SSH_CMD,设置变量->带参数的内网服务器ssh命令
RUN,scp -P \${REMOTE_PORT} \${JAR_FILE} \${REMOTE_USER}@\${REMOTE_HOST}:\${SJX_BIN},0,NULL,NULL,NULL,NULL,上传Jar文件到内网服务器
RUN,\${SSH_CMD} 'sha1sum \${SJX_BIN}',0,NULL,NULL,^([\d\w]+) .*$,remote_hash,内网服务器->生成Jar文件的哈希值
STATEMENT,'\${local_hash}' == '\${remote_hash}',NULL,INT,1,NULL,NULL,对比Jar文件的哈希值
RUN,\${SSH_CMD} 'systemctl stop sjx',0,NULL,NULL,NULL,NULL,内网服务器->停止实践星服务
RUN,\${SSH_CMD} 'cd /opt/sjx/bin && ln -fs \${JAR_FILENAME} sjx.jar',0,NULL,NULL,NULL,NULL,内网服务器->更新软链接
RUN,\${SSH_CMD} 'systemctl start sjx',0,NULL,NULL,NULL,NULL,内网服务器->启动实践星服务
RUN,\${SSH_CMD} 'systemctl status sjx',0,NULL,NULL,NULL,NULL,内网服务器->查看实践星服务状态
MESSAGE,NULL,NULL,NULL,NULL,NULL,NULL,结束......构建项目——\${PROJECT}
EOF

8.7.2. 重新运行内网实践星的Jenkins任务

8.7.3. 再次查看内网实践星的构建日志

ch08.s7 1
控制台输出

上图中,行号:20行号:29 都是最终版规则文件的新增内容,完成Jar文件上传、文件校验、重启系统服务等操作

8.8. 退出Jenkins终端

现在,已经完成内网实践星Jenkins任务的创建和运行,是时候退出Jenkins终端了

查看当前终端:

位置:内网服务器,用户:jenkins
id
终端输出
uid=992(jenkins) gid=989(jenkins) groups=989(jenkins) (1)
1 明显还处于Jenkins终端状态

执行 exit 命令,退出Jenkins终端:

终端输出
[jenkins@lan_server ~]$ exit
exit
[root@lan_server ~]#

8.8.1. 禁用Jenkins终端登录

恢复Jenkins用户终端默认设置,禁止终端登录:

位置:内网服务器,用户:root
usermod --shell /bin/false jenkins

8.9. 总结

本章利用Jenkins任务代替人工完成了
  1. 克隆或拉取实践星源代码

  2. 运行实践星构建,生成Jar文件

  3. 上传实践星Jar文件

  4. 重启实践星服务

在工作实践中,实践星的构建过程会不分时间地点,重复成百上千次,是IT产品生命周期中出现频率最高的操作,将其自动化是非常有意义且重要和的事情

内网实践星URL: http://test.sjx.com

9. 运行实践星三:生产服务器A

操作位置
  • 生产服务器A

9.1. 配置生产环境一:安装Java11

9.2. 配置生产环境二:安装MySQL

9.2.1. 为生产环境制定MySQL访问规则

只允许生产服务器A/B/C之间访问

位置:生产服务器A,用户:root
# 删除MySQL放行规则
firewall-cmd --permanent --remove-service=mysql

# 新建MySQL访问规则
firewall-cmd --get-zones | grep 'mysql-access' && firewall-cmd --permanent --delete-zone=mysql-access
firewall-cmd --permanent --new-zone=mysql-access
firewall-cmd --zone=mysql-access --permanent --add-service=mysql
firewall-cmd --zone=mysql-access --permanent --add-source=192.168.0.0/16
firewall-cmd --reload

firewall-cmd --zone=mysql-access --list-all

9.3. 配置生产环境三:安装Redis

9.3.1. 为生产环境制定Redis访问规则

只允许生产服务器A/B/C之间访问

位置:生产服务器A,用户:root
# 删除Redis放行规则
firewall-cmd --permanent --remove-service=REDIS_6379

# 新建Redis访问规则
firewall-cmd --get-zones | grep 'redis-access' && firewall-cmd --permanent --delete-zone=redis-access
firewall-cmd --permanent --new-zone=redis-access
firewall-cmd --zone=redis-access --permanent --add-service=REDIS_6379
firewall-cmd --zone=redis-access --permanent --add-source=192.168.0.0/16
firewall-cmd --reload

firewall-cmd --zone=redis-access --list-all

9.4. 配置生产环境四:配置实践星A

9.4.1. 为生产实践星A设置运行参数

只监听本地网络的8080端口,本机外无法访问

位置:生产服务器A,用户:root
crudini --set --existing /etc/sjx/application.properties "" server.address '127.0.0.1'

crudini --set --existing /etc/sjx/application.properties "" server.port '8080'

9.5. 配置生产环境五:安装Nginx

9.5.1. 新建生产实践星A的Nginx配置文件

位置:生产服务器A,用户:root
# 删除过时的测试配置
rm -f /etc/nginx/conf.d/test.sjx.com.conf

cat << EOF > /etc/nginx/conf.d/www.sjx.com.conf
server {
    listen       80;
    server_name  www.sjx.com;
    root         /var/lib/sjx;

    location / {
        proxy_set_header Host \$http_host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-Proto http;

        proxy_pass http://127.0.0.1:8080;
    }
}
EOF

nginx -s reload

9.6. 配置生产环境六:运行实践星A

9.6.1. 启动生产实践星A

位置:生产服务器A,用户:root
systemctl start sjx
位置:生产服务器A,用户:root
systemctl status sjx
终端输出
● sjx.service - ShiJianXing: A Spring Boot Demo
   Loaded: loaded (/usr/lib/systemd/system/sjx.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2023-01-18 23:05:12 CST; 4min 43s ago
 Main PID: 7984 (java)
   CGroup: /system.slice/sjx.service
           └─7984 /usr/bin/java -Dspring.config.location=/etc/sjx/application.properties -jar /opt/sjx/bin/sjx.jar

Jan 18 23:05:12 lan_server systemd[1]: Started ShiJianXing: A Spring Boot Demo.

9.6.2. 开发工作机访问生产实践星A

操作位置:开发工作机

生产实践星A的URL: http://www.sjx.com

(未完待续…​…​)

set 限制解除