[{"categories":["容器技术","DevOps"],"content":"Harbor 主备部署 环境准备 操作系统： CentOS 8.4 64bit\nHarbor 版本： harbor-offline-installer-v2.11.1.tgz\nDocker Compose： docker-compose-linux-x86_64\n角色 主机名 IP 地址 主 Harbor Harbor01 192.168.8.161 备 Harbor Harbor02 192.168.8.162 Docker 主机 web01 192.168.8.188 Harbor 是 VMware 公司开源的企业级 Docker Registry 项目，以 Docker 官方 Registry 为基础，提供了管理 UI、基于角色的访问控制（RBAC）、AD/LDAP 集成和审计日志等企业功能，同时原生支持中文。Harbor 的每个组件都以 Docker 容器形式构建，使用 Docker Compose 进行部署。\n一、部署主 Harbor（HTTP 模式，不使用证书） 此方式适用于内网环境，通过 HTTP 直接访问 Harbor，无需配置 SSL 证书。\n1. 安装 Docker 1 2 3 4 5 6 7 8 9 10 11 12 13 # 删除所有已存在的 yum 仓库文件 rm -rf /etc/yum.repos.d/* # 从阿里云镜像站下载 yum 仓库文件 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo # 安装 Docker CE yum install -y yum-utils yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 启动 Docker 并设置开机自启 systemctl enable docker.service --now 2. 配置 Docker 加速器 1 vim /etc/docker/daemon.json 写入以下内容：\n1 2 3 4 5 6 { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://37564ea7e7cc4824a5049fd666807c27.mirror.swr.myhuaweicloud.com\u0026#34; ], \u0026#34;insecure-registries\u0026#34;: [\u0026#34;http://192.168.8.161\u0026#34;] } 1 2 systemctl daemon-reload systemctl restart docker.service 3. 安装 Docker Compose 和 Harbor 1 2 3 4 5 6 7 8 9 10 # 将 docker-compose 和 harbor 离线包放到 /data 目录 ls /data # docker-compose-linux-x86_64 harbor-offline-installer-v2.11.1.tgz # 安装 docker-compose cp /data/docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod a+x /usr/local/bin/docker-compose # 解压 harbor tar -zxvf /data/harbor-offline-installer-v2.11.1.tgz -C /usr/local/ 4. 修改 Harbor 配置 1 2 3 cd /usr/local/harbor/ cp harbor.yml.tmpl harbor.yml vim harbor.yml 修改以下关键配置：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 hostname: 192.168.8.161 http: port: 80 # 将 https 段落全部注释掉（HTTP 模式不需要证书） #https: # port: 443 # certificate: /your/certificate/path # private_key: /your/private/key/path harbor_admin_password: Harbor12345 data_volume: /images 其余配置保持默认即可。\n5. 执行安装 1 ./install.sh 安装完成后，浏览器访问 http://192.168.8.161，使用 admin / Harbor12345 登录。\n6. 创建用户和项目 在 Harbor Web 界面中创建用户 user1：\n使用 user1 身份登录：\n以user1身份新建项目（如 openssh）：\n7. 测试镜像推送和拉取 在 Docker 主机（web01）上操作：\n1 2 3 4 5 6 7 8 9 10 11 # 登录 Harbor docker login 192.168.8.161 Username: user1 Password: # Login Succeeded # 给镜像打标签 docker tag nginx:latest 192.168.8.161/openssh/nginx:latest # 推送到 Harbor docker push 192.168.8.161/openssh/nginx:latest 推送成功后可在 Harbor 中看到该镜像：\n1 2 3 # 删除本地镜像后重新拉取 docker image rm -f 192.168.8.161/openssh/nginx:latest docker pull 192.168.8.161/openssh/nginx:latest 拉取后 Harbor 会显示下载次数 +1：\n二、部署备 Harbor 备端部署方法与主端完全一致，只需将 hostname 改为备端 IP。\n1 vim /usr/local/harbor/harbor.yml 1 hostname: 192.168.8.162 部署完成后访问 http://192.168.8.162，创建 user2 用户：\n使用 user2 登录备端 Harbor：\n三、配置主备复制 1. 在主端新建复制目标 使用 admin 登录主端 Harbor，进入 系统管理 → 仓库管理，新建目标：\n填写备端信息：\n端点 URL：http://192.168.8.162 访问 ID / 访问密钥：user2 的账号密码 创建成功：\n2. 创建复制策略 进入 系统管理 → 复制管理，新建复制策略：\n目标仓库：选择刚创建的 Harbor02 模式：事件驱动（也可选其他） 3. 验证同步结果 在 Docker 主机上推送更多镜像到主端：\n1 2 3 4 5 docker tag httpd:2.4 192.168.8.161/openssh/httpd:2.4 docker push 192.168.8.161/openssh/httpd:2.4 docker tag mysql:5.7 192.168.8.161/openssh/mysql:5.7 docker push 192.168.8.161/openssh/mysql:5.7 查看主端镜像：\n查看备端镜像（已自动同步）：\n备端能看到主端同步过来的镜像，说明主备复制配置成功。\n注：同步之前主端已经存在的镜像需要手动同步。\n四、启用 HTTPS（自签名证书方式） 如果需要在生产环境或跨公网使用 Harbor，建议启用 HTTPS。以下为使用 OpenSSL 自签名证书的完整流程。\n1. 生成 CA 证书和服务器证书 在 Harbor 服务器上执行：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 mkdir -p /data/cert \u0026amp;\u0026amp; cd /data/cert # 生成 CA 私钥 openssl genrsa -out ca.key 4096 # 生成 CA 证书（有效期 10 年） openssl req -x509 -new -nodes -sha512 -days 3650 \\ -subj \u0026#34;/C=CN/CN=HarborRootCA\u0026#34; \\ -key ca.key \\ -out ca.crt # 生成服务器私钥 openssl genrsa -out harbor.key 4096 # 生成服务器证书请求（CN 填写 Harbor 的 IP 或域名） openssl req -sha512 -new \\ -subj \u0026#34;/C=CN/CN=192.168.8.161\u0026#34; \\ -key harbor.key \\ -out harbor.csr 2. 配置证书扩展信息 创建 v3.ext 文件，指定 SAN（Subject Alternative Name）：\n1 2 3 4 5 6 7 8 9 10 cat \u0026gt; v3.ext \u0026lt;\u0026lt;-EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] IP.1=192.168.8.161 EOF IP.1 必须与 Harbor 的实际访问地址一致，否则 Docker 客户端登录时会报证书域名不匹配的错误。\n3. 签发服务器证书 1 2 3 4 5 openssl x509 -req -sha512 -days 3650 \\ -extfile v3.ext \\ -CA ca.crt -CAkey ca.key -CAcreateserial \\ -in harbor.csr \\ -out harbor.crt 4. 修改 Harbor 配置启用 HTTPS 1 vim /usr/local/harbor/harbor.yml 取消 https 部分的注释，并修改证书路径：\n1 2 3 4 5 6 hostname: 192.168.8.161 https: port: 443 certificate: /data/cert/harbor.crt private_key: /data/cert/harbor.key hostname 必须和 v3.ext 中的 IP.1 保持一致。\n5. 重新部署 Harbor 1 2 3 4 cd /usr/local/harbor/ docker-compose down ./prepare docker-compose up -d 注意：不要使用 docker-compose down -v，-v 会删除 named volumes，导致 Harbor 数据库和用户数据丢失。\n部署完成后通过 https://192.168.8.161 访问。\n6. 配置 Docker 客户端信任证书 每台需要拉取/推送镜像的机器都要执行：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 创建证书目录（目录名必须是 Harbor 的 IP 或域名） mkdir -p /etc/docker/certs.d/192.168.8.161 # 将 CA 证书复制到该目录 # 方式一：从 Harbor 服务器 scp scp root@192.168.8.161:/data/cert/ca.crt /etc/docker/certs.d/192.168.8.161/ # 方式二：手动复制，将 /data/cert/ca.crt 文件内容写入上述目录 # 重启 Docker systemctl restart docker # 测试登录 docker login 192.168.8.161 # 输入 admin 和密码，显示 Login Succeeded 即成功 五、常见问题 Q：HTTP 模式和 HTTPS 模式能否切换？\n可以。修改 harbor.yml 中的 https 配置段落后，重新执行 ./install.sh 即可。从 HTTP 切换到 HTTPS 需要先生成证书；从 HTTPS 切回 HTTP 则需要注释掉 https 段。\nQ：Docker 客户端登录 HTTPS Harbor 报 \u0026ldquo;x509: certificate relies on legacy Common Name field\u0026rdquo; 怎么办？\n这是因为证书没有配置 SAN（Subject Alternative Name）。重新生成证书时确保 v3.ext 中包含 IP.1=\u0026lt;Harbor的IP\u0026gt;，并用 -extfile v3.ext 参数签发。\nQ：主备复制失败怎么排查？\n检查主备两端的网络连通性、目标端仓库配置中的用户名密码是否正确、以及目标端对应用户是否有项目写入权限。\n","date":"2026-08-04","description":"","objectID":"/posts/harbor-master-slave/","readingTime":4,"tags":["Harbor","Docker","镜像仓库","主备复制","HTTPS"],"title":"Harbor 主备部署实战：从安装到 HTTPS 与主备复制","url":"/posts/harbor-master-slave/","wordCount":742},{"categories":["DevOps"],"content":"ES集群的API查看 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 root@elk02:~# apt -y install jq root@elk02:~# curl -s -u elastic:123456 11.0.1.111:9200/_cluster/health | jq { \u0026#34;cluster_name\u0026#34;: \u0026#34;dawn\u0026#34;, \u0026#34;status\u0026#34;: \u0026#34;green\u0026#34;, \u0026#34;timed_out\u0026#34;: false, \u0026#34;number_of_nodes\u0026#34;: 3, \u0026#34;number_of_data_nodes\u0026#34;: 3, \u0026#34;active_primary_shards\u0026#34;: 103, \u0026#34;active_shards\u0026#34;: 286, \u0026#34;relocating_shards\u0026#34;: 0, \u0026#34;initializing_shards\u0026#34;: 0, \u0026#34;unassigned_shards\u0026#34;: 0, \u0026#34;delayed_unassigned_shards\u0026#34;: 0, \u0026#34;number_of_pending_tasks\u0026#34;: 0, \u0026#34;number_of_in_flight_fetch\u0026#34;: 0, \u0026#34;task_max_waiting_in_queue_millis\u0026#34;: 0, \u0026#34;active_shards_percent_as_number\u0026#34;: 100 } root@elk02:~# curl -s -u elastic:123456 11.0.1.111:9200/_cluster/health | jq \u0026#34;.status\u0026#34; \u0026#34;green\u0026#34; root@elk02:~# curl -s -u elastic:123456 11.0.1.111:9200/_cluster/health | jq \u0026#34;.active_shards_percent_as_number\u0026#34; 100 相关参数说明: cluster_name 集群的名称。\nstatus 集群的健康状态，基于其主分片和副本分片的状态。 ES集群有以下三种状态: green 所有分片都已分配。 yellow 所有主分片都已分配，但一个或多个副本分片未分配。 如果集群中的某个节点发生故障，则在修复该节点之前，某些数据可能不可用。 red 一个或多个主分片未分配，因此某些数据不可用。这可能会在集群启动期间短暂发生，因为分配了主分片。\ntimed_out 是否在参数false指定的时间段内返回响应（默认情况下30秒）。\nnumber_of_nodes 集群内的节点数。\nnumber_of_data_nodes 作为专用数据节点的节点数。\nactive_primary_shards 可用主分片的数量。\nactive_shards 可用主分片和副本分片的总数。\nrelocating_shards 正在重定位的分片数。\ninitializing_shards 正在初始化的分片数。\nunassigned_shards 未分配的分片数。\ndelayed_unassigned_shards 分配因超时设置而延迟的分片数。\nnumber_of_pending_tasks 尚未执行的集群级别更改的数量。\nnumber_of_in_flight_fetch 未完成的提取次数。\ntask_max_waiting_in_queue_millis 自最早启动的任务等待执行以来的时间（以毫秒为单位）。\nactive_shards_percent_as_number 集群中活动分片的比率，以百分比表示。\n官方文档： https://www.elastic.co/guide/en/elasticsearch/reference/7.17/cluster-health.html\n","date":"2026-10-21","description":"","objectID":"/posts/es-cluster-health/","readingTime":1,"tags":["ElasticStack"],"title":"ES 集群健康状态查看实战：_cluster/health 全参数解析","url":"/posts/es-cluster-health/","wordCount":130},{"categories":["DevOps"],"content":"kibana的RBAC 内置用户：\n用户 角色 用途 elastic superuser 超级管理员，本环境密码为 123456 kibana_system kibana_system Kibana 服务连接 ES 专用 logstash_system logstash_system logstash 集中写 ES beats_system beats_system filebeat 等 beats 写 ES apm_system apm_system APM Server 1. 创建角色（RBAC 的核心） 角色由三段权限组成：\n段 控制对象 典型内容 cluster 集群级操作 monitor、manage_index_template、manage_security indices 索引级数据操作 names（索引名模式，支持通配）+ privileges（read/write/all 等） applications Kibana 等应用功能 feature_xxx.read / .write / .all + resources（空间） 创建\u0026quot;日志只读查看员\u0026quot;角色，全部在界面点：\n左侧导航最下方 Stack Management（齿轮图标）→ 左侧「安全」分区 → 角色 → 右上角创建角色 角色名称填 dawn_log_reader 集群权限：勾选 monitor（集群只读监控） 索引权限：点添加索引权限 → 「索引」填 dawn-log*，「权限」勾选 read 和 view_index_metadata Kibana 权限：点添加 Kibana 权限 → 「工作区」下拉选默认 → 在功能权限表里把 Discover、仪表板、可视化、地图 四行设为只读，其余保持无 保存 权限设计要点：\nindices 的索引模式用通配 dawn-log*，覆盖 dawn-log-nginx、dawn-log-filestream-、dawn-logstash- 等所有日志索引 privileges 最小集：view_index_metadata（Kibana 列索引、查字段必需）+ read（检索）；不给 write 就是只读用户 Kibana 权限控制到功能按钮级：Discover 只给只读就只能看不能存检索，不给「开发工具」就用不了 Dev Tools 「工作区」控制空间：选「* 所有工作区」可进所有空间，只选「默认」就进不了别的空间 2. 创建用户并绑定角色 Stack Management → 安全 → 用户 → 右上角创建用户 表单填写： 用户名：elk_viewer 密码 / 确认密码：Dawn123456 全名：ELK 日志只读查看员 电子邮件地址：elk_viewer@dawn.com 角色：下拉选择 dawn_log_reader 保存 用户与角色是多对多，一个用户可以配多个角色，最终权限取并集，我演示的是一个用户绑一个角色\n3. 准备索引模式 Stack Management →（Kibana 分组下）索引模式，确认列表里有 dawn-log-tomcat-errorlog（149 条 tomcat 错误日志，elk_viewer 授权范围内）和 dawn-apps-*（授权范围外，作对照） 列表里没有的，点创建索引模式补建：名称填索引模式名（如 dawn-log-tomcat-errorlog），下一步选时间字段 @timestamp，保存 4. 用 elk_viewer 登录验证 开一个无痕窗口，用 elk_viewer / Dawn123456 登录\n验证 1：空间切换器只剩「默认」。 看页面左上角的空间切换器，elk_viewer 只能看到「默认」；elastic 视角是「默认」+「运维空间」两个。原因是 dawn_log_reader 的 Kibana 权限只授权了「默认」工作区\n验证 2：授权范围内有数据。 进 Discover → 左上角索引模式选 dawn-log-tomcat-errorlog → 文档列表正常展示\n验证 3：授权范围外看得见对象、读不出数据。 在 Discover 左上角把索引模式切换成 dawn-apps-*，会报错或查不出数据。默认空间里能看到 dawn-apps-* 的索引模式对象名，但读数据仍然按角色里的索引权限卡\n验证 4：界面是只读的。\nDiscover 里改完检索条件，没有可用的「保存」入口（或提示无权限）——Discover 只授了只读 左侧导航没有 Stack Management 入口——本角色没有授予任何管理类功能 地址栏直接输入 http://11.0.1.114:5601/app/dev_tools 也进不去（返回 403）——没给「开发工具」功能权限 验证 5：elk_viewer 自己建不了索引模式。 即使索引模式名指向授权索引（如 dawn-log-es*），elk_viewer 尝试创建索引模式同样会被拒，所以索引模式只能由 elastic 建好给大家用\n5. Kibana 空间 Kibana 用\u0026quot;空间\u0026quot;给索引模式、仪表板、可视化分组，角色 Kibana 权限里的「工作区」决定用户能进哪些空间。\n创建空间：\n点左上角空间切换器 → 管理空间 点创建空间，ID 填 elk_ops，名称填「运维空间」 保存 ","date":"2026-10-21","description":"","objectID":"/posts/kibana-rbac/","readingTime":2,"tags":["ElasticStack"],"title":"Kibana RBAC 实战：角色创建、用户授权与空间隔离","url":"/posts/kibana-rbac/","wordCount":214},{"categories":["DevOps"],"content":"ES集群加密认证 1. 生成证书文件 1 2 3 4 root@elk01:~# /usr/share/elasticsearch/bin/elasticsearch-certutil cert --days 3650 -out /etc/elasticsearch/config/elastic-certtes.p12 -pass \u0026#34;\u0026#34; ... Directory /etc/elasticsearch/config does not exist. Do you want to create it? [Y/n]Y ... 2. 查看证书文件属主和属组 1 2 3 4 5 6 7 8 root@elk01:~# ll /etc/elasticsearch/config/ total 12 drwxr-sr-x 2 root elasticsearch 4096 Oct 21 13:36 ./ drwxr-s--- 4 root elasticsearch 4096 Oct 21 13:36 ../ -rw------- 1 root elasticsearch 3596 Oct 21 13:36 elastic-certificates.p12 root@elk01:~# chmod +r /etc/elasticsearch/config/elastic-certificates.p12 root@elk01:~# ll /etc/elasticsearch/config/elastic-certificates.p12 -rw-r--r-- 1 root elasticsearch 3596 Oct 21 13:36 /etc/elasticsearch/config/elastic-certificates.p12 3. 同步证书文件到其他节点 1 2 3 4 5 6 root@elk01:~# scp -r /etc/elasticsearch/config/ 11.0.1.112:/etc/elasticsearch/ root@11.0.1.112\u0026#39;s password: elastic-certificates.p12 100% 3596 2.9MB/s 00:00 root@elk01:~# scp -r /etc/elasticsearch/config/ 11.0.1.113:/etc/elasticsearch/ root@11.0.1.113\u0026#39;s password: elastic-certificates.p12 100% 3596 5.0MB/s 00:00 4. 修改ES集群的配置文件 1 root@elk01:~# vim /etc/elasticsearch/elasticsearch.yml 1 2 3 4 5 6 7 ... # 在最后一行添加以下内容 xpack.security.enabled: true xpack.security.transport.ssl.enabled: true xpack.security.transport.ssl.verification_mode: certificate xpack.security.transport.ssl.keystore.path: config/elastic-certificates.p12 xpack.security.transport.ssl.truststore.path: config/elastic-certificates.p12 5. 将配置文件同步到其他节点 1 2 3 4 5 6 root@elk01:~# scp /etc/elasticsearch/elasticsearch.yml 11.0.1.112:/etc/elasticsearch/elasticsearch.yml root@11.0.1.112\u0026#39;s password: elasticsearch.yml 100% 3928 1.0MB/s 00:00 root@elk01:~# scp /etc/elasticsearch/elasticsearch.yml 11.0.1.113:/etc/elasticsearch/elasticsearch.yml root@11.0.1.113\u0026#39;s password: elasticsearch.yml 100% 3928 2.9MB/s 00:00 6. 所有节点重启ES集群 1 2 3 root@elk01:~# systemctl restart elasticsearch.service root@elk02:~# systemctl restart elasticsearch.service root@elk03:~# systemctl restart elasticsearch.service 7. 检查集群节点端口是否监听 1 2 3 4 5 6 7 8 9 root@elk01:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* root@elk02:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* root@elk03:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* 8. 测试访问ES集群 1 2 3 root@elk01:~# curl 11.0.1.113:9200 {\u0026#34;error\u0026#34;:{\u0026#34;root_cause\u0026#34;:[{\u0026#34;type\u0026#34;:\u0026#34;security_exception\u0026#34;,\u0026#34;reason\u0026#34;:\u0026#34;missing authentication credentials for REST request [/]\u0026#34;,\u0026#34;header\u0026#34;:{\u0026#34;WWW-Authenticate\u0026#34;:\u0026#34;Basic realm=\\\u0026#34;security\\\u0026#34; charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026#34;}}],\u0026#34;type\u0026#34;:\u0026#34;security_exception\u0026#34;,\u0026#34;reason\u0026#34;:\u0026#34;missing authentication credentials for REST request [/]\u0026#34;,\u0026#34;header\u0026#34;:{\u0026#34;WWW-Authenticate\u0026#34;:\u0026#34;Basic realm=\\\u0026#34;security\\\u0026#34; charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026#34;}},\u0026#34;status\u0026#34;:401}root@elk01:~# curl 11.0.1.113:9200/_cat/nodes {\u0026#34;error\u0026#34;:{\u0026#34;root_cause\u0026#34;:[{\u0026#34;type\u0026#34;:\u0026#34;security_exception\u0026#34;,\u0026#34;reason\u0026#34;:\u0026#34;missing authentication credentials for REST request [/_cat/nodes]\u0026#34;,\u0026#34;header\u0026#34;:{\u0026#34;WWW-Authenticate\u0026#34;:\u0026#34;Basic realm=\\\u0026#34;security\\\u0026#34; charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026#34;}}],\u0026#34;type\u0026#34;:\u0026#34;security_exception\u0026#34;,\u0026#34;reason\u0026#34;:\u0026#34;missing authentication credentials for REST request [/_cat/nodes]\u0026#34;,\u0026#34;header\u0026#34;:{\u0026#34;WWW-Authenticate\u0026#34;:\u0026#34;Basic realm=\\\u0026#34;security\\\u0026#34; charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026#34;}},\u0026#34;status\u0026#34;:401} ES集群加密成功，用户无法直接访问，直接会拒绝，状态码为401\n9. 生成ES的随机密码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 root@elk01:~# /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME Initiating the setup of passwords for reserved users elastic,apm_system,kibana,kibana_system,logstash_system,beats_system,remote_monitoring_user. The passwords will be randomly generated and printed to the console. Please confirm that you would like to continue [y/N]y Changed password for user apm_system PASSWORD apm_system = iU3hx3Md2e1HoKNwAXpc Changed password for user kibana_system PASSWORD kibana_system = Apxpi9MYK5nK0yfQ5oVC Changed password for user kibana PASSWORD kibana = Apxpi9MYK5nK0yfQ5oVC Changed password for user logstash_system PASSWORD logstash_system = FLLwK9WLan5fgtTsoFnM Changed password for user beats_system PASSWORD beats_system = XOiZBPaNfwVt4WoDmlpd Changed password for user remote_monitoring_user PASSWORD remote_monitoring_user = 9LYltf3G7dnJ74m9Amb6 Changed password for user elastic PASSWORD elastic = vM2bNP9zylFUyoOLlR3W 10. 使用密码访问ES集群 1 2 3 4 root@elk01:~# curl -u elastic:vM2bNP9zylFUyoOLlR3W 11.0.1.113:9200/_cat/nodes 11.0.1.112 69 76 4 0.00 0.06 0.09 cdfhilmrstw * elk02 11.0.1.113 74 75 3 0.02 0.04 0.05 cdfhilmrstw - elk03 11.0.1.111 61 50 4 0.00 0.05 0.09 cdfhilmrstw - elk01 kibana页面登录认证及elastic密码修改 1. 修改kibana的配置文件 1 root@kibana:~# vim /etc/kibana/kibana.yml 1 2 3 ... elasticsearch.username: \u0026#34;kibana_system\u0026#34; elasticsearch.password: \u0026#34;Apxpi9MYK5nK0yfQ5oVC\u0026#34; 2. 重启kibana服务 1 root@kibana:~# systemctl restart kibana.service 3. 检查kibana服务是否监听 1 2 root@kibana:~# ss -ntl | grep 5601 LISTEN 0 511 0.0.0.0:5601 0.0.0.0:* 4. 访问kibana的WebUI使用elastic用户登录 http://11.0.1.114:5601/（需要使用elastic用户登录）\n5. 修改elastic的初始密码 postman访问ES加密集群 es-head访问ES加密集群 filebeat访问ES加密集群 1. 编写filebeat配置文件 1 root@elk01:/etc/filebeat# vim config/17-tcp-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 filebeat.inputs: - type: tcp host: \u0026#34;0.0.0.0:9000\u0026#34; output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-es-tls\u0026#34; username: \u0026#34;elastic\u0026#34; password: \u0026#34;123456\u0026#34; setup.ilm.enabled: false setup.template.name: \u0026#34;dawn-modules\u0026#34; setup.template.pattern: \u0026#34;dawn-log*\u0026#34; setup.template.overwrite: false 1 root@elk01:/etc/filebeat# filebeat -e -c config/17-tcp-to-es.yaml 2. 发送测试数据 1 root@elk02:~# echo 123456 | nc 11.0.1.111 9000 3. kibana查看数据 logstash访问ES加密集群 1. 编写配置文件 1 root@elk03:/etc/logstash# vim config/09-tcp-to-es_tls.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 input { tcp { port =\u0026gt; 8888 } } output { stdout {} elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-es-tls-logstash-%{+yyyy.MM.dd}\u0026#34; user =\u0026gt; \u0026#34;elastic\u0026#34; password =\u0026gt; \u0026#34;123456\u0026#34; } } 1 root@elk03:/etc/logstash# logstash -f config/09-tcp-to-es_tls.conf 2. 发送测试数据 1 root@elk02:~# echo \u0026#34;7891011\u0026#34; | nc 11.0.1.113 8888 3. kibana查看数据 ","date":"2026-10-21","description":"","objectID":"/posts/es-cluster-encryption-auth/","readingTime":4,"tags":["ElasticStack"],"title":"ES 集群加密认证实战：TLS 证书、密码登录与客户端接入","url":"/posts/es-cluster-encryption-auth/","wordCount":653},{"categories":["DevOps"],"content":"logstash的多分支语句、多实例与pipeline语法 一、logstash的多分支语句 1. 自定义模式文件 1 2 3 4 5 root@elk03:/etc/logstash/config# vim /etc/logstash/dawn-patterns/dawn SCHOOL (dawn|devops|k8s) CLAss [a-z0-9]+[0-9]{2} YEAR4 (19|20)[0-9]{2} year (19|20)[0-9]{2} 2. 测试数据 xixi 分支：\n1 2 3 123.112.21.112 - - [20/Oct/2026:21:20:00 +0800] \u0026#34;GET /index.html HTTP/1.1\u0026#34; 200 1024 \u0026#34;https://www.dawn.com/\u0026#34; \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36\u0026#34; 202.96.134.133 - - [20/Oct/2026:21:20:05 +0800] \u0026#34;POST /api/login HTTP/1.1\u0026#34; 404 512 \u0026#34;-\u0026#34; \u0026#34;Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148\u0026#34; 8.8.8.8 - - [20/Oct/2026:21:20:10 +0800] \u0026#34;GET /static/app.js HTTP/1.1\u0026#34; 200 20480 \u0026#34;https://www.dawn.com/index.html\u0026#34; \u0026#34;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/119.0.0.0 Safari/537.36\u0026#34; haha 分支：\n1 2 3 4 5 6 7 8 9 filebeat.inputs: - type: log paths: [\u0026#34;/tmp/haha.log\u0026#34;] fields: type: haha fields_under_root: true output.logstash: hosts: [\u0026#34;127.0.0.1:9999\u0026#34;] hehe 分支：\n1 2 3 4 5 exec 3\u0026lt;\u0026gt;/dev/tcp/127.0.0.1/8888 printf \u0026#39;%s\\n\u0026#39; \u0026#34;dawn haha devops01 xixi 2024 123.112.21.112\u0026#34; \u0026gt;\u0026amp;3 printf \u0026#39;%s\\n\u0026#39; \u0026#34;devops haha k8s07 xixi 2025 8.8.8.8\u0026#34; \u0026gt;\u0026amp;3 printf \u0026#39;%s\\n\u0026#39; \u0026#34;这一行没有IP 应该grok失败\u0026#34; \u0026gt;\u0026amp;3 exec 3\u0026gt;\u0026amp;- 3. 配置文件 1 root@elk03:/etc/logstash# vim config/08-input_multiple-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 input { file { path =\u0026gt; [\u0026#34;/tmp/xixi.log\u0026#34;] start_position =\u0026gt; \u0026#34;beginning\u0026#34; type =\u0026gt; \u0026#34;xixi\u0026#34; } beats { port =\u0026gt; 9999 type =\u0026gt; \u0026#34;haha\u0026#34; } tcp { port =\u0026gt; 8888 type =\u0026gt; \u0026#34;hehe\u0026#34; } } filter { # 如果类型是\u0026#34;xixi\u0026#34;，则遵循if语句代码块的处理逻辑。 if [type] == \u0026#34;xixi\u0026#34; { grok { match =\u0026gt; { message =\u0026gt; \u0026#34;%{HTTPD_COMMONLOG}\u0026#34; } } geoip { source =\u0026gt; \u0026#34;clientip\u0026#34; # 不写 database 插件会自己去下载 mmdb，且每次新 path.data 都要重下 database =\u0026gt; \u0026#34;/etc/logstash/GeoLite2-City.mmdb\u0026#34; } useragent { source =\u0026gt; \u0026#34;message\u0026#34; target =\u0026gt; \u0026#34;dawn-device\u0026#34; } date { match =\u0026gt; [\u0026#34;timestamp\u0026#34;,\u0026#34;dd/MMM/yyyy:HH:mm:ss Z\u0026#34;] } mutate { remove_field =\u0026gt; [ \u0026#34;ecs\u0026#34;,\u0026#34;agent\u0026#34;,\u0026#34;tags\u0026#34;,\u0026#34;input\u0026#34;,\u0026#34;@version\u0026#34;,\u0026#34;log\u0026#34;,\u0026#34;host\u0026#34; ] } } else if [type] == \u0026#34;haha\u0026#34; { mutate { remove_field =\u0026gt; [ \u0026#34;ecs\u0026#34;,\u0026#34;agent\u0026#34;,\u0026#34;tags\u0026#34;,\u0026#34;input\u0026#34;,\u0026#34;@version\u0026#34;,\u0026#34;log\u0026#34;, \u0026#34;host\u0026#34;] } } else { grok { patterns_dir =\u0026gt; [\u0026#34;/etc/logstash/dawn-patterns/\u0026#34;] match =\u0026gt; { message =\u0026gt; \u0026#34;%{SCHOOL:dawn-school} haha %{CLAss:dawn-class} xixi %{year:dawn-year} %{IPV4:dawn-ip}\u0026#34; } tag_on_failure =\u0026gt; [\u0026#34;_grokparsefailure\u0026#34;] } mutate { remove_field =\u0026gt; [ \u0026#34;ecs\u0026#34;,\u0026#34;agent\u0026#34;,\u0026#34;tags\u0026#34;,\u0026#34;input\u0026#34;,\u0026#34;@version\u0026#34;,\u0026#34;log\u0026#34;,\u0026#34;host\u0026#34; ] } } } output { stdout {} if [type] == \u0026#34;xixi\u0026#34; { elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-if-xixi-%{+yyyy.MM.dd}\u0026#34; } } else if [type] == \u0026#34;haha\u0026#34; { elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-if-haha-%{+yyyy.MM.dd}\u0026#34; } }else { elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-if-hehe-%{+yyyy.MM.dd}\u0026#34; } } } 4. 启动与验证 1 logstash -f /etc/logstash/config/08-input_multiple-to-es.yaml --path.data=/tmp/logstash-08f --path.logs=/tmp/logstash-08f/logs 查询\n1 curl -s \u0026#34;http://11.0.1.111:9200/dawn-logstash-if-hehe-2026.10.20/_count\u0026#34; 二、logstash的多实例 1. 创建测试数据 1 2 3 4 5 6 7 cat \u0026gt; /tmp/apps.log \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; INFO 2026-10-20 09:15:32|1001|login|svip1|99.50 INFO 2026-10-20 09:16:05|1002|logout|svip2|0.00 WARN 2026-10-20 10:20:11|1003|buy|svip3|199.00 ERROR 2026-10-20 11:05:47|1004|refund|svip4|59.90 INFO 2026-10-20 12:30:00|1005|login|svip5|128.00 EOF 2. 启动三个实例 1 2 3 4 5 6 7 8 # 实例1 logstash -rf config/02-file-to-stdout.conf --path.data=/tmp/logstash-haha # 实例2 logstash -rf config/03-file-filter-elasticsearch.conf --path.data=/tmp/logstash-haha2 # 实例3 logstash -rf config/04-file-filter-elasticsearch_custom_index.conf --path.data=/tmp/logstash-xixi 3. 验证 1 2 3 curl -s \u0026#34;http://127.0.0.1:9200/dawn-apps-2026.10.20/_count\u0026#34; curl -s \u0026#34;http://127.0.0.1:9200/_cat/indices/logstash-*?v\u0026amp;h=index,docs.count\u0026#34; 三、logstash的pipeline语法 1. pipelines.yml 1 2 mkdir -pv /usr/share/logstash/config/ vim /usr/share/logstash/config/pipelines.yml 1 2 3 4 5 - pipeline.id: dawn_xixi path.config: \u0026#34;/etc/logstash/config/02-file-to-stdout.conf\u0026#34; - pipeline.id: dawn_haha path.config: \u0026#34;/etc/logstash/config/03-file-filter-elasticsearch.conf\u0026#34; 2. 启动验证 1 logstash -r 日志确认两个 pipeline 都加载（一个进程，多个 pipeline）：\n1 [INFO ] agent - Pipelines running {:count=\u0026gt;2, :running_pipelines=\u0026gt;[:dawn_xixi, :dawn_haha], :non_running_pipelines=\u0026gt;[]} 用 -f 时自动忽略 pipelines.yml：\n1 [WARN ] [LogStash::Runner] multilocal - Ignoring the \u0026#39;pipelines.yml\u0026#39; file because modules or command line options are specified 3. sincedb 1 2 rm -f /usr/share/logstash/data/plugins/inputs/file/.sincedb_* # 裸命令方式 rm -f /var/lib/logstash/plugins/inputs/file/.sincedb_* # --path.settings /etc/logstash 方式 4. 三者的关系 pipeline 和多分支语句的关系：在每个 pipeline 中，都可以使用多分支语法 logstash 实例和 pipeline 的关系：一个 logstash 可以启动多个 pipeline，本质上就是一个进程加载了多个配置文件 logstash 多实例：启动多个 logstash 进程，每个进程都有一个 main pipeline，本质上就是每个进程加载一个配置文件 ","date":"2026-10-21","description":"","objectID":"/posts/logstash-multi-branch-instance-pipeline/","readingTime":3,"tags":["ElasticStack","Logstash"],"title":"Logstash 进阶实战：多分支语句、多实例与 pipeline 语法","url":"/posts/logstash-multi-branch-instance-pipeline/","wordCount":613},{"categories":["DevOps"],"content":"一、Logstash file 输入的 offset 与重新采集 1.1 offset 记录在 sincedb 文件中 Logstash 会在 /usr/share/logstash/data/plugins/inputs/file/ 目录下生成 .sincedb_\u0026lt;文件路径哈希\u0026gt; 文件，记录每个被采集文件当前的读取位置（offset）。\n下次启动时从这个位置继续采集，不会把整个文件重读一遍。\n1.2 重新导入旧数据的完整步骤 1 停止 logstash\n2 清除 sincedb 文件\n1 root@elk03:/etc/logstash# rm -f /usr/share/logstash/data/plugins/inputs/file/.sincedb_c3e21f60a15f19878632de9b335e4596 .sincedb_ 后的哈希由被采集文件的路径决定，不同文件文件名不同；删之前先 ls /usr/share/logstash/data/plugins/inputs/file/ 确认。\n3 重启 logstash\n要指定采集文件的起始位置。\n1 root@elk03:/etc/logstash# vim config/03-file-filter-elasticsearch.conf 1 2 3 4 5 6 input { file { path =\u0026gt; [\u0026#34;/tmp/apps.log\u0026#34;] start_position =\u0026gt; \u0026#34;beginning\u0026#34; } } 本节只关注 input 的读取位置行为，filter 拆分解法与完整配置见 2.2 节。\n演示用日志 /tmp/apps.log，每行以 | 分隔 5 段，首段为「程序名 + 日期 + 时间」：\n1 apps 2026-10-19 10:00:00|1001|login|svip01|9.9 1 root@elk03:/etc/logstash# logstash -rf config/03-file-filter-elasticsearch.conf 二、Logstash 处理日志的访问时间（date 插件） 2.1 date 插件 专门用于处理日志的日期模块，将其作为时间戳可以替换\u0026quot;@timestamp\u0026quot;字段。\n将来kibana基于该字段进行时间查询。\n2.2 编写 logstash 配置文件 1 root@elk03:/etc/logstash# vim config/03-file-filter-elasticsearch.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 input { # 输入类型是一个file，代表的是文本文件 file { # 指定的文件的路径，可以指定多个 path =\u0026gt; [\u0026#34;/tmp/apps.log\u0026#34;] # 指定从源文件采集的位置，有效值为: beginning, end(默认值)。 # 该参数仅在首次采集(没有在\u0026#34;.sincedb*\u0026#34;文件中记录)新文件时生效，第二次采集则无视此参数。 start_position =\u0026gt; \u0026#34;beginning\u0026#34; } } filter { mutate { # 对指定字段进行切分，本案例是将message字段按照\u0026#34;|\u0026#34;进行切分 split =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;|\u0026#34; } # 添加字段 add_field =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34;%{[message][0]}\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;%{[message][1]}\u0026#34; \u0026#34;action\u0026#34; =\u0026gt; \u0026#34;%{[message][2]}\u0026#34; \u0026#34;svip\u0026#34; =\u0026gt; \u0026#34;%{[message][3]}\u0026#34; \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;%{[message][4]}\u0026#34; } } mutate { split =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34; \u0026#34;} add_field =\u0026gt; { datetime =\u0026gt; \u0026#34;%{[other][1]} %{[other][2]}\u0026#34; } # 移除字段 remove_field =\u0026gt; [ \u0026#34;message\u0026#34; , \u0026#34;other\u0026#34;, \u0026#34;@version\u0026#34;] } mutate { # 对字段进行数据转换 convert =\u0026gt; { \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;float\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } } # 处理时间相关的模块 date { # 匹配日期字段，将\u0026#34;datetime\u0026#34; 转换为日期格式 match =\u0026gt; [ \u0026#34;datetime\u0026#34;, \u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34; ] # 使用match匹配到的时间数据类型存储在哪个字段中，若不指定，则默认使用覆盖\u0026#34;@timestamp\u0026#34; target =\u0026gt; \u0026#34;dawn-datetime\u0026#34; } } output { stdout {} elasticsearch {} } 注意：date 插件不指定 timezone 时默认按 UTC 解析，本地时间（+08:00）解析后在 Kibana 里查会差 8 小时；需要与本地时间一致时加上 timezone =\u0026gt; \u0026quot;Asia/Shanghai\u0026quot;。\n2.3 启动 logstash 实例 1 root@elk03:/etc/logstash# logstash -rf config/03-file-filter-elasticsearch.conf 2.4 Kibana 查询验证 三、Logstash 写入 ES 自定义索引名称 1 root@elk03:/etc/logstash# vim config/04-file-filter-elasticsearch_custom_index.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 input { # 输入类型是一个file，代表的是文本文件 file { # 指定的文件的路径，可以指定多个 path =\u0026gt; [\u0026#34;/tmp/apps.log\u0026#34;] # 指定从源文件采集的位置，有效值为: beginning, end(默认值)。 # 该参数仅在首次采集(没有在\u0026#34;.sincedb*\u0026#34;文件中记录)新文件时生效，第二次采集则无视此参数。 start_position =\u0026gt; \u0026#34;beginning\u0026#34; } } filter { mutate { # 对指定字段进行切分，本案例是将message字段按照\u0026#34;|\u0026#34;进行切分 split =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;|\u0026#34; } # 添加字段 add_field =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34;%{[message][0]}\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;%{[message][1]}\u0026#34; \u0026#34;action\u0026#34; =\u0026gt; \u0026#34;%{[message][2]}\u0026#34; \u0026#34;svip\u0026#34; =\u0026gt; \u0026#34;%{[message][3]}\u0026#34; \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;%{[message][4]}\u0026#34; } } mutate { split =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34; \u0026#34;} add_field =\u0026gt; { datetime =\u0026gt; \u0026#34;%{[other][1]} %{[other][2]}\u0026#34; } # 移除字段 remove_field =\u0026gt; [ \u0026#34;message\u0026#34; , \u0026#34;other\u0026#34;, \u0026#34;@version\u0026#34;] } mutate { # 对字段进行数据转换 convert =\u0026gt; { \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;float\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } } # 处理时间相关的模块 date { # 匹配日期字段，将\u0026#34;datetime\u0026#34; 转换为日期格式 match =\u0026gt; [ \u0026#34;datetime\u0026#34;, \u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34; ] # 使用match匹配到的时间数据类型存储在哪个字段中，若不指定，则默认使用覆盖\u0026#34;@timestamp\u0026#34; target =\u0026gt; \u0026#34;dawn-datetime\u0026#34; } } output { stdout {} elasticsearch { # 指定ES集群地址 hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] # 指定ES自定义索引的名称 index =\u0026gt; \u0026#34;dawn-apps-%{+yyyy.MM.dd}\u0026#34; } } 1 root@elk03:/etc/logstash# logstash -rf config/04-file-filter-elasticsearch_custom_index.conf 四、Logstash 的 grok 自定义正则与常用 filter 插件 4.1 grok 的语法 把正则封装成可复用的模式，再给捕获组起字段名。\n1 2 %{SYNTAX:SEMANTIC} # 模式名:字段名 %{SYNTAX:SEMANTIC:TYPE} # 可加类型转换 int/float，如 %{NUMBER:bytes:int} 字段名不要带.，会被当成嵌套层级。\n4.2 自定义模式文件 1 2 root@elk03:/etc/logstash# mkdir -pv /etc/logstash/dawn-patterns/ root@elk03:/etc/logstash# vim /etc/logstash/dawn-patterns/dawn 1 2 3 SCHOOL (dawn|devops|k8s) CLASS [a-z0-9]+[0-9]{2} YEAR4 (19|20)[0-9]{2} 1 root@elk03:/etc/logstash# vim /etc/logstash/dawn-patterns/school.yml 1 2 3 \u0026#34;dawn\u0026#34;: \u0026#34;黎明实验室\u0026#34; \u0026#34;devops\u0026#34;: \u0026#34;DevOps 学院\u0026#34; \u0026#34;k8s\u0026#34;: \u0026#34;云原生学院\u0026#34; yml 字典的 key 要加引号，否则数字 key 会和字段里的字符串对不上。\n4.3 编写 07 完整配置 1 root@elk03:/etc/logstash# vim config/07-grok-custom-filters-es.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 input { # filebeat 走 beats 协议 beats { port =\u0026gt; 9999 } # 调试链路，nc/bash 直接送纯文本，省掉 filebeat tcp { port =\u0026gt; 9998 } } filter { # 自定义 grok，时间用模式抽 grok { patterns_dir =\u0026gt; [\u0026#34;/etc/logstash/dawn-patterns/\u0026#34;] match =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;%{TIMESTAMP_ISO8601:dawn_datetime} %{SCHOOL:dawn_school} haha %{CLASS:dawn_class} xixi %{YEAR4:dawn_year} %{IP:dawn_ip} %{GREEDYDATA:dawn_ua}\u0026#34; } tag_on_failure =\u0026gt; [\u0026#34;_grokparsefailure\u0026#34;, \u0026#34;dawn_grok_fail\u0026#34;] } # date，把日志真实时间写进 @timestamp date { match =\u0026gt; [\u0026#34;dawn_datetime\u0026#34;, \u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34;, \u0026#34;ISO8601\u0026#34;] target =\u0026gt; \u0026#34;@timestamp\u0026#34; } # geoip，指定本地库 geoip { source =\u0026gt; \u0026#34;dawn_ip\u0026#34; database =\u0026gt; \u0026#34;/etc/logstash/GeoLite2-City.mmdb\u0026#34; } useragent { source =\u0026gt; \u0026#34;dawn_ua\u0026#34; target =\u0026gt; \u0026#34;dawn_device\u0026#34; } # translate，字典富化 translate { source =\u0026gt; \u0026#34;dawn_school\u0026#34; target =\u0026gt; \u0026#34;dawn_school_cn\u0026#34; dictionary_path =\u0026gt; \u0026#34;/etc/logstash/dawn-patterns/school.yml\u0026#34; fallback =\u0026gt; \u0026#34;未知\u0026#34; } mutate { rename =\u0026gt; { \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;dawn-host\u0026#34; } convert =\u0026gt; { \u0026#34;dawn_year\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } add_field =\u0026gt; { \u0026#34;dawn_label\u0026#34; =\u0026gt; \u0026#34;%{dawn_school}-%{dawn_class}\u0026#34; } } # ruby，把 geoip 的 {lat,lon} 对象转成 [经度,纬度] 数组 ruby { code =\u0026gt; \u0026#39; loc = event.get(\u0026#34;[geoip][location]\u0026#34;) if loc event.set(\u0026#34;dawn_geo\u0026#34;, [loc[\u0026#34;lon\u0026#34;], loc[\u0026#34;lat\u0026#34;]]) end \u0026#39; } # 收尾清理 mutate { remove_field =\u0026gt; [\u0026#34;ecs\u0026#34;, \u0026#34;input\u0026#34;, \u0026#34;@version\u0026#34;, \u0026#34;log\u0026#34;, \u0026#34;message\u0026#34;] } } output { stdout { codec =\u0026gt; rubydebug } elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-grok-%{+yyyy.MM.dd}\u0026#34; } } 4.4 启动 logstash 1 root@elk03:/etc/logstash# logstash -rf config/07-grok-custom-filters-es.conf 日志出现 Pipelines running 才算就绪\n4.5 发送测试数据 1 2 3 4 5 6 7 8 9 10 11 12 13 root@elk03:/tmp# cat \u0026gt; send_testdata.sh \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; #!/bin/bash exec 3\u0026lt;\u0026gt;/dev/tcp/127.0.0.1/9998 || { echo \u0026#34;CONNECT_FAIL\u0026#34;; exit 1; } sent=0 while IFS= read -r line; do [ -z \u0026#34;$line\u0026#34; ] \u0026amp;\u0026amp; continue printf \u0026#39;%s\\n\u0026#39; \u0026#34;$line\u0026#34; \u0026gt;\u0026amp;3 sent=$((sent+1)) done \u0026lt; /tmp/grok-exp-testdata.txt exec 3\u0026gt;\u0026amp;- echo \u0026#34;SENT_TOTAL=$sent\u0026#34; EOF root@elk03:/tmp# bash send_testdata.sh 测试数据，5 行覆盖 3 个学校、4 种 UA，境内外 IP，最后一行故意写坏用来验证失败标签。\n1 2 3 4 5 2026-10-20 17:20:00 dawn haha linux92 xixi 2024 123.112.21.112 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36 2026-10-20 17:20:05 devops haha k8s07 xixi 2025 8.8.8.8 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Version/17.0 Safari/605.1.15 2026-10-20 17:20:10 dawn haha python88 xixi 2023 202.96.134.133 Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/119.0.0.0 Safari/537.36 2026-10-20 17:20:15 k8s haha docker33 xixi 2026 1.1.1.1 Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148 2026-10-20 17:20:20 dawn haha java55 xixi # 这一行故意写坏 用来触发_grokparsefailure 4.6 Kibana 查询验证 Stack Management → Index Patterns → Create index pattern，索引 dawn-logstash-grok-*，时间字段 @timestamp Discover 里确认字段：dawn_school、dawn_school_cn、dawn_class、dawn_year、dawn_label、dawn_ip、dawn_device.*、geoip.*、dawn_geo 地图用 Maps → Add layer → Documents，地理字段选 dawn_geo 时间过滤器默认过去 15 分钟，历史数据要把右上角时间范围改到覆盖数据的时间段 控制台每条事件的输出：\n1 2 3 4 5 6 7 8 9 10 11 12 { \u0026#34;@timestamp\u0026#34; =\u0026gt; 2026-10-20T17:20:00.000Z, \u0026#34;dawn_school\u0026#34; =\u0026gt; \u0026#34;dawn\u0026#34;, \u0026#34;dawn_school_cn\u0026#34; =\u0026gt; \u0026#34;黎明实验室\u0026#34;, \u0026#34;dawn_class\u0026#34; =\u0026gt; \u0026#34;linux92\u0026#34;, \u0026#34;dawn_year\u0026#34; =\u0026gt; 2024, \u0026#34;dawn_label\u0026#34; =\u0026gt; \u0026#34;dawn-linux92\u0026#34;, \u0026#34;dawn_ip\u0026#34; =\u0026gt; \u0026#34;123.112.21.112\u0026#34;, \u0026#34;dawn_device\u0026#34; =\u0026gt; { \u0026#34;name\u0026#34; =\u0026gt; \u0026#34;Chrome\u0026#34;, \u0026#34;os_name\u0026#34; =\u0026gt; \u0026#34;Windows\u0026#34;, \u0026#34;device\u0026#34; =\u0026gt; \u0026#34;Other\u0026#34; }, \u0026#34;geoip\u0026#34; =\u0026gt; { \u0026#34;country_name\u0026#34; =\u0026gt; \u0026#34;China\u0026#34;, \u0026#34;city_name\u0026#34; =\u0026gt; \u0026#34;Beijing\u0026#34; }, \u0026#34;dawn_geo\u0026#34; =\u0026gt; [116.3889, 39.9288] } 故意写坏那条的 tags 是 _grokparsefailure + dawn_grok_fail + _geoip_lookup_failure，后一个是连带的（没抽出 IP，geoip 自然失败），看到两个一起出现先查 grok。\n1 2 root@elk03:/etc/logstash# curl -s \u0026#39;http://11.0.1.111:9200/dawn-logstash-grok-2026.10.20/_count\u0026#39; {\u0026#34;count\u0026#34;:5,\u0026#34;_shards\u0026#34;:{\u0026#34;total\u0026#34;:1,\u0026#34;successful\u0026#34;:1,\u0026#34;skipped\u0026#34;:0,\u0026#34;failed\u0026#34;:0}} 4.7 常用 filter 插件速查 1. mutate 字段增删改、类型转换，出场率最高。注意 mutate 内部各操作是固定顺序（coerce→rename→update→replace→convert→gsub→uppercase→capitalize→lowercase→strip→remove→split→join→merge→copy），跟书写顺序无关，有先后依赖要拆成多个 mutate 块。\n1 2 3 4 5 6 7 8 mutate { split =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;|\u0026#34; } add_field =\u0026gt; { \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;%{[message][1]}\u0026#34; } convert =\u0026gt; { \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;float\u0026#34;, \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } rename =\u0026gt; { \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;dawn-host\u0026#34; } gsub =\u0026gt; [ \u0026#34;request\u0026#34;, \u0026#34;\\?.*$\u0026#34;, \u0026#34;\u0026#34; ] remove_field =\u0026gt; [ \u0026#34;message\u0026#34;, \u0026#34;@version\u0026#34; ] } 2. date 1 2 3 4 date { match =\u0026gt; [\u0026#34;timestamp\u0026#34;, \u0026#34;dd/MMM/yyyy:HH:mm:ss Z\u0026#34;, \u0026#34;ISO8601\u0026#34;] target =\u0026gt; \u0026#34;@timestamp\u0026#34; } 不写 target 就覆盖 @timestamp。MMM 匹配英文月份缩写，依赖 JVM locale，中文 locale 下要加 -Duser.language=en -Duser.country=US 或改用数字月份。\n3. geoip 1 2 3 4 5 geoip { source =\u0026gt; \u0026#34;clientip\u0026#34; database =\u0026gt; \u0026#34;/etc/logstash/GeoLite2-City.mmdb\u0026#34; target =\u0026gt; \u0026#34;geoip\u0026#34; } 不写 database 会联网下库，内网首次卡 90s+ 再失败。内网 IP 查不到地理信息，字段为空是正常的。\n4. useragent 1 2 3 4 useragent { source =\u0026gt; \u0026#34;agent\u0026#34; target =\u0026gt; \u0026#34;dawn_device\u0026#34; } 产出 dawn_device.name（浏览器）、os_name（系统）、device（设备类型）。\n5. translate 1 2 3 4 5 6 7 translate { source =\u0026gt; \u0026#34;dawn_school\u0026#34; target =\u0026gt; \u0026#34;dawn_school_cn\u0026#34; dictionary_path =\u0026gt; \u0026#34;/etc/logstash/dawn-patterns/school.yml\u0026#34; fallback =\u0026gt; \u0026#34;未知\u0026#34; refresh_interval =\u0026gt; 300 } 6. kv 1 2 3 4 5 6 7 kv { source =\u0026gt; \u0026#34;request\u0026#34; field_split =\u0026gt; \u0026#34;\u0026amp;\u0026#34; value_split =\u0026gt; \u0026#34;=\u0026#34; trim_key =\u0026gt; \u0026#34;?\u0026#34; target =\u0026gt; \u0026#34;dawn_params\u0026#34; } 不写 target 会平铺到事件顶层，容易和别的字段打架。\n7. json 1 2 3 4 json { source =\u0026gt; \u0026#34;message\u0026#34; tag_on_failure =\u0026gt; [\u0026#34;_jsonparsefailure\u0026#34;] } 日志本身就是 JSON 的话，优先在 filebeat 端用 json.keys_under_root: true 解析，省掉 Logstash 这一步。\n8. csv 1 2 3 4 5 csv { source =\u0026gt; \u0026#34;message\u0026#34; columns =\u0026gt; [\u0026#34;time\u0026#34;, \u0026#34;level\u0026#34;, \u0026#34;msg\u0026#34;, \u0026#34;cost\u0026#34;] convert =\u0026gt; { \u0026#34;cost\u0026#34; =\u0026gt; \u0026#34;float\u0026#34; } } 9. dissect 固定分隔符的日志优先用它，比 grok 快数倍，不做正则回溯。\n1 2 3 4 5 6 dissect { mapping =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;%{clientip} - %{ident} [%{timestamp}] \\\u0026#34;%{verb} %{request}\\\u0026#34; %{response} %{bytes}\u0026#34; } remove_field =\u0026gt; [\u0026#34;message\u0026#34;] } 10. ruby 1 2 # 代码同 4.3 节 07 配置中的 ruby 块：把 geoip.location 的 {lat,lon} 对象拼成 [经度,纬度] 数组。 # dawn-geo 在 ES 侧映射为 geo_point 后，Kibana Maps 才能直接选它做地理图层。 同步阻塞，高吞吐场景能不用就不用。\n","date":"2026-10-20","description":"","objectID":"/posts/logstash-sincedb-date-index/","readingTime":7,"tags":["ElasticStack","Logstash"],"title":"Logstash 进阶实战：sincedb 重新采集、date 时间处理与自定义索引","url":"/posts/logstash-sincedb-date-index/","wordCount":1439},{"categories":["DevOps"],"content":"基于ELFK架构分析nginx访问日志 1. logstash代码 1 root@elk03:/etc/logstash# vim config/05-beats-filter-es.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 input { # 接收beats组件的数据，此处指filebeat beats { port =\u0026gt; 9999 } } filter { mutate { remove_field =\u0026gt; [ \u0026#34;ecs\u0026#34;,\u0026#34;agent\u0026#34;,\u0026#34;tags\u0026#34;,\u0026#34;input\u0026#34;,\u0026#34;@version\u0026#34; ] } # 基于正则匹配nginx的访问日志并解析相应格式，提取任意文本类型。 # nginx 日志为 combined 格式，改用 HTTPD_COMBINEDLOG（= COMMONLOG + referrer + agent） grok { match =\u0026gt; { message =\u0026gt; \u0026#34;%{HTTPD_COMBINEDLOG}\u0026#34; } } # 基于公网地址分析经纬度，城市的名称等信息，指定本地 geoip 数据库 # 注意: 内网 IP（如 11.0.1.1）不会生成 geoip 字段，仅对公网 IP 生效 geoip { source =\u0026gt; \u0026#34;clientip\u0026#34; database =\u0026gt; \u0026#34;/etc/logstash/GeoLite2-City.mmdb\u0026#34; } # geoip 插件的 location 是 {lat,lon} 对象，ES 动态映射不会将其识别为 geo_point，（只有 [lon,lat] 数组/GeoJSON/geohash 字符串才会） # 用 ruby 过滤器把经纬度拼成 [lon,lat] 数组字段 dawn-geo，动态映射为 geo_point ruby { code =\u0026gt; \u0026#34;event.set(\u0026#39;[dawn-geo]\u0026#39;, [event.get(\u0026#39;[geoip][longitude]\u0026#39;), event.get(\u0026#39;[geoip][latitude]\u0026#39;)]) if event.get(\u0026#39;[geoip][longitude]\u0026#39;)\u0026#34; } # 基于agent字段分析用户的设备类型,将分析的结果存储在\u0026#34;dawn-device\u0026#34;字段中 useragent { source =\u0026gt; \u0026#34;agent\u0026#34; target =\u0026gt; \u0026#34;dawn-device\u0026#34; } mutate { rename =\u0026gt; { \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;dawn-host\u0026#34; \u0026#34;agent\u0026#34; =\u0026gt; \u0026#34;dawn-agent\u0026#34; } } date { match =\u0026gt; [\u0026#34;timestamp\u0026#34;,\u0026#34;dd/MMM/yyyy:HH:mm:ss Z\u0026#34;] } } output { # stdout {} elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-nginx-%{+yyyy.MM.dd}\u0026#34; } } 1 root@elk03:/etc/logstash# logstash -rf config/05-beats-filter-es.conf 2. 启动filebeat 1 root@elk01:/etc/filebeat# vim config/14-modules_nginx-to-logstash.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 filebeat.config.modules: # 此处的\u0026#34;${path.config}\u0026#34;对应的路径是/etc/filebeat目录 path: ${path.config}/modules.d/*.yml # 支持热加载 reload.enabled: true # 指定每间隔多长时间检测一次\u0026#34;${path.config}/modules.d/*.yml\u0026#34; reload.period: 5s #output.console: # pretty: true # 将数据推送logstash output.logstash: hosts: [\u0026#34;11.0.1.113:9999\u0026#34;] 1 2 root@elk01:/etc/filebeat# rm -rf /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/14-modules_nginx-to-logstash.yaml 3. Kibana 索引模式与可视化 3.1 建索引模式 Stack Management → Index Patterns → Create：dawn-logstash-nginx-*，Time field 选 @timestamp。\n3.2 可视化（3 个）+ 地理分布（Maps 1 个） 可视化 类型 聚合字段 ELFK-nginx 访客地理分布 Maps 应用对象（面板类型 map） dawn-geo，ES_SEARCH 文档图层 ELFK-nginx 设备类型分布 Pie dawn-device.name，terms ELFK-nginx 操作系统分布 Pie dawn-device.os，terms ELFK-nginx 响应状态码分布 Vertical Bar（类型名 histogram） response，terms 3.3 仪表板 把 3 个可视化和 1 个 Maps 面板拖进同一个 Dashboard 即可，布局：地图通栏，下面三张图各占 1/3。\n仪表板地址：http://11.0.1.114:5601/app/dashboards#/view/788cabb0-cbda-11f1-9207-335bca41b28c Maps 对象地址：http://11.0.1.114:5601/app/maps#/map/a78a0830-cbe0-11f1-9207-335bca41b28c\n3.4 效果字段速查 geoip：country_name / country_code2 / continent_code / timezone / latitude / longitude / city_name / location dawn-device：name（浏览器）/ os（操作系统）/ os_name / os_version / device（设备） nginx：clientip / request / response / bytes / referrer / timestamp 4. 项目整合: ELFK架构分析出图展示 filebeat（采集）→ logstash（grok 解析 + geoip + useragent + date）→ elasticsearch（三节点存储）→ kibana（索引模式 + 地图/饼图/柱状图仪表板）\n","date":"2026-10-20","description":"","objectID":"/posts/elfk-nginx-access-log/","readingTime":2,"tags":["ElasticStack","ELFK","Nginx日志"],"title":"ELFK 架构分析 Nginx 访问日志实战：Filebeat + Logstash + Kibana 全流程","url":"/posts/elfk-nginx-access-log/","wordCount":380},{"categories":["DevOps"],"content":"ES的JVM基础环境优化 1. 修改ES的JVM大小 1 root@elk01:/etc/filebeat# vim /etc/elasticsearch/jvm.options 1 2 3 4 ... # 学习环境推荐为256M，生产环境建议设置为物理内存一半，但不建议超过32GB -Xms256m -Xmx256m 2. 拷贝配置文件 1 2 3 4 5 6 root@elk01:/etc/filebeat# scp /etc/elasticsearch/jvm.options 11.0.1.112:/etc/elasticsearch root@11.0.1.112\u0026#39;s password: jvm.options 100% 3484 895.4KB/s 00:00 root@elk01:/etc/filebeat# scp /etc/elasticsearch/jvm.options 11.0.1.113:/etc/elasticsearch root@11.0.1.113\u0026#39;s password: jvm.options 100% 3484 2.0MB/s 00:00 3. 重启服务 1 2 3 root@elk01:/etc/filebeat# systemctl restart elasticsearch.service root@elk02:~# systemctl restart elasticsearch.service root@elk03:~# systemctl restart elasticsearch.service logstash环境搭建 1. 概述 用于处理日志，聚合日志，分析的功能。功能丰富但比较笨重，目前ELK的市场逐渐被EFK替代。\nfilebeat很多功能基本是都是基于logstash为原型使用Golang编程语言重写的。轻量级的日志采集工具。\nLogstash是免费且开放的服务器端数据处理管道，能够从多个来源采集数据，转换数据，然后将数据发送到您最喜欢的“存储库”中。\n2. 下载logstash 1 root@elk03:~# wget https://artifacts.elastic.co/downloads/logstash/logstash-7.17.22-amd64.deb 3. 安装logstash 1 root@elk03:~# dpkg -i logstash-7.17.22-amd64.deb 4. 添加logstash命令到PATH环境变量 1 2 root@elk03:~# ln -svf /usr/share/logstash/bin/logstash /usr/local/sbin/ \u0026#39;/usr/local/sbin/logstash\u0026#39; -\u0026gt; \u0026#39;/usr/share/logstash/bin/logstash\u0026#39; 5. 编写logstash的配置文件 1 2 3 4 5 6 7 8 9 10 11 root@elk03:~# cd /etc/logstash/ root@elk03:/etc/logstash# mkdir config root@elk03:/etc/logstash# vim config/01-stdin-to-stdout.conf input { stdin {} } output { stdout {} } 6. 启动logstash实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@elk03:/etc/logstash# logstash -f config/01-stdin-to-stdout.conf ...\t{ \u0026#34;@version\u0026#34; =\u0026gt; \u0026#34;1\u0026#34;, \u0026#34;@timestamp\u0026#34; =\u0026gt; 2026-10-18T16:37:36.202Z, \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;\u0026#34;, \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;elk03\u0026#34; } 111111111 { \u0026#34;@version\u0026#34; =\u0026gt; \u0026#34;1\u0026#34;, \u0026#34;@timestamp\u0026#34; =\u0026gt; 2026-10-18T16:37:55.092Z, \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;111111111\u0026#34;, \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;elk03\u0026#34; } logstash处理自定义业务的日志 1. 准备Python脚本生成数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 root@elk03:~# vim generate_log.py #!/usr/bin/env python # -*- coding: UTF-8 -*- # @author : Jason Yin import datetime import random import logging import time import sys LOG_FORMAT = \u0026#34;%(levelname)s %(asctime)s [com.dawn.%(module)s] - %(message)s \u0026#34; DATE_FORMAT = \u0026#34;%Y-%m-%d %H:%M:%S\u0026#34; # 配置root的logging.Logger实例的基本配置 logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, datefmt=DATE_FORMAT, filename=sys.argv[1] , filemode=\u0026#39;a\u0026#39;,) actions = [\u0026#34;浏览页面\u0026#34;, \u0026#34;评论商品\u0026#34;, \u0026#34;加入收藏\u0026#34;, \u0026#34;加入购物车\u0026#34;, \u0026#34;提交订单\u0026#34;, \u0026#34;使用优惠券\u0026#34;, \u0026#34;领取优惠券\u0026#34;, \u0026#34;搜索\u0026#34;, \u0026#34;查看订单\u0026#34;, \u0026#34;付款\u0026#34;, \u0026#34;清空购物车\u0026#34;] while True: time.sleep(random.randint(1, 5)) user_id = random.randint(1, 10000) # 对生成的浮点数保留2位有效数字. price = round(random.uniform(15000, 30000),2) action = random.choice(actions) svip = random.choice([0,1]) logging.info(\u0026#34;DAU|{0}|{1}|{2}|{3}\u0026#34;.format(user_id, action,svip,price)) 1 2 3 root@elk03:~# apt update root@elk03:~# apt install python-is-python3 root@elk03:~# python generate_log.py /tmp/apps.log 2. 编写配置文件 1 root@elk03:/etc/logstash# vim config/02-file-to-stdout.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 input { # 输入类型是一个file，代表的是文本文件 file { # 指定的文件的路径，可以指定多个 path =\u0026gt; [\u0026#34;/tmp/apps.log\u0026#34;] # 指定从源文件采集的位置，有效值为: beginning, end(默认值)。 start_position =\u0026gt; \u0026#34;beginning\u0026#34; } } filter { mutate { # 对指定字段进行切分，本案例是将message字段按照\u0026#34;|\u0026#34;进行切分 split =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;|\u0026#34; } # 添加字段 add_field =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34;%{[message][0]}\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;%{[message][1]}\u0026#34; \u0026#34;action\u0026#34; =\u0026gt; \u0026#34;%{[message][2]}\u0026#34; \u0026#34;svip\u0026#34; =\u0026gt; \u0026#34;%{[message][3]}\u0026#34; \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;%{[message][4]}\u0026#34; } } mutate { split =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34; \u0026#34;} add_field =\u0026gt; { datetime =\u0026gt; \u0026#34;%{[other][1]} %{[other][2]}\u0026#34; } # 移除字段 remove_field =\u0026gt; [ \u0026#34;message\u0026#34; , \u0026#34;other\u0026#34;, \u0026#34;@version\u0026#34;] } mutate { convert =\u0026gt; { \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;float\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } } } output { # stdout {} elasticsearch {} } 3. 启动实例 1 root@elk03:/etc/logstash# logstash -r -f config/02-file-to-stdout.conf ","date":"2026-10-19","description":"","objectID":"/posts/es-jvm-logstash/","readingTime":3,"tags":["ElasticStack","Elasticsearch","Logstash"],"title":"ElasticStack 实战：ES JVM 优化与 Logstash 环境搭建及日志处理","url":"/posts/es-jvm-logstash/","wordCount":469},{"categories":["DevOps"],"content":"filebeat模块使用 1. filebeat模块概述 filebeat模块是官方提供的一种对各种中间件进行日志采集的解决方案。\n2. 还原nginx的访问日志格式 1 root@elk01:/etc/filebeat# vim /etc/nginx/nginx.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ... #log_format dawn_nginx_json \u0026#39;{\u0026#34;@timestamp\u0026#34;:\u0026#34;$time_iso8601\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;host\u0026#34;:\u0026#34;$server_addr\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;clientip\u0026#34;:\u0026#34;$remote_addr\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;SendBytes\u0026#34;:$body_bytes_sent,\u0026#39; # \u0026#39;\u0026#34;responsetime\u0026#34;:$request_time,\u0026#39; # \u0026#39;\u0026#34;upstreamtime\u0026#34;:\u0026#34;$upstream_response_time\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;upstreamhost\u0026#34;:\u0026#34;$upstream_addr\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;http_host\u0026#34;:\u0026#34;$host\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;uri\u0026#34;:\u0026#34;$uri\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;domain\u0026#34;:\u0026#34;$host\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;xff\u0026#34;:\u0026#34;$http_x_forwarded_for\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;referer\u0026#34;:\u0026#34;$http_referer\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;tcp_xff\u0026#34;:\u0026#34;$proxy_protocol_addr\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;http_user_agent\u0026#34;:\u0026#34;$http_user_agent\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;status\u0026#34;:\u0026#34;$status\u0026#34;}\u0026#39;; #access_log /var/log/nginx/access.log dawn_nginx_json; access_log /var/log/nginx/access.log; ... 1 2 3 root@elk01:/etc/filebeat# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful 3. 重启nginx的配置 1 2 3 4 5 root@elk01:/etc/filebeat# \u0026gt;/var/log/nginx/access.log root@elk01:/etc/filebeat# systemctl reload nginx root@elk01:/etc/filebeat# tail -f /var/log/nginx/access.log 11.0.1.1 - - [18/Oct/2026:20:46:20 +0800] \u0026#34;GET / HTTP/1.1\u0026#34; 200 266 \u0026#34;-\u0026#34; \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0\u0026#34; 11.0.1.1 - - [18/Oct/2026:20:46:20 +0800] \u0026#34;GET /phone.jpg HTTP/1.1\u0026#34; 200 49378 \u0026#34;http://11.0.1.111/\u0026#34; \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0\u0026#34; 4. filebeat启用，禁用和查看模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 root@elk01:/etc/filebeat# ll modules.d/ | egrep \u0026#34;tomcat|nginx\u0026#34; -rw-r--r-- 1 root root 784 May 30 2024 nginx.yml.disabled -rw-r--r-- 1 root root 623 May 30 2024 tomcat.yml.disabled root@elk01:/etc/filebeat# filebeat modules enable nginx tomcat Enabled nginx Enabled tomcat root@elk01:/etc/filebeat# ll modules.d/ | egrep \u0026#34;tomcat|nginx\u0026#34; -rw-r--r-- 1 root root 784 May 30 2024 nginx.yml -rw-r--r-- 1 root root 623 May 30 2024 tomcat.yml root@elk01:/etc/filebeat# filebeat modules list # 查看启用的列表 Enabled: nginx tomcat Disabled: activemq apache auditd aws awsfargate azure barracuda bluecoat cef checkpoint cisco coredns crowdstrike cyberark cyberarkpas cylance elasticsearch envoyproxy f5 fortinet gcp google_workspace googlecloud gsuite haproxy ibmmq icinga iis imperva infoblox iptables juniper kafka kibana logstash microsoft misp mongodb mssql mysql mysqlenterprise nats netflow netscout o365 okta oracle osquery panw pensando postgresql proofpoint rabbitmq radware redis santa snort snyk sonicwall sophos squid suricata system threatintel traefik zeek zookeeper zoom zscaler 1 2 3 4 5 6 root@elk01:/etc/filebeat# filebeat modules disable nginx tomcat Disabled nginx Disabled tomcat root@elk01:/etc/filebeat# ll modules.d/ | egrep \u0026#34;tomcat|nginx\u0026#34; -rw-r--r-- 1 root root 784 May 30 2024 nginx.yml.disabled -rw-r--r-- 1 root root 623 May 30 2024 tomcat.yml.disabled 所谓的启用模块，本质上是将\u0026quot;/etc/filebeat/modules.d/\u0026ldquo;目录下的\u0026rdquo;.yml.disabled\u0026quot;文件更名为\u0026quot;.yml\u0026quot;\n5. 使用模块采集nginx日志 启用nginx模块并修改配置文件\n1 2 3 4 5 6 7 8 9 10 root@elk01:/etc/filebeat# vim modules.d/nginx.yml - module: nginx access: enabled: true var.paths: [\u0026#34;/var/log/nginx/access.log*\u0026#34;] error: enabled: true var.paths: [\u0026#34;/var/log/nginx/error.log*\u0026#34;] ingress_controller: enabled: false 编写filebeat配置文件\n1 root@elk01:/etc/filebeat# vim config/13-modules_nginx-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 filebeat.config.modules: # 注意，此处的\u0026#34;${path.config}\u0026#34;对应的路径是/etc/filebeat目录 path: ${path.config}/modules.d/*.yml # 支持热加载 reload.enabled: true # 指定每间隔多长时间检测一次“${path.config}/modules.d/*.yml” reload.period: 5s #output.console: # pretty: true output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-modules-nginx\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称,和ES最好别冲突 setup.template.name: \u0026#34;dawn-modules\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false 启动filebeat实例\n1 2 3 4 root@elk01:/etc/filebeat# filebeat test config -c config/13-modules_nginx-to-es.yaml Config OK root@elk01:/etc/filebeat# rm -rf /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/13-modules_nginx-to-es.yaml kibana查看数据\n","date":"2026-10-18","description":"","objectID":"/posts/filebeat-modules/","readingTime":3,"tags":["ElasticStack","Filebeat","modules"],"title":"Filebeat 模块（modules）使用实战：启用与 nginx 日志采集","url":"/posts/filebeat-modules/","wordCount":551},{"categories":["DevOps"],"content":"filebeat的filestream解析json格式日志 1. 编写配置文件 1 root@elk01:/etc/filebeat# vim config/10-filestream-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 filebeat.inputs: - type: filestream paths: - /var/log/nginx/access.log # 配置解析 parsers: # 解析json格式 - ndjson: # 将解析的数据放在哪个字段，若为\u0026#34;\u0026#34;代表放在顶级字段中 # target: \u0026#34;zlm\u0026#34; target: \u0026#34;\u0026#34; # 对哪个字段进行解析，若不指定，则默认会对message字段进行解析，并删除该字段。 message_key: message output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-filestream-nginx\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 2. 启动filebeat实例 1 2 root@elk01:/etc/filebeat# rm -fr /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/10-filestream-to-es.yaml filebeat的filestream实现多行匹配 1. 编写配置文件 1 root@elk01:/etc/filebeat# vim config/11-filestream_multiple-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 filebeat.inputs: - type: filestream paths: - /dawn/softwares/apache-tomcat-10.1.25/logs/catalina.out parsers: - multiline: type: pattern pattern: \u0026#39;^\\d{2}\u0026#39; negate: true match: after output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-filestream-tomcat-errorlog\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 2. 启动filebeat实例 1 2 root@elk01:/etc/filebeat# rm -rf /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/11-filestream_multiple-to-es.yaml filestream综合案例 1. 测试文件 1 2 3 4 5 6 7 8 9 root@elk01:/etc/filebeat# vim /tmp/apps.json { \u0026#34;name\u0026#34;: \u0026#34;张三\u0026#34;, \u0026#34;hobby\u0026#34;: [\u0026#34;111\u0026#34;,\u0026#34;222\u0026#34;,\u0026#34;333\u0026#34;] } { \u0026#34;name\u0026#34;: \u0026#34;李四\u0026#34;, \u0026#34;hobby\u0026#34;: [\u0026#34;444\u0026#34;,\u0026#34;555\u0026#34;,\u0026#34;666\u0026#34;] } 2. 编写配置文件 1 root@elk01:/etc/filebeat# vim config/12-filestream_json_multiple_line-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 filebeat.inputs: - type: filestream paths: - /tmp/apps.json parsers: - multiline: type: count count_lines: 4 - ndjson: target: \u0026#34;\u0026#34; message_key: message #output.console: # pretty: true output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-filestream-case-demo\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 3. 启动filebeat实例 1 2 root@elk01:/etc/filebeat# rm -rf /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/12-filestream_json_multiple_line-to-es.yaml ","date":"2026-10-18","description":"","objectID":"/posts/filebeat-filestream/","readingTime":2,"tags":["ElasticStack","Filebeat","filestream"],"title":"Filebeat filestream 采集实战：ndjson 解析、multiline 多行匹配与综合案例","url":"/posts/filebeat-filestream/","wordCount":373},{"categories":["DevOps"],"content":"EFK架构采集tomcat日志 1. 部署tomcat 1 root@elk01:~# wget https://archive.apache.org/dist/tomcat/tomcat-10/v10.1.25/bin/apache-tomcat-10.1.25.tar.gz 2. 创建工作目录 1 root@elk01:~# mkdir /dawn/softwares -pv 3. 解压软件包 1 root@elk01:~# tar xf apache-tomcat-10.1.25.tar.gz -C /dawn/softwares/ 4. 配置tomcat的环境变量 1 root@elk01:~# vim /etc/profile.d/tomcat.sh 1 2 3 4 5 #!/bin/bash export JAVA_HOME=/usr/share/elasticsearch/jdk export TOMCAT_HOME=/dawn/softwares/apache-tomcat-10.1.25 export PATH=$PATH:$JAVA_HOME/bin:$TOMCAT_HOME/bin 1 root@elk01:~# source /etc/profile.d/tomcat.sh 5. 启动tomcat服务 1 2 3 4 5 6 7 8 9 10 root@elk01:~# catalina.sh start Using CATALINA_BASE: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_HOME: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_TMPDIR: /dawn/softwares/apache-tomcat-10.1.25/temp Using JRE_HOME: /usr/share/elasticsearch/jdk Using CLASSPATH: /dawn/softwares/apache-tomcat-10.1.25/bin/bootstrap.jar:/dawn/softwares/apache-tomcat-10.1.25/bin/tomcat-juli.jar Using CATALINA_OPTS: Tomcat started. root@elk01:~# ss -ntl | grep 8080 LISTEN 0 100 *:8080 *:* 6. 访问tomcat服务 http://11.0.1.111:8080/\n7. 查看访问日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 root@elk01:~# cat /dawn/softwares/apache-tomcat-10.1.25/logs/localhost_access_log.2026-10-17.txt 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET / HTTP/1.1\u0026#34; 200 11239 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /tomcat.svg HTTP/1.1\u0026#34; 200 67795 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /tomcat.css HTTP/1.1\u0026#34; 200 5584 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /bg-nav.png HTTP/1.1\u0026#34; 200 1401 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /bg-middle.png HTTP/1.1\u0026#34; 200 1918 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /bg-button.png HTTP/1.1\u0026#34; 200 713 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /asf-logo-wide.svg HTTP/1.1\u0026#34; 200 27235 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /bg-upper.png HTTP/1.1\u0026#34; 200 3103 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /favicon.ico HTTP/1.1\u0026#34; 200 21630 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET / HTTP/1.1\u0026#34; 200 11239 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /tomcat.css HTTP/1.1\u0026#34; 200 5584 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /tomcat.svg HTTP/1.1\u0026#34; 200 67795 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /bg-nav.png HTTP/1.1\u0026#34; 200 1401 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /asf-logo-wide.svg HTTP/1.1\u0026#34; 200 27235 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /bg-upper.png HTTP/1.1\u0026#34; 200 3103 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /bg-button.png HTTP/1.1\u0026#34; 200 713 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /bg-middle.png HTTP/1.1\u0026#34; 200 1918 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /favicon.ico HTTP/1.1\u0026#34; 200 21630 自定义tomcat日志并使用filebeat采集 1. 自定义日志格式 1 root@elk01:~# vim /dawn/softwares/apache-tomcat-10.1.25/conf/server.xml 1 2 3 4 5 6 7 8 9 ...(143行左右) \u0026lt;Host name=\u0026#34;tomcat.dawn.com\u0026#34; appBase=\u0026#34;webapps\u0026#34; unpackWARs=\u0026#34;true\u0026#34; autoDeploy=\u0026#34;true\u0026#34;\u0026gt; \u0026lt;Valve className=\u0026#34;org.apache.catalina.valves.AccessLogValve\u0026#34; directory=\u0026#34;logs\u0026#34; prefix=\u0026#34;tomcat.dawn.com_access_log\u0026#34; suffix=\u0026#34;.json\u0026#34; pattern=\u0026#34;{\u0026amp;quot;clientip\u0026amp;quot;:\u0026amp;quot;%h\u0026amp;quot;,\u0026amp;quot;ClientUser\u0026amp;quot;:\u0026amp;quot;%l\u0026amp;quot;,\u0026amp;quot;authenticated\u0026amp;quot;:\u0026amp;quot;%u\u0026amp;quot;,\u0026amp;quot;AccessTime\u0026amp;quot;:\u0026amp;quot;%t\u0026amp;quot;,\u0026amp;quot;request\u0026amp;quot;:\u0026amp;quot;%r\u0026amp;quot;,\u0026amp;quot;status\u0026amp;quot;:\u0026amp;quot;%s\u0026amp;quot;,\u0026amp;quot;SendBytes\u0026amp;quot;:\u0026amp;quot;%b\u0026amp;quot;,\u0026amp;quot;Query?string\u0026amp;quot;:\u0026amp;quot;%q\u0026amp;quot;,\u0026amp;quot;partner\u0026amp;quot;:\u0026amp;quot;%{Referer}i\u0026amp;quot;,\u0026amp;quot;http_user_agent\u0026amp;quot;:\u0026amp;quot;%{User-Agent}i\u0026amp;quot;}\u0026#34;/\u0026gt; \u0026lt;/Host\u0026gt; 2. 重启tomcat服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@elk01:~# catalina.sh stop Using CATALINA_BASE: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_HOME: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_TMPDIR: /dawn/softwares/apache-tomcat-10.1.25/temp Using JRE_HOME: /usr/share/elasticsearch/jdk Using CLASSPATH: /dawn/softwares/apache-tomcat-10.1.25/bin/bootstrap.jar:/dawn/softwares/apache-tomcat-10.1.25/bin/tomcat-juli.jar Using CATALINA_OPTS: root@elk01:~# catalina.sh start Using CATALINA_BASE: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_HOME: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_TMPDIR: /dawn/softwares/apache-tomcat-10.1.25/temp Using JRE_HOME: /usr/share/elasticsearch/jdk Using CLASSPATH: /dawn/softwares/apache-tomcat-10.1.25/bin/bootstrap.jar:/dawn/softwares/apache-tomcat-10.1.25/bin/tomcat-juli.jar Using CATALINA_OPTS: Tomcat started. 3. 验证日志格式 1 使用curl工具测试 1 root@elk03:/etc/filebeat# curl -H \u0026#39;HOST: tomcat.dawn.com\u0026#39; 11.0.1.111:8080 2 查看日志 1 2 3 4 5 6 root@elk01:~# cat /dawn/softwares/apache-tomcat-10.1.25/logs/ catalina.2026-10-17.log localhost_access_log.2026-10-17.txt catalina.out tomcat.dawn.com_access_log.2026-10-17.json localhost.2026-10-17.log root@elk01:~# cat /dawn/softwares/apache-tomcat-10.1.25/logs/tomcat.dawn.com_access_log.2026-10-17.json {\u0026#34;clientip\u0026#34;:\u0026#34;11.0.1.113\u0026#34;,\u0026#34;ClientUser\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;authenticated\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;AccessTime\u0026#34;:\u0026#34;[17/Oct/2026:11:06:28 +0000]\u0026#34;,\u0026#34;request\u0026#34;:\u0026#34;GET / HTTP/1.1\u0026#34;,\u0026#34;status\u0026#34;:\u0026#34;200\u0026#34;,\u0026#34;SendBytes\u0026#34;:\u0026#34;11239\u0026#34;,\u0026#34;Query?string\u0026#34;:\u0026#34;\u0026#34;,\u0026#34;partner\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;http_user_agent\u0026#34;:\u0026#34;curl/7.81.0\u0026#34;} 4. 编写filebeat采集tomcat日志 1 root@elk01:/etc/filebeat# vim config/07-tomcat-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 filebeat.inputs: - type: log paths: - /dawn/softwares/apache-tomcat-10.1.25/logs/tomcat.dawn.com_access_log*.json # 将message的json格式解析后放在顶级字段中，并删除message字段。 json.keys_under_root: true output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-tomcat\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 5. 启动filebeat 1 root@elk01:/etc/filebeat# filebeat -e -c config/07-tomcat-to-es.yaml filebeat多行匹配采集tomcat启动日志 1. 编写filebeat配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 root@elk01:/etc/filebeat# vim config/08-tomcat_errlog-to-es.yaml filebeat.inputs: - type: log paths: - /dawn/softwares/apache-tomcat-10.1.25/logs/catalina.out multiline: # 指定多行匹配的模式，支持pattern模式 type: pattern # 指定匹配模式 pattern: \u0026#39;^\\d{2}\u0026#39; # 参考链接: https://www.elastic.co/guide/en/beats/filebeat/7.17/multiline-examples.html negate: true match: after output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-tomcat-errorlog\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 2. 启动实例 1 root@elk01:/etc/filebeat# filebeat -e -c config/08-tomcat_errlog-to-es.yaml 3. kibana查看数据 ","date":"2026-10-17","description":"","objectID":"/posts/filebeat-tomcat-log-collection/","readingTime":3,"tags":["ElasticStack","Filebeat","Tomcat日志"],"title":"Filebeat 采集 Tomcat 日志实战：自定义 JSON 访问日志与多行错误日志匹配","url":"/posts/filebeat-tomcat-log-collection/","wordCount":624},{"categories":["DevOps"],"content":"filebeat配置多个input写入到不同的ES索引 1. 编写配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 root@elk01:/etc/filebeat# vim config/09-input_multiple-to-es.yaml # ============================== Filebeat inputs =============================== filebeat.inputs: # 输入 1：采集本地日志文件 /tmp/zlm.log - type: log enabled: true paths: - /tmp/zlm.log # tags 规范为字符串数组，便于 when.contains 精确匹配 tags: [\u0026#34;zlm\u0026#34;] # 多行合并：每 4 行合并为一个事件 multiline: type: count count_lines: 4 # 输入 2：接收 TCP 日志 - type: tcp enabled: true host: \u0026#34;0.0.0.0:9000\u0026#34; tags: [\u0026#34;lmy\u0026#34;] # ====================== Elasticsearch output ====================== output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; # 根据 tags 字段将事件分流到不同索引 indices: - index: \u0026#34;dawn-log-zlm-%{+yyyy.MM.dd}\u0026#34; when: contains: tags: \u0026#34;zlm\u0026#34; - index: \u0026#34;dawn-log-lmy-%{+yyyy.MM.dd}\u0026#34; when: contains: tags: \u0026#34;lmy\u0026#34; # ====================== 索引模板设置 ====================== # 禁用 ILM，否则自定义索引名会被 Filebeat 默认的 ILM 策略覆盖 setup.ilm.enabled: false # 自定义索引模板名称，匹配所有 dawn-log* 索引 setup.template.name: \u0026#34;dawn\u0026#34; setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果模板已存在，是否覆盖。生产环境建议保持 false，避免多实例并发更新模板 setup.template.overwrite: false # 索引分片与副本设置 setup.template.settings: index.number_of_shards: 5 index.number_of_replicas: 0 2. 启动filebeat 1 root@elk01:/etc/filebeat# filebeat -e -c config/09-input_multiple-to-es.yaml 3. 查看kibana数据 filebeat的多实例案例 1. 启动实例1 1 root@elk01:/etc/filebeat# filebeat -e -c config/02-stdin-to-es.yaml 2. 启动实例2 1 root@elk01:~# filebeat -e -c config/03-tcp-to-es.yaml --path.data /tmp/data 3. 查看主机的filebeat进程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 root@elk01:~# ps -ef | grep filebeat | grep -v grep root 18713 18699 0 00:41 pts/1 00:00:00 /usr/share/filebeat/bin/filebeat --path.home /usr/share/filebeat --path.config /etc/filebeat --path.data /var/lib/filebeat --path.logs /var/log/filebeat -e -c config/03-tcp-to-es.yaml --path.data /tmp/data root 18726 1617 0 00:42 pts/0 00:00:00 /usr/share/filebeat/bin/filebeat --path.home /usr/share/filebeat --path.config /etc/filebeat --path.data /var/lib/filebeat --path.logs /var/log/filebeat -e -c config/02-stdin-to-es.yaml root@elk01:~# ll /tmp/data total 16 drwxr-x--- 3 root root 4096 Oct 18 00:41 ./ drwxrwxrwt 17 root root 4096 Oct 18 00:41 ../ -rw------- 1 root root 0 Oct 18 00:41 filebeat.lock -rw------- 1 root root 100 Oct 18 00:41 meta.json drwxr-x--- 3 root root 4096 Oct 18 00:41 registry/ root@elk01:~# ll /var/lib/filebeat/ total 16 drwxr-x--- 3 root root 4096 Oct 18 00:42 ./ drwxr-xr-x 45 root root 4096 Oct 16 17:11 ../ -rw------- 1 root root 0 Oct 18 00:42 filebeat.lock -rw------- 1 root root 100 Oct 16 17:11 meta.json drwxr-x--- 3 root root 4096 Oct 16 17:11 registry/ 使用dpkg安装的包，启动filebeat会自动携带参数，如果我们定义了相同的参数会直接覆盖原来的配置\n","date":"2026-10-17","description":"","objectID":"/posts/filebeat-multi-input-multi-instance/","readingTime":2,"tags":["ElasticStack","Filebeat","多实例部署"],"title":"Filebeat 多 Input 写入不同 ES 索引与多实例部署实战","url":"/posts/filebeat-multi-input-multi-instance/","wordCount":376},{"categories":["DevOps"],"content":"kibana查询和dashboard制作 1.kibana查询PV统计 2.kibana查询IP统计 3.kibana查询带宽统计 4.kibana查询网站状态码统计 5.kibana查询网站站点访问统计 6.kibana的dashboard制作 选好后拖拽形成一张图，然后保存\n","date":"2026-10-17","description":"","objectID":"/posts/kibana-query-dashboard/","readingTime":1,"tags":["ElasticStack","Kibana","可视化"],"title":"Kibana 查询与 Dashboard 制作实战：PV、IP、带宽、状态码统计与可视化面板","url":"/posts/kibana-query-dashboard/","wordCount":8},{"categories":["DevOps"],"content":"自定义nginx的日志格式 1. 安装nginx服务 1 root@elk01:~# apt -y install nginx 2. 修改nginx的日志格式 1 root@elk01:~# vim /etc/nginx/nginx.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... log_format dawn_nginx_json \u0026#39;{\u0026#34;@timestamp\u0026#34;:\u0026#34;$time_iso8601\u0026#34;,\u0026#39; \u0026#39;\u0026#34;host\u0026#34;:\u0026#34;$server_addr\u0026#34;,\u0026#39; \u0026#39;\u0026#34;clientip\u0026#34;:\u0026#34;$remote_addr\u0026#34;,\u0026#39; \u0026#39;\u0026#34;SendBytes\u0026#34;:$body_bytes_sent,\u0026#39; \u0026#39;\u0026#34;responsetime\u0026#34;:$request_time,\u0026#39; \u0026#39;\u0026#34;upstreamtime\u0026#34;:\u0026#34;$upstream_response_time\u0026#34;,\u0026#39; \u0026#39;\u0026#34;upstreamhost\u0026#34;:\u0026#34;$upstream_addr\u0026#34;,\u0026#39; \u0026#39;\u0026#34;http_host\u0026#34;:\u0026#34;$host\u0026#34;,\u0026#39; \u0026#39;\u0026#34;uri\u0026#34;:\u0026#34;$uri\u0026#34;,\u0026#39; \u0026#39;\u0026#34;domain\u0026#34;:\u0026#34;$host\u0026#34;,\u0026#39; \u0026#39;\u0026#34;xff\u0026#34;:\u0026#34;$http_x_forwarded_for\u0026#34;,\u0026#39; \u0026#39;\u0026#34;referer\u0026#34;:\u0026#34;$http_referer\u0026#34;,\u0026#39; \u0026#39;\u0026#34;tcp_xff\u0026#34;:\u0026#34;$proxy_protocol_addr\u0026#34;,\u0026#39; \u0026#39;\u0026#34;http_user_agent\u0026#34;:\u0026#34;$http_user_agent\u0026#34;,\u0026#39; \u0026#39;\u0026#34;status\u0026#34;:\u0026#34;$status\u0026#34;}\u0026#39;; access_log /var/log/nginx/access.log dawn_nginx_json; 1 2 # 将默认的日志格式注释掉 # access_log /var/log/nginx/access.log; 3. 检查nginx的配置文件语法 1 2 3 root@elk01:~# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful 4. 启动nginx服务 1 root@elk01:~# systemctl restart nginx 5. 访问测试 1 2 root@elk01:~# tail -f /var/log/nginx/access.log {\u0026#34;@timestamp\u0026#34;:\u0026#34;2026-10-16T15:53:58+08:00\u0026#34;,\u0026#34;host\u0026#34;:\u0026#34;11.0.1.111\u0026#34;,\u0026#34;clientip\u0026#34;:\u0026#34;11.0.1.1\u0026#34;,\u0026#34;SendBytes\u0026#34;:396,\u0026#34;responsetime\u0026#34;:0.000,\u0026#34;upstreamtime\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;upstreamhost\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;http_host\u0026#34;:\u0026#34;11.0.1.111\u0026#34;,\u0026#34;uri\u0026#34;:\u0026#34;/index.nginx-debian.html\u0026#34;,\u0026#34;domain\u0026#34;:\u0026#34;11.0.1.111\u0026#34;,\u0026#34;xff\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;referer\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;tcp_xff\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;http_user_agent\u0026#34;:\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0\u0026#34;,\u0026#34;status\u0026#34;:\u0026#34;200\u0026#34;} Ubuntu基于nfs实现filebeat配置文件共享 1.另外两个节点也安装filebeat 1 2 root@elk01:~# dpkg -i filebeat-7.17.22-amd64.deb root@elk02:~# dpkg -i filebeat-7.17.22-amd64.deb 2. 所有节点安装nfs工具 1 2 3 4 5 6 7 8 root@elk01:~# apt -y remove needrestart root@elk01:~# apt -y install nfs-kernel-server root@elk02:~# apt -y remove needrestart root@elk02:~# apt -y install nfs-kernel-server root@elk03:~# apt -y remove needrestart root@elk03:~# apt -y install nfs-kernel-server 3. 配置nfs的服务端 1 2 3 4 root@elk01:~# mkdir /etc/filebeat/config root@elk01:~# cat \u0026gt; /etc/exports \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; /etc/filebeat/config *(rw,no_root_squash) EOF 3. 启动nfs服务 1 2 3 4 5 6 7 root@elk01:~# systemctl enable --now nfs-kernel-server Synchronizing state of nfs-kernel-server.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable nfs-kernel-server root@elk01:~# systemctl restart nfs-kernel-server root@elk01:~# exportfs /etc/filebeat/config \u0026lt;world\u0026gt; 4. 客户端测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@elk02:~# mount 11.0.1.111:/etc/filebeat/config/ /mnt/ root@elk02:~# umount /mnt root@elk03:~# mount 11.0.1.111:/etc/filebeat/config/ /mnt/ root@elk03:~# mv config/* /mnt/ root@elk03:~# ll /mnt/ total 28 drwxr-xr-x 2 root root 4096 Oct 16 16:33 ./ drwxr-xr-x 20 root root 4096 Oct 14 15:44 ../ -rw-r--r-- 1 root root 63 Oct 16 13:14 01-stdin-to-stdout.yaml -rw-r--r-- 1 root root 141 Oct 16 13:21 02-stdin-to-es.yaml -rw-r--r-- 1 root root 161 Oct 16 13:35 03-tcp-to-es.yaml -rw-r--r-- 1 root root 729 Oct 16 14:26 04-tcp-to-es-custom-index.yaml -rw-r--r-- 1 root root 772 Oct 16 14:43 05-log_ubuntu_system-to-es.yaml root@elk03:~# umount /mnt 5. 所有客户端挂载 1 2 3 4 5 6 7 8 9 root@elk02:~# mkdir /etc/filebeat/config root@elk02:~# mount 11.0.1.111:/etc/filebeat/config/ /etc/filebeat/config root@elk02:~# cd /etc/filebeat/ root@elk02:/etc/filebeat# root@elk03:~# mkdir /etc/filebeat/config root@elk03:~# mount 11.0.1.111:/etc/filebeat/config/ /etc/filebeat/config root@elk03:~# cd /etc/filebeat/ root@elk03:/etc/filebeat# EFK架构采集nginx日志; 1. 进入到filebeat的配置文件目录 1 2 root@elk01:~# cd /etc/filebeat/ root@elk01:/etc/filebeat# 2. 编写filebeat采集nginx日志 1 root@elk01:/etc/filebeat# vim config/06-nginx-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 filebeat.inputs: - type: log paths: - /var/log/nginx/access.log output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-nginx\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 3. 启动filebeat 1 root@elk01:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml filebeat采集json格式日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 root@elk01:/etc/filebeat# vim config/06-nginx-to-es.yaml filebeat.inputs: - type: log paths: - /var/log/nginx/access.log # 将message的json格式解析后放在顶级字段中，并删除message字段。 json.keys_under_root: true output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-nginx\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 1 root@elk01:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml 基于Nginx七层分流的Web集群部署与Filebeat日志采集 1. elk02节点环境准备 1 安装nginx 1 root@elk02:~# apt -y install nginx 2 修改nginx的主配置文件 1 root@elk01:/etc/filebeat# scp /etc/nginx/nginx.conf 11.0.1.112:/etc/nginx/nginx.conf 3 修改nginx的子配置文件 1 root@elk02:~# vim /etc/nginx/sites-enabled/default 4 准备测试页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 root@elk02:~# vim /var/www/html/index.html \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;/\u0026gt; \u0026lt;title\u0026gt;DAWN’s phone\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; div img { width: 900px; height: 600px; margin: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1 style=\u0026#34;color: red\u0026#34;\u0026gt;手机测试页面 \u0026lt;/h1\u0026gt; \u0026lt;div\u0026gt; \u0026lt;img src=\u0026#34;phone.jpg\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; root@elk02:/var/www/html# ls index.html index.nginx-debian.html phone.jpg 5 重启nginx服务 1 2 3 4 5 6 7 8 9 10 11 root@elk02:~# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful root@elk02:~# systemctl reload nginx root@elk02:~# systemctl restart nginx root@elk02:~# systemctl enable nginx Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable nginx root@elk02:~# ss -ntl | grep 81 LISTEN 0 511 0.0.0.0:81 0.0.0.0:* LISTEN 0 4096 [::]:42815 [::]:* 6 测试访问http://11.0.1.112:81/\n2. elk03节点环境准备 1 安装nginx 1 root@elk03:~# apt -y install nginx 2 修改nginx的主配置文件 1 root@elk01:/etc/filebeat# scp /etc/nginx/nginx.conf 11.0.1.113:/etc/nginx/nginx.conf 3 修改nginx的子配置文件 1 root@elk03:~# vim /etc/nginx/sites-enabled/default 4 准备测试页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 root@elk03:~# vim /var/www/html/index.html \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;/\u0026gt; \u0026lt;title\u0026gt;DAWN’s pc\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; div img { width: 900px; height: 600px; margin: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1 style=\u0026#34;color: green\u0026#34;\u0026gt;电脑测试页面 \u0026lt;/h1\u0026gt; \u0026lt;div\u0026gt; \u0026lt;img src=\u0026#34;pc.jpg\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; root@elk03:/var/www/html# ls index.html index.nginx-debian.html pc.jpg 5 重启nginx服务 1 2 3 4 5 6 7 8 9 10 root@elk03:~# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful root@elk03:~# systemctl reload nginx root@elk03:~# systemctl restart nginx root@elk03:~# systemctl enable nginx Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable nginx root@elk03:~# ss -ntl | grep 82 LISTEN 0 511 0.0.0.0:82 0.0.0.0:* 6 测试访问http://11.0.1.113:82/\n3. elk01负载均衡器配置 1 编写配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 root@elk01:~# vim /etc/nginx/sites-enabled/default upstream phone { server 11.0.1.112:81; } upstream pc { server 11.0.1.113:82; } server { listen 80 ; server_name phone.dawn.com; location / { proxy_pass http://phone; } } server { listen 80 ; server_name pc.dawn.com; location / { proxy_pass http://pc; } } 2 重新加载nginx服务 1 2 3 4 root@elk01:~# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful root@elk01:~# systemctl reload nginx 3 测试访问 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 root@elk03:~# curl -H \u0026#39;host: pc.dawn.com\u0026#39; 11.0.1.111 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;/\u0026gt; \u0026lt;title\u0026gt;DAWN’s pc\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; div img { width: 900px; height: 600px; margin: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1 style=\u0026#34;color: green\u0026#34;\u0026gt;电脑测试页面 \u0026lt;/h1\u0026gt; \u0026lt;div\u0026gt; \u0026lt;img src=\u0026#34;pc.jpg\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 root@elk03:~# curl -H \u0026#39;host: phone.dawn.com\u0026#39; 11.0.1.111 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;/\u0026gt; \u0026lt;title\u0026gt;DAWN’s phone\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; div img { width: 900px; height: 600px; margin: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1 style=\u0026#34;color: red\u0026#34;\u0026gt;手机测试页面 \u0026lt;/h1\u0026gt; \u0026lt;div\u0026gt; \u0026lt;img src=\u0026#34;phone.jpg\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 4.filebeat采集Web集群日志\n1 2 3 root@elk01:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml root@elk02:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml root@elk03:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml 5.windows测试 在**C:\\Windows\\System32\\drivers\\etc/hosts**添加hosts解析。\n1 11.0.1.111 phone.dawn.com pc.dawn.com ","date":"2026-10-16","description":"","objectID":"/posts/filebeat-nginx-log-collection/","readingTime":6,"tags":["ElasticStack","Filebeat","Nginx日志"],"title":"Filebeat 采集 Nginx 日志实战：自定义 JSON 日志格式、NFS 配置共享与七层 Web 集群","url":"/posts/filebeat-nginx-log-collection/","wordCount":1115},{"categories":["DevOps"],"content":"filebeat环境搭建 1. 下载filebeat 希望采集哪个节点数据就在哪个节点部署\n1 root@elk03:~# wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.17.22-amd64.deb 2. 安装filebeat 1 root@elk03:~# dpkg -i filebeat-7.17.22-amd64.deb 3. 查看filebeat的帮助信息 1 root@elk03:~# filebeat -h filebeat的架构 1. 作用 能够采集任意文本类型日志，都可以进行分析。\n2. filebeat的组成部分 3. 案例 1 2 root@elk03:~# mkdir config root@elk03:~# vim config/01-stdin-to-stdout.yaml 1 2 3 4 5 filebeat.inputs: - type: stdin output.console: pretty: true 1 root@elk03:~# filebeat -e -c /root/config/01-stdin-to-stdout.yaml EFK架构测试样例 1. 输入端为stdin 1 root@elk03:~# vim config/02-stdin-to-es.yaml 1 2 3 4 5 filebeat.inputs: - type: stdin output.elasticsearch: hosts: [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] 1 root@elk03:~# filebeat -e -c /root/config/02-stdin-to-es.yaml 2. 输入端为tcp 1 root@elk03:~# vim config/03-tcp-to-es.yaml 1 2 3 4 5 6 filebeat.inputs: - type: tcp host: \u0026#34;0.0.0.0:9000\u0026#34; output.elasticsearch: hosts: [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] 1 root@elk03:~# filebeat -e -c /root/config/03-tcp-to-es.yaml 发送测试数据\n1 root@elk01:~# echo \u0026#34;欢迎来龙餐馆\u0026#34; | nc 11.0.1.113 9000 kibana查看数据\nfilebeat的后端运行的两种方案 方式一：\n1 2 root@elk03:~# filebeat -e -c /root/config/03-tcp-to-es.yaml \u0026amp; root@elk03:~# nohup filebeat -e -c /root/config/03-tcp-to-es.yaml \u0026gt; filebeat.log \u0026amp; 方式二:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cat \u0026gt; /usr/lib/systemd/system/filebeat-tcp-to-es.service \u0026lt;\u0026lt;EOF [Unit] Description=filebeat After=network.target [Service] ExecStart=filebeat -e -c /root/config/03-tcp-to-es.yaml Type=simple [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl start filebeat-tcp-to-es.service filebeat写入ES自定义索引和分片和副本案例 1. 移动到默认的配置文件目录或者创建符号链接（仅仅是为了方便，可选非必要） 1 2 3 root@elk03:~# mv config/ /etc/filebeat/ 或 root@elk03:~# ln -svf /etc/filebeat/config config 2. 编写配置文件并启动filebeat 1 root@elk03:~# vim config/04-tcp-to-es-custom-index.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 filebeat.inputs: - type: tcp host: \u0026#34;0.0.0.0:9000\u0026#34; output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-tcp\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-tcp*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 3 # 设置索引的副本数量 index.number_of_replicas: 0 1 root@elk03:~# filebeat -e -c /root/config/04-tcp-to-es-custom-index.yaml 3. 发送测试数据 1 root@elk01:~# echo 你好，李焕英 | nc 11.0.1.113 9000 4. kibana查看数据 filebeat采集ubuntu系统日志到ES集群 1. 分析 Centos的系统日志： /var/log/message\nUbuntu的系统日志: /var/log/syslog\n其他日志: /var/log/alternatives.log /var/log/auth.log /var/log/bootstrap.log /var/log/cloud-init.log /var/log/cloud-init-output.log /var/log/dpkg.log /var/log/kern.log /var/log/ubuntu-advantage.log /var/log/vmware-network.log /var/log/vmware-vmsvc-root.1.log /var/log/vmware-vmsvc-root.2.log /var/log/vmware-vmsvc-root.3.log /var/log/vmware-vmsvc-root.log /var/log/vmware-vmtoolsd-root.log\n2. 编写配置文件并启动filebeat 1 root@elk03:~# vim config/05-log_ubuntu_system-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 filebeat.inputs: - type: log paths: - /var/log/syslog - /var/log/*.log output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-ubuntu-syslog\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 1 root@elk03:~# filebeat -e -c /root/config/05-log_ubuntu_system-to-es.yaml 3. kibana查看数据 filebeat的log类型是按行读取数据;\nfilebeat会在本地记录采集文本日志的offset;\n1 2 3 4 5 6 7 8 dpkg包部署存储路径： /var/lib/filebeat/ 二进制部署: 在 \u0026#34;./data\u0026#34; 自定义数据路径： 启动时使用\u0026#34;--path.data \u0026#34;指定即可。 ","date":"2026-10-15","description":"","objectID":"/posts/filebeat-deployment/","readingTime":2,"tags":["ElasticStack","Filebeat","日志采集"],"title":"Filebeat 部署与日志采集实战：安装、架构与 stdin/tcp/log 输入","url":"/posts/filebeat-deployment/","wordCount":412},{"categories":["DevOps"],"content":"IP案例 1. 自定义数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 PUT 11.0.1.111:9200/dawn-ip { \u0026#34;mappings\u0026#34;: { \u0026#34;properties\u0026#34;: { \u0026#34;ip_addr\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;ip\u0026#34; } } }, \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 3, \u0026#34;number_of_replicas\u0026#34;: 0 } } 2. 查看自定义类型索引 1 GET 11.0.1.111:9200/dawn-ip 3. 创建测试数据 1 2 3 4 5 6 7 8 9 10 11 12 13 POST 11.0.1.111:9200/_bulk { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;192.168.10.101\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;192.168.10.201\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;172.31.10.100\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;11.0.1.222\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;192.168.20.91\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;11.0.1.113\u0026#34; } 4. 查看IP的网段 1 2 3 4 5 6 7 8 GET 11.0.1.111:9200/dawn-ip/_search { \u0026#34;query\u0026#34;: { \u0026#34;match\u0026#34; : { \u0026#34;ip_addr\u0026#34;: \u0026#34;192.168.0.0/16\u0026#34; } } } 5. 删除索引 1 DELETE 11.0.1.111:9200/dawn-ip date案例 1. 创建索引 1 2 3 4 5 6 7 8 9 10 11 PUT 11.0.1.111:9200/dawn-date { \u0026#34;mappings\u0026#34;: { \u0026#34;properties\u0026#34;: { \u0026#34;birthday\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;date\u0026#34;, \u0026#34;format\u0026#34;: \u0026#34;yyyy-MM-dd\u0026#34; } } } } 2. 创建测试数据 1 2 3 4 5 6 7 8 POST 11.0.1.111:9200/_bulk { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-date\u0026#34;} } { \u0026#34;name\u0026#34;: \u0026#34;father\u0026#34;,\u0026#34;birthday\u0026#34;: \u0026#34;1980-11-01\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-date\u0026#34;} } { \u0026#34;name\u0026#34;: \u0026#34;mother\u0026#34;,\u0026#34;birthday\u0026#34;: \u0026#34;1982-08-01\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-date\u0026#34;} } { \u0026#34;name\u0026#34;: \u0026#34;brother\u0026#34;,\u0026#34;birthday\u0026#34;: \u0026#34;2010-06-01\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-date\u0026#34;} } 3. 查看年龄，从大到小(比较的是数字，数字大的在下面) 1 2 3 4 GET 11.0.1.111:9200/dawn-date/_search { \u0026#34;sort\u0026#34;: { \u0026#34;birthday\u0026#34;: \u0026#34;asc\u0026#34;} } geo_point案例 1. 创建索引 1 2 3 4 5 6 7 8 9 10 PUT 11.0.1.113:9200/dawn-map { \u0026#34;mappings\u0026#34;: { \u0026#34;properties\u0026#34;: { \u0026#34;location\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;geo_point\u0026#34; } } } } 2. 写入地理位置（lat代表纬度,lon代表经度） 1 2 3 4 5 6 7 8 9 10 11 POST 11.0.1.113:9200/_bulk { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 39.914,\u0026#34;lon\u0026#34;: 116.386}} { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 42.210,\u0026#34;lon\u0026#34;: 101.666}} { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 32.850,\u0026#34;lon\u0026#34;: 98.124}} { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 79.850,\u0026#34;lon\u0026#34;: 128.124}} { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 63.850,\u0026#34;lon\u0026#34;: 179.124}} 3. kibana的map展示 ","date":"2026-10-15","description":"","objectID":"/posts/elasticsearch-data-mapping/","readingTime":2,"tags":["ElasticStack","Elasticsearch","映射"],"title":"Elasticsearch 数据映射（ip/date/geo_point）","url":"/posts/elasticsearch-data-mapping/","wordCount":345},{"categories":["DevOps"],"content":"1. 下载kibana 1 root@kibana:~# wget https://artifacts.elastic.co/downloads/kibana/kibana-7.17.22-amd64.deb 2. 安装kibana 1 root@kibana:~# dpkg -i kibana-7.17.22-amd64.deb 3. 修改kibana的配置文件 1 root@kibana:~# vim /etc/kibana/kibana.yml 1 2 3 4 5 6 7 8 # 监听的端口 server.port: 5601 # 监听的地址 server.host: \u0026#34;0.0.0.0\u0026#34; # 指定ES集群地址 elasticsearch.hosts: [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] # 国际化语言 i18n.locale: \u0026#34;zh-CN\u0026#34; 4. 启动kibana 1 2 3 root@kibana:~# systemctl enable --now kibana.service root@kibana:~# ss -ntl | grep 5601 LISTEN 0 511 0.0.0.0:5601 0.0.0.0:* 5. 访问kibana的WebUI http://11.0.1.114:5601/\n","date":"2026-10-14","description":"","objectID":"/posts/kibana-setup/","readingTime":1,"tags":["ElasticStack","Kibana","可视化"],"title":"Kibana 环境搭建与配置：从 deb 安装到接入 Elasticsearch 集群","url":"/posts/kibana-setup/","wordCount":67},{"categories":["DevOps"],"content":"1. 核心配置项 action.destructive_requires_name\n这个配置项决定了删除索引时是否必须指定明确的索引名称。\ntrue（推荐，ES 7.x 及以后版本的默认值）：必须指定明确索引名，禁止使用通配符（*）或 _all 删除。\nfalse 允许使用通配符或 _all 删除。\n2. 动态配置方式（推荐，无需重启） 通过 API 动态修改集群设置，立即生效：\n1 2 3 4 5 6 PUT 11.0.1.111:9200/_cluster/settings { \u0026#34;persistent\u0026#34;: { \u0026#34;action.destructive_requires_name\u0026#34;: true } } persistent：持久生效，重启 ES 后依然有效\ntransient：临时生效，集群重启后失效\n3. 静态配置方式（修改配置文件） 在每个节点的/etc/elasticsearch/elasticsearch.yml中添加：\n1 action.destructive_requires_name: true 修改后需要滚动重启整个集群生效，这种方法比动态设置更底层、更安全\n4. 验证配置效果 尝试通配符删除（报错）\n1 DELETE 11.0.1.111:9200/dawn* 尝试明确名称删除（成功）\n1 DELETE 11.0.1.111:9200/dawn ","date":"2026-10-14","description":"","objectID":"/posts/elasticsearch-destructive-requires-name/","readingTime":1,"tags":["ElasticStack","Elasticsearch","集群配置"],"title":"Elasticsearch 集群安全配置：action.destructive_requires_name 防误删保护","url":"/posts/elasticsearch-destructive-requires-name/","wordCount":54},{"categories":["DevOps"],"content":"ES单个文档管理 文档创建 1. 文档的创建(推荐) 1 2 3 4 5 6 7 8 9 POST 11.0.1.113:9200/dawn/doc { \u0026#34;name\u0026#34;: \u0026#34;zlm\u0026#34;, \u0026#34;hobby\u0026#34;: [ \u0026#34;吃饭\u0026#34;, \u0026#34;睡觉\u0026#34;, \u0026#34;上课\u0026#34; ] } 2. 文档的创建指定文档id(不推荐，可能存在id冲突问题) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 POST 11.0.1.113:9200/dawn/doc/2300160645 { \u0026#34;school\u0026#34;: \u0026#34;czu\u0026#34;, \u0026#34;class\u0026#34;: \u0026#34;计算机001\u0026#34;, \u0026#34;student\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;dark\u0026#34;, \u0026#34;hobby\u0026#34;: [ \u0026#34;打球\u0026#34;, \u0026#34;看书\u0026#34; ] }, { \u0026#34;name\u0026#34;: \u0026#34;ocean\u0026#34;, \u0026#34;hobby\u0026#34;: [ \u0026#34;唱歌\u0026#34;, \u0026#34;rap\u0026#34;, \u0026#34;篮球\u0026#34; ] } ] } 文档查看 1. 查看所有的文档 1 GET 11.0.1.113:9200/dawn/_search 2. 查看指定的文档 1 GET 11.0.1.113:9200/dawn/doc/2300160645 3. 基于DSL语句查询 1 2 3 4 5 6 7 8 POST 11.0.1.113:9200/dawn/_search { \u0026#34;query\u0026#34;: { \u0026#34;match\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;zlm\u0026#34; } } } 文档修改和删除 1. 修改文档 1 2 3 4 5 6 POST 11.0.1.113:9200/dawn/doc/2300160645/_update { \u0026#34;doc\u0026#34;:{ \u0026#34;school\u0026#34; : \u0026#34;thu\u0026#34; } } 2. 删除文档 1 DELETE 11.0.1.113:9200/dawn/doc/2300160645 ES文档批量操作 文档批量创建（注意最后有一个换行） 只能采用扁平化的方式去写，不能采用Beautify的方式去写\n1 2 3 4 5 6 7 POST 11.0.1.113:9200/_bulk { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;张三\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;1\u0026#34;,\u0026#34;2\u0026#34;,\u0026#34;3\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;李四\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;4\u0026#34;,\u0026#34;5\u0026#34;,\u0026#34;6\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;王五\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;7\u0026#34;,\u0026#34;8\u0026#34;,\u0026#34;9\u0026#34;]} 文档批量查看 1 2 3 4 5 6 7 8 9 10 11 12 13 POST 11.0.1.113:9200/_mget { \u0026#34;docs\u0026#34;: [ { \u0026#34;_index\u0026#34;: \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34;: \u0026#34;854sPqEBMns9ROCbUcEr\u0026#34; }, { \u0026#34;_index\u0026#34;: \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34;: \u0026#34;8p4sPqEBMns9ROCbUcEq\u0026#34; } ] } 文档批量修改（注意最后有一个换行） 1 2 3 POST 11.0.1.113:9200/_bulk { \u0026#34;update\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;,\u0026#34;_id\u0026#34; : \u0026#34;9J4sPqEBMns9ROCbUcEr\u0026#34;} } { \u0026#34;doc\u0026#34; : {\u0026#34;age\u0026#34; : 30, \u0026#34;hobby\u0026#34;: [\u0026#34;10\u0026#34;,\u0026#34;11\u0026#34;,\u0026#34;12\u0026#34;]} } 文档批量删除（注意最后有一个换行） 1 2 3 4 POST 11.0.1.113:9200/_bulk { \u0026#34;delete\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34; : \u0026#34;854sPqEBMns9ROCbUcEr\u0026#34; } } { \u0026#34;delete\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34; : \u0026#34;8p4sPqEBMns9ROCbUcEq\u0026#34; } } { \u0026#34;delete\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34; : \u0026#34;9J4sPqEBMns9ROCbUcEr\u0026#34; } } 对于文档的批量操作而言，使用\u0026quot;_bulk\u0026quot;的API实现数据的写操作(创建，修改，删除)，使用\u0026quot;_mget\u0026quot;实现读取操作。\n","date":"2026-10-14","description":"","objectID":"/posts/elasticsearch-document-api-management/","readingTime":2,"tags":["ElasticStack","Elasticsearch","文档管理"],"title":"Elasticsearch 文档 API 管理：单文档与批量操作","url":"/posts/elasticsearch-document-api-management/","wordCount":282},{"categories":["DevOps"],"content":"索引创建和查看 1. 创建索引 1 PUT 11.0.1.111:9200/dawn 默认创建一个分片和一个副本\n2. 查看索引 查看单个索引\n1 GET 11.0.1.111:9200/dawn 查看所有索引\n1 GET 11.0.1.111:9200/_cat/indices?v 3. 创建索引自定义分片数量 1 2 3 4 5 6 PUT 11.0.1.111:9200/dawn-01 { \u0026#34;settings\u0026#34;:{ \u0026#34;number_of_shards\u0026#34;: 3 } } 4. 创建索引自定义分片副本数量 1 2 3 4 5 6 7 PUT 11.0.1.111:9200/dawn-02 { \u0026#34;settings\u0026#34;:{ \u0026#34;number_of_shards\u0026#34;: 5, \u0026#34;number_of_replicas\u0026#34;: 0 } } 索引修改和删除 1. 修改索引的副本数量 1 2 3 4 PUT 11.0.1.111:9200/dawn-02/_settings { \u0026#34;number_of_replicas\u0026#34;: 2 } 2. 删除指定索引 1 DELETE\t11.0.1.111:9200/dawn-02 3. 批量删除索引数据 1 DELETE 11.0.1.111:9200/dawn* 一旦删除了索引，意味着该索引下的所有文档全部删除！\n索引开启和关闭 1. 关闭索引 1 POST 11.0.1.111:9200/dawn/_close 2. 开启索引 1 POST 11.0.1.111:9200/dawn/_open 一旦一个索引被关闭，则意味着该索引不可访问;\n被关闭的索引数据并不会丢失;\n默认情况下，ES的索引都是处于开启状态。\n索引模板 1. 索引模板介绍 索引模板就是创建索引时要遵循的模板规则。\n索引模板仅对新创建的索引有效，已经创建的索引并不受索引模板的影响。\n2. 查看所有的索引模板 1 GET 11.0.1.111:9200/_index_template 3. 创建自定义索引模板 1 2 3 4 5 6 7 8 9 10 11 12 POST 11.0.1.111:9200/_index_template/dawn { \u0026#34;index_patterns\u0026#34;: [ \u0026#34;dawn*\u0026#34; ], \u0026#34;template\u0026#34;: { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 5, \u0026#34;number_of_replicas\u0026#34;: 0 } } } 4. 查看单个索引模板 1 GET 11.0.1.111:9200/_index_template/dawn 5. 创建测试的索引 1 PUT 11.0.1.111:9200/dawn-01 6. 修改索引模板 1 2 3 4 5 6 7 8 9 10 11 12 PUT 11.0.1.111:9200/_index_template/dawn { \u0026#34;index_patterns\u0026#34;: [ \u0026#34;dawn*\u0026#34; ], \u0026#34;template\u0026#34;: { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 3, \u0026#34;number_of_replicas\u0026#34;: 2 } } } 7. 再次创建索引 1 PUT 11.0.1.111:9200/dawn-02 8. 删除索引模板 1 DELETE 11.0.1.111:9200/_index_template/dawn 组件模板和索引模板联合使用 1. 查看现有的组件模板 1 GET 11.0.1.111:9200/_component_template/ 2. 创建自定义的组件模板 1 2 3 4 5 6 7 8 POST 11.0.1.111:9200/_component_template/dark { \u0026#34;template\u0026#34;: { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 10 } } } 3. 查看单个组件模板 1 GET 11.0.1.111:9200/_component_template/dark 4. 创建索引模板指定组件模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST 11.0.1.111:9200/_index_template/dawn { \u0026#34;index_patterns\u0026#34;: [ \u0026#34;dawn*\u0026#34; ], \u0026#34;composed_of\u0026#34;: [ \u0026#34;dark\u0026#34; ], \u0026#34;template\u0026#34;: { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_replicas\u0026#34;: 2 } } } 5. 创建测试索引 1 PUT 11.0.1.111:9200/dawn-03 索引别名的创建 1. 索引别名介绍 所谓的索引别名，其实就是给索引起名字。\n其应用场景可以将多个不同的索引打上相同的别名，将来基于别名进行查询，就可以将这些不同索引的数据一起查询。\n2. 准备测试数据 1 2 3 4 5 6 7 8 9 10 11 12 13 POST 11.0.1.111:9200/_bulk { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-235\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小张\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;1\u0026#34;,\u0026#34;2\u0026#34;,\u0026#34;3\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-235\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小王\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;4\u0026#34;,\u0026#34;5\u0026#34;,\u0026#34;6\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-236\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小赵\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;7\u0026#34;,\u0026#34;8\u0026#34;,\u0026#34;9\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-231\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小李\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;10\u0026#34;,\u0026#34;11\u0026#34;,\u0026#34;12\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-232\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小黄\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;13\u0026#34;,\u0026#34;14\u0026#34;,\u0026#34;15\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-234\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小陈\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;16\u0026#34;,\u0026#34;17\u0026#34;,\u0026#34;18\u0026#34;]} 3. 添加别名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 POST 11.0.1.111:9200/_aliases { \u0026#34;actions\u0026#34;: [ { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-235\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;czu\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-234\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;czu\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-231\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;ok\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-232\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;ok\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-236\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;ok\u0026#34; } } ] } 4. kibana基于别名查询数据 索引别名的查看、删除和修改 1. 查看所有索引的别名信息 1 GET 11.0.1.111:9200/_alias 2. 查看某个索引的别名信息 1 GET 11.0.1.111:9200/czu-235/_alias 3. 删除索引别名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST 11.0.1.111:9200/_aliases { \u0026#34;actions\u0026#34;: [ { \u0026#34;remove\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-235\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;czu\u0026#34; } }, { \u0026#34;remove\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-234\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;czu\u0026#34; } } ] } 删除前\n删除后\n4. 修改别名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST 11.0.1.111:9200/_aliases { \u0026#34;actions\u0026#34;: [ { \u0026#34;remove\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-231\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;ok\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-231\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;no\u0026#34; } } ] } 修改前\n修改后\n","date":"2026-10-14","description":"","objectID":"/posts/elasticsearch-index-api-management/","readingTime":3,"tags":["ElasticStack","Elasticsearch","索引管理"],"title":"Elasticsearch 索引 API 管理","url":"/posts/elasticsearch-index-api-management/","wordCount":540},{"categories":["DevOps"],"content":"ElasticStack ElasticSearch，简称:\u0026ldquo;es\u0026rdquo;\n数据存储 数据查询 filebeat:\n采集数据 kibana:\n用于出图展示 logstash:\n日志分析处理。 zookeeper:\n分布式协调服务，用于协调其他服务组件。 kafka:\n分布式消息队列，可以缓存数据。依赖zk实现元数据管理。 ElasticSearch单点部署 1. 下载软件包 1 root@elk01:~# wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.22-amd64.deb 2. 安装ES服务 1 root@elk01:~# dpkg -i elasticsearch-7.17.22-amd64.deb 3. 修改配置文件 1 root@elk01:~# vim /etc/elasticsearch/elasticsearch.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 指定集群的名称，每个ES集群的名称要唯一 cluster.name: dawn # 数据的存储路径 path.data: /var/lib/elasticsearch # 日志的存储路径 path.logs: /var/log/elasticsearch # 指定监听的地址 network.host: 0.0.0.0 # 指定ES集群监听的端口号 http.port: 9200 # 数据传输端口 transport.port: 9300 # 部署集群为单点类型 discovery.type: \u0026#34;single-node\u0026#34; 4. 启动ES服务 1 root@elk01:~# systemctl enable --now elasticsearch.service 5. 验证ES服务是否启动成功 1 2 3 root@elk01:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* 6. 访问ES的WebUI 7. 查看集群的节点信息 1 2 3 root@elk01:~# curl 11.0.1.111:9200/_cat/nodes?v ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 11.0.1.111 58 65 0 2.94 1.36 0.53 cdfhilmrstw * elk01 ElasticSearch单点卸载 1. 停止es服务 1 root@elk01:~# systemctl stop elasticsearch.service 2. 卸载软件 1 2 3 4 5 root@elk01:~# dpkg -r elasticsearch (Reading database ... 76508 files and directories currently installed.) Removing elasticsearch (7.17.22) ... Stopping elasticsearch service... OK Deleting log directory... OK 此步骤只会清理日志文件，卸载脚本，删除ES软件等操作，并不会删除ES的数据。\n3. 清理数据(生产环境不建议做，谨慎，数据需要备份或者迁移) 1 root@elk01:~# rm -rf /var/lib/elasticsearch/* /tmp/* Elasticsearch集群搭建 1. 下载ES软件包 将下载好的软件包拷贝到其他两个节点\n1 2 root@elk01:~# scp elasticsearch-7.17.22-amd64.deb 11.0.1.112:~ root@elk01:~# scp elasticsearch-7.17.22-amd64.deb 11.0.1.113:~ 2. 修改各节点的时区 1 2 3 4 5 6 root@elk01:~# ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \u0026#39;/etc/localtime\u0026#39; -\u0026gt; \u0026#39;/usr/share/zoneinfo/Asia/Shanghai\u0026#39; root@elk02:~# ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \u0026#39;/etc/localtime\u0026#39; -\u0026gt; \u0026#39;/usr/share/zoneinfo/Asia/Shanghai\u0026#39; root@elk03:~# ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \u0026#39;/etc/localtime\u0026#39; -\u0026gt; \u0026#39;/usr/share/zoneinfo/Asia/Shanghai\u0026#39; 3. 安装ES服务 1 2 3 root@elk01:~# dpkg -i elasticsearch-7.17.22-amd64.deb root@elk02:~# dpkg -i elasticsearch-7.17.22-amd64.deb root@elk03:~# dpkg -i elasticsearch-7.17.22-amd64.deb 4. 修改ES集群的配置文件 1 2 3 4 5 6 7 8 9 10 11 [root@elk91 ~]# vim /etc/elasticsearch/elasticsearch.yml cluster.name: dawn path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch network.host: 0.0.0.0 http.port: 9200 transport.port: 9300 # 配置ES集群的服务发现列表主机 discovery.seed_hosts: [\u0026#34;11.0.1.111\u0026#34;,\u0026#34;11.0.1.112\u0026#34;,\u0026#34;11.0.1.113\u0026#34;] # 配置ES集群启动时参与master选举的节点 cluster.initial_master_nodes: [\u0026#34;11.0.1.111\u0026#34;,\u0026#34;11.0.1.112\u0026#34;,\u0026#34;11.0.1.113\u0026#34;] 5. 将配置文件拷贝到其他节点 1 2 3 4 5 6 root@elk01:~# scp /etc/elasticsearch/elasticsearch.yml 11.0.1.112:/etc/elasticsearch/elasticsearch.yml root@11.0.1.112\u0026#39;s password: elasticsearch.yml 100% 3641 4.1MB/s 00:00 root@elk01:~# scp /etc/elasticsearch/elasticsearch.yml 11.0.1.113:/etc/elasticsearch/elasticsearch.yml root@11.0.1.113\u0026#39;s password: elasticsearch.yml 100% 3641 5.0MB/s 00:00 6. 启动ES集群 1 2 3 root@elk01:~# systemctl enable --now elasticsearch.service root@elk02:~# systemctl enable --now elasticsearch.service root@elk03:~# systemctl enable --now elasticsearch.service 7. 检查ES服务端口是否存在 1 2 3 4 5 6 7 8 9 root@elk01:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* root@elk02:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* root@elk03:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* 8. 查看集群的节点信息 1 2 3 4 5 6 7 8 9 10 11 root@elk01:~# curl -s 11.0.1.111:9200 | grep cluster_uuid \u0026#34;cluster_uuid\u0026#34; : \u0026#34;JwoG2sV0RuOGHqWYzSWVvw\u0026#34;, root@elk01:~# curl -s 11.0.1.112:9200 | grep cluster_uuid \u0026#34;cluster_uuid\u0026#34; : \u0026#34;JwoG2sV0RuOGHqWYzSWVvw\u0026#34;, root@elk01:~# curl -s 11.0.1.113:9200 | grep cluster_uuid \u0026#34;cluster_uuid\u0026#34; : \u0026#34;JwoG2sV0RuOGHqWYzSWVvw\u0026#34;, root@elk01:~# curl -s 11.0.1.111:9200/_cat/nodes 11.0.1.112 25 96 3 0.00 0.10 0.07 cdfhilmrstw * elk02 11.0.1.113 34 96 2 0.00 0.08 0.06 cdfhilmrstw - elk03 11.0.1.111 36 97 3 0.01 0.04 0.17 cdfhilmrstw - elk01 ","date":"2026-10-14","description":"","objectID":"/posts/elastic-stack/","readingTime":3,"tags":["ElasticStack","Elasticsearch","集群"],"title":"ElasticStack 架构设计与 Elasticsearch 集群部署","url":"/posts/elastic-stack/","wordCount":470},{"categories":["DevOps"],"content":"一.数据库服务用户管理 1. 创建数据库用户 用户信息由两部分组成：'用户名'@'主机域/白名单'\n用户名：用户字符串标识； 主机域（白名单）：限制允许从哪些客户端 IP 或主机连接该用户。连接主机的客户端 IP 必须符合白名单，否则拒绝握手。 主机域白名单的编写形式：\n类型 示例 覆盖范围与说明 本地限定 root@localhost 仅允许本机通过本地 Socket 连接登录 单机限定 root@'11.0.1.101' 仅允许指定的某台 IP 主机访问 网段（掩码形式） root@'11.0.1.0/255.255.255.0' 允许 11.0.1.1 ~ 11.0.1.254 网段访问。注意：VLSM 子网掩码需长格式指明 网段（通配符形式） root@'11.0.1.%' 匹配该 C 段所有地址 大网段 root@'11.0.%' 匹配 11.0.0.1 ~ 11.0.255.254 整个 B 段 部分 IP 通配 root@'11.0.1.5%' 匹配 11.0.1.50 ~ 11.0.1.59 任意主机 root@'%' 允许所有外部网络主机访问 1 2 mysql\u0026gt; create user dawn@\u0026#39;11.0.1.%\u0026#39; identified by \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.03 sec) 2. 查询数据库用户信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -- 查询所有用户、主机域及加密认证串 mysql\u0026gt; SELECT user, host, authentication_string FROM mysql.user; +------------------+-----------+------------------------------------------------------------------------+ | user | host | authentication_string | +------------------+-----------+------------------------------------------------------------------------+ | dawn | 11.0.1.% | $A$005$Oe@d?4 6UQC h%/XEVaaydjHRpm98F7Zj0Yxrpgf0oz09INWeSa2l.AN7 | | mysql.infoschema | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.session | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.sys | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | root | localhost | $A$005$iqraslXlVV;\\_ z_@dB52NA2pwaK37yFR1GffitWH9mHsG.mAT36BqSDhzQL0wA | +------------------+-----------+------------------------------------------------------------------------+ 5 rows in set (0.00 sec) -- 查看当前登录会话的用户信息 mysql\u0026gt; SELECT user(); +----------------+ | user() | +----------------+ | root@localhost | +----------------+ 1 row in set (0.00 sec) 3. 修改数据库用户密码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 mysql\u0026gt; ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED BY \u0026#39;000000\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; SELECT user, host, authentication_string FROM mysql.user; +------------------+-----------+------------------------------------------------------------------------+ | user | host | authentication_string | +------------------+-----------+------------------------------------------------------------------------+ | dawn | 11.0.1.% | $A$005$ -4w8;QNe]R\u0026#34;D`S/SUP8EwHTypF11Yu0sZCn.3tVzPbHrnEdFFm7WlZOjWEQ8 | | mysql.infoschema | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.session | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.sys | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | root | localhost | $A$005$iqraslXlVV;\\_ z_@dB52NA2pwaK37yFR1GffitWH9mHsG.mAT36BqSDhzQL0wA | +------------------+-----------+------------------------------------------------------------------------+ 5 rows in set (0.00 sec) 4. 删除数据库用户信息 生产规范：在生产环境中严禁随意直接物理删除用户，建议对不再使用的用户进行账号锁定（Lock）。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 mysql\u0026gt; SELECT user, host, authentication_string FROM mysql.user; +------------------+-----------+------------------------------------------------------------------------+ | user | host | authentication_string | +------------------+-----------+------------------------------------------------------------------------+ | dawn | 11.0.1.% | $A$005$ -4w8;QNe]R\u0026#34;D`S/SUP8EwHTypF11Yu0sZCn.3tVzPbHrnEdFFm7WlZOjWEQ8 | | mysql.infoschema | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.session | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.sys | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | root | localhost | $A$005$iqraslXlVV;\\_ z_@dB52NA2pwaK37yFR1GffitWH9mHsG.mAT36BqSDhzQL0wA | +------------------+-----------+------------------------------------------------------------------------+ 5 rows in set (0.00 sec) mysql\u0026gt; DROP USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; SELECT user, host, authentication_string FROM mysql.user; +------------------+-----------+------------------------------------------------------------------------+ | user | host | authentication_string | +------------------+-----------+------------------------------------------------------------------------+ | mysql.infoschema | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.session | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.sys | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | root | localhost | $A$005$iqraslXlVV;\\_ z_@dB52NA2pwaK37yFR1GffitWH9mHsG.mAT36BqSDhzQL0wA | +------------------+-----------+------------------------------------------------------------------------+ 4 rows in set (0.00 sec) 5. 锁定与解锁数据库用户 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 -- 1. 锁定用户 mysql\u0026gt; ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; ACCOUNT LOCK; Query OK, 0 rows affected (0.00 sec) -- 2. 检查锁定状态（account_locked 列：Y 表示锁定，N 表示正常） mysql\u0026gt; SELECT user, host, account_locked FROM mysql.user; +------------------+-----------+----------------+ | user | host | account_locked | +------------------+-----------+----------------+ | dawn | 11.0.1.% | Y | | mysql.infoschema | localhost | Y | | mysql.session | localhost | Y | | mysql.sys | localhost | Y | | root | localhost | N | +------------------+-----------+----------------+ 5 rows in set (0.00 sec) -- 被锁定后登录测试，将报错拒绝访问： [root@master ~]#mysql -h 11.0.1.101 -P 3306 -u dawn -p Enter password: ERROR 3118 (HY000): Access denied for user \u0026#39;dawn\u0026#39;@\u0026#39;master\u0026#39;. Account is locked. -- 3. 解锁用户 [root@master ~]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 11 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; ACCOUNT UNLOCK; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; exit Bye [root@master ~]#mysql -h 11.0.1.101 -P 3306 -u dawn -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 12 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. 6. MySQL 5.7 与 8.0 用户管理核心特性区别 区别一：创建与授权语法解耦 MySQL 5.7 及早期版本：一条 GRANT 命令可同时隐式完成用户创建、密码设置与权限授予：\n1 GRANT ALL ON *.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; MySQL 8.0：系统字典全部改为 InnoDB 引擎管理，权限更严格，必须先 CREATE USER 创建用户，再执行 GRANT 授权；GRANT 命令废弃了 IDENTIFIED BY 子句：\n1 2 CREATE USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; GRANT ALL ON *.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; 区别二：默认密码加密插件变更 早期版本（5.7）：默认使用 mysql_native_password，兼容性好； 新版本（8.0+）：默认采用 caching_sha2_password，安全性更高，但会导致旧版客户端软件（如旧版 Navicat、旧驱动）报错无法登录。 应对方案：\n方案一：将用户或全局加密插件调整回 mysql_native_password 进行向下兼容：\n1 2 3 4 5 -- 方法 1：新建用户时指定兼容插件 CREATE USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED WITH mysql_native_password BY \u0026#39;123456\u0026#39;; -- 方法 2：修改已有用户的加密插件 ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED WITH mysql_native_password BY \u0026#39;123456\u0026#39;; 1 2 3 # 方法 3：修改 /etc/my.cnf 全局默认配置 [mysqld] default_authentication_plugin=mysql_native_password 方案二：升级应用系统的数据库连接驱动版本，全面支持 caching_sha2_password。\n二. 数据库服务权限管理 1. 核心权限与系统视图 查看数据库支持的所有权限类型：\n1 SHOW PRIVILEGES; 序号 输出列名 解释说明 01 Privilege 授权权限名称（如 SELECT, INSERT 等） 02 Context 权限作用的资源范围级别（Databases, Tables, Columns 等） 03 Comment 对该权限实际功能的描述说明 核心关键权限详解：\n序号 权限名称 授权资源 解释说明 01 Select Tables 查询数据表中的数据 02 Insert Tables 插入/写入新数据 03 Update Tables 修改已存在的数据 04 Delete Tables 删除数据记录 05 Alter Tables 修改表结构（添加/修改字段、索引属性等） 06 Index Tables 创建或删除索引 07 Create Databases, Tables 创建新数据库或数据表 08 Drop Databases, Tables 删除数据库或数据表（高危操作） 注：ALL PRIVILEGES 拥有除 GRANT OPTION（给其他用户赋权能力）之外的所有权限；普通管理员切忌赋予 WITH GRANT OPTION。\n权限在元数据表中的存储层级：\n序号 授权表名称 存储范围与级别 01 mysql.user 用户全局级别权限（*.*），最高作用域 02 mysql.db 数据库级别权限（db.*） 03 mysql.tables_priv 数据表级别权限（db.table） 04 mysql.columns_priv 字段列级别权限（细粒度） 2. 用户授权与权限回收实战 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -- 授权基本语法： -- GRANT 权限列表 ON 作用范围 TO \u0026#39;用户\u0026#39;@\u0026#39;主机域\u0026#39;; -- 1. 全局授权（*.*） GRANT ALL ON *.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; -- 2. 库级授权（dawn.*） GRANT SELECT, INSERT, UPDATE, DELETE ON dawn.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; -- 3. 表级授权（dawn.t1） GRANT SELECT ON dawn.t1 TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; -- 4. 查看指定用户的权限 SHOW GRANTS FOR \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; -- 5. 回收权限 REVOKE DELETE ON *.* FROM \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; 用户常规授权实战（最小权限原则）：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -- 业务场景对应权限： -- 浏览页面 -\u0026gt; SELECT -- 发表内容 -\u0026gt; INSERT -- 修改内容 -\u0026gt; UPDATE -- 删除内容 -\u0026gt; DELETE -- 创建独立业务库并按最小权限赋权 mysql\u0026gt; CREATE DATABASE dawn; Query OK, 1 row affected (0.00 sec) mysql\u0026gt; USE dawn; Database changed mysql\u0026gt; CREATE TABLE t1 ( -\u0026gt; id INT PRIMARY KEY AUTO_INCREMENT, -\u0026gt; name VARCHAR(50) -\u0026gt; ); Query OK, 0 rows affected (0.03 sec) mysql\u0026gt; create user dawn@\u0026#39;11.0.1.%\u0026#39; identified by \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; GRANT SELECT, INSERT, UPDATE, DELETE ON dawn.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; Query OK, 0 rows affected (0.00 sec) 3. MySQL 8.0 新特性：Role（角色）管理 8.0 引入了角色（Role）机制，允许将一组常用权限打包为一个逻辑角色实体，批量赋予多个用户，极大简化了用户权限维护成本。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 -- 1. 创建角色（可创建读写角色与只读角色） mysql\u0026gt; CREATE ROLE \u0026#39;dawn_rw\u0026#39;, \u0026#39;dawn_r\u0026#39;; Query OK, 0 rows affected (0.00 sec) -- 2. 为角色分配权限 mysql\u0026gt; GRANT SELECT ON dawn.* TO \u0026#39;dawn_r\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; GRANT SELECT, UPDATE, INSERT, DELETE ON dawn.* TO \u0026#39;dawn_rw\u0026#39;; Query OK, 0 rows affected (0.00 sec) -- 3. 创建测试用户并将角色赋予用户 mysql\u0026gt; CREATE USER \u0026#39;user01\u0026#39;@\u0026#39;%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; CREATE USER \u0026#39;user02\u0026#39;@\u0026#39;%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; GRANT \u0026#39;dawn_r\u0026#39; TO \u0026#39;user01\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; GRANT \u0026#39;dawn_rw\u0026#39; TO \u0026#39;user02\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) -- 4. 激活角色 -- 方式一：管理员指定默认生效角色 mysql\u0026gt; SET DEFAULT ROLE ALL TO \u0026#39;user01\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; SET DEFAULT ROLE ALL TO \u0026#39;user02\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) -- 方式二：全局配置自动激活角色（登录时生效） SET GLOBAL activate_all_roles_on_login = ON; -- 5. 验证角色与权限拦截 -- 以 user01 登录后查看激活角色： mysql\u0026gt; SELECT CURRENT_USER(), CURRENT_ROLE(); +----------------+----------------+ | CURRENT_USER() | CURRENT_ROLE() | +----------------+----------------+ | user01@% | `dawn_r`@`%` | +----------------+----------------+ 1 row in set (0.00 sec) -- 此时 user01 执行 INSERT 操作将被严格拦截： mysql\u0026gt; USE dawn; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql\u0026gt; SHOW TABLES; +----------------+ | Tables_in_dawn | +----------------+ | t1 | +----------------+ 1 row in set (0.00 sec) mysql\u0026gt; SELECT * FROM t1; Empty set (0.00 sec) mysql\u0026gt; INSERT INTO t1 VALUES (1); ERROR 1142 (42000): INSERT command denied to user \u0026#39;user01\u0026#39;@\u0026#39;master\u0026#39; for table \u0026#39;t1\u0026#39; 三. 数据库服务连接管理 方式一：内置客户端命令行连接 本地 Socket 方式连接：\n仅适用于本机访问，通过本地 Unix Domain Socket 文件（如 /tmp/mysql.sock）直接通信，速度极快；\n命令：mysql -uroot -p123456 -S /tmp/mysql.sock\nTCP/IP 网络方式连接：\n适用于远程或跨主机访问，需显式指明 -h IP 与 -P 端口；\n命令：mysql -uroot -p123456 -h 11.0.1.101 -P 3306\n方式二：远程图形化工具管理（DBeaver） 利用DBeaver等远程管理客户端，可视化操作与维护数据库对象。\nDBeaver基础管理流程：\n新建 MySQL 连接与连接测试： 输入主机 IP、端口、用户名与密码，点击测试连接确保网络互通。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 这个 Public Key Retrieval is not allowed 错误是 DBeaver 连接 MySQL 8.0+ 时很常见的问题，通常是因为 MySQL 8.0 默认使用了 caching_sha2_password 认证插件，而 JDBC 驱动出于安全考虑，默认禁止从服务器获取公钥。 解决方案：在 DBeaver 中修改驱动属性 最直接的方法是修改 DBeaver 的驱动属性，在连接配置中允许公钥检索。 打开连接设置：在 DBeaver 左侧的“数据库导航器”中，右键点击你的 dawn 连接，选择 “编辑连接”。 进入驱动属性：在弹出的设置窗口中，切换到 “驱动属性” 选项卡。 添加属性：在属性列表中，右键选择 “添加新属性”，或直接在下方空白处双击添加。 设置参数：添加以下两个属性： 属性名：allowPublicKeyRetrieval，值：true。 属性名：useSSL，值：false（如果你的网络环境可信，这是为了快速连接；如果生产环境需要加密，请保持 true 并配置好 SSL 证书）。 保存并测试：点击 “确定” 保存设置，然后再次点击 “测试连接”，应该就能成功了。 备选方案：修改 MySQL 用户认证方式 如果不想在客户端修改参数，也可以从服务端入手，将用户的认证插件改为 mysql_native_password。 ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED WITH mysql_native_password BY \u0026#39;123456\u0026#39;; FLUSH PRIVILEGES; 执行后，DBeaver 端可以不用修改 allowPublicKeyRetrieval 属性，直接连接即可。 # 安全提示 开启 allowPublicKeyRetrieval=true 会存在中间人攻击（MITM）的风险，因为客户端会通过不安全的连接向服务器索取公钥。因此，该设置仅建议在受信任的本地或内网开发环境中使用。在生产环境中，更安全的做法是配置 SSL/TLS 加密连接，或者使用 mysql_native_password 认证插件。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 mysql\u0026gt; CREATE USER \u0026#39;root\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; GRANT ALL PRIVILEGES ON *.* TO \u0026#39;root\u0026#39;@\u0026#39;11.0.1.%\u0026#39; WITH GRANT OPTION; Query OK, 0 rows affected (0.02 sec) mysql\u0026gt; FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; exit Bye [root@master ~]#mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 15 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; SELECT user, host FROM mysql.user WHERE user=\u0026#39;root\u0026#39;; +------+-----------+ | user | host | +------+-----------+ | root | 11.0.1.% | | root | localhost | +------+-----------+ 2 rows in set (0.00 sec) 图形化界面与创建数据库： 选择连接，右键新建数据库，指定数据库名称、字符集（推荐 utf8mb4）与排序规则（utf8mb4_general_ci 或 utf8mb4_bin）。 创建数据表与表结构设计： 设置字段名、数据类型、长度、是否为主键、自增属性等。 插入与维护数据项： 方式三：程序代码连接数据库 应用程序通过特定编程语言的数据库驱动接口与 MySQL 服务端通信。\n序号 开发语言 常见连接数据库驱动程序 01 Python pymysql、mysql-connector-python 02 Go (Golang) go-sql-driver/mysql 03 Java mysql-connector-java (JAR 包 / JDBC) 04 PHP mysqli、pdo_mysql 驱动官方下载地址：https://dev.mysql.com/downloads/\n四. 数据库服务初始配置 初始化配置方式一：源码编译时指定参数 在源码编译阶段通过 CMake 参数对数据库功能进行深度固化定制：\n1 2 3 4 5 6 7 8 9 10 11 # 源码依赖准备 yum -y install wget cmake gcc gcc-c++ ncurses ncurses-devel libaio-devel openssl openssl-devel # 下载与解压带 boost 的源码包 tar zxf mysql-boost-8.0.12.tar.gz -C /usr/local/ # CMake 编译参数配置 cd /usr/local/src/mysql-8.0.12 cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql8.0 -DMYSQL_DATADIR=/usr/local/mysql/data -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/usr/local/mysql-8.0.12/ -DSYSCONFDIR=/etc -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_EMBEDDED_SERVER=1 -DENABLE_DTRACE=0 -DENABLED_LOCAL_INFILE=1 -DDEFAULT_CHARSET=utf8mb4 make -j$(nproc) \u0026amp;\u0026amp; make install 初始化配置方式二：编写与修改 /etc/my.cnf 配置文件 配置文件读取加载顺序：\n通过 mysqld --help --verbose | grep my.cnf 可查看配置文件的搜索顺序：\n1 /etc/my.cnf -\u0026gt; /etc/mysql/my.cnf -\u0026gt; /usr/local/mysql/etc/my.cnf -\u0026gt; ~/.my.cnf 注：若同一参数在多个配置文件中出现，后加载的配置会覆盖先加载的配置。若启动时使用 --defaults-file=/path/to/my.cnf，则只加载该指定文件。\n配置文件结构规范：\n带 [] 部分：配置标签区（Group），区分服务端与客户端生效作用域： 服务端标签：[mysqld]、[mysqld_safe]、[server] 客户端标签：[mysql]、[mysqldump]、[client]（只影响本地客户端连接） 不带 [] 部分：具体键值对参数（key=value） 核心常用配置参数详解：\n标签 参数配置项 解释说明 [mysqld] user=mysql 守护进程的系统运行用户 [mysqld] basedir=/usr/local/mysql MySQL 软件程序根目录 [mysqld] datadir=/data/3306/data 数据文件持久化存放路径 [mysqld] server_id=6 数据库实例全局唯一标识节点编号（主从复制集群必须唯一） [mysqld] port=3306 服务端监听网络端口 [mysqld] socket=/tmp/mysql.sock 本地通信 Unix Socket 文件路径 [mysqld] default_authentication_plugin=mysql_native_password 指定默认认证插件（用于向下兼容旧客户端） [mysql] socket=/tmp/mysql.sock 本地客户端连接使用的 Socket 文件路径 初始化配置方式三：启动命令行传参 启动命令所附带的参数（如 mysqld_safe --skip-grant-tables --skip-networking \u0026amp;）具备最高优先级，会直接覆盖配置文件中的预设值。\n","date":"2026-10-14","description":"","objectID":"/posts/mysql-database-basic-management/","readingTime":10,"tags":["DevOps","MySQL","数据库","用户管理","权限管理"],"title":"MySQL 数据库基础管理：用户·权限·连接与初始化配置","url":"/posts/mysql-database-basic-management/","wordCount":1943},{"categories":["DevOps"],"content":"1.分布式构建介绍 如果项目需要定期集成，同时每次集成都需要较长时间。如果都运行在master服务器上，会消耗过多资源，导致其他项目搁置无法集成，这时就需要在建立多台设备，并配置作为slave机器来为master提供负载服务。\n2.部署分布式服务器 找一台或者目前已有的服务器配置为slave端（当前使用nexus服务器作为slave测试）\n在nexus上安装java jdk git和nexus客户端（如已安装则略过）\n1 [root@nexus ~]# yum -y install java git 复制jenkins上已配置好的客户端到此服务器sonar和maven\njenkins服务器操作\n1 2 3 4 5 6 7 8 9 10 11 # 优化java.sh脚本 # 原来 code_dir=\u0026#39;/var/lib/jenkins/workspace/java_job/target/*.war\u0026#39; # 改成 code_dir=\u0026#34;$WORKSPACE/target/*.war\u0026#34; [root@jenkins ~]#scp -r /usr/local/sonar 11.0.1.203:/usr/local/ [root@jenkins ~]#scp -r /usr/local/maven/ 11.0.1.203:/usr/local/ [root@nexus ~]#mkdir /server/scripts -p [root@jenkins ~]#scp /server/scripts/* 11.0.1.203:/server/scripts/ [root@jenkins ~]#ssh-copy-id -i .ssh/id_rsa.pub 11.0.1.203 把从节点的公钥上传至gitlab 拥有下载代码的权限\n1 2 3 [root@nexus ~]#ssh-keygen [root@nexus ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzCEs3V2MqRK8BO/ewSoUcjXTkdEj/c1vBz8UOLYgkKK+Jh/MvV71nsCeohy5slQ2phTE6DXFAwfm54FBDL2Wy4THL3tGmZH0sLHa0xg3d8MLjknXWVqizyX8V0SxALcJ9hTj1v0BlB8F1U60DCKqoScezJ7ZDKtbSS2Q4niARjSGxlcAYxLgb3mUr0BX+HHqtiBw+lGW2Q/wRMcCue0WFmYLN9jxiOxNIJ4xPQ6G6gHzpHsGJYyhIM/SKctbGMpqgTvideHqOzThYQGaiLo9DFu7p9QSc3wAHRxE8kyZlmNKEzknebFx+pbQwMzZ7Za0I5c1PTHPmPqhuDOGaXUBZ root@nexus 点击系统管理 -\u0026gt; 节点管理 -\u0026gt; 新建节点\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [root@jenkins ~]#cat .ssh/id_rsa -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAvpGo/zRMBymiBKzBApVWLhtyblq/Qg+16bjuWGeiX7TJjXQD zK70tJlqj+Eg8k+v5XtI9URpAOPBrIqxEnzKlyr80kneQLoSD5lAA7RjzSulUnoj CsqFoIjQRS5+kSDgHFyNDLma2EDCyc/pDmK+pBfXpv+jHxUKy5LfQ8yJZ0fW+wjC 8g5zzmfBRuuNA6ql9LGfBvotr+NVV/3O0RiFgIBISWPpIjEuXA8VpU39pPIaGZod FSGvxvQMJ725JclIhOAAnfRrRCRucVyc3nZ+6tP2IH69uk9vbfikk90pcq1yGhGx AuVsCDMpghCQwjzwhs6i47FmvuHWyYaBDNWn1QIDAQABAoIBABpNFirqt9eBOng0 s4q8u6EvbpKmeaEPdAbm3BZdbz6QQAUCzwdv2hwW1Gs5IgfvUZJ1enNHVsqlpHRA zAGGMZu9dhk70q3SQG3gs8IPInsNnAqPCskRfHHUkBIMQbGb/PJNH7Zy41kXTwr1 Y+/1VuRk2gGVZJkEmXHROnksAIa9WYhr9BD8p+hDjjmiaK8uD2E/mIIWhnWWuxUd fsrlVnu0Vtdwei5wvvCTwguaJMeMsgZvVlu4rt6HMdmS1KCVJYKNBCtQ6BCcmWHt 3G3Lz23S5LibvoJFRMzIrwQop6nFkm7BErYTJf/UBWwOOVPzXCIFNsQFpm1l/DwQ /G1skF0CgYEA32q6BSwSIOquH8gxN+HApznPv6U1U05V0ZatZA/hGv0Bf658NsEm URO4VcEI2xKOhr8BoNFWvt0eTCiM3kKB4ZoBrggVulfxH+9R8m1RHRNOXGm8nfe7 UOTwtSrQwQBdUlI82sHnsS+Dh3+dGFpDCF6DXVElU6RDMbFmI7vy/psCgYEA2lyO WjvRj6FBuc1HYqUFPCN9milx93HoEKbHJoFcgq7jf4++o9VtF9UKkZsvwWqjYees 7QziRjZkf8/2WOF5U2WhtKVK96MTPVdpGj+Nchkecvs1vPuAiBqNDK4Wr979VRDW sRaDLRPlLMbTqUCH3zswyUmghmFkxpp7xN1hok8CgYEAqJ0OuVqSb/9N3LSk2Kzr TgnmjZes0Ct9v0k9oRgO5uRtniRcoKJTPbtpUmb2PQgbQvyiyPFUy4VsGUQ9VHzT MyWdAPAfHdzDITvoF9OC9TOd30b50CqoVxwBwmkG1bJlu/xO6zaOmv3mAXxBXqC4 mHdkIyhbyG1fPQDKuktdz+MCgYEApBJll+FYjd5xv0VCPRmXfF+CccJwcqfw4goe m40Z0MvN/rwoV3v3M7AM9+kuEdupldf5NEi5AF7AVtUQ2inY+tPjlBOa58VtoGq8 e0+DXVcIbQbakvw60Il6sCtiupUHrtIvLvKmb8mCKone4k6tiQs1lhkvd6lYim3r T7m0MbsCgYALDbkebVxvLgEdRc4F4IIkM5wYhEurR//kZqJXDHQCrzeUKKXMnXm5 rSQVgN9RrOF7W6Fbcwa0WljA77moNLfqh9o8AGPybCGLigiNJyjH+ZQa8Kydsyn+ d+B6pUXKtJO6lPaz9/t+N3YNqQgL9zs3Bd5Nris7YsgPBJhiszy3GQ== -----END RSA PRIVATE KEY----- 这里由于agent端Java版本太老所以导致代理启动失败\n1 2 3 4 5 6 7 8 # 在11.0.1.203上面装Java 11 [root@nexus ~]#yum install -y java-11-openjdk [root@nexus ~]#alternatives --install /usr/bin/java java /usr/lib/jvm/jre-11/bin/java 200000 [root@nexus ~]#alternatives --set java /usr/lib/jvm/jre-11/bin/java [root@nexus ~]#java -version openjdk version \u0026#34;11.0.23\u0026#34; 2024-04-16 LTS OpenJDK Runtime Environment (Red_Hat-11.0.23.0.9-2.el7_9) (build 11.0.23+9-LTS) OpenJDK 64-Bit Server VM (Red_Hat-11.0.23.0.9-2.el7_9) (build 11.0.23+9-LTS, mixed mode, sharing) 在JenkinsUI给节点指定用Java 11启动： 节点列表 → node1-11.0.1.203 → 配置 → 展开 高级 → JavaPath\n启动成功\n从节点的公钥复制给web01和web02，实现免密\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@nexus ~]#ssh-copy-id -i .ssh/id_rsa.pub 11.0.1.8 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: \u0026#34;.ssh/id_rsa.pub\u0026#34; The authenticity of host \u0026#39;11.0.1.8 (11.0.1.8)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? 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@11.0.1.8\u0026#39;s password: Number of key(s) added: 1 Now try logging into the machine, with: \u0026#34;ssh \u0026#39;11.0.1.8\u0026#39;\u0026#34; and check to make sure that only the key(s) you wanted were added. [root@nexus ~]#ssh-copy-id -i .ssh/id_rsa.pub 11.0.1.7 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: \u0026#34;.ssh/id_rsa.pub\u0026#34; The authenticity of host \u0026#39;11.0.1.7 (11.0.1.7)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? 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@11.0.1.7\u0026#39;s password: Number of key(s) added: 1 Now try logging into the machine, with: \u0026#34;ssh \u0026#39;11.0.1.7\u0026#39;\u0026#34; and check to make sure that only the key(s) you wanted were added. 手动做一次git克隆yes验证\n1 [root@nexus ~]#git clone git@11.0.1.200:dawn/java.git 将master节点修改成以下用法，这样构建的时候就不会调度到master节点上了，然后测试一下slave可用性\n测试构建成功\n将master节点配置修改回来，同时都修改并发为2\n构建执行状态显示master和node1同时执行\n","date":"2026-10-13","description":"","objectID":"/posts/jenkins-distributed-build/","readingTime":3,"tags":["DevOps","Jenkins","分布式构建","Slave","CI/CD"],"title":"Jenkins 分布式构建与 Slave 节点配置","url":"/posts/jenkins-distributed-build/","wordCount":467},{"categories":["DevOps"],"content":"1.Maven介绍 Maven是一个项目管理和综合工具。Maven提供给开发人员构建一个完整的生命周期框架。 开发团队可以自动完成该项目的基础设施建设，Maven使用标准的目录结构和默认构建生命周期。 Apache的开源项目主要服务于JAVA平台的构建、依赖管理、项目管理。 Project Object Model，项目对象模型。通过xml格式保存的pom.xml文件。该文件用于管理：源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的url、项目的依赖关系等等。该文件是由开发维护，我们运维人员可以不用去关心。\n2.Maven安装 下载Maven安装包\n官网：http://maven.apache.org/download.cgi\n清华镜像：https://mirrors.tuna.tsinghua.edu.cn/apache/maven/\n安装Maven\n1 2 3 4 5 6 7 8 9 10 [root@jenkins ~]#cd /usr/local/ [root@jenkins local]#tar xf apache-maven-3.3.9-bin.tar.gz [root@jenkins local]#ln -s /usr/local/apache-maven-3.3.9/ /usr/local/maven [root@jenkins local]#/usr/local/maven/bin/mvn -v Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00) Maven home: /usr/local/maven Java version: 17.0.11, vendor: Oracle Corporation Java home: /usr/lib/jvm/jdk-17-oracle-x64 Default locale: en_US, platform encoding: UTF-8 OS name: \u0026#34;linux\u0026#34;, version: \u0026#34;3.10.0-1160.el7.x86_64\u0026#34;, arch: \u0026#34;amd64\u0026#34;, family: \u0026#34;unix\u0026#34; 3.编译部署java项目 1 2 3 4 5 6 7 8 9 [root@jenkins local]#vim /etc/profile export PATH=\u0026#34;/usr/local/apache-maven-3.3.9/bin/:$PATH\u0026#34; [root@jenkins local]#mvn -v Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00) Maven home: /usr/local/apache-maven-3.3.9 Java version: 17.0.11, vendor: Oracle Corporation Java home: /usr/lib/jvm/jdk-17-oracle-x64 Default locale: en_US, platform encoding: UTF-8 OS name: \u0026#34;linux\u0026#34;, version: \u0026#34;3.10.0-1160.el7.x86_64\u0026#34;, arch: \u0026#34;amd64\u0026#34;, family: \u0026#34;unix\u0026#34; 上传一个简单的java项目包 hello-world.tar.gz 进行解压\n1 [root@jenkins ~]#tar -xf hello-world.tar.gz 进入目录执行打包命令\n1 2 3 4 5 [root@jenkins ~]#cd hello-world-war/ [root@jenkins hello-world-war]#mvn package [root@jenkins hello-world-war]#cd target/ [root@jenkins target]#ls hello-world-war-1.0.0 hello-world-war-1.0.0.war maven-archiver 启动web02,和tomcat服务，将默认页删除\n1 2 3 4 5 6 7 8 9 10 [root@web02 ~]#/usr/local/tomcat/bin/startup.sh Using CATALINA_BASE: /usr/local/tomcat Using CATALINA_HOME: /usr/local/tomcat Using CATALINA_TMPDIR: /usr/local/tomcat/temp Using JRE_HOME: /usr Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar Using CATALINA_OPTS: Tomcat started. [root@web02 ~]#cd /usr/local/tomcat/webapps/ROOT [root@web02 ROOT]#rm -fr * 将编译的hello-world-war-1.0.0.war放到/usr/local/tomcat/webapps/ROOT目录下并解压\n1 2 3 [root@jenkins target]#scp hello-world-war-1.0.0.war 11.0.1.8:/usr/local/tomcat/webapps/ROOT/ [root@web02 ROOT]#yum install -y unzip [root@web02 ROOT]#unzip hello-world-war-1.0.0.war 测试没问题\nMaven 生命周期与常用命令说明\nvalidate（验证）：验证项目正确，并且所有必要信息可用。 compile（编译）：编译项目源码。 test（测试）：使用合适的单元测试框架测试编译后的源码。 package（打包）：源码编译之后，使用合适的格式（例如JAR格式）对编译后的源码进行打包。 integration-test（集成测试）：如果有需要，把包处理并部署到可以运行集成测试的环境中去。 verify（验证）：进行各种测试来验证包是否有效并且符合质量标准。 install（安装）：把包安装到本地仓库，使该包可以作为其他本地项目的依赖。 deploy（部署）：在集成或发布环境中完成，将最终软件包复制到远程存储库，以与其他开发人员和项目共享。 常用操作：\n1 2 3 4 5 6 7 8 9 10 11 mvn clean # 清除上次编译的结果 mvn package -Dmaven.test.skip=true # 跳过测试用例 mvn package # 会去maven的中央仓库去下载需要的依赖包和插件到.m2目录下 # 国外仓库慢，可以修改为国内的仓库，比如阿里云 [root@jenkins ~]#vim /usr/local/maven/conf/settings.xml 159 \u0026lt;mirror\u0026gt; 160 \u0026lt;id\u0026gt;nexus-aliyun\u0026lt;/id\u0026gt; 161 \u0026lt;mirrorOf\u0026gt;*\u0026lt;/mirrorOf\u0026gt; 162 \u0026lt;name\u0026gt;Nexus aliyun\u0026lt;/name\u0026gt; 163 \u0026lt;url\u0026gt;http://maven.aliyun.com/nexus/content/groups/public\u0026lt;/url\u0026gt; 164 \u0026lt;/mirror\u0026gt; 4.推送项目至gitlab仓库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 # 清除上次编译的结果 [root@jenkins hello-world-war]#mvn clean [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Hello World Web Application Repository 1.0.0 [INFO] ------------------------------------------------------------------------ Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom (4 KB at 2.4 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at 28.0 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar (25 KB at 40.6 KB/sec) [INFO] [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world-war --- Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.pom Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.pom (4 KB at 8.5 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/sonatype/spice/spice-parent/16/spice-parent-16.pom Downloaded: https://repo.maven.apache.org/maven2/org/sonatype/spice/spice-parent/16/spice-parent-16.pom (9 KB at 14.0 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.jar Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.jar (221 KB at 212.1 KB/sec) [INFO] Deleting /root/hello-world-war/target [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5.242 s [INFO] Finished at: 2026-10-12T12:45:21+08:00 [INFO] Final Memory: 9M/29M [INFO] ------------------------------------------------------------------------ # 删除本地仓库的默认远程仓库配置 [root@jenkins hello-world-war]#git remote -v origin git@10.0.0.200:test/hello-world.git (fetch) origin git@10.0.0.200:test/hello-world.git (push) [root@jenkins hello-world-war]#git remote remove origin [root@jenkins hello-world-war]#git remote -v # 提交代码到本地仓库，保证工作区干净 [root@jenkins hello-world-war]#git status # On branch master # Changes not staged for commit: # (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to update what will be committed) # (use \u0026#34;git checkout -- \u0026lt;file\u0026gt;...\u0026#34; to discard changes in working directory) # # modified: pom.xml # modified: src/main/webapp/index.jsp # no changes added to commit (use \u0026#34;git add\u0026#34; and/or \u0026#34;git commit -a\u0026#34;) [root@jenkins hello-world-war]#git add . [root@jenkins hello-world-war]#git config --global user.email \u0026#34;jenkins@example.com\u0026#34; [root@jenkins hello-world-war]#git config --global user.name \u0026#34;jenkins\u0026#34; [root@jenkins hello-world-war]#git commit -m \u0026#34;1\u0026#34; [master fc309ae] 1 2 files changed, 3 insertions(+), 3 deletions(-) [root@jenkins hello-world-war]#git status # On branch master nothing to commit, working directory clean 创建新的项目\n配置远程仓库并推送\n1 2 3 4 5 6 7 8 9 10 11 12 [root@jenkins hello-world-war]#git remote add origin git@11.0.1.200:dawn/java.git [root@jenkins hello-world-war]#git remote -v origin git@11.0.1.200:dawn/java.git (fetch) origin git@11.0.1.200:dawn/java.git (push) [root@jenkins hello-world-war]#git push -u origin master Counting objects: 44, done. Compressing objects: 100% (29/29), done. Writing objects: 100% (44/44), 4.90 KiB | 0 bytes/s, done. Total 44 (delta 11), reused 0 (delta 0) To git@11.0.1.200:dawn/java.git * [new branch] master -\u0026gt; master Branch master set up to track remote branch master from origin. 5.jenkins集成Maven 构建测试，编译成功\n编写配置shell脚本\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 [root@jenkins ~]#vim /server/scripts/java.sh #!/bin/bash code_dir=\u0026#39;/var/lib/jenkins/workspace/java_job/target/*.war\u0026#39; IP=11.0.1.8 DIR=/usr/local/tomcat/webapps SCP(){ scp $code_dir $IP:$DIR } # 清理上一次残留的 war 源包与历史 java_* 版本目录 CLEAN(){ ssh $IP \u0026#34;rm -f $DIR/*.war; rm -rf $DIR/java_*\u0026#34; } MK(){ ssh $IP \u0026#34;mkdir -p $DIR/java_$BUILD_ID\u0026#34; } XF(){ ssh $IP \u0026#34;unzip -o $DIR/*.war -d $DIR/java_$BUILD_ID\u0026#34; } # 解压完成后删除 webapps 下的 war 源包，避免 Tomcat 自动部署出多余上下文 DELWAR(){ ssh $IP \u0026#34;rm -f $DIR/*.war\u0026#34; } RM(){ ssh $IP \u0026#34;rm -rf $DIR/ROOT\u0026#34; } LN(){ ssh $IP \u0026#34;ln -sfn $DIR/java_$BUILD_ID $DIR/ROOT\u0026#34; } # 重启 Tomcat 使新 ROOT 生效 RESTART(){ ssh $IP \u0026#34;/usr/local/tomcat/bin/shutdown.sh; sleep 3; /usr/local/tomcat/bin/startup.sh\u0026#34; } main(){ CLEAN SCP MK XF DELWAR RM LN RESTART } main jenkins对web02做免密\n1 [root@jenkins ~]#ssh-copy-id root@11.0.1.8 将web02的/usr/local/tomcat/webapps/ROOT/清空，方便查看后面的构建测试效果\n1 2 3 4 [root@web02 ~]#cd /usr/local/tomcat/webapps/ROOT/ [root@web02 ROOT]#ls hello-world-war-1.0.0.war index.jsp META-INF WEB-INF [root@web02 ROOT]#rm -fr * 构建测试成功\n6. 配置web-hook 开启触发器\n生成token并保存\n复制Token配置到Gitlab\n配置完成\n测试: 修改代码推送到gitlab仓库的java项目中,会自动触发Jenkins拉取代码并将拉取到的修改后的代码推送到web02服务器\n修改前：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@jenkins hello-world-war]#vim src/main/webapp/index.jsp \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Hello World!\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello World! v1.0\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt; It is now \u0026lt;%= new java.util.Date() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; You are coming from \u0026lt;%= request.getRemoteAddr() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; 修改后：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@jenkins hello-world-war]#vim src/main/webapp/index.jsp \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Hello World!\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello World! v1.1\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt; It is now \u0026lt;%= new java.util.Date() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; You are coming from \u0026lt;%= request.getRemoteAddr() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; 推送代码，自动触发构建\n1 2 3 4 5 6 7 8 9 10 11 [root@jenkins hello-world-war]#git commit -am \u0026#34;v1.1\u0026#34; [master d15a2eb] v1.1 1 file changed, 1 insertion(+), 1 deletion(-) [root@jenkins hello-world-war]#git push -u origin master Counting objects: 11, done. Compressing objects: 100% (4/4), done. Writing objects: 100% (6/6), 442 bytes | 0 bytes/s, done. Total 6 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/java.git fc309ae..d15a2eb master -\u0026gt; master Branch master set up to track remote branch master from origin. 7.Jenkins集成SonarQube jenkins上配置SonarQube服务端\n将Build Steps和执行shell调换位置，Build Steps在上面，执行shell在下面\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@jenkins hello-world-war]#vim src/main/webapp/index.jsp \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Hello World!\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello World! v2.2\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt; It is now \u0026lt;%= new java.util.Date() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; You are coming from \u0026lt;%= request.getRemoteAddr() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; [root@jenkins hello-world-war]#git commit -am \u0026#34;v2.2\u0026#34; [master d0ebc06] v2.2 1 file changed, 1 insertion(+), 1 deletion(-) [root@jenkins hello-world-war]#git push -u origin master Counting objects: 17, done. Compressing objects: 100% (8/8), done. Writing objects: 100% (12/12), 849 bytes | 0 bytes/s, done. Total 12 (delta 4), reused 0 (delta 0) To git@11.0.1.200:dawn/java.git 6b4a15b..d0ebc06 master -\u0026gt; master Branch master set up to track remote branch master from origin. 自动构建推送成功\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 # 构建过程 Started by GitLab push by Administrator Running as SYSTEM Building in workspace /var/lib/jenkins/workspace/java_job The recommended git tool is: NONE No credentials specified \u0026gt; git rev-parse --resolve-git-dir /var/lib/jenkins/workspace/java_job/.git # timeout=10 Fetching changes from the remote Git repository \u0026gt; git config remote.origin.url git@11.0.1.200:dawn/java.git # timeout=10 Fetching upstream changes from git@11.0.1.200:dawn/java.git \u0026gt; git --version # timeout=10 \u0026gt; git --version # \u0026#39;git version 1.8.3.1\u0026#39; \u0026gt; git fetch --tags --progress git@11.0.1.200:dawn/java.git +refs/heads/*:refs/remotes/origin/* # timeout=10 \u0026gt; git rev-parse remotes/origin/master^{commit} # timeout=10 \u0026gt; git branch -a -v --no-abbrev --contains d0ebc065fd6d63fb107c9ddc7c0e5305f840099e # timeout=10 Checking out Revision d0ebc065fd6d63fb107c9ddc7c0e5305f840099e (origin/master) \u0026gt; git config core.sparsecheckout # timeout=10 \u0026gt; git checkout -f d0ebc065fd6d63fb107c9ddc7c0e5305f840099e # timeout=10 Commit message: \u0026#34;v2.2\u0026#34; \u0026gt; git rev-list --no-walk 6b4a15b008c56778e80256b0131a6f89f4a5d433 # timeout=10 Parsing POMs Established TCP socket on 33973 [java_job] $ java -cp /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven33-agent-1.14.jar:/usr/local/maven/boot/plexus-classworlds-2.5.2.jar:/usr/local/maven/conf/logging jenkins.maven3.agent.Maven33Main /usr/local/maven/ /var/lib/jenkins/%C/jenkins/war/WEB-INF/lib/remoting-3107.v665000b_51092.jar /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven33-interceptor-1.14.jar /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven3-interceptor-commons-1.14.jar 33973 \u0026lt;===[JENKINS REMOTING CAPACITY]===\u0026gt;channel started Executing Maven: -B -f /var/lib/jenkins/workspace/java_job/pom.xml clean package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Hello World Web Application Repository 1.0.0 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world-war --- [INFO] Deleting /var/lib/jenkins/workspace/java_job/target [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world-war --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /var/lib/jenkins/workspace/java_job/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world-war --- [INFO] No sources to compile [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world-war --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /var/lib/jenkins/workspace/java_job/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world-war --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world-war --- [INFO] No tests to run. [JENKINS] Recording test results [INFO] [INFO] --- maven-war-plugin:3.3.1:war (default-war) @ hello-world-war --- [INFO] Packaging webapp [INFO] Assembling webapp [hello-world-war] in [/var/lib/jenkins/workspace/java_job/target/hello-world-war-1.0.0] [INFO] Processing war project [INFO] Copying webapp resources [/var/lib/jenkins/workspace/java_job/src/main/webapp] [INFO] Building war: /var/lib/jenkins/workspace/java_job/target/hello-world-war-1.0.0.war [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.040 s [INFO] Finished at: 2026-10-12T15:53:20+08:00 [INFO] Final Memory: 16M/85M [INFO] ------------------------------------------------------------------------ Waiting for Jenkins to finish collecting data [JENKINS] Archiving /var/lib/jenkins/workspace/java_job/pom.xml to com.efsavage/hello-world-war/1.0.0/hello-world-war-1.0.0.pom [JENKINS] Archiving /var/lib/jenkins/workspace/java_job/target/hello-world-war-1.0.0.war to com.efsavage/hello-world-war/1.0.0/hello-world-war-1.0.0.war channel stopped [java_job] $ /usr/local/sonar/bin/sonar-scanner -Dsonar.host.url=http://11.0.1.202:9000/ ******** -Dsonar.projectName=java_job -Dsonar.projectKey=java -Dsonar.sources=. -Dsonar.projectBaseDir=/var/lib/jenkins/workspace/java_job INFO: Scanner configuration file: /usr/local/sonar-scanner-4.2.0.1873-linux/conf/sonar-scanner.properties INFO: Project root configuration file: NONE INFO: SonarQube Scanner 4.2.0.1873 INFO: Java 11.0.3 AdoptOpenJDK (64-bit) INFO: Linux 3.10.0-1160.el7.x86_64 amd64 INFO: User cache: /root/.sonar/cache INFO: SonarQube server 7.0.0 INFO: Default locale: \u0026#34;en_US\u0026#34;, source code encoding: \u0026#34;UTF-8\u0026#34; INFO: Publish mode INFO: Load global settings INFO: Load global settings (done) | time=61ms INFO: Server id: AaD4EZTNcTUVzuJgme-j INFO: User cache: /root/.sonar/cache INFO: Load plugins index INFO: Load plugins index (done) | time=24ms INFO: Load/download plugins INFO: Plugin [l10nzh] defines \u0026#39;l10nen\u0026#39; as base plugin. This metadata can be removed from manifest of l10n plugins since version 5.2. INFO: Load/download plugins (done) | time=19ms INFO: Process project properties INFO: Load project repositories INFO: Load project repositories (done) | time=30ms INFO: Load quality profiles INFO: Load quality profiles (done) | time=36ms INFO: Load active rules INFO: Load active rules (done) | time=670ms INFO: Load metrics repository INFO: Load metrics repository (done) | time=19ms INFO: Project key: java INFO: ------------- Scan java_job INFO: Load server rules INFO: Load server rules (done) | time=46ms INFO: Base dir: /var/lib/jenkins/workspace/java_job INFO: Working dir: /var/lib/jenkins/workspace/java_job/.scannerwork INFO: Source paths: . INFO: Source encoding: UTF-8, default locale: en_US INFO: Index files INFO: 9 files indexed INFO: Quality profile for xml: Sonar way WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by net.sf.cglib.core.ReflectUtils$1 (file:/root/.sonar/cache/cc8ad346e85db9d3735898b533d37d34/sonar-javascript-plugin-4.0.0.5862.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) WARNING: Please consider reporting this to the maintainers of net.sf.cglib.core.ReflectUtils$1 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release INFO: Sensor SonarJavaXmlFileSensor [java] INFO: 3 source files to be analyzed INFO: Sensor SonarJavaXmlFileSensor [java] (done) | time=413ms INFO: Sensor HTML [web] INFO: 3/3 source files have been analyzed INFO: Sensor HTML [web] (done) | time=95ms INFO: Sensor XML Sensor [xml] INFO: Sensor XML Sensor [xml] (done) | time=117ms INFO: Sensor Zero Coverage Sensor INFO: Sensor Zero Coverage Sensor (done) | time=5ms INFO: Sensor CPD Block Indexer INFO: Sensor CPD Block Indexer (done) | time=0ms INFO: SCM provider for this project is: git INFO: 5 files to be analyzed INFO: 3/5 files analyzed WARN: Missing blame information for the following files: WARN: * target/hello-world-war-1.0.0/WEB-INF/web.xml WARN: * target/hello-world-war-1.0.0/index.jsp WARN: This may lead to missing/broken features in SonarQube INFO: Calculating CPD for 2 files INFO: CPD calculation finished INFO: Analysis report generated in 96ms, dir size=31 KB INFO: Analysis reports compressed in 22ms, zip size=14 KB INFO: Analysis report uploaded in 340ms INFO: ANALYSIS SUCCESSFUL, you can browse http://11.0.1.202:9000/dashboard/index/java INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report INFO: More about the report processing at http://11.0.1.202:9000/api/ce/task?id=AaEvGigpXfNgiDsaIH6i INFO: Task total time: 3.816 s INFO: ------------------------------------------------------------------------ INFO: EXECUTION SUCCESS INFO: ------------------------------------------------------------------------ INFO: Total time: 4.908s INFO: Final Memory: 13M/116M INFO: ------------------------------------------------------------------------ [java_job] $ /bin/sh -xe /tmp/jenkins9664183222777241323.sh + sh /server/scripts/java.sh Archive: /usr/local/tomcat/webapps/hello-world-war-1.0.0.war inflating: /usr/local/tomcat/webapps/java_16/META-INF/MANIFEST.MF creating: /usr/local/tomcat/webapps/java_16/WEB-INF/ creating: /usr/local/tomcat/webapps/java_16/WEB-INF/classes/ inflating: /usr/local/tomcat/webapps/java_16/WEB-INF/web.xml inflating: /usr/local/tomcat/webapps/java_16/index.jsp inflating: /usr/local/tomcat/webapps/java_16/META-INF/maven/com.efsavage/hello-world-war/pom.xml inflating: /usr/local/tomcat/webapps/java_16/META-INF/maven/com.efsavage/hello-world-war/pom.properties Tomcat started. Finished: SUCCESS 8.创建Maven私服nexus 部署私服 nexus 下载：https://www.sonatype.com/download-oss-sonatype\n配置仓库两个选项：\n项目下的 pom.xml 配置、只生效当前的项目。 在 maven 配置全局所有项目生效。 上传JDK和nexus安装包：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@nexus ~]#rpm -ivh jdk-8u181-linux-x64.rpm warning: jdk-8u181-linux-x64.rpm: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk1.8-2000:1.8.0_181-fcs ################################# [100%] Unpacking JAR files... tools.jar... plugin.jar... javaws.jar... deploy.jar... rt.jar... jsse.jar... charsets.jar... localedata.jar... [root@nexus ~]#tar xf nexus-3.13.0-01-unix.tar.gz -C /usr/local/ [root@nexus ~]#ln -s /usr/local/nexus-3.13.0-01/ /usr/local/nexus [root@nexus bin]#/usr/local/nexus/bin/nexus start WARNING: ************************************************************ WARNING: Detected execution as \u0026#34;root\u0026#34; user. This is NOT recommended! WARNING: ************************************************************ Starting nexus [root@nexus nexus]#netstat -ntplu Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:35916 0.0.0.0:* LISTEN 2848/java tcp 0 0 0.0.0.0:8081 0.0.0.0:* LISTEN 2848/java tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1012/sshd tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1225/master tcp6 0 0 :::22 :::* LISTEN 1012/sshd tcp6 0 0 ::1:25 :::* LISTEN 1225/master 访问地址：11.0.1.203:8081\n默认账号密码：admin / admin123\n更改代理仓库为阿里云\n修改/usr/local/maven/conf/settings.xml，配置默认仓库为私服nexus\n配置Maven全局配置文件：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 [root@jenkins ~]#cat /usr/local/maven/conf/settings.xml \u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --\u0026gt; \u0026lt;!-- | This is the configuration file for Maven. It can be specified at two levels: | | 1. User Level. This settings.xml file provides configuration for a single user, | and is normally provided in ${user.home}/.m2/settings.xml. | | NOTE: This location can be overridden with the CLI option: | | -s /path/to/user/settings.xml | | 2. Global Level. This settings.xml file provides configuration for all Maven | users on a machine (assuming they\u0026#39;re all using the same Maven | installation). It\u0026#39;s normally provided in | ${maven.home}/conf/settings.xml. | | NOTE: This location can be overridden with the CLI option: | | -gs /path/to/global/settings.xml | | The sections in this sample file are intended to give you a running start at | getting the most out of your Maven installation. Where appropriate, the default | values (values used when the setting is not specified) are provided. | |--\u0026gt; \u0026lt;settings xmlns=\u0026#34;http://maven.apache.org/SETTINGS/1.0.0\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd\u0026#34;\u0026gt; \u0026lt;!-- localRepository | The path to the local repository maven will use to store artifacts. | | Default: ${user.home}/.m2/repository \u0026lt;localRepository\u0026gt;/path/to/local/repo\u0026lt;/localRepository\u0026gt; --\u0026gt; \u0026lt;!-- interactiveMode | This will determine whether maven prompts you when it needs input. If set to false, | maven will use a sensible default value, perhaps based on some other setting, for | the parameter in question. | | Default: true \u0026lt;interactiveMode\u0026gt;true\u0026lt;/interactiveMode\u0026gt; --\u0026gt; \u0026lt;!-- offline | Determines whether maven should attempt to connect to the network when executing a build. | This will have an effect on artifact downloads, artifact deployment, and others. | | Default: false \u0026lt;offline\u0026gt;false\u0026lt;/offline\u0026gt; --\u0026gt; \u0026lt;!-- pluginGroups | This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e. | when invoking a command line like \u0026#34;mvn prefix:goal\u0026#34;. Maven will automatically add the group identifiers | \u0026#34;org.apache.maven.plugins\u0026#34; and \u0026#34;org.codehaus.mojo\u0026#34; if these are not already contained in the list. |--\u0026gt; \u0026lt;pluginGroups\u0026gt; \u0026lt;!-- pluginGroup | Specifies a further group identifier to use for plugin lookup. \u0026lt;pluginGroup\u0026gt;com.your.plugins\u0026lt;/pluginGroup\u0026gt; --\u0026gt; \u0026lt;/pluginGroups\u0026gt; \u0026lt;!-- proxies | This is a list of proxies which can be used on this machine to connect to the network. | Unless otherwise specified (by system property or command-line switch), the first proxy | specification in this list marked as active will be used. |--\u0026gt; \u0026lt;proxies\u0026gt; \u0026lt;!-- proxy | Specification for one proxy, to be used in connecting to the network. | \u0026lt;proxy\u0026gt; \u0026lt;id\u0026gt;optional\u0026lt;/id\u0026gt; \u0026lt;active\u0026gt;true\u0026lt;/active\u0026gt; \u0026lt;protocol\u0026gt;http\u0026lt;/protocol\u0026gt; \u0026lt;username\u0026gt;proxyuser\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;proxypass\u0026lt;/password\u0026gt; \u0026lt;host\u0026gt;proxy.host.net\u0026lt;/host\u0026gt; \u0026lt;port\u0026gt;80\u0026lt;/port\u0026gt; \u0026lt;nonProxyHosts\u0026gt;local.net|some.host.com\u0026lt;/nonProxyHosts\u0026gt; \u0026lt;/proxy\u0026gt; --\u0026gt; \u0026lt;/proxies\u0026gt; \u0026lt;!-- servers | This is a list of authentication profiles, keyed by the server-id used within the system. | Authentication profiles can be used whenever maven must make a connection to a remote server. |--\u0026gt; \u0026lt;servers\u0026gt; \u0026lt;!-- server | Specifies the authentication information to use when connecting to a particular server, identified by | a unique name within the system (referred to by the \u0026#39;id\u0026#39; attribute below). | | NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are | used together. | \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;deploymentRepo\u0026lt;/id\u0026gt; \u0026lt;username\u0026gt;repouser\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;repopwd\u0026lt;/password\u0026gt; \u0026lt;/server\u0026gt; --\u0026gt; \u0026lt;!-- Another sample, using keys to authenticate. \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;siteServer\u0026lt;/id\u0026gt; \u0026lt;privateKey\u0026gt;/path/to/private/key\u0026lt;/privateKey\u0026gt; \u0026lt;passphrase\u0026gt;optional; leave empty if not used.\u0026lt;/passphrase\u0026gt; \u0026lt;/server\u0026gt; --\u0026gt; \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;my-nexus-releases\u0026lt;/id\u0026gt; \u0026lt;username\u0026gt;admin\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;admin123\u0026lt;/password\u0026gt; \u0026lt;/server\u0026gt; \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;my-nexus-snapshot\u0026lt;/id\u0026gt; \u0026lt;username\u0026gt;admin\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;admin123\u0026lt;/password\u0026gt; \u0026lt;/server\u0026gt; \u0026lt;/servers\u0026gt; \u0026lt;!-- mirrors | This is a list of mirrors to be used in downloading artifacts from remote repositories. | | It works like this: a POM may declare a repository to use in resolving certain artifacts. | However, this repository may have problems with heavy traffic at times, so people have mirrored | it to several places. | | That repository definition will have a unique id, so we can create a mirror reference for that | repository, to be used as an alternate download site. The mirror site will be the preferred | server for that repository. |--\u0026gt; \u0026lt;mirrors\u0026gt; \u0026lt;mirror\u0026gt; \u0026lt;id\u0026gt;nexus\u0026lt;/id\u0026gt; \u0026lt;mirrorOf\u0026gt;*\u0026lt;/mirrorOf\u0026gt; \u0026lt;url\u0026gt;http://11.0.1.203:8081/repository/maven-public/\u0026lt;/url\u0026gt; \u0026lt;/mirror\u0026gt; \u0026lt;!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | \u0026lt;mirror\u0026gt; \u0026lt;id\u0026gt;mirrorId\u0026lt;/id\u0026gt; \u0026lt;mirrorOf\u0026gt;repositoryId\u0026lt;/mirrorOf\u0026gt; \u0026lt;name\u0026gt;Human Readable Name for this Mirror.\u0026lt;/name\u0026gt; \u0026lt;url\u0026gt;http://my.repository.com/repo/path\u0026lt;/url\u0026gt; \u0026lt;/mirror\u0026gt; --\u0026gt; \u0026lt;/mirrors\u0026gt; \u0026lt;!-- profiles | This is a list of profiles which can be activated in a variety of ways, and which can modify | the build process. Profiles provided in the settings.xml are intended to provide local machine- | specific paths and repository locations which allow the build to work in the local environment. | | For example, if you have an integration testing plugin - like cactus - that needs to know where | your Tomcat instance is installed, you can provide a variable here such that the variable is | dereferenced during the build process to configure the cactus plugin. | | As noted above, profiles can be activated in a variety of ways. One way - the activeProfiles | section of this document (settings.xml) - will be discussed later. Another way essentially | relies on the detection of a system property, either matching a particular value for the property, | or merely testing its existence. Profiles can also be activated by JDK version prefix, where a | value of \u0026#39;1.4\u0026#39; might activate a profile when the build is executed on a JDK version of \u0026#39;1.4.2_07\u0026#39;. | Finally, the list of active profiles can be specified directly from the command line. | | NOTE: For profiles defined in the settings.xml, you are restricted to specifying only artifact | repositories, plugin repositories, and free-form properties to be used as configuration | variables for plugins in the POM. | |--\u0026gt; \u0026lt;profiles\u0026gt; \u0026lt;!-- profile | Specifies a set of introductions to the build process, to be activated using one or more of the | mechanisms described above. For inheritance purposes, and to activate profiles via \u0026lt;activatedProfiles/\u0026gt; | or the command line, profiles have to have an ID that is unique. | | An encouraged best practice for profile identification is to use a consistent naming convention | for profiles, such as \u0026#39;env-dev\u0026#39;, \u0026#39;env-test\u0026#39;, \u0026#39;env-production\u0026#39;, \u0026#39;user-jdcasey\u0026#39;, \u0026#39;user-brett\u0026#39;, etc. | This will make it more intuitive to understand what the set of introduced profiles is attempting | to accomplish, particularly when you only have a list of profile id\u0026#39;s for debug. | | This profile example uses the JDK version to trigger activation, and provides a JDK-specific repo. \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;jdk-1.4\u0026lt;/id\u0026gt; \u0026lt;activation\u0026gt; \u0026lt;jdk\u0026gt;1.4\u0026lt;/jdk\u0026gt; \u0026lt;/activation\u0026gt; \u0026lt;repositories\u0026gt; \u0026lt;repository\u0026gt; \u0026lt;id\u0026gt;jdk14\u0026lt;/id\u0026gt; \u0026lt;name\u0026gt;Repository for JDK 1.4 builds\u0026lt;/name\u0026gt; \u0026lt;url\u0026gt;http://www.myhost.com/maven/jdk14\u0026lt;/url\u0026gt; \u0026lt;layout\u0026gt;default\u0026lt;/layout\u0026gt; \u0026lt;snapshotPolicy\u0026gt;always\u0026lt;/snapshotPolicy\u0026gt; \u0026lt;/repository\u0026gt; \u0026lt;/repositories\u0026gt; \u0026lt;/profile\u0026gt; --\u0026gt; \u0026lt;!-- | Here is another profile, activated by the system property \u0026#39;target-env\u0026#39; with a value of \u0026#39;dev\u0026#39;, | which provides a specific path to the Tomcat instance. To use this, your plugin configuration | might hypothetically look like: | | ... | \u0026lt;plugin\u0026gt; | \u0026lt;groupId\u0026gt;org.myco.myplugins\u0026lt;/groupId\u0026gt; | \u0026lt;artifactId\u0026gt;myplugin\u0026lt;/artifactId\u0026gt; | | \u0026lt;configuration\u0026gt; | \u0026lt;tomcatLocation\u0026gt;${tomcatPath}\u0026lt;/tomcatLocation\u0026gt; | \u0026lt;/configuration\u0026gt; | \u0026lt;/plugin\u0026gt; | ... | | NOTE: If you just wanted to inject this configuration whenever someone set \u0026#39;target-env\u0026#39; to | anything, you could just leave off the \u0026lt;value/\u0026gt; inside the activation-property. | \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;env-dev\u0026lt;/id\u0026gt; \u0026lt;activation\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;target-env\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;dev\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/activation\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;tomcatPath\u0026gt;/path/to/tomcat/instance\u0026lt;/tomcatPath\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;/profile\u0026gt; --\u0026gt; \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;nexus\u0026lt;/id\u0026gt; \u0026lt;repositories\u0026gt; \u0026lt;repository\u0026gt; \u0026lt;id\u0026gt;central\u0026lt;/id\u0026gt; \u0026lt;url\u0026gt;http://11.0.1.203:8081/repository/maven-public/\u0026lt;/url\u0026gt; \u0026lt;releases\u0026gt;\u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\u0026lt;/releases\u0026gt; \u0026lt;snapshots\u0026gt;\u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\u0026lt;/snapshots\u0026gt; \u0026lt;/repository\u0026gt; \u0026lt;/repositories\u0026gt; \u0026lt;pluginRepositories\u0026gt; \u0026lt;pluginRepository\u0026gt; \u0026lt;id\u0026gt;central\u0026lt;/id\u0026gt; \u0026lt;url\u0026gt;http://11.0.1.203:8081/repository/maven-public/\u0026lt;/url\u0026gt; \u0026lt;releases\u0026gt;\u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\u0026lt;/releases\u0026gt; \u0026lt;snapshots\u0026gt;\u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\u0026lt;/snapshots\u0026gt; \u0026lt;/pluginRepository\u0026gt; \u0026lt;/pluginRepositories\u0026gt; \u0026lt;/profile\u0026gt; \u0026lt;/profiles\u0026gt; \u0026lt;!-- activeProfiles | List of profiles that are active for all builds. | \u0026lt;activeProfiles\u0026gt; \u0026lt;activeProfile\u0026gt;alwaysActiveProfile\u0026lt;/activeProfile\u0026gt; \u0026lt;activeProfile\u0026gt;anotherAlwaysActiveProfile\u0026lt;/activeProfile\u0026gt; \u0026lt;/activeProfiles\u0026gt; --\u0026gt; \u0026lt;activeProfiles\u0026gt; \u0026lt;activeProfile\u0026gt;nexus\u0026lt;/activeProfile\u0026gt; \u0026lt;/activeProfiles\u0026gt; \u0026lt;/settings\u0026gt; 测试nexus私服是否配置成功\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@jenkins hello-world-war]#mvn package ... Downloaded: http://11.0.1.203:8081/repository/maven-public/org/apache/maven/shared/maven-filtering/3.1.1/maven-filtering-3.1.1.jar (0at 0.0 KB/sec) Downloaded: http://11.0.1.203:8081/repository/maven-public/org/codehaus/plexus/plexus-utils/3.3.0/plexus-utils-3.3.0.jar (0 B at 0.0 sec) Downloaded: http://11.0.1.203:8081/repository/maven-public/org/apache/maven/shared/maven-mapping/3.0.0/maven-mapping-3.0.0.jar (0 B a.0 KB/sec) Downloaded: http://11.0.1.203:8081/repository/maven-public/org/sonatype/plexus/plexus-build-api/0.0.7/plexus-build-api-0.0.7.jar (0 B 0.0 KB/sec) [INFO] Packaging webapp [INFO] Assembling webapp [hello-world-war] in [/root/hello-world-war/target/hello-world-war-1.0.0] [INFO] Processing war project [INFO] Copying webapp resources [/root/hello-world-war/src/main/webapp] [INFO] Building war: /root/hello-world-war/target/hello-world-war-1.0.0.war [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 25.059 s [INFO] Finished at: 2026-10-12T17:40:28+08:00 [INFO] Final Memory: 13M/102M [INFO] ------------------------------------------------------------------------ 仓库地址11.0.1.203，配置成功\n","date":"2026-10-12","description":"","objectID":"/posts/java-project-deploy/","readingTime":19,"tags":["DevOps","Java","Maven","Jenkins","CI/CD"],"title":"Java 项目 CI/CD 流水线：Maven+GitLab+Jenkins+SonarQube+Nexus","url":"/posts/java-project-deploy/","wordCount":4000},{"categories":["DevOps"],"content":"代码发布和回滚 代码发布前，先将之前测试流程的触发器关闭\n1.将稳定版本的main打标签 tag 1 2 3 4 [root@gitlab game]#vim index.html 修改为v1.0.1 [root@gitlab game]#git commit -am \u0026#34;v1.0.1\u0026#34; #为当前的仓库的代码打tag [root@gitlab game]#git tag -a v1.0.1 -m \u0026#34;v1.0.1\u0026#34; 2.将tag提交到gitlab的game项目中 1 2 推送tag到远程仓库 [root@gitlab game]#git push -u origin v1.0.1 3.jenkins获取gitlab中的tag 1 2 3 jenkins需要通过使用 git parameter插件获取项目中tag版本号信息 注: 接下来安装插件、在可选插件中搜索Git Parameter 直接安装即可，已安装则忽略次步骤 进入到项目把前面的参数化去掉重新选择使用git parameter 4.新建一个任务，只用于代码发布和回滚 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@jenkins scripts]#vim deploy.sh #!/bin/sh CODE_DIR=`pwd` WEB_DIR=\u0026#34;/code/\u0026#34; get_code_tar(){ cd $CODE_DIR \u0026amp;\u0026amp; tar zcf /opt/web-$git_version.tar.gz ./* } scp_code_web(){ scp /opt/web-$git_version.tar.gz 11.0.1.7:$WEB_DIR } code_tarxf(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp;mkdir web-$git_version \u0026amp;\u0026amp; tar xf web-$git_version.tar.gz -C web-$git_version\u0026#34; } ln_html(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp; rm -rf html \u0026amp;\u0026amp; ln -s web-$git_version html\u0026#34; } main(){ get_code_tar; scp_code_web; code_tarxf; ln_html; } main 测试构建，为了演示效果，先删除web服务器/code/目录内容\n1 [root@web01 code]#rm -fr * 1 2 3 4 5 [root@web01 code]#ll total 7740 lrwxrwxrwx 1 root root 10 Oct 10 15:56 html -\u0026gt; web-v1.0.1 drwxr-xr-x 6 root root 126 Oct 10 15:56 web-v1.0.1 -rw-r--r-- 1 root root 7922995 Oct 10 15:56 web-v1.0.1.tar.gz 更新版本（多推送几个版本进行测试）\n使用参数化构建中的选项参数\n优化发布和回滚操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #!/bin/sh CODE_DIR=`pwd` WEB_DIR=\u0026#34;/code/\u0026#34; get_code_tar(){ cd $CODE_DIR \u0026amp;\u0026amp; tar zcf /opt/web-$git_version.tar.gz ./* } scp_code_web(){ scp /opt/web-$git_version.tar.gz 11.0.1.7:$WEB_DIR } code_tarxf(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp;mkdir web-$git_version \u0026amp;\u0026amp; tar xf web-$git_version.tar.gz -C web-$git_version\u0026#34; } ln_html(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp; rm -rf html \u0026amp;\u0026amp; ln -s web-$git_version html\u0026#34; } main(){ get_code_tar; scp_code_web; code_tarxf; ln_html; } if [ $game = \u0026#34;deploy\u0026#34; ];then main elif [ $game = \u0026#34;rollback\u0026#34; ];then ln_html fi 这样发布过的版本就可以直接rollback，不用deploy，可以减少重复构建，不用重新执行整个main函数，只需要执行ln_html就可以了\n5.拉取对应的版本 ","date":"2026-10-10","description":"","objectID":"/posts/jenkins-code-deploy-rollback/","readingTime":2,"tags":["DevOps","Jenkins","代码发布","回滚","Git Parameter"],"title":"Jenkins 代码发布与回滚","url":"/posts/jenkins-code-deploy-rollback/","wordCount":282},{"categories":["DevOps"],"content":"Pipeline流水线 一. CI/CD持续集成/持续部署 持续集成(Continuous integration)是一种软件开发实践，即团队开发成员经常集成它们的工作，通过每个成员每天至少集成一次，也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建（包括编译，发布，自动化测试）来验证，从而尽早地发现集成错误。\n持续部署（continuous deployment）是通过自动化的构建、测试和部署循环来快速交付高质量的产品。某种程度上代表了一个开发团队工程化的程度，毕竟快速运转的互联网公司人力成本会高于机器，投资机器优化开发流程化相对也提高了人的效率。\n持续交付 Continuous Delivery:频繁地将软件的新版本，交付给质量团队或者用户，以供评审尽早发现生产环境中存在的问题；如果评审通过，代码就进入生产阶段。\n敏捷思想中提出的这三个观点：通过技术手段自动化这三个工作，加快交付速度。\n二.pipeline介绍 什么是pipeline Jenkins 2.0的精髓是Pipeline as Code，是帮助Jenkins实现CI到CD转变的重要角色。什么是Pipeline，简单来说，就是一套运行于 Jenkins上的工作流框架，将原本独立运行于单个或者多个节点的任务连接起来，实现单个任务难以完成的复杂发布流程。Pipeline的实现方式是一套Groovy DSL，任何发布流程都可以表述为一段Groovy脚本，并且Jenkins支持从代码库直接读取脚本，从而实现了Pipeline as Code的理念。\nPipeline 概念 Pipeline 是一个用户定义的 CD 流水线模式。Pipeline 代码定义了通常包含构建、测试和发布步骤的完整的构建过程。\nNode node 是一个机器，它是 Jenkins 环境的一部分，并且能够执行 Pipeline。同时，node 代码块也是脚本式 Pipeline 语法的关键特性。\nStage Stage 块定义了在整个 Pipeline 中执行的概念上不同的任务子集（例如“构建”，“测试”和“部署”阶段），许多插件使用它来可视化或呈现 Jenkins 管道状态/进度。\nStep 一项任务。从根本上讲，一个步骤告诉 Jenkins 在特定时间点（或过程中的“步骤”）要做什么。例如，使用 sh step：sh \u0026lsquo;make\u0026rsquo; 可以执行 make 这个 shell 命令。\n三.Pipeline 重构发布流程 pipeline示例代码，做一个简单测试\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 pipeline{ agent any stages{ stage(\u0026#34;get code\u0026#34;){ steps{ echo \u0026#34;get code from scm\u0026#34; } } stage(\u0026#34;package\u0026#34;){ steps{ echo \u0026#34;packge code\u0026#34; } } stage(\u0026#34;deploy\u0026#34;){ steps{ echo \u0026#34;deploy packge to node1\u0026#34; } } } } 构建成功\n如果想更直观一点的话，可以打开Blue Ocean看板\n看板效果\n用Pipeline重构前面的发布流程\n1 2 3 4 [root@jenkins ~]#vim /usr/local/sonar/conf/sonar-scanner.properties sonar.host.url=http://11.0.1.202:9000 sonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b sonar.sourceEncoding=UTF-8 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 [root@gitlab game]#vim Jenkinsfile pipeline{ agent any stages{ stage(\u0026#34;get code\u0026#34;){ steps{ echo \u0026#34;get code\u0026#34; } } stage(\u0026#34;unit test\u0026#34;){ steps{ sh \u0026#39;/usr/local/sonar/bin/sonar-scanner -Dsonar.projectKey=html -Dsonar.projectName=${JOB_NAME} -Dsonar.sources=.\u0026#39; } } stage(\u0026#34;package\u0026#34;){ steps{ sh \u0026#39;tar zcf /opt/web-${BUILD_ID}.tar.gz ./* --exclude=./git --exclude=Jenkinsfile\u0026#39; } } stage(\u0026#34;deploy\u0026#34;){ steps{ sh \u0026#39;ssh 11.0.1.7 \u0026#34;cd /root/ \u0026amp;\u0026amp; mkdir web-${BUILD_ID}\u0026#34;\u0026#39; sh \u0026#39;scp /opt/web-${BUILD_ID}.tar.gz 11.0.1.7:/root/web-${BUILD_ID}\u0026#39; sh \u0026#39;ssh 11.0.1.7 \u0026#34;cd /root/web-${BUILD_ID} \u0026amp;\u0026amp; tar xf web-${BUILD_ID}.tar.gz \u0026amp;\u0026amp; rm -rf web-${BUILD_ID}.tar.gz\u0026#34;\u0026#39; sh \u0026#39;ssh 11.0.1.7 \u0026#34;cd /root \u0026amp;\u0026amp; rm -rf game \u0026amp;\u0026amp; ln -s web-${BUILD_ID} game\u0026#34;\u0026#39; } } } } [root@gitlab game]#git commit -am \u0026#34;new Jenkinsfile\u0026#34; [main ca6f632] new Jenkinsfile 1 file changed, 2 insertions(+), 1 deletion(-) [root@gitlab game]#git push -u origin main Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 317 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/game.git ce0d6dc..ca6f632 main -\u0026gt; main Branch main set up to track remote branch main from origin. ","date":"2026-10-09","description":"","objectID":"/posts/jenkins-pipeline/","readingTime":2,"tags":["DevOps","Jenkins","Pipeline","CI/CD","Jenkinsfile"],"title":"Jenkins Pipeline 流水线","url":"/posts/jenkins-pipeline/","wordCount":322},{"categories":["DevOps"],"content":"01 .Jenkins集成SonarQube 一. jenkins上配置SonarQube服务端 将Build Steps和执行shell调换位置，Build Steps在上面，执行shell在下面\n1.Name定义\n2.URL添加SonarQube的地址 token添加一个secret text 填写之前安装SonarQube的Token\n二. Jekins配置sonar客户端的位置 jenkins配置执行sonar-scanner命令的家目录，让jenkins能找到该命令 系统管理-\u0026gt;全局工具配置\n三. 项目中集成SonarQube 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@gitlab game]#vim index.html .... [\u0026#39;魂斗罗v5\u0026#39;, \u0026#39;roms/Contra1(U)30.nes\u0026#39;], ... [root@gitlab game]#git commit -am \u0026#34;v5\u0026#34; [main c03f21c] v5 1 file changed, 1 insertion(+), 1 deletion(-) [root@gitlab game]#git push -u origin main Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 273 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/game.git d0cef87..c03f21c main -\u0026gt; main Branch main set up to track remote branch main from origin. 自动推送成功\n02. Jenkins集成微信 一. 集成微信介绍 jenkins集成微信，测试环境流程，开发写完代码直接提交到gitlab，自动触发Jenkins执行构建，运行结果可以直接展示在测试平台，每次发版的测试版本信息可以通过Jenkins发送到企业微信通知相关项目人员。\n二. 注册企业微信公众号 网址: https://mp.weixin.qq.com/\n选择完成后把下面信息进行完善-\u0026gt;使用自己微信扫描二维码进行管理后-点注册\n注册成功登陆后点击应用管理-\u0026gt;创建应用\n上传一张log-\u0026gt;填写应用名称-可见范围-\u0026gt;创建应用-保存好agentld和secret写入py脚本\n三. 修改py脚本信息 1 2 3 4 5 6 #1. 上传脚本到/server/scripts [root@jenkins ~]# mkdir -p /server/scripts [root@jenkins ~]# cd /server/scripts [root@jenkins scripts]#ll total 4 -rw-r--r-- 1 root root 2008 Oct 2 18:59 jenkins_notify.py 四. 本地执行脚本测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@jenkins ~]#yum -y install python-pip [root@jenkins ~]#wget https://bootstrap.pypa.io/pip/2.7/get-pip.py [root@jenkins ~]#python get-pip.py [root@jenkins ~]#pip2.7 install requests [root@jenkins scripts]#python jenkins_notify.py 11 /etc/hosts game [变更日志] : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 [变更日志] : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 {\u0026#34;errcode\u0026#34;:0,\u0026#34;errmsg\u0026#34;:\u0026#34;ok\u0026#34;,\u0026#34;msgid\u0026#34;:\u0026#34;cpB2NxqCi8uaKeFHyR1iukyQoHK88hteL3ZQ36qc1J-s6dxwr9nVl5U1rnbiYNHHMEhpIkDaLTfEWfIwu7JVlRs_bPedgINU2lOaYHsoih9vWaXL-BAMnxRfc6wD5f19\u0026#34;} [root@jenkins scripts]#python jenkins_notify.py 11.0.1.201 /etc/hosts game [变更日志] : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 [变更日志] : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 {\u0026#34;errcode\u0026#34;:0,\u0026#34;errmsg\u0026#34;:\u0026#34;ok\u0026#34;,\u0026#34;msgid\u0026#34;:\u0026#34;cpB2NxqCi8uaKeFHyR1iukyQoHK88hteL3ZQ36qc1J_hJhqCw0rb1QjAxDrJIwd1leNF0jHEWgispRPFbstm-I_VCKgfsRHM266lQO-Mvzg0XD0JGP2Vi-cc9yPN98Fd\u0026#34;} 五. 集成到Jenkins 安装第三方插件:\n1.由于jenkins没有官网的插件来完成此功能，所以我们只能用网络上一些开源的插件\ngithub下载代码 https://github.com/daniel-beck/changelog-environment-plugin 解压到某个目录-\u0026gt; 进入目录执行以下操作 cd 到 changelog-environment-plugin-master 下，执行 mvn verify 时间较长，会在changelog-environment-plugin-master/target/下有个changelog-environment.hpi文件，上传到jenkins即可使用\n2.安装插件\n3.重启Jenkins服务\n1 [root@jenkins scripts]#systemctl restart jenkins 4.集成到jenkins\n进入到项目:\n1 2 Entry Format中添加 %3$s(at %4$s via %1$s)，参数分别为ChangeLog内容，时间，提交人。 Date Format中添加 yyyy-MM-dd HH:mm:ss 就是时间格式。 1 2 3 4 5 复制执行命令 echo \u0026#34;==========Start Notify==============\u0026#34; echo ${SCM_CHANGELOG} \u0026gt; /tmp/${JOB_NAME}_change.log python /server/scripts/jenkins_notify.py ${BUILD_URL} /tmp/${JOB_NAME}_change.log ${JOB_NAME} rm -fv /tmp/${JOB_NAME}_change.log 5.测试效果\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@gitlab game]#git add . [root@gitlab game]#git commit -am \u0026#34;v8\u0026#34; [main 77d13e2] v8 1 file changed, 1 insertion(+), 1 deletion(-) [root@gitlab game]#git push -u origin main Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 270 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/game.git bb52c00..77d13e2 main -\u0026gt; main Branch main set up to track remote branch main from origin. ","date":"2026-10-09","description":"","objectID":"/posts/jenkins-sonarqube-wecom-notification/","readingTime":2,"tags":["DevOps","Jenkins","SonarQube","企业微信","CI/CD"],"title":"Jenkins 集成 SonarQube 质量门禁与企业微信通知","url":"/posts/jenkins-sonarqube-wecom-notification/","wordCount":393},{"categories":["DevOps"],"content":"01. Jenkins拉取gitlab代码 一. gitlab创建仓库上传game代码 二. Jenkins创建项目 1.创建项目\n2.配置连接的gitlab项目地址（这里没有报错是因为之前测试的时候做过免密钥了）\n3.Jenkins服务器生成密钥对\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #配置免密钥 [root@jenkins ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Rr4ouinx/cPqVxJsxju2Cjd1mhWmWSuKGOKG532uTvk root@jenkins The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | | | | | o = | | *B o | |o o=oS | |=o . +=O.. | |o=+.Bo+=. | |oo.B.+* | | .=+BOE. | +----[SHA256]-----+ [root@jenkins ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+kaj/NEwHKaIErMEClVYuG3JuWr9CD7XpuO5YZ6JftMmNdAPMrvS0mWqP4SDyT6/le0j1RGkA48GsirESfMqXKvzSSd5AuhIPmUADtGPNK6VSeiMKyoWgiNBFLn6RIOAcXI0MuZrYQMLJz+kOYr6kF9em/6MfFQrLkt9DzIlnR9b7CMLyDnPOZ8FG640DqqX0sZ8G+i2v41VX/c7RGIWAgEhJY+kiMS5cDxWlTf2k8hoZmh0VIa/G9AwnvbklyUiE4ACd9GtEJG5xXJzedn7q0/Ygfr26T29t+KST3SlyrXIaEbEC5WwIMymCEJDCPPCGzqLjsWa+4dbJhoEM1afV root@jenkins 4.将公钥拷贝到gitlab页面的SSH秘钥验证页面\n执行完成后还需要在jenkins服务器中执行一次克隆命令输入一次yes（这里没有提示是因为之前做测试的时候输过了）\n1 2 3 4 5 6 7 8 [root@jenkins ~]#git clone git@11.0.1.200:dawn/game.git Cloning into \u0026#39;game\u0026#39;... remote: Enumerating objects: 98, done. remote: Counting objects: 100% (98/98), done. remote: Compressing objects: 100% (96/96), done. remote: Total 98 (delta 3), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (98/98), 7.35 MiB | 4.49 MiB/s, done. Resolving deltas: 100% (3/3), done. 5.在查看jenkins的项目配置(如果还是看到报错提示,点击保存退出后在重新进入到项目)\n完成后点击立即构建:\n拉取成功\n查看项目目录下是否拉取game代码成功\n1 2 3 4 5 6 7 8 9 [root@jenkins ~]#ll /var/lib/jenkins/workspace/game_job/ total 48 -rw-r--r-- 1 root root 28032 Oct 2 16:11 bgm.mp3 drwxr-xr-x 2 root root 23 Oct 2 16:11 css drwxr-xr-x 2 root root 23 Oct 2 16:11 images -rw-r--r-- 1 root root 8956 Oct 2 16:11 index.html drwxr-xr-x 2 root root 213 Oct 2 16:11 js drwxr-xr-x 2 root root 4096 Oct 2 16:11 roms -rw-r--r-- 1 root root 811 Oct 2 16:11 shuoming.html 02. Jenkins推送代码到web 将拉取到的代码推送到web服务器的Nginx站点目录运行\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1.创建web01服务器 2.安装Nginx服务 3.配置Nginx server [root@web01 conf.d]# cat default.conf server { listen 80 default_server; server_name _; location / { root /code/html; index index.html index.htm; } } 4.jenkins和web服务器做免秘钥 [root@jenkins ~]# ssh-copy-id -i .ssh/id_rsa.pub 11.0.1.7 jenkins项目中使用shell命令做打包推送动作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Shell脚本: DATE=$(date +%Y-%m-%d-%H-%M-%S) CODE_DIR=\u0026#34;./\u0026#34; WEB_DIR=\u0026#34;/code\u0026#34; get_code_tar(){ cd $CODE_DIR \u0026amp;\u0026amp; tar zcf /opt/web-$DATE.tar.gz ./* } scp_code_web(){ scp /opt/web-$DATE.tar.gz 11.0.1.7:$WEB_DIR } code_tarxf(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp;mkdir web-$DATE \u0026amp;\u0026amp; tar xf web-$DATE.tar.gz -C web-$DATE\u0026#34; } ln_html(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp; rm -rf html \u0026amp;\u0026amp; ln -s web-$DATE html\u0026#34; } main(){ get_code_tar; scp_code_web; code_tarxf; ln_html; } main 保存点击立即构建:\n浏览器访问web服务器显示成功\n03. 配置web-hook 1.开启触发器\n生成token并保存\n2.复制Token配置到Gitlab\n配置完成\n测试: 修改代码推送到gitlab仓库的游戏项目中,会自动触发Jenkins拉取代码并将拉取到的修改后的代码推送到web服务器\n修改前：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@gitlab game]#vim index.html .... [\u0026#39;魂斗罗v2\u0026#39;, \u0026#39;roms/Contra1(U)30.nes\u0026#39;], ... [root@gitlab game]#git commit -am \u0026#34;v2\u0026#34; [main ce12479] v2 1 file changed, 2 insertions(+), 2 deletions(-) [root@gitlab game]#git push -u origin main Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 275 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/game.git 0385f69..ce12479 main -\u0026gt; main Branch main set up to track remote branch main from origin 修改后：\n","date":"2026-10-09","description":"","objectID":"/posts/jenkins-gitlab-cicd-auto-deploy/","readingTime":3,"tags":["DevOps","Jenkins","GitLab","CI/CD","自动部署"],"title":"Jenkins + GitLab 打通实现 CI/CD 自动部署","url":"/posts/jenkins-gitlab-cicd-auto-deploy/","wordCount":509},{"categories":["MySQL"],"content":"数据库版本升级方法 方法一：本地升级 （Inplace）（本实验采用此方式） 单台服务器进行程序升级\n1 11.0.1.101 mysql5.7 --\u0026gt; mysql8.0 (加载数据) 业务影响 方法二：迁移升级 （Mergeing）（后续实验操作） 两台服务器进行程序升级\n1 2 11.0.1.101 mysql5.7 数据信息 迁移 业务影响-最小 11.0.1.102 mysql8.0 数据信息 恢复 测试业务 主从同步 数据库升级规范 数据库服务版本升级时，只支持在GA（General Availability）版本之间进行升级 数据库服务版本升级时，支持从数据库5.6到5.7再到8.0，跨版本升级，但是需要先将5.6升级到最新小版本，在进行跨版本升级 数据库服务版本升级时，需要提前考虑好版本回退的方案，最好升级前做好数据备份（特别是向8.0版本升级） 数据库服务版本升级时，制定的升级方案和升级步骤，需要尽可能降低数据库服务停机的时间 数据库服务官方参考资料：https://dev.mysql.com/doc/refman/8.0/en/upgrade-paths.html\n数据库升级流程 数据库服务数据备份保存（可以采用热备和冷备两种方案，冷备是需要停止业务后备份，热备是无需停止业务备份） 数据库服务最新程序安装（最新版本数据库服务安装过程时，无需停止原有数据库旧版服务） 数据库服务原有程序关闭（网站显示维护页面）2个页面 提前通知 升级过程说明信息 数据库服务最新程序启动（加载原有程序数据实现挂库升级，并采用跳过授权表和跳过网络方式启动） 采用安全模式启动新的数据库服务 \u0026ndash;skip-grant-tables \u0026ndash;skip-networking 加载旧版本数据信息 数据库服务升级数据结构（数据库服务升级程序后，还需要升级数据系统结构信息，因此升级时间和数据量无关） 数据库服务可以正常重启（数据库服务升级完毕后，确认数据库服务是可以正常完成重启操作） 数据库服务功能测试验证（反复核实验证与数据库服务相关的各项功能是否正常） 数据库服务升级工作完毕（取消网站维护页面，恢复正常网站线上运营业务） 数据库升级涉及两个方面 数据库程序升级：安装新的程序（安装新版数据库实例） 新的程序加载升级后的数据目录\n数据库数据升级：数据目录结构升级/将授权表结构信息升级\n数据库版本升级流程 环境准备：安装旧的数据库程序（详情见systemd 服务管理与MySQL 多版本部署） 模拟创建数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 [root@master ~]#mysql -uroot -S /tmp/mysql3356.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 1 Server version: 5.6.48 MySQL Community Server (GPL) Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; create database zlm; Query OK, 1 row affected (0.00 sec) mysql\u0026gt; create database lmy; Query OK, 1 row affected (0.00 sec) mysql\u0026gt; use zlm; Database changed mysql\u0026gt; create table t1 (id int,name char(10),age int); Query OK, 0 rows affected (0.02 sec) mysql\u0026gt; show tables; +---------------+ | Tables_in_zlm | +---------------+ | t1 | +---------------+ 1 row in set (0.00 sec) mysql\u0026gt; insert into t1 values (1,\u0026#39;laoming\u0026#39;,20),(2,\u0026#39;xiaoming\u0026#39;,21),(3,\u0026#39;dawn\u0026#39;,22); Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql\u0026gt; SELECT * FROM t1; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | laoming | 20 | | 2 | xiaoming | 21 | | 3 | dawn | 22 | +------+----------+------+ 3 rows in set (0.00 sec) mysql\u0026gt; 5.6 \u0026ndash; 5.7 步骤一：进行数据备份 （冷备） 1 2 3 4 5 6 7 8 [root@master ~]#systemctl stop mysqld3356 [root@master ~]#netstat -ntpl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 989/sshd tcp6 0 0 :::22 :::* LISTEN 989/sshd [root@master ~]#mkdir /backup/ [root@master ~]#cp -a /data/3356/data /backup/data_3356_bak 步骤二：安全模式启动新版数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master ~]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql57 datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master ~]#mkdir -p /run/mysqld3356 [root@master ~]#chown mysql:mysql /run/mysqld3356 [root@master ~]#/usr/local/mysql57/bin/mysqld_safe --defaults-file=/etc/my3356.cnf --skip-grant-tables --skip-networking \u0026amp; 数据信息没有升级前，授权表结构 5.6 mysql.user 信息\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 mysql\u0026gt; select * from mysql.user\\G *************************** 1. row *************************** Host: localhost User: root Password: Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N *************************** 2. row *************************** Host: master User: root Password: Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N *************************** 3. row *************************** Host: 127.0.0.1 User: root Password: Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N *************************** 4. row *************************** Host: ::1 User: root Password: Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N *************************** 5. row *************************** Host: localhost User: Password: Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: NULL password_expired: N *************************** 6. row *************************** Host: master User: Password: Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: NULL password_expired: N 6 rows in set (0.00 sec) 步骤三：进行数据目录和授权表结构信息升级 1 2 #修改授权表结构 [root@master ~]#/usr/local/mysql57/bin/mysql_upgrade -S /tmp/mysql3356.sock --force 数据信息已经升级后，授权表结构 5.7 mysql.user 信息\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 mysql\u0026gt; select * from mysql.user\\G *************************** 1. row *************************** Host: localhost User: root Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 2. row *************************** Host: master User: root Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 3. row *************************** Host: 127.0.0.1 User: root Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 4. row *************************** Host: ::1 User: root Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 5. row *************************** Host: localhost User: Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: NULL password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 6. row *************************** Host: master User: Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: NULL password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 7. row *************************** Host: localhost User: mysql.session Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: Y Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: Y *************************** 8. row *************************** Host: localhost User: mysql.sys Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: Y 8 rows in set (0.00 sec) 步骤四：正常重新启动数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master ~]#pkill mysql [root@master ~]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target [root@master ~]#systemctl daemon-reload [root@master ~]#systemctl start mysqld3356 5.7 \u0026ndash; 8.0 步骤一：进行数据备份 （冷备） 1 2 [root@master ~]#systemctl stop mysqld3356 [root@master ~]#cp -a /data/3356/data /backup/data_3357_bak 步骤二：安全模式启动新版数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@master ~]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master ~]#mkdir -p /run/mysqld3356 \u0026amp;\u0026amp; chown mysql:mysql /run/mysqld3356 [root@master ~]#mysqld_safe --defaults-file=/etc/my.cnf --skip-grant-tables --skip-networking \u0026amp; 步骤三：正常重新启动数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@master ~]#pkill mysql [root@master ~]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target [root@master ~]#systemctl daemon-reload [root@master ~]#systemctl start mysqld3356.service 5.7程序升级8.0 mysql-shell（函数功能可以检测数据库程序是否可以升级到8.0版本） 步骤一：下载安装mysql-shell软件工具 https://downloads.mysql.com/archives/shell/\n1 2 3 4 5 6 7 [root@master ~]#cd /usr/local/ [root@master local]#tar xf mysql-shell-8.0.26-linux-glibc2.12-x86-64bit.tar.gz [root@master local]#ln -s mysql-shell-8.0.26-linux-glibc2.12-x86-64bit mysqlsh [root@master local]#vim /etc/profile export PATH=$PATH:/usr/local/mysqlsh/bin [root@master local]#. /etc/profile 步骤二：进行检测低版本数据库程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 # 准备5.7数据库服务环境 [root@master ~]#vim /usr/lib/systemd/system/mysqld3357.service [Unit] Description=MySQL 5.7 Server (3357) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3357.cnf RuntimeDirectory=mysqld3357 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target [root@master ~]#vim /etc/my3357.cnf [mysql] socket=/tmp/mysql3357.sock [mysqld] user=mysql basedir=/usr/local/mysql57 datadir=/data/3357/data socket=/tmp/mysql3357.sock port=3357 pid-file=/run/mysqld3357/mysqld.pid [root@master ~]#systemctl start mysqld3357.service [root@master ~]#mysql -uroot -S /tmp/mysql3357.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 2 Server version: 5.7.40 MySQL Community Server (GPL) Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; create user root@\u0026#39;11.0.1.%\u0026#39; identified by \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.02 sec) mysql\u0026gt; grant all on *.* to root@\u0026#39;11.0.1.%\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; quit Bye [root@master ~]#mysqlsh root:123456@11.0.1.101:3357 -e \u0026#34;util.checkForServerUpgrade()\u0026#34; 执行以上命令，若没有error提示，表示可以实现版本升级 5.7.40 \u0026ndash;\u0026gt; 8.0.26\n数据库程序升级失败，进行程序回退 步骤一：关闭新版数据库服务程序 1 [root@master ~]#systemctl stop mysqld3356 步骤二：还原备份数据信息 5.6 - 5.7 备份56数据重新恢复到56程序的数据目录\n1 2 [root@master ~]#\\rm -rf /data/3356/data/* [root@master ~]#cp -a /backup/data_3356_bak/* /data/3356/data/ 步骤三：重新运行启动旧版本数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@master ~]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql56 datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master ~]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql56/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target [root@master ~]#systemctl daemon-reload [root@master ~]#systemctl start mysqld3356 步骤四：检查旧版数据库服务状态 1 2 3 [root@master ~]#ps -ef|grep mysql /mysql56/bin/mysqld --defaults-file=/etc/my3356.cnf root 4117 1384 0 21:40 pts/0 00:00:00 grep --color=auto mysql ","date":"2026-10-08","description":"","objectID":"/posts/mysql-version-upgrade-rollback/","readingTime":14,"tags":["MySQL","版本升级","回退","mysql-shell"],"title":"MySQL 版本升级实战与回退：5.6→5.7→8.0 就地升级、mysql-shell 检测与回退方案","url":"/posts/mysql-version-upgrade-rollback/","wordCount":2839},{"categories":["MySQL"],"content":"步骤一：架构环境规划 master 11.0.1.101 搭建3个数据库服务实例\n1 2 3 mysql01 mysql-8.0.26 /data/3307/data /data/3307/my.cnf 3307 /tmp/mysql3307.sock mysql02 mysql-8.0.26 /data/3308/data /data/3308/my.cnf 3308 /tmp/mysql3308.sock mysql03 mysql-8.0.26 /data/3309/data /data/3309/my.cnf 3309 /tmp/mysql3309.sock 步骤二：创建多实例环境 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@master ~]#mkdir -p /data/33{07..09}/data [root@master ~]#chown -R mysql.mysql /data [root@master ~]#cat \u0026gt;/data/3307/my.cnf \u0026lt;\u0026lt;EOF \u0026gt; [mysqld] \u0026gt; mysqlx=0 \u0026gt; port=3307 \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3307/data \u0026gt; socket=/tmp/mysql3307.sock \u0026gt; EOF [root@master ~]#cat \u0026gt;/data/3308/my.cnf \u0026lt;\u0026lt;EOF \u0026gt; [mysqld] \u0026gt; mysqlx=0 \u0026gt; port=3308 \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3308/data \u0026gt; socket=/tmp/mysql3308.sock \u0026gt; EOF [root@master ~]#cat \u0026gt;/data/3309/my.cnf \u0026lt;\u0026lt;EOF \u0026gt; [mysqld] \u0026gt; mysqlx=0 \u0026gt; port=3309 \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3309/data \u0026gt; socket=/tmp/mysql3309.sock \u0026gt; EOF 步骤三：多实例初始化 1 2 3 [root@master ~]#mysqld --initialize-insecure --user=mysql --datadir=/data/3307/data --basedir=/usr/local/mysql [root@master ~]#mysqld --initialize-insecure --user=mysql --datadir=/data/3308/data --basedir=/usr/local/mysql [root@master ~]#mysqld --initialize-insecure --user=mysql --datadir=/data/3309/data --basedir=/usr/local/mysql 步骤四：运行启动多实例 命令启动：\n1 2 3 4 5 6 [root@master ~]#mysqld --defaults-file=/data/3307/my.cnf \u0026amp; [1] 1821 [root@master ~]#mysqld --defaults-file=/data/3308/my.cnf \u0026amp; [2] 1859 [root@master ~]#mysqld --defaults-file=/data/3309/my.cnf \u0026amp; [3] 1897 利用service文件启动：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 [root@master ~]#cat \u0026gt;/usr/lib/systemd/system/mysqld3307.service\u0026lt;\u0026lt;EOF \u0026gt; [Unit] \u0026gt; Description=MySQL Server \u0026gt; Documentation=mysqld.service \u0026gt; After=network.target \u0026gt; After=syslog.target \u0026gt; \u0026gt; [Install] \u0026gt; WantedBy=multi-user.target \u0026gt; \u0026gt; [Service] \u0026gt; User=mysql \u0026gt; Group=mysql \u0026gt; ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/data/3307/my.cnf \u0026gt; LimitNOFILE = 5000 \u0026gt; EOF [root@master ~]#cat \u0026gt;/usr/lib/systemd/system/mysqld3308.service\u0026lt;\u0026lt;EOF \u0026gt; [Unit] \u0026gt; Description=MySQL Server \u0026gt; Documentation=mysqld.service \u0026gt; After=network.target \u0026gt; After=syslog.target \u0026gt; \u0026gt; [Install] \u0026gt; WantedBy=multi-user.target \u0026gt; \u0026gt; [Service] \u0026gt; User=mysql \u0026gt; Group=mysql \u0026gt; ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/data/3308/my.cnf \u0026gt; LimitNOFILE = 5000 \u0026gt; EOF [root@master ~]#cat \u0026gt;/usr/lib/systemd/system/mysqld3309.service\u0026lt;\u0026lt;EOF \u0026gt; [Unit] \u0026gt; Description=MySQL Server \u0026gt; Documentation=mysqld.service \u0026gt; After=network.target \u0026gt; After=syslog.target \u0026gt; \u0026gt; [Install] \u0026gt; WantedBy=multi-user.target \u0026gt; \u0026gt; [Service] \u0026gt; User=mysql \u0026gt; Group=mysql \u0026gt; ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/data/3309/my.cnf \u0026gt; LimitNOFILE = 5000 \u0026gt; EOF [root@master ~]#systemctl daemon-reload [root@master ~]#systemctl start mysqld3307 [root@master ~]#systemctl start mysqld3308 [root@master ~]#systemctl start mysqld3309 步骤五：多实例环境验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 [root@master ~]#ps -ef|grep mysql mysql 1821 1434 0 13:48 pts/0 00:00:02 mysqld --defaults-file=/data/3307/my.cnf mysql 1859 1434 0 13:49 pts/0 00:00:02 mysqld --defaults-file=/data/3308/my.cnf mysql 1897 1434 0 13:49 pts/0 00:00:02 mysqld --defaults-file=/data/3309/my.cnf [root@master ~]#netstat -lntup|grep mysql tcp6 0 0 :::3307 :::* LISTEN 1821/mysqld tcp6 0 0 :::3308 :::* LISTEN 1859/mysqld tcp6 0 0 :::3309 :::* LISTEN 1897/mysqld [root@master ~]#mysql -uroot -S /tmp/mysql3307.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; select @@port; +--------+ | @@port | +--------+ | 3307 | +--------+ 1 row in set (0.00 sec) mysql\u0026gt; quit Bye [root@master ~]#mysql -uroot -S /tmp/mysql3308.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; select @@port; +--------+ | @@port | +--------+ | 3308 | +--------+ 1 row in set (0.00 sec) mysql\u0026gt; quit Bye [root@master ~]#mysql -uroot -S /tmp/mysql3309.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; select @@port; +--------+ | @@port | +--------+ | 3309 | +--------+ 1 row in set (0.00 sec) mysql\u0026gt; quit Bye ","date":"2026-10-08","description":"","objectID":"/posts/mysql-multi-instance-deployment/","readingTime":4,"tags":["MySQL","多实例","systemd","部署"],"title":"MySQL 多实例部署实战：单主机三实例规划、初始化与 systemd 托管","url":"/posts/mysql-multi-instance-deployment/","wordCount":761},{"categories":["OpenStack"],"content":"一、flat网络设计 1 2 [root@Controller ~]# source keystonerc_admin [root@Controller ~(keystone_admin)]# neutron agent-list 每台云主机每块网卡都有一个qbr，因为安全组是关联到网卡的，每块网卡关联不同的安全组\n1 2 3 4 [root@Compute01 ~]# brctl show bridge name\tbridge id\tSTP enabled\tinterfaces qbr24c7a169-96\t8000.ceb36944e179\tno\tqvb24c7a169-96 tap24c7a169-96 QEMU虚拟化套件 + KVM虚拟化内核\n1 integration_bridge=br-int 1 [root@Compute01 ~]# ovs-vsctl show 让VM1（web01）可以访问Internet，或者与其他网段互通\n在网络节点上配置neutron 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@Controller ~(keystone_admin)]# cd /etc/neutron/plugins/ml2/ [root@Controller ml2(keystone_admin)]# vim ml2_conf.ini [ml2] type_drivers=geneve,flat,vlan,vxlan tenant_network_types=flat mechanism_drivers=openvswitch path_mtu=0 extension_drivers=port_security,qos [securitygroup] enable_security_group=True [ml2_type_geneve] max_header_size=38 vni_ranges=10:100 [ml2_type_flat] flat_networks=default,extent [root@Controller ml2(keystone_admin)]# vim openvswitch_agent.ini [ovs] bridge_mappings=default:br-eth1,extnet:br-ex integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.10 [root@Controller ~(keystone_admin)]# systemctl restart neutron-* 在openstack web界面上，使用admin身份登录，在管理员菜单上，创建外部网络，选择flat，名称为extnet 使用租户身份登录，创建路由器，关联外部网络和子网 相关问题 问题1： 弹性IP地址是配置在哪里的？在哪里可以找到这个弹性IP地址\n1 2 3 [root@Controller ~(keystone_admin)]# ip netns ls qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 (id: 1) qdhcp-7d04c8e1-3ccf-4efe-85c3-9370bfc6a88a (id: 0) namespace 命名空间\n1 2 3 4 5 [root@Controller ~(keystone_admin)]# ip netns exec qdhcp-7d04c8e1-3ccf-4efe-85c3-9370bfc6a88a ip addr list [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 ip addr list [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 route -n [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 ping www.baidu.com [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 ping 192.168.31.1 问题2： 弹性IP绑定在路由器上，那是如何通过弹性IP访问云主机的\n1 2 [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 iptables -t nat -nL #iptables -t nat -A PREROUTING -d 192.168.31.17 -j DNAT --to-dest 192.168.33.254 问题3： 如果云主机绑定了弹性IP地址，会优先通过弹性IP地址出去，如果没有绑定，会通过路由器开启SNAT出去访问\nVLAN 静态路由 NAT TCP三层握手\n弹性IP地址不一定是为访问Internet而准备的，而是为了访问服务，也可以把弹性IP地址绑定给ELB负载均衡\nflat网络拓扑图\n二、VLAN网络设计和实现 配置网络节点和计算节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Controller ~]# cd /etc/neutron/plugins/ml2/ [root@Controller ml2]# vim ml2_conf.ini [ml2] type_drivers=geneve,flat,vlan,vxlan tenant_network_types=vlan mechanism_drivers=openvswitch path_mtu=0 extension_drivers=port_security,qos [ml2_type_flat] flat_networks=default,extent [ml2_type_vlan] network_vlan_ranges=mynetwork:100:1000 [root@Controller ml2]# vim openvswitch_agent.ini [ovs] bridge_mappings=mynetwork:br-eth1,extnet:br-ex integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.10 [root@Controller ~]# systemctl restart neutron-* 计算节点\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@Compute01 ~]# cd /etc/neutron/plugins/ml2/ [root@Compute01 ml2]# vim openvswitch_agent.ini [ovs] bridge_mappings=mynetwork:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.11 [root@Compute01 ~]# systemctl restart neutron-openvswitch-agent.service [root@Compute02 ~]# cd /etc/neutron/plugins/ml2/ [root@Compute02 ml2]# vim openvswitch_agent.ini [ovs] bridge_mappings=mynetwork:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.12 [root@Compute02 ~]# systemctl restart neutron-openvswitch-agent.service 在openstack web界面上，使用admin身份创建网络 使用zlm身份创建两个云主机 查看\nopenstack neutron流表\nVLAN网络的流表分析\n1 [root@Compute01 ~]# ovs-ofctl dump-flows br-eth1 priority=4 数字越大，优先级越高\n1 2 3 cookie=0x28f07cbfb122feff, duration=1736.064s, table=0, n_packets=267, n_bytes=39412, priority=4,in_port=\u0026#34;phy-br-eth1\u0026#34;,dl_vlan=1 actions=mod_vlan_vid:100,NORMAL [root@Compute01 ~]# ovs-ofctl dump-flows br-int cookie=0xffd369a083b424eb, duration=2050.913s, table=0, n_packets=1241, n_bytes=236428, priority=3,in_port=\u0026#34;int-br-eth1\u0026#34;,dl_vlan=100 actions=mod_vlan_vid:1,resubmit(,60) flat网络流表分析\n1 2 [root@Compute01 ~]# ovs-ofctl dump-flows br-eth1 cookie=0x632acfea08b01b9d, duration=311.151s, table=0, n_packets=81, n_bytes=4210, priority=4,in_port=\u0026#34;phy-br-eth1\u0026#34;,dl_vlan=1 actions=strip_vlan,NORMAL cookie=0x632acfea08b01b9d, duration=326.012s, table=0, n_packets=4, n_bytes=352, priority=2,in_port=\u0026#34;phy-br-eth1\u0026#34; actions=drop cookie=0x632acfea08b01b9d, duration=326.023s, table=0, n_packets=13485, n_bytes=6729360, priority=0 actions=NORMAL 三、VXLAN的网络设计和实现 解决：大二层问题，跨数据中心云主机热迁移问题，热迁移只能在二层网络中\nVXLAN实现将二层网络封装在三层中进行传输，到了对方解封装，实现二层通讯\n修改网络节点配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@Controller ~]# vim /etc/neutron/plugins/ml2/ml2_conf.ini [ml2] type_drivers=geneve,flat,vlan,vxlan tenant_network_types=vxlan mechanism_drivers=openvswitch path_mtu=0 extension_drivers=port_security,qos [ml2_type_vxlan] vxlan_group=224.0.0.1 vni_ranges=10:100 [root@Controller ~]# vim /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings=mynetwork:br-eth1,extnet:br-ex integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.10 [root@Controller ~]# systemctl restart neutron-server.service 相关问题 Compute01： web111云主机 192.168.99.140\nCompute02： web222云主机 192.168.99.14\n问题1： web111可以ping通web222吗？\n可以\n问题2： 192.168.88.126可以ping通192.168.99.0/24主机？\n可以\n**问题3：**云主机是如何获得metadata信息的？\nmetadata元数据\nnova-metadata\nneutron-metadata\n当云主机开机时，会运行cloud-init程序，cloud-init会通知云主机去访问http://169.254.169.254 因为在云主机中有一条默认路由，route -n 169.254.169.254 192.168.99.254 255.255.255.255 UGH 100 0 0 ens3\n从而将请求转发给网关，即VRouter，这个请求将进入网络节点\n网络节点收到这个请求后，将该请求转发给neutron-ns-proxy进程，这个进程通过内部接口转发给neutron-metadata-agent 1 2 3 4 5 6 [root@Controller ~]# ip netns ls [root@Controller ~]# ip netns exec qrouter-d88a6264-ff05-498c-ab98-f56a261da0e0 iptables -t nat -nL REDIRECT tcp -- 0.0.0.0/0 169.254.169.254 tcp dpt:80 redir ports 9697 [root@Controller ~]# ip netns exec qrouter-d88a6264-ff05-498c-ab98-f56a261da0e0 netstat -tulnp [root@Controller ~]# ps aux |grep proxy |grep neutron neutron 52920 0.0 0.0 156032 2700 ? Ss 14:37 0:00 haproxy -f /var/lib/neutron/ns-metadata-proxy/d88a6264-ff05-498c-ab98-f56a261da0e0.conf 因为neutron网络节点与控制节点管理网络互通，当用户申请云主机时，会填写云主机名称，root密码，秘钥等信息，当用户输入这些信息时，将这些请求发送给controller节点nova-api，nova-api会转发给nova-metadata-api，因为网络节点和控制节点管理网络互通，所以neutron-metadata服务向nova-metadata-api服务请求用户自定义信息，从而让云主机得到这些信息。 云主机先得到IP地址，IP地址是从DHCP中获取的，跟metadata无关，当云主机得到IP地址时，才触发了以上流程\n当用户申请云主机时，会输入云主机metadata数据（计算机名，root密码，ssh秘钥等），这个请求会发送nova-api，nova-api会将这些信息转发给nova-api-metadata进行存储\n云主机创建完成后，开机，通过DHCP获取到IP地址，运行cloud-init，cloud-init根据配置文件，去请求http://169.254.169.254\n当用户没有创建路由器时，无法得到用户的metadata信息\n","date":"2026-10-07","description":"","objectID":"/posts/openstack-neutron-flat-vlan-vxlan/","readingTime":3,"tags":["OpenStack","Neutron","VLAN","VXLAN","OVS"],"title":"OpenStack Neutron 网络类型设计与实现：flat、VLAN 与 VXLAN","url":"/posts/openstack-neutron-flat-vlan-vxlan/","wordCount":518},{"categories":["OpenStack"],"content":"一、Neutron网络服务 SDN： Software Define Network 软件定义网络\n虚拟交换机\nBridge： 桥接器\nOVS：Open Vswitch\nOVN： Open Virtual Network\n云租户隔离机制\nnamespace机制 命名空间\n1 2 3 4 #用于管理Linux以太网网桥 [root@Controller ~]# yum -y install bridge-utils [root@Compute01 ~]# yum -y install bridge-utils [root@Compute02 ~]# yum -y install bridge-utils tap设备： 虚拟机虚拟网卡名称，在虚拟机操作系统中网卡eth0，在openstack看来，就是一个tap设备\nqbr：Qemu Bridge 提供安全组规则，因为目前openswitch不支持iptables防火墙规则，引入qbr来实现iptables规则\nqvb : Bridge qbr\nqvo: OVS qvo ovs\nbr-int： 内部虚拟交换机\n如果VM1和VM2在同一个子网中，并且在同一台计算节点上，则VM1访问VM2，直接通过br-int这个内部虚拟交换机来实现即可，提升访问性能\n二、跨主机数据流 由于之前电脑资源限制，只创建了一台计算节点，现在为了满足实验需要，现在增加一台计算节点Compute02\n1.克隆Compute,将原来的Compute节点主机名修改成Compute01，将克隆的节点主机名修改成Compute02\n2.将克隆后的主机所有网卡重新生成MAC地址，以防冲突\n3.将克隆后的主机IP地址修改，以防止冲突\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Compute02 ~]# ip a 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens160: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:50:56:29:d6:05 brd ff:ff:ff:ff:ff:ff inet 192.168.100.12/24 brd 192.168.100.255 scope global noprefixroute ens160 valid_lft forever preferred_lft forever inet6 fe80::250:56ff:fe29:d605/64 scope link valid_lft forever preferred_lft forever 3: ens192: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:50:56:26:dc:b9 brd ff:ff:ff:ff:ff:ff inet 192.168.200.11/24 brd 192.168.200.255 scope global noprefixroute ens192 valid_lft forever preferred_lft forever inet6 fe80::250:56ff:fe26:dcb9/64 scope link valid_lft forever preferred_lft forever 4.将Controller、Compute01、Compute02和storage四台的/etc/hosts配置文件同步\n1 2 3 4 5 vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute01 192.168.100.12 Compute02 192.168.200.20 storage 6.将应答文件answers.txt修改，然后进行自动化部署将Compute02节点加入\n1 2 3 [root@Controller ~]# vim /etc/hosts CONFIG_COMPUTE_HOSTS=192.168.100.11,192.168.100.12 [root@Controller ~]# packstack --answer-file=/root/answers.txt 三、跨主机，跨网络数据流 四、访问Internet数据流 OpenStack Neutron SDN 软件定义网络 交换机OVS Router VLAN VXLAN隧道网络 Flat网络\n计算节点运行L2 Agent\nopenstack-openvswitch.service\n网络节点运行 L3 Agent\nneutron-l3-agent.service\nopenstack-openvswitch.service\ndhcp agent\nmetadata agent cloud-init，如果没有该服务，即便安装了cloud-init也无法实现自定义个性化信息，主机名 IP地址，root密码等\n查看各个节点运行的网络组件\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@Controller ~(keystone_admin)]# neutron agent-list neutron CLI is deprecated and will be removed in the future. Use openstack CLI instead. +--------------------------------------+--------------------+------------+-------------------+-------+----------------+---------------------------+ | id | agent_type | host | availability_zone | alive | admin_state_up | binary | +--------------------------------------+--------------------+------------+-------------------+-------+----------------+---------------------------+ | 1eea40a9-8ead-435a-a8b4-e08f39ed44ee | Metadata agent | Controller | | :-) | True | neutron-metadata-agent | | 2b5e9b53-f364-4ff2-8923-2b9124db90c6 | Open vSwitch agent | Compute02 | | :-) | True | neutron-openvswitch-agent | | 4b77d79e-4ce8-4f37-a35c-2c663199f8b3 | Open vSwitch agent | Compute | | :-) | True | neutron-openvswitch-agent | | 7c5771f1-ab63-41ab-b52a-d137b5d09dfd | Metering agent | Controller | | :-) | True | neutron-metering-agent | | 8751ee3e-a7e4-4d70-8233-44b4fb4b8bec | Open vSwitch agent | Controller | | :-) | True | neutron-openvswitch-agent | | 922ff7f3-8a7c-48e2-831c-57621542222c | L3 agent | Controller | nova | :-) | True | neutron-l3-agent | | ac7660d8-58d0-40da-81cd-3e8a2eec89ae | DHCP agent | Controller | nova | :-) | True | neutron-dhcp-agent | +--------------------------------------+--------------------+------------+-------------------+-------+----------------+---------------------------+ Agent： 代理，具体处理业务的存在 L2 Agent 处理真正二层交换数据\n**L3 Agent：**处理路由的数据\n在一个网络中，L2 Agent可以由Bridge或者OVS来实现，但同时只能使用其中之一\n所有的节点必须要么是Bridge，要么是OVS，如果想要我们的环境支持多种L2 Agent，可以引入ML2， 支持多种L2 Agent\n改造现有的VXLAN网络为flat网络（无VLAN隔离的网络类型）\n**LOCAL：**如果Neutron租户采用该网络，只有同一个计算节点的虚拟机才能通讯，仅主机模式，一般不采用\n**Flat：**扁平网络 不采用VLAN隔离的网络\n云主机之间不走192.168.100.0/24，减少网络压力\n单独使用一个网络来走云主机通讯网络\nVLAN： 带vlan tag的网络\nVXLAN,GRE等： 隧道网络\n五、云主机跨主机实现互访 给Controller、Compute01和Compute02分别再添加一张仅主机网卡\n控制节点：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [root@Controller network-scripts]# ip a 14: ens161: \u0026lt;BROADCAST,MULTICAST\u0026gt; mtu 1500 qdisc noop master ovs-system state DOWN group default qlen 1000 link/ether 00:0c:29:0b:63:c0 brd ff:ff:ff:ff:ff:ff [root@Controller network-scripts]# vim ifcfg-ens161 TYPE=Ethernet NAME=ens161 DEVICE=ens161 ONBOOT=yes HWADDR=00:0c:29:0b:63:c0 [root@Controller ~]# systemctl restart network [root@Controller ~]# ovs-vsctl add-br br-eth1 [root@Controller ~]# ovs-vsctl add-port br-eth1 ens161 [root@Controller ~]# ovs-vsctl show ... Bridge br-eth1 Port br-eth1 Interface br-eth1 type: internal Port ens161 Interface ens161 ... 分别在两个计算节点上\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 # Compute01 [root@Compute01 network-scripts]# ip a 3: ens161: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:0c:29:fa:35:77 brd ff:ff:ff:ff:ff:ff inet6 fe80::20c:29ff:fefa:3577/64 scope link valid_lft forever preferred_lft forever [root@Compute01 network-scripts]# vim ifcfg-ens161 TYPE=Ethernet NAME=ens161 DEVICE=ens161 ONBOOT=yes HWADDR=00:0c:29:fa:35:77 [root@Compute01 ~]# nmcli con reload [root@Compute01 ~]# nmcli con down ens161;nmcli con up ens161 Connection \u0026#39;ens161\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/2) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4) [root@Compute01 ~]# ovs-vsctl add-br br-eth1 [root@Compute01 ~]# ovs-vsctl add-port br-eth1 ens161 [root@Compute01 network-scripts]# vim ifcfg-ens161 ... Bridge br-eth1 Port br-eth1 Interface br-eth1 type: internal Port ens161 Interface ens161 ... # Compute02 [root@Compute02 network-scripts]# ip a 3: ens161: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:0c:29:f4:89:20 brd ff:ff:ff:ff:ff:ff inet6 fe80::20c:29ff:fef4:8920/64 scope link valid_lft forever preferred_lft forever [root@Compute02 network-scripts]# vim ifcfg-ens161 TYPE=Ethernet NAME=ens161 DEVICE=ens161 ONBOOT=yes HWADDR=00:0c:29:f4:89:20 [root@Compute02 network-scripts]# nmcli con reload [root@Compute02 network-scripts]# nmcli con down ens161;nmcli con up ens161 Connection \u0026#39;ens161\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/2) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4) [root@Compute02 network-scripts]# ovs-vsctl add-br br-eth1 [root@Compute02 network-scripts]# ovs-vsctl add-port br-eth1 ens161 [root@Compute02 network-scripts]# ovs-vsctl show ... Bridge br-eth1 Port br-eth1 Interface br-eth1 type: internal Port ens161 Interface ens161 ... 在控制节点上：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Controller plugins]# cd /etc/neutron/plugins/ml2/ [root@Controller ml2]# ls ml2_conf.ini openvswitch_agent.ini sriov_agent.ini [root@Controller ml2]# vim ml2_conf.ini [ml2] type_drivers=geneve,flat,vlan,vxlan tenant_network_types=flat mechanism_drivers=openvswitch path_mtu=0 extension_drivers=port_security,qos [ml2_type_flat] flat_networks=extnet,default [root@Controller ml2]# vim openvswitch_agent.ini [ovs] bridge_mappings =extnet:br-ex,default:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.10 [root@Controller ~]# systemctl restart neutron-server.service neutron-openvswitch-agent.service 计算节点：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@Compute01 ~]# vim /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings=default:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.11 [root@Compute01 ~]# systemctl restart neutron-openvswitch-agent.service [root@Compute02 ~]# vim /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings=default:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.12 [root@Compute02 ~]# systemctl restart neutron-openvswitch-agent.service 使用admin身份创建网络\n使用zlm用户身份创建两台云主机，测试是否可以互访\n","date":"2026-10-07","description":"","objectID":"/posts/openstack-neutron-network-service/","readingTime":5,"tags":["OpenStack","Neutron","OVS","namespace"],"title":"OpenStack Neutron 网络服务详解：虚拟交换组件与跨主机数据流分析","url":"/posts/openstack-neutron-network-service/","wordCount":877},{"categories":["OpenStack"],"content":"一、OpenStack 核心组件及作用 1. HORIZON —— 统一图形界面服务 核心功能：提供基于 Web 的 Dashboard 图形管理控制台，供云管理员和普通租户进行云资源的可视化调配与监控。 2. NOVA —— 计算资源管理服务 核心功能：负责管理和配置云计算环境中的虚拟化算力资源（nova-compute）。 角色分工： Controller 控制节点：运行 nova-api、nova-scheduler、nova-conductor 等调度服务。当用户发起创建云主机请求时，nova-api 接收请求并解析，nova-scheduler 根据规格策略选择最佳计算节点进行负载调度； Compute 计算节点：实际承载云主机运行的工作节点（支持对接 KVM、VMware ESXi、Xen 等异构虚拟化主机）。 控制节点的高可用与性能瓶颈： 控制节点承担所有 API 网关、数据库和消息队列，存在高可用与性能压力； 3. GLANCE —— 镜像服务 核心功能：负责操作系统镜像的发现、注册、检索与存储管理。 常见磁盘镜像格式： KVM 常用格式：qcow2（支持瘦分配与写时复制 COW）； VMware 常用格式：vmdk。 部署建议：通常复用并集成部署在控制节点上以实现高可用。 4. SWIFT —— 对象存储服务 核心功能：提供高可用、分布式、最终一致性的对象存储服务（Object Storage），专门用于存储静态无结构数据、云主机系统镜像、数据全量备份等。 5. NEUTRON —— 软件定义网络（SDN） 核心功能：提供云计算环境中的虚拟网络即服务，管理虚拟路由、交换机、安全组、浮动 IP 及跨节点流量打通。 网络技术：全面支持 VLAN、VXLAN、Geneve 及分布式虚拟路由（DVR）； 硬件 SDN 模式：生产环境中通常配备 2 台专用网络节点搭建双活或主备集群。 6. CINDER —— 块存储服务 核心功能：为云主机提供持久化的块存储（云硬盘），可动态挂载、卸载、快照与扩容。 底层对接能力： 对接传统存储阵列：华为 OceanStor、IBM、EMC 等高端硬件存储； 对接开源分布式存储：Ceph RBD、GlusterFS 等。 底层机制：当用户申请一块云硬盘时，Cinder 会向下透传指令到后端存储系统，本质上相当于在物理/分布式存储中划分并映射出一个 LUN。 对比说明：对象存储（如百度网盘、Swift、S3）无法作为块设备直接格式化挂载为云主机文件系统，而 Cinder 块设备可以直接格式化并进行挂载使用。 7. HEAT —— 基础设施编排服务 核心功能：基于声明式模板（HOT 模板或 AWS CloudFormation 格式），通过图形化编排或编写脚本，一键自动化批量部署多台虚拟机、数据库集群、网络拓扑与监控策略。 8. CEILOMETER —— 计量监控服务 核心功能：采集物理资源与虚拟资源的性能指标、运行时间与使用量。 公有云场景：记录 API 调用量、网络流出量、算力时长，对接计费计费系统； 私有云场景：监控租户配额水位，识别资源超用或空闲情况，为容量规划提供支撑。 9. KEYSTONE —— 统一身份认证与授权服务 核心功能：OpenStack 整体安全基石，负责全平台的租户（Project）、用户（User）、角色（Role）、服务目录与 Token 鉴权认证。 企业级身份联邦对接： 可与企业现有的 Windows Active Directory (AD) 或 Linux OpenLDAP 无缝整合，实现企业级统一账号单点登录（SSO）； 流程审批与权限隔离（以华为云为例）： 支持云主机多级申请与审批工作流（支持高达 5 级审批）； 贯彻“三权分立”安全准则（安全员、系统管理员、审计员分权管理）； 支持对接客户企业自研 OA/BPM 审批流系统。 核心组件功能速查表： 组件名称 代码代号 核心服务功能 Horizon Dashboard 统一 Web 图形化管理控制台 Nova Compute 计算资源生命周期与实例调度管理 Glance Image 虚拟机镜像存储与版本注册 Swift Object 大规模非结构化对象存储服务 Neutron Network 软件定义网络（SDN）、路由与安全组管理 Cinder Block 云主机持久化块存储（云硬盘）管理 Heat Orchestration 基础设施即代码（IaC）多资源栈编排 Ceilometer Metering 资源使用量统计、监控计量与配额分析 Keystone Identity 统一身份认证、鉴权令牌与服务目录管理 二、OpenStack 部署规划（以 Victoria 版为例） 1. 部署方式比较 开源 OpenStack 手动部署：按组件逐个编译配置，过程繁琐复杂，常用于底层机制深度学习； 工具化自动化部署： Ansible 部署（如 OpenStack-Ansible、Kolla-Ansible）； Packstack 部署：RedHat 开源的基于 Puppet 的自动化快速交付工具（本实战采用）； 2. 集群节点规格与网络规划 （1）Controller 控制节点（1 台） 承载组件： API 服务：nova-api、cinder-api、glance-api、neutron-server 等； 底层基础服务：MariaDB / MySQL、RabbitMQ 消息队列、Memcached、Keystone； 网络与管理服务：网络节点外网路由转发、YUM 本地缓存、Chrony 时间同步服务端。 系统配置要求： CPU：4 vCPU 起 内存：4GB 内存起 硬盘：100GB 可用磁盘空间 网卡：3 块网卡（网卡 1 配置为桥接模式访问互联网；网卡 2 与网卡 3 配置为仅主机 Host-Only 模式走内网业务） IP 地址规划： 内部管理 IP：192.168.100.10 外部访问 IP：192.168.10.10 （2）Compute 计算节点（1 台） 承载组件：nova-compute、网络 L2 Agent（Open vSwitch Agent） 系统配置要求： CPU：4 vCPU 起 内存：8GB 内存起（最低 4GB） 硬盘：100GB 磁盘空间 网卡：3 块网卡（全配置为仅主机 Host-Only 模式） IP 地址规划： Compute：192.168.100.11 （3）YUM 软件源服务器 + Chrony 时间服务器（1 台） 系统配置要求： CPU：2 vCPU 内存：2GB 网卡：2 块（一块桥接连接 Internet 同步公网时间与上游软件源，另一块仅主机模式为内网节点提供服务） IP 地址规划： 外网 IP：192.168.10.11 内网 IP：192.168.100.20 环境降配说明：若个人电脑硬件资源紧张，可最低压缩为 2 台虚拟机（控制节点复用为 YUM 源与时间服务器，另配 1 台计算节点）。注意：各节点系统时间必须保持高度一致，时钟漂移不得超过 5 分钟，否则 Keystone 握手与消息队列通信将全面失效。\n三、OpenStack 自动化部署实战全流程 阶段一：YUM 源服务器与 Chrony 时间服务器搭建 1. 配置网络接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 # 1. 配置内部通信网卡 ens160 [root@Cloud ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens160 TYPE=Ethernet BOOTPROTO=none NAME=ens160 DEVICE=ens160 ONBOOT=yes IPADDR=192.168.100.20 PREFIX=24 # 2. 配置可连接外网的网卡 ens192（用于同步公网时钟） [root@Cloud ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens192 TYPE=Ethernet BOOTPROTO=none NAME=ens192 DEVICE=ens192 ONBOOT=yes PROXY_METHOD=none BROWSER_ONLY=no IPADDR=192.168.10.11 PREFIX=24 GATEWAY=192.168.10.1 DNS1=192.168.10.1 DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=no UUID=03da7500-2101-c722-2438-d0d006c28c73 # 重启网卡使配置生效 [root@Cloud ~]# nmcli connection down ens160 ; nmcli connection up ens160 Connection \u0026#39;ens160\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/1) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4) [root@Cloud ~]# nmcli connection down ens192 ; nmcli connection up ens192 Connection \u0026#39;ens192\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/3) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/5) 2. 配置本地光盘 YUM 源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Cloud ~]# cd /etc/yum.repos.d/ [root@Cloud yum.repos.d]# mkdir bak [root@Cloud yum.repos.d]# mv CentOS-Linux-* bak/ # 挂载操作系统 ISO 光盘 mount /dev/cdrom /media/ # 编写光盘本地源配置文件 [root@Cloud ~]# cat \u0026gt; /etc/yum.repos.d/dvd.repo \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; \u0026gt; [BaseOS] \u0026gt; name=BaseOS \u0026gt; baseurl=file:///media/BaseOS \u0026gt; gpgcheck=0 \u0026gt; \u0026gt; [AppStream] \u0026gt; name=AppStream \u0026gt; baseurl=file:///media/AppStream \u0026gt; gpgcheck=0 \u0026gt; EOF 3. 配置 Chrony 时间同步服务端 由于 OpenStack 对节点间时钟同步要求极其严苛，先配置时间源：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # 编辑 /etc/chrony.conf [root@Cloud ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool ntp.tencent.com iburst # Allow NTP client access from local network. allow 192.168.100.0/24 # Serve time even if not synchronized to a time source. local stratum 10 # 重启并验证时间同步服务 [root@Cloud ~]# systemctl restart chronyd.service [root@Cloud ~]# chronyc sources 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* 106.55.184.199 2 6 17 2 -224us[ -144us] +/- 54ms # 关闭防火墙与 SELinux [root@Cloud ~]# systemctl disable firewalld.service --now [root@Cloud ~]# setenforce 0 [root@Cloud ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config 4. 搭建 httpd本地web yum仓库 将 CentOS 8 及 OpenStack Victoria 软件仓库发布为 HTTP 镜像源，供所有集群节点拉取。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 # 1. 安装并启动 Apache Web 服务 [root@Cloud ~]# yum -y install httpd [root@Cloud ~]# systemctl enable httpd --now # 2. 创建目录并挂载镜像文件 [root@Cloud ~]# mkdir /isos [root@Cloud ~]# mkdir -p /var/www/html/centos8 [root@Cloud ~]# mkdir -p /var/www/html/openstack # 挂载基础系统光盘与 OpenStack Victoria 安装镜像 [root@Cloud ~]# mount /dev/cdrom /var/www/html/centos8/ [root@Cloud ~]# mount /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack/ # 3. 持久化挂载写入 /etc/fstab [root@Cloud ~]# vim /etc/fstab /dev/cdrom /var/www/html/centos8 iso9660 defaults 0 0 /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack iso9660 defaults 0 0 # 4. 配置 HTTP YUM 仓库文件 [root@Cloud ~]# vim /etc/yum.repos.d/dvd.repo [BaseOS] name=BaseOS baseurl=http://192.168.100.20/centos8/BaseOS gpgcheck=0 [AppStream] name=AppStream baseurl=http://192.168.100.20/centos8/AppStream gpgcheck=0 [root@Cloud ~]# vim /etc/yum.repos.d/openstack.repo [centos-advanced-virtualization] name=centos-advanced-virtualization baseurl=http://192.168.100.20/openstack/centos-advanced-virtualization gpgcheck=0 [centos-nfv-openvswitch] name=centos-nfv-openvswitch baseurl=http://192.168.100.20/openstack/centos-nfv-openvswitch gpgcheck=0 [centos-rabbitmq-38] name=centos-rabbitmq-38 baseurl=http://192.168.100.20/openstack/centos-rabbitmq-38 gpgcheck=0 [PowerTools] name=PowerTools baseurl=http://192.168.100.20/openstack/PowerTools gpgcheck=0 [centos-ceph-nautilus] name=centos-ceph-nautilus baseurl=http://192.168.100.20/openstack/centos-ceph-nautilus gpgcheck=0 [centos-openstack-victoria] name=centos-openstack-victoria baseurl=http://192.168.100.20/openstack/centos-openstack-victoria gpgcheck=0 [extras] name=extras baseurl=http://192.168.100.20/openstack/extras gpgcheck=0 # 5. 将写好的 repo 文件批量分发至各控制节点与计算节点 [root@Cloud ~]# scp /etc/yum.repos.d/dvd.repo /etc/yum.repos.d/openstack.repo root@192.168.100.10:/etc/yum.repos.d/ [root@Cloud ~]# scp /etc/yum.repos.d/dvd.repo /etc/yum.repos.d/openstack.repo root@192.168.100.11:/etc/yum.repos.d/ 阶段二：集群各节点系统环境初始化 1. Controller控制节点初始化（192.168.100.10） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 # 1. 配置管理网络 ens160 [root@Controller ~]# nmcli connection modify ens160 ipv4.addresses 192.168.100.10/24 ipv4.method manual autoconnect yes [root@Controller ~]# nmcli connection down ens160 ; nmcli connection up ens160 # 2. 配置外部网络网卡 ens224 [root@Controller ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens224 TYPE=Ethernet BOOTPROTO=none NAME=ens224 DEVICE=ens224 ONBOOT=yes [root@Controller ~]# nmcli connection reload [root@Controller ~]# nmcli connection modify ens224 ipv4.addresses 192.168.10.10/24 ipv4.gateway 192.168.10.1 ipv4.dns 192.168.10.1 ipv4.method manual autoconnect yes [root@Controller ~]# nmcli connection down ens224 ; nmcli connection up ens224 # 3. 配置 Chrony 时间同步客户端 [root@Controller ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool 192.168.100.20 iburst [root@Controller ~]# systemctl restart chronyd.service [root@Controller ~]# chronyc sources 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* 192.168.100.20 3 6 377 24 -22us[ -46us] +/- 43ms # 4. 配置主机名 [root@Controller ~]# hostnamectl set-hostname Controller # 5. 关闭防火墙与 SELinux [root@Controller ~]# systemctl disable firewalld.service --now [root@Controller ~]# setenforce 0 [root@Controller ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config # 6. 配置集群 /etc/hosts 解析 [root@Controller ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 7. 清理默认系统 repo 缓存 [root@Controller ~]# cd /etc/yum.repos.d [root@Controller yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ 2. Compute计算节点初始化（192.168.100.11） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 1. 配置管理网络 [root@Compute ~]# nmcli connection modify ens160 ipv4.addresses 192.168.100.11/24 ipv4.method manual autoconnect yes [root@Compute ~]# nmcli connection down ens160 ; nmcli connection up ens160 # 2. 配置 Chrony 时间同步客户端 [root@Compute ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool 192.168.100.20 iburst [root@Compute ~]# systemctl enable chronyd.service --now # 3. 设置主机名 [root@Compute ~]# hostnamectl set-hostname Compute # 4. 关闭防火墙与 SELinux [root@Compute ~]# systemctl disable firewalld.service --now [root@Compute ~]# setenforce 0 [root@Compute ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config # 5. 配置集群 hosts 解析 [root@Compute ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 6. 清理默认系统 repo [root@Compute ~]# cd /etc/yum.repos.d [root@Compute yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ ⚠️ 关键检查点：此时建议将所有虚拟机关机，**统一拍摄快照，以便出现异常时快速回退。\n阶段三：Packstack 自动化应答部署 1. 安装 Packstack 部署工具 在控制节点（Controller）执行：\n1 [root@Controller ~]# yum -y install openstack-packstack 2. 生成默认部署应答文件 1 [root@Controller ~]# packstack --gen-answer-file=/root/answers.txt 网络方案说明：OpenStack Victoria 支持 OVN（Open Virtual Network）与传统 OVS（Open vSwitch）。\n3. 精准定制修改应答文件参数 1 2 # 将自动探测的外网 IP 统一替换为内网管理 IP 192.168.100.10 [root@Controller ~]# sed -i \u0026#39;s/192.168.10.10/192.168.100.10/g\u0026#39; /root/answers.txt 编辑 /root/answers.txt，核对并修改以下核心配置项：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # 1. 指定计算节点集群 IP 列表（逗号分隔） CONFIG_COMPUTE_HOSTS=192.168.100.11 # 2. 设置 Keystone 管理员 root 初始密码 CONFIG_KEYSTONE_ADMIN_PW=000000 # 3. 禁用 Demo 演示租户网络（生产化规范） CONFIG_PROVISION_DEMO=n # 4. 开启 Heat 编排组件支持 CONFIG_HEAT_INSTALL=y # 5. Neutron 核心驱动与网络类型配置（ML2 + OVS） CONFIG_NEUTRON_ML2_TYPE_DRIVERS=geneve,flat,vxlan,vlan CONFIG_NEUTRON_ML2_MECHANISM_DRIVERS=openvswitch CONFIG_NEUTRON_ML2_TENANT_NETWORK_TYPES=vxlan CONFIG_NEUTRON_L2_AGENT=openvswitch # 6. OVS 外部网络网桥绑定配置（关联物理外部网卡 ens224） CONFIG_NEUTRON_OVS_BRIDGE_MAPPINGS=extnet:br-ex CONFIG_NEUTRON_OVS_BRIDGE_IFACES=br-ex:ens224 # 7. 预设存储配额容量 CONFIG_SWIFT_STORAGE_SIZE=20G CONFIG_CINDER_VOLUMES_SIZE=50G ⚠️ 关键检查点：修改完应答文件后，建议再次将所有虚拟机关机拍摄快照。\n4. 执行一键自动化部署 开机后在控制节点运行部署命令：\n1 [root@Controller ~]# packstack --answer-file=/root/answers.txt 部署脚本将基于 Puppet 自动在所有节点完成软件源配置、数据库建表、消息队列集群建立、证书生成以及各组件协同联调。完成后将在终端打印登录 Dashboard 的 URL 及 keystonerc_admin 环境变量文件路径。\n四、Linux 系统日志管理与 OpenStack 故障排查 1. Linux Rsyslog 日志分级与配置架构 在 Linux 系统中，系统底层与大部分服务的日志均交由 rsyslog 守护进程集中管理。\n查看配置文件： 1 2 rpm -qc rsyslog # 核心主配置文件: /etc/rsyslog.conf 日志规则语法解析： 1 服务类别(Facility).日志等级(Priority) 日志存储路径 常见配置示例：\n1 2 3 4 5 6 7 8 9 # 1. 系统通用常规消息： # 记录所有服务 \u0026gt;=info 级别的日志，但排除邮件、认证安全和计划任务日志 *.info;mail.none;authpriv.none;cron.none /var/log/messages # 2. 调试日志： *.debug /var/log/debug # 3. 错误级别日志： *.err /var/log/error.log 语法规则要点： mail：服务类别（Facility）； info：日志优先级等级； .：表示匹配**大于等于（\u0026gt;=）**该优先级的日志消息； *.info：表示所有服务的 \u0026gt;= info 级别日志均汇聚写入目标文件。 实时动态监控日志命令： 1 2 3 4 5 # 跟踪系统日志末尾输出 tail /var/log/messages # 持续挂起实时监听日志流 tail -f /var/log/messages 2. OpenStack 自定义服务日志排查准则 排错原则：各组件通常具备独立的自定义日志文件体系（位于 /var/log/\u0026lt;组件名\u0026gt;/ 目录下）。若组件运行异常，优先排查该组件专属日志，无自定义日志时再退回排查系统全局日志 /var/log/messages。\n实战案例：以 Neutron 网络服务故障定位为例 在部署或运行过程中，若虚拟网络创建失败或 OVS 端口未上线，可通过正则匹配直接捕获错误与警告信息：\n1 2 3 4 5 # 1. 跨文件正则过滤所有 Neutron 日志中的错误与告警 tail -f /var/log/neutron/*.log | grep -iE \u0026#39;(err|warn)\u0026#39; # 2. 深度排查 neutron-server 核心 API 服务日志，并输出匹配项前后 3 行上下文： tail -f /var/log/neutron/server.log | grep -iE -A3 -B3 \u0026#39;(err|warn)\u0026#39; 参数说明：\n-i：忽略大小写； -E：开启扩展正则表达式匹配 (err|warn)； -A 3 (After)：打印匹配行后 3 行； -B 3 (Before)：打印匹配行前 3 行。 ","date":"2026-10-06","description":"","objectID":"/posts/openstack-core-architecture-deployment/","readingTime":8,"tags":["OpenStack","云计算","自动化部署","Packstack","Victoria"],"title":"OpenStack 云操作系统核心架构与自动化部署","url":"/posts/openstack-core-architecture-deployment/","wordCount":1508},{"categories":["OpenStack"],"content":"cinder创建卷和挂载卷架构 创建卷流程是由cinder节点提供（控制节点），挂载卷流程是由nova提供的\n本地 LVM + iSCSI 卷存储配置 在answers.txt应答文件中指定要创建cinder卷，考虑到实验环境无法提供专门的存储设备，所以系统采用本地LVM作为cinder存储 系统默认会创建一个cinder-volumes卷组，大小为50G（用户手动指定）\n该卷组创建在控制节点上\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [root@Controller ~]# vgdisplay --- Volume group --- VG Name cl System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 3 Open LV 3 Max PV 0 Cur PV 1 Act PV 1 VG Size \u0026lt;99.00 GiB PE Size 4.00 MiB Total PE 25343 Alloc PE / Size 25343 / \u0026lt;99.00 GiB Free PE / Size 0 / 0 VG UUID K2c9TX-MlXI-k9Ga-CPPm-3zuu-WNoK-mRuUEZ --- Volume group --- VG Name cinder-volumes System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 1 Open LV 0 Max PV 0 Cur PV 1 Act PV 1 VG Size \u0026lt;51.50 GiB PE Size 4.00 MiB Total PE 13183 Alloc PE / Size 12551 / \u0026lt;49.03 GiB Free PE / Size 632 / \u0026lt;2.47 GiB VG UUID nXhaeU-1PwG-is18-Ef2N-cK5R-9LRk-3ksn1k 在/etc/cinder/cinder.conf调用LVM驱动来识别创建的卷组 将该控制节点当做ISCSI存储（target），将用户创建的卷使用ISCSI共享给compute节点 当用户创建2G的云硬盘，系统会自动调用LVM驱动在卷组中创建2G LV\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 [root@Controller ~]# lvdisplay --- Logical volume --- LV Path /dev/cl/swap LV Name swap VG Name cl LV UUID f9Aqve-GgxT-gQNg-v0W0-ogyz-1cJT-WIEOMw LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 2 LV Size \u0026lt;7.88 GiB Current LE 2016 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:1 --- Logical volume --- LV Path /dev/cl/home LV Name home VG Name cl LV UUID 5f8ntZ-4pxS-yYFl-3BHC-qKaW-pFsi-2ZGkHg LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 1 LV Size 29.89 GiB Current LE 7653 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:2 --- Logical volume --- LV Path /dev/cl/root LV Name root VG Name cl LV UUID 32JpBQ-Ld4z-0bLU-jHuG-iMRC-WLoO-WtcrnT LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 1 LV Size \u0026lt;61.23 GiB Current LE 15674 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:0 --- Logical volume --- LV Name cinder-volumes-pool VG Name cinder-volumes LV UUID ughXDw-Fl7B-Mbo9-vsHS-OvMy-Pi60-E2GPaa LV Write Access read/write (activated read only) LV Creation host, time Controller, 2026-07-08 16:03:29 +0800 LV Pool metadata cinder-volumes-pool_tmeta LV Pool data cinder-volumes-pool_tdata LV Status available # open 0 LV Size \u0026lt;48.93 GiB Allocated pool data 0.00% Allocated metadata 10.44% Current LE 12525 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:6 --- Logical volume --- LV Path /dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e LV Name volume-3561d822-2929-4b47-87c5-25f769f8f44e VG Name cinder-volumes LV UUID WoSBug-zWc4-wVYn-39xk-MWVw-z3Pj-UOdvok LV Write Access read/write LV Creation host, time Controller, 2026-10-05 15:56:30 +0800 LV Pool name cinder-volumes-pool LV Status available # open 0 LV Size 2.00 GiB Mapped size 0.00% Current LE 512 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:7 当用户要将该卷附加给云主机web01，cinder发送指令给存储端，存储端就会把该LV共享给compute主机，从而让compute主机识别到该硬盘，产生了一个新的2G硬盘，并通过libvirt调用xml文件，给云主机添加\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 [root@Controller ~]# systemctl status target ● target.service - Restore LIO kernel target configuration Loaded: loaded (/usr/lib/systemd/system/target.service; enabled; vendor preset: disabled) Active: active (exited) since Mon 2026-10-05 14:18:51 CST; 1h 30min ago Process: 1873 ExecStart=/usr/bin/targetctl restore (code=exited, status=0/SUCCESS) Main PID: 1873 (code=exited, status=0/SUCCESS) Tasks: 0 (limit: 36406) Memory: 0B CGroup: /system.slice/target.service Oct 05 14:18:50 Controller systemd[1]: Starting Restore LIO kernel target configuration... Oct 05 14:18:51 Controller target[1873]: No saved config file at /etc/target/saveconfig.json, ok, exiting Oct 05 14:18:51 Controller systemd[1]: Started Restore LIO kernel target configuration. [root@Controller ~]# targetcli targetcli shell version 2.1.53 Copyright 2011-2013 by Datera, Inc and others. For help on commands, type \u0026#39;help\u0026#39;. /\u0026gt; ls o- / ......................................................................................................................... [...] o- backstores .............................................................................................................. [...] | o- block .................................................................................................. [Storage Objects: 1] | | o- iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e [/dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e (2.0GiB) write-thru activated] | | o- alua ................................................................................................... [ALUA Groups: 1] | | o- default_tg_pt_gp ....................................................................... [ALUA state: Active/optimized] | o- fileio ................................................................................................. [Storage Objects: 0] | o- pscsi .................................................................................................. [Storage Objects: 0] | o- ramdisk ................................................................................................ [Storage Objects: 0] o- iscsi ............................................................................................................ [Targets: 1] | o- iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e ............................................. [TPGs: 1] | o- tpg1 .......................................................................................... [no-gen-acls, auth per-acl] | o- acls .......................................................................................................... [ACLs: 1] | | o- iqn.1994-05.com.redhat:1220933cde3c ...................................................... [1-way auth, Mapped LUNs: 1] | | o- mapped_lun0 ................. [lun0 block/iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (rw)] | o- luns .......................................................................................................... [LUNs: 1] | | o- lun0 [block/iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (/dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e) (default_tg_pt_gp)] | o- portals .................................................................................................... [Portals: 1] | o- 192.168.100.10:3260 .............................................................................................. [OK] o- loopback ......................................................................................................... [Targets: 0] /\u0026gt; [root@Compute ~]# cat /etc/iscsi/initiatorname.iscsi InitiatorName=iqn.1994-05.com.redhat:1220933cde3c [root@Compute ~]# iscsiadm -m session tcp: [1] 192.168.100.10:3260,1 iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (non-flash) [root@Compute ~]# vim /etc/libvirt/qemu/instance-00000002.xml \u0026lt;disk type=\u0026#39;block\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;raw\u0026#39; cache=\u0026#39;none\u0026#39; io=\u0026#39;native\u0026#39;/\u0026gt; \u0026lt;source dev=\u0026#39;/dev/sda\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vdb\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;serial\u0026gt;3561d822-2929-4b47-87c5-25f769f8f44e\u0026lt;/serial\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x00\u0026#39; slot=\u0026#39;0x07\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; 存储端配置\n安装targetcli软件包，并启动服务 1 2 3 4 5 6 7 8 9 #yum -y install targetcli #systemctl enable target --now #targetcli /\u0026gt; /backstores/block create dev=/dev/sdb1 name=data01 /\u0026gt; /iscsi create iqn.2024.com.storage:disk /\u0026gt; /iscsi/ iqn.2024.com.storage:disk/tpg1/acls create iqn.1994-05.com.redhat:b4ff583759f4 /\u0026gt;/iscsi/ iqn.2024.com.storage:disk /tpg1/luns create data01 /\u0026gt; /iscsi/ iqn.2024.com.storage:disk/tpg1/portals create 192.168.100.10:3260 /\u0026gt; saveconfig 目标器 业务服务器 存储客户端 1 2 3 [root@Compute ~]# iscsiadm -m discovery -t st -p 192.168.100.10 [root@Compute ~]# iscsiadm -m node -T iqn.2024.com.storage:disk -p 192.168.100.10:3260 -l #fdisk -l Cinder对接NFS存储 根据上图规划和配置网络\n1. 配置NFS存储 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #配置网络 [root@storage ~]# nmcli connection modify ens160 ipv4.addresses 192.168.200.20/24 ipv4.method manual autoconnect yes [root@storage ~]# nmcli connection down ens160 ; nmcli connection up ens160 [root@storage ~]# cd /etc/sysconfig/network-scripts/ [root@storage network-scripts]# cp ifcfg-ens160 ifcfg-ens192 [root@storage network-scripts]# cat ifcfg-ens192 # Generated by parse-kickstart TYPE=Ethernet DEVICE=ens192 ONBOOT=yes BOOTPROTO=none NAME=ens192 [root@storage ~]# nmcli connection reload [root@storage ~]# nmcli connection modify ens192 ipv4.addresses 192.168.8.20/24 ipv4.method manual autoconnect yes [root@storage ~]# nmcli connection down ens192 ; nmcli connection up ens192 #安装配置安装NFS服务 [root@storage ~]# yum install -y nfs-utils rpcbind [root@storage ~]# mkdir /storage [root@storage ~]# cat /etc/exports /storage 192.168.200.0/24(rw,sync,no_root_squash) /storage 192.168.8.0/24(rw,sync,no_root_squash) [root@storage ~]# systemctl enable nfs-server --now [root@storage ~]# systemctl enable rpcbind --now 2. 配置cinder节点网络（控制节点） 1 2 3 4 5 6 7 8 [root@Controller ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens192 TYPE=Ethernet NAME=ens192 DEVICE=ens192 ONBOOT=yes IPADDR=192.168.8.10 NETMASK=255.255.255.0 [root@Controller ~]# systemctl restart network 3. 配置计算节点网络 1 2 [root@Compute ~]# nmcli connection modify ens192 ipv4.addresses 192.168.200.10/24 autoconnect yes ipv4.method manual [root@Compute ~]# nmcli connection down ens192 ; nmcli connection up ens192 4.配置cinder 1 2 3 4 5 6 7 8 9 10 [root@Controller ~]# vim /etc/cinder/cinder.conf 410 default_volume_type=nfs 436 enabled_backends=lvm,nfs666 [nfs666] volume_backend_name=nfsdata volume_driver=cinder.volume.drivers.nfs.NfsDriver nfs_shares_config = /etc/cinder/nfs_shares [root@Controller ~]# cat /etc/cinder/nfs_shares storage:/storage [root@Controller ~]# systemctl restart openstack-cinder-* Cinder节点\n1 2 [root@Controller ~]# cat /etc/hosts ####额外增加一条记录 192.168.8.20 storage 计算节点\n1 2 3 4 5 6 [root@Compute ~]# cat /etc/hosts 192.168.200.20 storage [root@Controller ~]# source keystonerc_admin [root@Controller ~(keystone_admin)]# cinder type-create nfs [root@Controller ~(keystone_admin)]# cinder type-key nfs set volume_backend_name=nfsdata [root@Controller ~(keystone_admin)]# systemctl restart openstack-cinder-* 在openstack web界面上，创建卷，选择nfs，将该卷关联给云主机\n在openstack web界面上，创建卷，选择nfs, 源选择镜像，创建云主机时，选择源为刚创建的卷\n创建完云主机后会产生一个卷\n将原来云主机删除，再次创建云主机这个卷就可以使用了\n","date":"2026-10-05","description":"","objectID":"/posts/openstack-cinder-block-storage/","readingTime":6,"tags":["OpenStack","Cinder","块存储","LVM","iSCSI","NFS"],"title":"OpenStack Cinder 块存储详解：LVM + iSCSI 本地卷与 NFS 后端对接","url":"/posts/openstack-cinder-block-storage/","wordCount":1278},{"categories":["OpenStack"],"content":"keystone服务 OpenStack多域支持\n1 2 [root@Controller ~]# vim /etc/openstack-dashboard/local_settings OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True 列出系统中所有服务\n1 [root@Controller ~(keystone_admin)]# openstack service list keystone地址 API接口\n1 OS_AUTH_URL=http://192.168.100.10:5000/v3 所有的服务都知道keystone地址，因为需要向keystone去检验用户token是否有效 keystone知道所有的服务地址是多少，所有的服务在创建时都向keystone进行注册 endpoint地址\n1 [root@Controller ~(keystone_admin)]# openstack endpoint list 每个服务都会有三个endpoint地址，这三个URL地址可以相同，也可以不相同\npublic url 外网用户访问URL地址\nadmin url ： 管理员访问URL地址\ninternel url：内部组件间访问URL地址\n创建云主机流程\n用户向keystone发送请求，输入用户名和密码，keystone认证成功后，给用户发放Token，用户使用Token向nova-api发送创建云主机请求 nova-api向keystone寻求帮助，帮我校验该Token是否有效，返回结果，如果成功 nova-api将请求发送给nova-compute主机 nova-compute主机根据用户请求信息，创建云主机，并返回给nova-api nova-api将结果返回给用户，云主机创建完成，该主机的信息记录在数据库中 我向nova-api发送创建主机的请求，我是不知道nova-api地址的，但是我知道keystone地址，我向keystone去查询nova-api地址，keystone存储了所有服务的endpoint地址，所以，我只需要知道keystone的地址即可，以下是如何查询keystone地址和通过keystone去查其他地址的方法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 [root@Controller ~(keystone_zlm)]# grep -E \u0026#34;^connection\\s*=.*keystone\u0026#34; /etc/keystone/*.conf /etc/keystone/keystone.conf:connection=mysql+pymysql://keystone_admin:c3fa63522e1f4586@192.168.100.10/keystone # 用户名：keystone_admin # 密码：c3fa63522e1f4586 [root@Controller ~(keystone_zlm)]# mysql -u keystone_admin -p -D keystone Enter password: Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 15494 Server version: 10.3.27-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [keystone]\u0026gt; SELECT s.type AS service_type, -\u0026gt; e.interface, -\u0026gt; e.url -\u0026gt; FROM endpoint e -\u0026gt; JOIN service s ON e.service_id = s.id -\u0026gt; ORDER BY s.type, e.interface; +----------------+-----------+--------------------------------------------------+ | service_type | interface | url | +----------------+-----------+--------------------------------------------------+ | alarming | admin | http://192.168.100.10:8042 | | alarming | internal | http://192.168.100.10:8042 | | alarming | public | http://192.168.100.10:8042 | | cloudformation | admin | http://192.168.100.10:8000/v1 | | cloudformation | internal | http://192.168.100.10:8000/v1 | | cloudformation | public | http://192.168.100.10:8000/v1 | | compute | admin | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | compute | internal | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | compute | public | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | identity | admin | http://192.168.100.10:5000 | | identity | internal | http://192.168.100.10:5000 | | identity | public | http://192.168.100.10:5000 | | image | admin | http://192.168.100.10:9292 | | image | internal | http://192.168.100.10:9292 | | image | public | http://192.168.100.10:9292 | | metering | admin | http://192.168.100.10:8777 | | metering | internal | http://192.168.100.10:8777 | | metering | public | http://192.168.100.10:8777 | | metric | admin | http://192.168.100.10:8041 | | metric | internal | http://192.168.100.10:8041 | | metric | public | http://192.168.100.10:8041 | | network | admin | http://192.168.100.10:9696 | | network | internal | http://192.168.100.10:9696 | | network | public | http://192.168.100.10:9696 | | object-store | admin | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | object-store | internal | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | object-store | public | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | orchestration | admin | http://192.168.100.10:8004/v1/%(tenant_id)s | | orchestration | internal | http://192.168.100.10:8004/v1/%(tenant_id)s | | orchestration | public | http://192.168.100.10:8004/v1/%(tenant_id)s | | placement | admin | http://192.168.100.10:8778/placement | | placement | internal | http://192.168.100.10:8778/placement | | placement | public | http://192.168.100.10:8778/placement | | volumev2 | admin | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev2 | internal | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev2 | public | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev3 | admin | http://192.168.100.10:8776/v3/%(tenant_id)s | | volumev3 | internal | http://192.168.100.10:8776/v3/%(tenant_id)s | | volumev3 | public | http://192.168.100.10:8776/v3/%(tenant_id)s | +----------------+-----------+--------------------------------------------------+ 39 rows in set (0.001 sec) MariaDB [keystone]\u0026gt; SELECT s.type AS service_type, -\u0026gt; e.interface, -\u0026gt; e.url -\u0026gt; FROM endpoint e -\u0026gt; JOIN service s ON e.service_id = s.id -\u0026gt; WHERE e.interface = \u0026#39;public\u0026#39; -\u0026gt; ORDER BY s.type; +----------------+-----------+--------------------------------------------------+ | service_type | interface | url | +----------------+-----------+--------------------------------------------------+ | alarming | public | http://192.168.100.10:8042 | | cloudformation | public | http://192.168.100.10:8000/v1 | | compute | public | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | identity | public | http://192.168.100.10:5000 | | image | public | http://192.168.100.10:9292 | | metering | public | http://192.168.100.10:8777 | | metric | public | http://192.168.100.10:8041 | | network | public | http://192.168.100.10:9696 | | object-store | public | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | orchestration | public | http://192.168.100.10:8004/v1/%(tenant_id)s | | placement | public | http://192.168.100.10:8778/placement | | volumev2 | public | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev3 | public | http://192.168.100.10:8776/v3/%(tenant_id)s | +----------------+-----------+--------------------------------------------------+ 13 rows in set (0.000 sec) MariaDB [keystone]\u0026gt; 部署openstack时，创建service\n进入数据库，创建database，比如glance 创建数据库用户glance，设置glance用户对glance数据库中的所有的表有ALL（增删改查）权限 创建glance service，并将glance-api地址注册到数据库中 endpoint地址 当用户需要访问glance-api时，会去向keystone发送请求，通过keystone 数据库查询到glance api endpoint地址\n1 2 #示例 如果将zlm用户加入admin角色，那他就拥有了admin角色权限 [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME admin 用户向nova-api发送请求，nova-api向keystone校验用户身份，Token是否在有效期，并不会判断用户是否有权限创建云主机，用户是否有权限跟每个服务的策略文件有关\n1 2 3 4 5 6 7 8 9 [root@Controller nova]# cd /etc/openstack-dashboard/ [root@Controller openstack-dashboard]# ls cinder_policy.json keystone_policy.json neutron_policy.json nova_policy.json glance_policy.json local_settings nova_policy.d [root@Controller nova(keystone_user)]# cp /etc/openstack-dashboard/glance_policy.json /etc/glance/policy.json [root@Controller nova(keystone_user)]# openstack image create --container-format bare --disk-format qcow2 --file /root/cirros-0.3.5-x86_64-disk.img --public test2 [root@Controller nova(keystone_user)]# chgrp glance /etc/glance/policy.json [root@Controller nova(keystone_user)]# systemctl restart openstack-glance-api.service nova服务 Controller节点\nnova-api 模块\nnova-scheduler 模块\nnova-conductor 模块\nnova-novncproxy模块\n计算节点\nnova-compute模块\nnova-api\n对外提供rest接口的处理\n对传入的参数进行合法性校验和约束限制\n对请求的资源进行配额（quota）的校验和预留\n资源的创建，更新，删除查询等\n虚拟机生命周期的入口\n可水平扩展部署\nnova-conductor\n解耦 (nova-compute)对数据库直接访问，当用户申请创建云主机，创建成功后，将由nova-conductor模块将云主机信息写入到数据库中\ndatabase： 存放跟该组件相关的信息，比如云主机信息，比如镜像信息，比如网络信息\nnova-scheduler\n当openstack环境中有多台Compute计算节点时，用户创建云主机，nova-scheduler模块会根据每台compute主机的权重来决定该云主机运行在哪台计算节点上\nHAProxy提供负载均衡，当用户要访问nova-api时，会将请求丢给HAProxy，该负载均衡软件根据各个nova-api的负载情况，决定将请求发送给哪个API节点\nMQ： Message Queue 消息队列\n当一个组件有多个模块时，模块与模块之间的访问，需要经过消息队列来进行拥塞控制等\n如果有1000个用户将请求发送给nova-api\nnova-api会将请求发送给nova-compute\nnova-api 会将这1000个请求发送给消息队列，消息队列会根据每个compute主机的消息消费情况，处理进度，有序的将这些消息发送给nova-compute节点来处理\nnova-compute\n真正用来运行虚拟机的模块\nnova-novncproxy\n工作原理： nova-compute主机会为每个运行的虚拟机开启一个VNC Server端口\nnova-compute主机不能对外暴露该端口，否则会导致Internet主机都可以访问该端口和虚拟机\n为了解决该问题，在控制节点上开启一个nova-novncproxy服务对外提供，当用户访问该虚拟机控制台，会先将请求发送给nova-novncproxy，根据用户提供的信息（虚拟机UUID，Token等）查询到该虚拟机运行在哪台nova-compute上，将请求发送给运行了该虚拟机的Compute主机\n通过VNC打开虚拟机工作原理\nvncserver: 5900端口\nvncviewer: vncviewer\n客户端不需要安装vncviewer，而是将该功能集成在浏览器html中，novnc协议\nglance和swift服务 一、部署云主机：\n当用户发送一个部署云主机的指令后，验证通过，nova-compute主机会从glance镜像服务器下载镜像到本地（云主机部署在本地），/var/lib/nova/instances/_base/\n云主机开机后，会产生一个差分盘，当新写入数据时，会将新数据写入到差分盘中\n实验： 将glance镜像存放到swift对象存储中，而不是存放在glance本地硬盘\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@Controller ~]# cd /etc/glance/ [root@Controller glance]# cp glance-api.conf glance-api.conf.bak [root@Controller ~]# vim /etc/glance/glance-api.conf 3057 stores=file,http,swift 3111 default_store=swift 3982 swift_store_region = RegionOne 4032 swift_store_endpoint_type = publicURL 4090 swift_store_container = public 4118 swift_store_large_object_size = 5120 4142 swift_store_large_object_chunk_size = 100 4160 swift_store_create_container_on_put = True 4182 swift_store_multi_tenant = True 4230 swift_store_admin_tenants = services 4382 swift_store_auth_version = 2 4391 swift_store_auth_address = http://192.168.100.10:5000/v3 4399 swift_store_user = zlm 4408 swift_store_key = 000000 [root@Controller ~]# systemctl restart openstack-glance-api.service 备注： zlm用户添加swiftOperation角色才允许对swift有操作权限\n","date":"2026-10-05","description":"","objectID":"/posts/openstack-keystone-glance-nova/","readingTime":5,"tags":["OpenStack","Keystone","Glance","Nova","Endpoint","镜像服务"],"title":"OpenStack 核心服务详解：Keystone 认证、Nova 计算与 Glance/Swift 镜像存储","url":"/posts/openstack-keystone-glance-nova/","wordCount":910},{"categories":["OpenStack"],"content":"一、图形化界面发放云主机 admin用户是云超级管理员\nadmin管理身份\n一、创建租户 tenant project 项\n二、创建用户，关联项目\n三、上传镜像 管理员上传镜像是可以设置为公共镜像，所有租户可以访问，用户自行上传镜像是私有镜像，只能在租户内共享\n四、创建规格\n五、创建外部网络\nL3 Agent部署在哪里哪个节点就是网络节点\n租户身份登录\n一、使用租户身份登录\n二、创建私有网络和子网 VPC\n三、创建路由器，关联外部网络和私有网络\n四、创建安全组和策略\n五、创建秘钥对\n六、发放云主机并且绑定浮动IP\n七、测试（登录，主机名，网络）\ncloud-init 初始化自定义信息 主机名 密码等\n假如我创建了一个镜像，这个镜像内容特别多，大概占用了20G硬盘，需要4G内存才能更流畅的运行，但租户不知道啊，他发放云主机时，选择了10G硬盘，2G内存的规格，导致发放失败，但租户不知道什么原因，所以此时我在上传镜像时，指定硬盘最少20G，内存至少4G，将来选择规格时，如果规格小于这个，则不能选择，只能选择满足该条件的规则才行。\n以下是OpenStack Nova 中用于发现并将新加入的计算节点映射到其所属 Cell 数据库的关键命令，在 Cells v2 架构下，每个计算节点必须经过此步骤完成注册，否则调度器无法在其上部署实例，API 也不会列出该主机\n1 [root@Controller ~(keystone_admin)]# nova-manage cell_v2 discover_hosts --verbose 发放云主机可以通过路由器访问Internet，那配置弹性IP的意义是可以对外提供服务，web服务器，可以通过SNAT出去访问，但远程用户不能进来访问该服务，以下是查看OpenStack底层使用iptables的策略\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 [root@Controller ~(keystone_admin)]# ip netns qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 (id: 1) qdhcp-a82a6d9e-26ec-497f-8f71-68d8395386ad (id: 0) [root@Controller ~(keystone_admin)]# ip netns exec qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 ip addr list 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 33: qg-d9305000-ef: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fa:16:3e:18:37:69 brd ff:ff:ff:ff:ff:ff inet 192.168.10.106/24 brd 192.168.10.255 scope global qg-d9305000-ef valid_lft forever preferred_lft forever inet 192.168.10.182/32 brd 192.168.10.182 scope global qg-d9305000-ef valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe18:3769/64 scope link valid_lft forever preferred_lft forever 34: qr-eb02e78e-a3: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fa:16:3e:18:b8:5f brd ff:ff:ff:ff:ff:ff inet 192.168.20.254/24 brd 192.168.20.255 scope global qr-eb02e78e-a3 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe18:b85f/64 scope link valid_lft forever preferred_lft forever [root@Controller ~(keystone_admin)]# ip netns exec qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 iptables -t nat -nL Chain PREROUTING (policy ACCEPT) target prot opt source destination neutron-l3-agent-PREROUTING all -- 0.0.0.0/0 0.0.0.0/0 Chain INPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination neutron-l3-agent-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 neutron-postrouting-bottom all -- 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination neutron-l3-agent-OUTPUT all -- 0.0.0.0/0 0.0.0.0/0 Chain neutron-l3-agent-OUTPUT (1 references) target prot opt source destination DNAT all -- 0.0.0.0/0 192.168.10.182 to:192.168.20.197 Chain neutron-l3-agent-POSTROUTING (1 references) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ! ctstate DNAT Chain neutron-l3-agent-PREROUTING (1 references) target prot opt source destination REDIRECT tcp -- 0.0.0.0/0 169.254.169.254 tcp dpt:80 redir ports 9697 DNAT all -- 0.0.0.0/0 192.168.10.182 to:192.168.20.197 Chain neutron-l3-agent-float-snat (1 references) target prot opt source destination SNAT all -- 192.168.20.197 0.0.0.0/0 to:192.168.10.182 random-fully Chain neutron-l3-agent-snat (1 references) target prot opt source destination neutron-l3-agent-float-snat all -- 0.0.0.0/0 0.0.0.0/0 SNAT all -- 0.0.0.0/0 0.0.0.0/0 to:192.168.10.106 random-fully SNAT all -- 0.0.0.0/0 0.0.0.0/0 mark match ! 0x2/0xffff ctstate DNAT to:192.168.10.106 random-fully Chain neutron-postrouting-bottom (1 references) target prot opt source destination neutron-l3-agent-snat all -- 0.0.0.0/0 0.0.0.0/0 /* Perform source NAT on outgoing traffic. */ 实验需求：创建一台云主机，可以访问Internet，yum源指向阿里云，并且安装httpd软件包，启动服务，从外部主机访问该web页面http://192.168.10.182\nCompute主机本质上是一台KVM宿主机，在openstack中发放云主机，会通过调度程序将云主机分发到某一台Compute主机上\n二、命令行界面发放云主机 1 [root@Controller ~(keystone_admin)]# cp keystonerc_admin keystonerc_zlm 需求一：使用命令行删除所有的资源\n1 2 3 4 5 6 7 8 9 10 11 12 [root@Controller ~(keystone_zlm)]# cat keystonerc_zlm unset OS_SERVICE_TOKEN export OS_USERNAME=zlm export OS_PASSWORD=\u0026#39;000000\u0026#39; export OS_REGION_NAME=RegionOne export OS_AUTH_URL=http://192.168.100.10:5000/v3 export PS1=\u0026#39;[\\u@\\h \\W(keystone_zlm)]\\$ \u0026#39; export OS_PROJECT_NAME=LMandMY_HOME export OS_USER_DOMAIN_NAME=Default export OS_PROJECT_DOMAIN_NAME=Default export OS_IDENTITY_API_VERSION=3 [root@Controller ~(keystone_zlm)]# source keystonerc_zlm 删除云主机 1 2 3 4 5 6 7 [root@Controller ~(keystone_zlm)]# openstack server list +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ | ID | Name | Status | Networks | Image | Flavor | +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ | 44f0fa66-94ed-44ac-a9ae-0a3a13c1faba | web | ACTIVE | VPC01=192.168.20.197, 192.168.10.182 | CentOS 8.4 64bit | m3.web | +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ [root@Controller ~(keystone_zlm)]# openstack server delete 44f0fa66-94ed-44ac-a9ae-0a3a13c1faba 删除卷 1 2 [root@Controller ~(keystone_zlm)]# openstack volume list [root@Controller ~(keystone_zlm)]# openstack volume delete data01 删除路由器 1 2 3 4 5 [root@Controller ~(keystone_zlm)]# openstack router list [root@Controller ~(keystone_zlm)]# openstack router show router01 [root@Controller ~(keystone_zlm)]# openstack subnet list [root@Controller ~(keystone_zlm)]# openstack router remove subnet router01 subnet01 [root@Controller ~(keystone_zlm)]# openstack router delete router01 删除安全组 1 2 [root@Controller ~(keystone_zlm)]# openstack security group list [root@Controller ~(keystone_zlm)]# openstack security group delete SG01 删除网络 1 2 [root@Controller ~(keystone_zlm)]# openstack network list [root@Controller ~(keystone_zlm)]# openstack network delete VPC01 进入admin身份删除public网络，镜像，规格，租户，用户等\n1 [root@Controller ~(keystone_zlm)]# source keystonerc_admin 删除public网络 1 2 [root@Controller ~(keystone_admin)]# openstack network list [root@Controller ~(keystone_admin)]# openstack network delete public 删除镜像 1 2 3 4 [root@Controller ~(keystone_admin)]# openstack image list [root@Controller ~(keystone_admin)]# openstack image delete e3336d90-e673-4b1b-82ec-69b70fd6402c [root@Controller ~(keystone_admin)]# openstack image delete cec813dd-8141-4a99-8883-805ac6e3ec17 [root@Controller ~(keystone_admin)]# openstack image delete c6ffe7db-ccb0-4df4-8715-e8ce0fd5dc7d 删除规格 1 2 [root@Controller ~(keystone_admin)]# openstack flavor list [root@Controller ~(keystone_admin)]# openstack flavor delete m3.web 删除租户和用户 1 2 3 4 [root@Controller ~(keystone_admin)]# openstack project list [root@Controller ~(keystone_admin)]# openstack project delete LMandMY_HOME [root@Controller ~(keystone_admin)]# openstack user list [root@Controller ~(keystone_admin)]# openstack user delete zlm 需求二：使用命令行创建所有的资源 发放云主机，添加弹性IP，访问云主机，创建网络\n1 [root@Controller ~(keystone_admin)]# source keystonerc_admin 创建项目，用户，将用户与项目关联 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@Controller ~(keystone_admin)]# openstack project create LMandMY_HOME [root@Controller ~(keystone_admin)]# openstack user create --project LMandMY_HOME --project-domain Default --password-prompt zlm [root@Controller ~(keystone_admin)]# openstack role list +----------------------------------+------------------+ | ID | Name | +----------------------------------+------------------+ | 0adc17230a5a434195d5995863280b3d | heat_stack_owner | | 2e5abdde67bb4f23bbf15454616af53e | ResellerAdmin | | 46d49fecd6f4411bbd72d0b9f575bcae | admin | | 5f5bf78c40b143bbb2df74fc855ea204 | reader | | 84ebb81407f644b18e1a529e0c317f16 | member | | b83f4c27c83445489554e988fb9af5a1 | heat_stack_user | | b8f8cd45891240bfa0f80e93f0e37ab9 | _member_ | | ebc22a15458c4712bf0dd4f4de09e0bf | SwiftOperator | +----------------------------------+------------------+ [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME b8f8cd45891240bfa0f80e93f0e37ab9 上传镜像 1 [root@Controller ~(keystone_admin)]# openstack image create --file /data/cirros-0.3.5-x86_64-disk.img --disk-format qcow2 --container-format bare --public cirros 创建规格 1 [root@Controller ~(keystone_admin)]# openstack flavor create --ram 1024 --disk 10 --public m3.web 创建public网络和子网 1 [root@Controller ~(keystone_admin)]# openstack network create --external --share --provider-network-type flat --provider-physical-network extnet --project LMandMY_HOME public 创建子网\n1 [root@Controller ~(keystone_admin)]# openstack subnet create --no-dhcp --subnet-range 192.168.10.0/24 --allocation-pool start=192.168.10.100,end=192.168.10.200 --gateway 192.168.10.1 --network public publicsub01 进入租户身份\n1 [root@Controller ~(keystone_admin)]# source keystonerc_zlm 创建网络和子网 1 [root@Controller ~(keystone_zlm)]# openstack network create VPC01 创建子网\n1 [root@Controller ~(keystone_zlm)]# openstack subnet create --network VPC01 --subnet-range 192.168.20.0/24 --dhcp --gateway 192.168.20.254 --dns-nameserver 114.114.114.114 subnet01 创建路由器 1 2 3 [root@Controller ~(keystone_zlm)]# openstack router create router01 [root@Controller ~(keystone_zlm)]# openstack router add subnet router01 subnet01 [root@Controller ~(keystone_zlm)]# openstack router set --external-gateway public router01 创建安全组 1 [root@Controller ~(keystone_zlm)]# openstack security group create SG01 放行ICMP协议\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol icmp --remote-ip 0.0.0.0/0 SG01 放行ssh服务\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 22 --remote-ip 0.0.0.0/0 SG01 放行80端口\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 80 --remote-ip 0.0.0.0/0 SG01 创建浮动IP 1 [root@Controller ~(keystone_zlm)]# openstack floating ip create public 创建秘钥对 1 [root@Controller ~(keystone_zlm)]# openstack key create key01 \u0026gt; key01.pem 创建云主机 1 2 3 4 5 6 7 8 [root@Controller ~(keystone_zlm)]# openstack network list +--------------------------------------+--------+--------------------------------------+ | ID | Name | Subnets | +--------------------------------------+--------+--------------------------------------+ | a82a6d9e-26ec-497f-8f71-68d8395386ad | VPC01 | 40bd75e0-3d22-4e7b-8659-8a6c0a44e3aa | | d99ba601-05e3-4f1f-88b8-f0587c6a5f6e | public | 5d39db23-8359-4e33-bde7-0f9ac6a0f7c7 | +--------------------------------------+--------+--------------------------------------+ [root@Controller ~(keystone_zlm)]# openstack server create --image cirros --security-group SG01 --key-name key01 --flavor m3.web --nic net-id=a82a6d9e-26ec-497f-8f71-68d8395386ad web 将弹性IP关联给该云主机\n1 2 [root@Controller ~(keystone_zlm)]# openstack port list [root@Controller ~(keystone_zlm)]# openstack floating ip set --port=46ba21d5-6f14-4693-a215-5a6fc8781adf 192.168.10.182 访问云主机\n1 2 [root@Controller ~(keystone_zlm)]# chmod 400 key01.pem [root@Controller ~(keystone_zlm)]# ssh -i key01.pem cirros@192.168.10.182 如果外网网卡不能通讯\n1 [root@Controller ~(keystone_zlm)]# ifup br-ex ; ifup ens224 ","date":"2026-10-04","description":"","objectID":"/posts/openstack-provision-cloud-server/","readingTime":6,"tags":["OpenStack","云主机","Horizon","Neutron","Nova","命令行"],"title":"OpenStack发放云主机","url":"/posts/openstack-provision-cloud-server/","wordCount":1107},{"categories":["MySQL"],"content":"1.整理 systemctl 数据库 Service 文件 方案一：标准生产守护进程配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [Unit] Description=MySQL Server After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/var/run/mysqld/mysqld.pid PrivateTmp=true RuntimeDirectory=mysqld [Install] WantedBy=multi-user.target 方案二：带配置文件与文件句柄限制配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=MySQL Server After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf RuntimeDirectory=mysqld LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target 修改完成后\n1 2 3 [root@master local]#systemctl daemon-reload [root@master local]#systemctl restart mysqld.service [root@master local]#systemctl enable mysqld.service 2.多版本 MySQL（5.6 / 5.7）安装与升级准备 步骤一：下载软件归档包 1 2 3 4 5 # MySQL 5.7.40 二进制包 [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz # MySQL 5.6.48 二进制包 [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz 步骤二：解压安装与创建软链接 1 2 3 4 5 6 7 8 9 # 安装 5.7 版本 [root@master local]#tar xf mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz [root@master local]#ln -s mysql-5.7.40-linux-glibc2.12-x86_64 mysql57 # 注意：多版本共存调试期间，暂不配置全局环境变量，避免命令路径冲突 # 安装 5.6 版本 [root@master local]#tar xf mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz [root@master local]#ln -s mysql-5.6.48-linux-glibc2.12-x86_64 mysql56 # 注意：暂不配置全局环境变量 步骤三：编写多实例专属配置文件 MySQL 5.7（3357 实例）配置：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master local]#vim /etc/my3357.cnf [mysql] socket=/tmp/mysql3357.sock [mysqld] user=mysql basedir=/usr/local/mysql57 datadir=/data/3357/data socket=/tmp/mysql3357.sock port=3357 pid-file=/run/mysqld3357/mysqld.pid [root@master local]#vim /usr/lib/systemd/system/mysqld3357.service [Unit] Description=MySQL 5.7 Server (3357) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3357.cnf RuntimeDirectory=mysqld3357 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target MySQL 5.6（3356 实例）配置：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master local]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql56 datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master local]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql56/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target 步骤四：初始化数据库软件 MySQL 5.6 初始化（数据目录 3356）：\n1 2 3 4 5 6 7 8 9 10 11 [root@master local]#mkdir -p /data/3356/data [root@master local]#chown -R mysql.mysql /data/3356/data [root@master ~]#/usr/local/mysql56/scripts/mysql_install_db --defaults-file=/etc/my3356.cnf --user=mysql --basedir=/usr/local/mysql56 --datadir=/data/3356/data FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql56/scripts/mysql_install_db: Data::Dumper # 缺少Perl模块 [root@master local]#yum install -y perl-Data-Dumper [root@master local]#/usr/local/mysql56/scripts/mysql_install_db --user=mysql --datadir=/data/3356/data --basedir=/usr/local/mysql56 # 观察输出信息中是否包含两个 OK，并确认退出状态码： [root@master local]#echo $? 0 MySQL 5.7 初始化（数据目录 3357）：\n1 2 3 4 5 [root@master local]#mkdir -p /data/3357/data [root@master local]#chown -R mysql.mysql /data/3357/data [root@master ~]#/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3357.cnf --initialize-insecure --user=mysql --basedir=/usr/local/mysql57 --datadir=/data/3357/data [root@master local]#echo $? 0 步骤五：运行启动数据库服务 启动/停止 MySQL 5.7 服务：\n1 2 3 4 5 6 7 8 9 10 11 # 第一种启动方式 [root@master local]#cp /usr/local/mysql57/support-files/mysql.server /etc/init.d/mysqld57 [root@master local]#chmod +x /etc/init.d/mysqld57 [root@master local]#/etc/init.d/mysqld57 start [root@master local]#/etc/init.d/mysqld57 stop # 第二种启动方式 [root@master local]#systemctl daemon-reload [root@master local]#systemctl start mysqld3357.service [root@master local]#systemctl enable mysqld3357.service Created symlink from /etc/systemd/system/multi-user.target.wants/mysqld3357.service to /usr/lib/systemd/system/mysqld3357.service. 启动/停止 MySQL 5.6 服务：\n1 2 3 4 5 6 7 8 9 10 11 # 第一种启动方式 [root@master local]#cp /usr/local/mysql56/support-files/mysql.server /etc/init.d/mysqld56 [root@master local]#chmod +x /etc/init.d/mysqld56 [root@master local]#/etc/init.d/mysqld56 start [root@master local]#/etc/init.d/mysqld56 stop # 第二种启动方式 [root@master local]#systemctl daemon-reload [root@master local]#systemctl start mysqld3356.service [root@master local]#systemctl enable mysqld3356.service Created symlink from /etc/systemd/system/multi-user.target.wants/mysqld3356.service to /usr/lib/systemd/system/mysqld3356.service. 三个服务状态验证（全都running）\n1 2 3 4 5 6 7 8 [root@master local]#for svc in mysqld mysqld3356 mysqld3357; do \u0026gt; a=$(systemctl show -p ActiveState \u0026#34;$svc\u0026#34; | cut -d= -f2) \u0026gt; s=$(systemctl show -p SubState \u0026#34;$svc\u0026#34; | cut -d= -f2) \u0026gt; echo \u0026#34;$svc: $a ($s)\u0026#34; \u0026gt; done mysqld: active (running) mysqld3356: active (running) mysqld3357: active (running) ","date":"2026-10-03","description":"","objectID":"/posts/systemd-mysql-multiversion/","readingTime":3,"tags":["MySQL","systemd","多版本部署"],"title":"systemd 服务管理与 MySQL 多版本部署","url":"/posts/systemd-mysql-multiversion/","wordCount":515},{"categories":["MySQL"],"content":"01-数据库服务安装部署 1.1 数据库服务程序下载 官方下载地址（历史归档二进制安装包）：\n1 2 3 # 存放目录 [root@master ~]#cd /usr/local/ [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 查看安装包信息\n1 2 [root@master local]#ll mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz -rw-r--r-- 1 root root 914806904 Jul 2 2021 mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 1.2 二进制方式安装数据库服务 步骤一：操作系统环境准备 1. 确认网络地址 1 2 3 4 5 6 7 [root@master local]#ip addr show eth0 2: eth0: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:4b:b7:38 brd ff:ff:ff:ff:ff:ff inet 11.0.1.101/24 brd 11.0.1.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe4b:b738/64 scope link valid_lft forever preferred_lft forever 2. 配置主机名解析 1 2 3 4 [root@master local]#vim /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 11.0.1.101 master 3. 关闭系统防火墙 1 [root@master local]#systemctl disable firewalld --now 4. 关闭 SELinux 1 2 3 4 5 [root@master local]#sed -i \u0026#39;s/^SELINUX=enforcing/SELINUX=disabled/\u0026#39; /etc/selinux/config # 重启生效 [root@master local]#reboot [root@master local]#getenforce Disabled 5. 避免数据库服务安装产生冲突 1 2 3 4 5 6 # 查看系统中是否存在自带的 mariadb 数据库程序 [root@master local]#rpm -qa | grep mariadb mariadb-libs-5.5.68-1.el7.x86_64 # 将系统自带的数据库程序包卸载 [root@master local]#yum remove -y mariadb-libs 6. 安装数据库程序依赖软件（缺少会导致初始化报错） 1 [root@master local]#yum install -y libaio-devel 步骤二：安装数据库软件程序 1. 解压安装包并创建软链接（便于后续版本管理） 1 2 [root@master local]#tar xf mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz [root@master local]#ln -s mysql-8.0.26-linux-glibc2.12-x86_64 mysql 2. 配置系统全局环境变量 1 2 3 4 5 [root@master local]#vim /etc/profile # 在文件末尾添加 export PATH=$PATH:/usr/local/mysql/bin # 生效配置 [root@master local]#source /etc/profile 3. 验证安装版本 1 2 [root@master local]#mysql -V mysql Ver 8.0.26 for Linux on x86_64 (MySQL Community Server - GPL) 步骤三：进行软件程序初始化操作 MySQL两个核心目录：\n程序目录：/usr/local/mysql 数据目录：/data/3306/data（存储实际数据信息） 1. 创建专用运行用户（无需系统登录权限） 1 [root@master local]#useradd mysql -M -s /sbin/nologin 2. 创建数据存储目录并授权 1 2 [root@master local]#mkdir /data/3306/data -p [root@master local]#chown mysql.mysql /data/3306/data 3. 执行数据目录初始化 1 2 3 4 5 # 清理可能存在的干扰配置文件 [root@master local]#rm -f /etc/my.cnf # 执行无密码初始化（初始化后管理员无密码） [root@master local]#mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 步骤四：编写数据库服务配置文件 创建并编辑 /etc/my.cnf：\n1 2 3 4 5 6 7 8 9 10 [root@master local]#cat \u0026gt; /etc/my.cnf \u0026lt;\u0026lt; \u0026#39;eof\u0026#39; \u0026gt; [mysql] \u0026gt; socket=/tmp/mysql.sock \u0026gt; \u0026gt; [mysqld] \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3306/data \u0026gt; socket=/tmp/mysql.sock \u0026gt; eof 注：配置文件中 basedir 与 datadir 路径需结合自己实际环境进行调整。\n步骤五：启动数据库服务程序 1. 利用官方服务管理脚本启动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@master local]#cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld [root@master local]#/etc/init.d/mysqld start Starting MySQL.Logging to \u0026#39;/data/3306/data/master.err\u0026#39;. . SUCCESS! # 能够正常进入数据库说明启动成功 [root@master local]#mysql -uroot Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; 2. 利用命令行直接启动 通过 mysqld 命令启动： 1 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data/3306/data --user=mysql --socket=/tmp/mysql.sock \u0026amp; 通过 mysqld_safe 安全守护进程启动： 1 /usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data \u0026amp; 3. 利用 systemctl / service 启动 方式一：编写专属的 Systemd Unit 文件（/etc/systemd/system/mysqld.service） 方式二：直接激活并调用 /etc/init.d/ 目录下的服务脚本： 1 2 3 4 systemctl enable mysqld service mysqld start # 或 systemctl start mysqld 1.3 补充知识介绍 1. 初始化操作的两种方式对比 方式一：不安全初始化（--initialize-insecure）\n1 mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 特点：数据库管理员没有密码，可以直接免密登录数据库，便于测试或脚本初始化。 方式二：安全初始化（--initialize）\n1 mysqld --initialize --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 特点：数据库管理员会生成随机临时密码，打印在日志中，用于首次登录数据库。 注意事项：临时密码只能用于首次登录数据库；登录成功后必须重新设置新密码，在修改密码前无法执行其他操作： 1 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 2. 数据库启动失败排错标准流程 步骤一：确认系统基础环境 检查 SELinux 是否已彻底关闭； 检查端口 3306 是否被占用，是否存在其他冲突的数据库服务进程。 步骤二：排查与分析错误日志 典型报错：Starting MySQL. ERROR! The server quit without updating PID file (/data/3306/data/master.pid). 查看错误日志明细： 1 2 3 cat /data/3306/data/master.err # 日志错误示例: # 2024-06-14T09:15:36.120048Z 1 [ERROR] [MY-012271] [InnoDB] The innodb_system data file \u0026#39;ibdata1\u0026#39; must be writable 步骤三：没有错误日志时的排查方法 检查配置文件 /etc/my.cnf： 确认配置文件所在路径与文件命名是否正确； 确认配置文件内部参数语法及路径配置是否正确。 02-数据库服务启动关闭 有多种方式可以启动与关闭数据库服务，常见方式汇总如下：\n序号 启动方式 对应关闭方式 说明与建议 01 mysqld \u0026amp; kill / pkill / killall 不建议；严禁使用 kill -9 02 mysqld_safe \u0026amp; kill / pkill / killall 不建议直接 kill 03 mysql.server start mysql.server stop 官方原生控制脚本 04 service mysqld start service mysqld stop 建议使用（经典服务管理） 05 systemctl start mysqld systemctl stop mysqld 建议使用（现代 Linux 标准） 06 客户端/管理命令 mysqladmin -uroot -p密码 shutdown 管理员远程或本地命令行优雅关闭 07 SQL 会话内关闭 进入终端执行 shutdown; 需管理员权限 03-数据库服务密码管理 1. 没有密码时设置密码 方式一：利用命令行操作\n1 2 3 [root@master local]#mysqladmin -uroot password \u0026#39;123456\u0026#39; mysqladmin: [Warning] Using a password on the command line interface can be insecure. Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety. 方式二：利用 SQL 语句操作\n1 2 mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;000000\u0026#39;; Query OK, 0 rows affected (0.02 sec) 2. 已有密码时修改密码 方式一：利用命令行操作 1 2 3 [root@master local]#mysqladmin -uroot -p000000 password \u0026#39;123456\u0026#39; mysqladmin: [Warning] Using a password on the command line interface can be insecure. Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety. 方式二：利用 SQL 语句操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master local]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 14 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; 3. 已有密码但是忘记密码（密码重置） 重置密码实操流程 步骤一：关闭数据库服务\n1 mysql\u0026gt; shutdown; 或在系统终端执行：\n1 2 3 4 [root@master local]#service mysqld stop Shutting down MySQL.. SUCCESS! # 或 pkill mysqld 步骤二：采用安全模式启动数据库（跳过授权表加载，实现免密登录）\n1 2 [root@master local]#/usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data --skip-grant-tables --skip-networking \u0026amp; [1] 2658 说明：--skip-networking 用于禁用网络监听，防止在免密期间其他网络客户端介入。\n步骤三：免密登录并重置密码\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master local]#mysql Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 7 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; FLUSH PRIVILEGES; Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; 在 MySQL 终端中依次执行：\n1 2 3 4 5 6 7 -- 1. 刷新权限表（同步内存与磁盘中的授权表数据） -- 作用一：将内存中授权表信息同步写入磁盘 -- 作用二：将磁盘中授权表信息重新加载到内存 FLUSH PRIVILEGES; -- 2. 重置密码 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 注意：若未执行 FLUSH PRIVILEGES; 直接执行 ALTER USER，系统会抛出报错：\n1 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement 步骤四：重新正常启动数据库服务\n1 2 3 4 5 6 7 # 终止临时安全模式进程 [root@master local]#pkill mysqld # 正常启动数据库服务 [root@master local]#/etc/init.d/mysqld start Starting MySQL.. SUCCESS! # 或 service mysqld start 步骤五：使用新密码进行登录访问验证\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master local]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ","date":"2026-10-03","description":"","objectID":"/posts/mysql-binary-deploy/","readingTime":6,"tags":["MySQL","数据库","二进制安装"],"title":"MySQL 实战：二进制安装部署、启动关闭与密码管理","url":"/posts/mysql-binary-deploy/","wordCount":1157},{"categories":["DevOps"],"content":"01. sonarQube简介 1 2 3 1. sonarQube基于java开发，需安装open JDK8版本 2. sonarQube需要依赖MySQL数据库，至少5.6版本以上 3. sonarQube的小型实例至少4G内存，大型实例需要16G内存 02. 部署服务 一. 安装运行环境JDK 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@sonarqube ~]#ll total 166040 -rw-r--r-- 1 root root 170023183 Oct 1 22:32 jdk-8u181-linux-x64.rpm [root@sonarqube ~]#rpm -ivh jdk-8u181-linux-x64.rpm warning: jdk-8u181-linux-x64.rpm: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk1.8-2000:1.8.0_181-fcs ################################# [100%] Unpacking JAR files... tools.jar... plugin.jar... javaws.jar... deploy.jar... rt.jar... jsse.jar... charsets.jar... localedata.jar... 二. 安装数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 # 1. 下载MySQL官方的Yum仓库配置文件 [root@sonarqube ~]#wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm # 2. 安装MySQL源 [root@sonarqube ~]#rpm -ivh mysql-community-release-el6-5.noarch.rpm Preparing... ################################# [100%] Updating / installing... 1:mysql-community-release-el6-5 ################################# [100%] # 3. 安装MySQL服务器 [root@sonarqube ~]# yum install mysql-community-server -y [root@sonarqube ~]#mysql --version mysql Ver 14.14 Distrib 5.6.51, for Linux (x86_64) using EditLine wrapper # 4. 启动MySQL服务 [root@sonarqube ~]#service mysqld start Starting mysqld (via systemctl): [ OK ] [root@sonarqube ~]#netstat -ntpl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1227/master tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1008/sshd tcp6 0 0 ::1:25 :::* LISTEN 1227/master tcp6 0 0 :::3306 :::* LISTEN 3113/mysqld tcp6 0 0 :::22 :::* LISTEN 1008/sshd # 5. 设置密码 [root@sonarqube ~]# mysqladmin -uroot password 000000 # 6. 创建sonarqube库 [root@sonarqube ~]# mysql -uroot -p000000 -e \u0026#34;CREATE DATABASE sonarqube DEFAULT CHARACTER SET utf8;\u0026#34; [root@sonarqube ~]#mysql -u root -p000000 -e \u0026#34;show databases\u0026#34; Warning: Using a password on the command line interface can be insecure. +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sonarqube | +--------------------+ 三. 安装sonarqubeqube 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 # 1. 上传soanrqube [root@sonarqube ~]# cd /usr/local/ [root@sonarqube local]#ll sonarqube-7.0.zip -rw-r--r-- 1 root root 155709573 Oct 1 22:50 sonarqube-7.0.zip # 2. 解压到当前目录 [root@sonarqube local]#yum install -y unzip [root@sonarqube local]# unzip sonarqube-7.0.zip [root@sonarqube local]# ln -s sonarqube-7.0 sonarqube # 3. 安装插件: 删除默认的插件: [root@sonarqube ~]# rm -rf /usr/local/sonarqube/extensions/plugins/* 上传插件: [root@sonarqube ~]#ll sonar_plugins.tar.gz -rw-r--r-- 1 root root 45106788 Oct 1 22:56 sonar_plugins.tar.gz [root@sonarqube ~]#tar xf sonar_plugins.tar.gz -C /usr/local/sonarqube/extensions/plugins/ [root@sonarqube ~]#ll /usr/local/sonarqube/extensions/plugins/ total 4 drwxr-xr-x 2 1000 1000 4096 Oct 24 2019 plugins [root@sonarqube ~]# cd /usr/local/sonarqube/extensions/plugins/ [root@sonarqube plugins]# mv plugins/* . [root@sonarqube plugins]#rm -fr plugins/ # 4. 创建普通用户 [root@sonarqube ~]# useradd sonarqube # 5. 授权代码属主属组为sonarqube [root@sonarqube ~]# chown -R sonarqube.sonarqube /usr/local/sonarqube/ [root@sonarqube ~]# ll /usr/local/sonarqube/ total 12 drwxr-xr-x 8 sonarqube sonarqube 136 Feb 2 2018 bin drwxr-xr-x 2 sonarqube sonarqube 50 May 15 15:09 conf -rw-r--r-- 1 sonarqube sonarqube 7651 Feb 2 2018 COPYING drwxr-xr-x 2 sonarqube sonarqube 24 Feb 2 2018 data drwxr-xr-x 7 sonarqube sonarqube 150 Feb 2 2018 elasticsearch drwxr-xr-x 4 sonarqube sonarqube 40 Feb 2 2018 extensions drwxr-xr-x 9 sonarqube sonarqube 138 Feb 2 2018 lib drwxr-xr-x 2 sonarqube sonarqube 6 Feb 2 2018 logs drwxr-xr-x 2 sonarqube sonarqube 24 Feb 2 2018 temp drwxr-xr-x 9 sonarqube sonarqube 4096 Feb 2 2018 web # 6. 配置连接数据库 [root@sonarqube ~]#vim /usr/local/sonarqube/conf/sonar.properties ... sonar.jdbc.username=root # 数据库用户名 sonar.jdbc.password=000000 # 数据库密码 #----- Embedded Database (default) # H2 embedded database server listening port, defaults to 9092 #sonarqube.embeddedDatabase.port=9092 #----- MySQL 5.6 or greater # Only InnoDB storage engine is supported (not myISAM). # Only the bundled driver is supported. It can not be changed. sonar.jdbc.url=jdbc:mysql://localhost:3306/sonarqube?useUnicode=true\u0026amp;characterE ncoding=utf8\u0026amp;rewriteBatchedStatements=true\u0026amp;useConfigs=maxPerformance\u0026amp;useSSL =false # 开启本地连接 ... 四. 运行sonarqubeQube 1 2 sonarqubeQube服务启动不能使用root,必须使用普通用户运行 [root@sonarqube ~]# su - sonarqube -c \u0026#34;/usr/local/sonarqube/bin/linux-x86-64/sonar.sh start\u0026#34; 五. 配置远程访问token 启动后的端口为9000使用浏览器直接访问http://11.0.1.202:9000/about 用户名admin 密码admin 生成一个tokin,只显示一次记录好。\n1 2 3 4 5 [root@jenkins ~]# sonar-scanner \\ -Dsonar.projectKey=html \\ -Dsonar.sources=. \\ -Dsonar.host.url=http://11.0.1.202:9000 \\ -Dsonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b 六. 客户端部署 客户端IP:11.0.1.201\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 1. 上传客户端 [root@jenkins ~]# cd /usr/local/ [root@jenkins ~]#ll sonar-scanner-cli-4.2.0.1873-linux.zip -rw-r--r-- 1 root root 42397119 Oct 2 00:09 sonar-scanner-cli-4.2.0.1873-linux.zip # 2. 解压做软链接 [root@jenkins local]#yum install -y unzip [root@jenkins local]#unzip /root/sonar-scanner-cli-4.2.0.1873-linux.zip [root@jenkins local]# ln -s sonar-scanner-4.2.0.1873-linux sonar # 3. 设置PATH全局变量 [root@jenkins ~]# export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 4. 永久变量写入/etc/profile [root@jenkins ~]# vim /etc/profile .. export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 5. 客户端测试将代码扫描到服务器进行代码检测 将/var/lib/jenkins/workspace/test_job/目录下的blog代码推送到SonrQube测试 [root@jenkins ~]#cd /var/lib/jenkins/workspace/test_job/ [root@jenkins test_job]#ll total 8 -rw-r--r-- 1 root root 7 Oct 1 20:32 a.txt -rw-r--r-- 1 root root 3634 Oct 1 20:32 blog_state.txt -rw-r--r-- 1 root root 0 Oct 1 21:12 c.txt drwxr-xr-x 2 root root 6 Oct 1 20:32 myblog drwxr-xr-x 2 root root 6 Oct 1 20:32 myblog-deploy [root@jenkins test_job]#sonar-scanner \\ -Dsonar.projectKey=html \\ -Dsonar.sources=. \\ -Dsonar.host.url=http://11.0.1.202:9000 \\ -Dsonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b 执行成功:\n查看服务器: 上传成功\n","date":"2026-10-02","description":"","objectID":"/posts/sonarqube-practice/","readingTime":4,"tags":["DevOps","SonarQube","CI/CD"],"title":"SonarQube 实战：私有化部署、Token 配置与代码质量扫描","url":"/posts/sonarqube-practice/","wordCount":820},{"categories":["DevOps"],"content":"01. Jenkins简介 官网 jenkins.io\nJenkins是一个开源软件项目，是基于Java开发的一种持续集成工具，用于监控持续重复的工作，旨在提供一个开放易用的软件平台，使软件的持续集成变成可能。\n02. Jenkins部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 一.环境准备 1、CentOS7.9 2、内存：实验（2G）生产（至少6G） 3、禁用防火墙，关闭selinux 4、JDK17 二.安装JDK 上传rpm包 [root@jenkins ~]#ll total 178212 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm [root@jenkins ~]#rpm -ivh jdk-17_linux-x64_bin.rpm warning: jdk-17_linux-x64_bin.rpm: Header V3 RSA/SHA256 Signature, key ID ad986da3: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk-17-2000:17.0.11-7 ################################# [100%] 查看是否安装成功: [root@jenkins ~]# rpm -qa|grep jdk jdk-17-17.0.11-7.x86_64 二.安装jenkins 上传jenkins包 [root@jenkins ~]#ll total 269432 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm -rw-r--r-- 1 root root 93405530 Oct 1 19:06 jenkins-2.405-1.1.noarch.rpm [root@jenkins ~]#rpm -ivh jenkins-2.405-1.1.noarch.rpm warning: jenkins-2.405-1.1.noarch.rpm: Header V4 RSA/SHA512 Signature, key ID ef5975ca: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jenkins-2.405-1.1 ################################# [100%] 三.修改Jenkins启动用户为root 1)修改配置文件的启动用户 [root@jenkins ~]# vim /etc/sysconfig/jenkins ... JENKINS_USER=\u0026#34;root\u0026#34; ... 2)修改启动配置的用户 [root@jenkins ~]# vim /usr/lib/systemd/system/jenkins.service ... User=root Group=root ... [root@jenkins ~]# systemctl daemon-reload 3)安装字体配置 [root@jenkins ~]# yum -y install fontconfig 四.启动Jenkins并加入开机自动启动 [root@jenkins ~]# systemctl start jenkins [root@jenkins ~]# systemctl enable jenkins 03. 登陆Jenkins 04. 修改密码 修改完密码后会重新登陆,用户名为admin\n05. 插件安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1.上传插件到jenkins家目录 [root@jenkins ~]# cd /var/lib/jenkins/ [root@jenkins ~]#ll total 576228 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm -rw-r--r-- 1 root root 93405530 Oct 1 19:06 jenkins-2.405-1.1.noarch.rpm -rw-r--r-- 1 root root 314156543 Oct 1 19:30 jenkins_plu.tar.gz 2.将插件解压到plugins目录中 [root@jenkins jenkins]#tar xf /root/jenkins_plu.tar.gz -C plugins/ 3.重启Jenkins生效 [root@jenkins ~]# systemctl restart jenkins 06. 创建项目 创建一个自由风格项目,执行pwd命令,查看使用jenkins运行默认的路径所在位置\n说明: 使用jenkins创建完项目后,系统会自动在jenkins的家目录/var/lib/jenkins/下创建一个workspace项目目录，并且在此目录下创建以项目名称命名的目录。\n后续jenkins拉取gitlab的代码就是存储在此项目目录下。\n1 2 [root@jenkins ~]#ls /var/lib/jenkins/workspace/ test_job 将gitlab与jenkins打通\n没打通前提示无法连接：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #配置免密钥 [root@jenkins ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Rr4ouinx/cPqVxJsxju2Cjd1mhWmWSuKGOKG532uTvk root@jenkins The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | | | | | o = | | *B o | |o o=oS | |=o . +=O.. | |o=+.Bo+=. | |oo.B.+* | | .=+BOE. | +----[SHA256]-----+ [root@jenkins ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+kaj/NEwHKaIErMEClVYuG3JuWr9CD7XpuO5YZ6JftMmNdAPMrvS0mWqP4SDyT6/le0j1RGkA48GsirESfMqXKvzSSd5AuhIPmUADtGPNK6VSeiMKyoWgiNBFLn6RIOAcXI0MuZrYQMLJz+kOYr6kF9em/6MfFQrLkt9DzIlnR9b7CMLyDnPOZ8FG640DqqX0sZ8G+i2v41VX/c7RGIWAgEhJY+kiMS5cDxWlTf2k8hoZmh0VIa/G9AwnvbklyUiE4ACd9GtEJG5xXJzedn7q0/Ygfr26T29t+KST3SlyrXIaEbEC5WwIMymCEJDCPPCGzqLjsWa+4dbJhoEM1afV root@jenkins #jenkins安装git [root@jenkins ~]#yum install -y git 在gitlab中添加jenkins生成的密钥：\n需要在jenkins上git clone一下，完成一次yes输入\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@jenkins ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. remote: Enumerating objects: 20, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (8/8), done. remote: Total 20 (delta 0), reused 0 (delta 0), pack-reused 11 Receiving objects: 100% (20/20), done. Resolving deltas: 100% (2/2), done. 然后就可以连接了\n再次构建一次测试拉取\n拉取成功\n配置webhook\n通过配置webhook触发jenkins自动拉取gitlab的代码,自动构建完成测试\n两边保存后测试\n项目修改后重新推送测试\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@gitlab blog]#git branch * master [root@gitlab blog]#touch c.txt [root@gitlab blog]#git add . [root@gitlab blog]#git commit -m \u0026#34;test_v2\u0026#34; [master 7b1f29e] test_v2 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 c.txt [root@gitlab blog]#git pull origin master remote: Enumerating objects: 17, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (8/8), done. remote: Total 17 (delta 0), reused 0 (delta 0), pack-reused 8 Unpacking objects: 100% (17/17), done. From 11.0.1.200:dawn/blog * branch master -\u0026gt; FETCH_HEAD Merge made by the \u0026#39;recursive\u0026#39; strategy. a.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 a.txt [root@gitlab blog]#git push -u origin master Counting objects: 7, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (5/5), 469 bytes | 0 bytes/s, done. Total 5 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git 47f606c..91e80a5 master -\u0026gt; master Branch master set up to track remote branch master from origin. 自动构建成功\n","date":"2026-10-01","description":"","objectID":"/posts/jenkins-practice/","readingTime":4,"tags":["DevOps","Jenkins","CI/CD"],"title":"Jenkins 实战：私有化部署、插件安装与 GitLab 联动 CI","url":"/posts/jenkins-practice/","wordCount":697},{"categories":["DevOps"],"content":"01. gitlab相关网站 官网：https://about.gitlab.com/\n国内镜像：https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/\n02.gitlab部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 一.环境准备 1、系统：CentOS7.9 2、内存：实验（4G）生产（至少8G） 3、禁用防火墙，关闭selinux 二.下载gitlab的rpm包 [root@gitlab ~]# wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm 三.安装gitlab # 安装过程中提示需要安装依赖 [root@gitlab ~]# rpm -ivh gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm warning: gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY error: Failed dependencies: policycoreutils-python is needed by gitlab-ce-16.9.8-ce.0.el7.x86_64 # 安装依赖 [root@gitlab ~]# yum -y install policycoreutils-python # 安装完依赖继续安装gitlab [root@gitlab ~]# rpm -ivh gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm warning: gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY Preparing... ################################# [100%] Updating / installing... 1:gitlab-ce-16.9.8-ce.0.el7 ################################# [100%] 四.启动前配置访问域名或IP [root@gitlab ~]# vim /etc/gitlab/gitlab.rb ... external_url \u0026#39;http://11.0.1.200\u0026#39; ... 配置完成后执行命令: [root@gitlab ~]# gitlab-ctl reconfigure 03. gitlab组件 1 2 3 4 5 6 7 nginx： 静态web服务器 gitlab-workhorse: 轻量级的反向代理服务器 logrotate： 日志文件管理工具 postgresql： 数据库 redis： 缓存数据库 sidekiq： 用于在后台执行队列任务（异步执行）。（Ruby） unicorn：An HTTP server for Rack applications，gitlab Rails应用是托管在这个服务器上面的。（Ruby Web Server,主要使用Ruby编写） 04. gitlab常用命令 1 2 3 4 5 gitlab-ctl start # 启动gitlab 在执行gitlab-ctl reconfigure 服务默认已经启动 gitlab-ctl status # 查看目前gitlab所有服务运维状态 gitlab-ctl stop # 停止gitlab服务 gitlab-ctl stop nginx # 单独停止某个服务 gitlab-ctl tail # 查看所有服务的日志 05. gitlab页面配置 登陆gitlab: 11.0.1.200\n一. 登陆密码 执行gitlab-ctl reconfigure完成后会提示密码存放的位置:\n二. 配置页面显示为中文 三. 修改默认密码 四. 开启钩子本地网络请求 06. gitlab实战 一. 创建组 二. 基于组创建项目 至此第一个代码仓库就创建完成了,不过里面是空的。接下来上传代码\n三. 将代码提交到项目中存储 项目创建完成后,需要将blog代码上传到此项目\n服务器和代码仓库连接方式有两种\n第一种: 基于用户名和密码的方式连接\n第二种: 基于SSH免秘钥方式\n我们使用基于免秘钥方式: 方便+安全性高\n1.生成密钥对(在这是直接使用的gitlab服务器)\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@gitlab ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:j3FozajJdufxM0ZRtgPa2ZNSwWKvo8bvxbMJl8F5V3M root@gitlab The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | ... | | + = | | + X +E| | =. =.B.+| | S + ++oo| | . + = +. +.| | = o.+o..* | | . . o+o++ + | | ..++oo | +----[SHA256]-----+ 查看秘钥对: [root@gitlab ~]# ll .ssh/ total 8 -rw------- 1 root root 1679 May 14 15:02 id_rsa # 私钥 -rw-r--r-- 1 root root 393 May 14 15:02 id_rsa.pub # 公钥 [root@gitlab ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJGpCKL64eidZ3I/nAVNCNJwD8yeDqelZP0GUTwlcp6IdKaZ7wUOIRDCtGn5r+YnOCKNwMhUNzb3VCOSVLDVIE0x53dPrCMK4OKLmZ6OtTYFBveGOZWWwdYyGQITx2eP7zOSBxeW3AnEWcya/UZdiZrYXA/d29DWjMjCt5QAxOit0EWSir1Z4i2MkTPuljOmjk6A55XAoem0WJRdg/d8KKIiCdcGrj4adYq2kNJ8oYH/9QcnpASUW/ANhzrALnLIX2WaH/p0T3gisLvk2KDUe3rVKD3XaKDlyLgWIViQFm3Me6L2e4taRFiu3VdsbfFfzjDevfDtLaqKLOqPtaczlF root@gitlab 2.将生成的公钥粘贴到gitlab页面的SSH秘钥管理页面\n至此就完成了服务器和gitlab页面之间的SSH免秘钥,接下来将代码推送到blog项目中\n推送代码的方式有两种:\n1.将空的仓库克隆到本地,然后将代码拷贝到仓库中,然后提交到远程仓库gitlab\n进入到项目中找到项目的链接地址\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 1.复制链接执行 [root@gitlab ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. warning: remote HEAD refers to nonexistent ref, unable to checkout. 2.查看将blog仓库下载到了本地 [root@gitlab ~]#ll total 1176964 drwxr-xr-x 3 root root 18 Oct 1 14:43 blog -rw-r--r-- 1 root root 84579612 Oct 1 14:35 blog_backup_20261001-1435.tar.gz drwxr-xr-x 3 root root 18 Sep 30 19:14 dawn -rw-r--r-- 1 root root 1120627456 Sep 30 23:12 gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm 3.进入blog目录 [root@gitlab ~]#cd blog/ 4.解压代码: [root@gitlab blog]#tar xf /root/blog_backup_20261001-1435.tar.gz -C . 5.将代码提交到本地仓库 [root@gitlab blog]#git add . [root@gitlab blog]#git commit -m \u0026#34;DAWN’s Blog\u0026#34; [master (root-commit) 5af4246] DAWN’s Blog 3 files changed, 112 insertions(+) create mode 100644 blog_state.txt create mode 160000 myblog create mode 160000 myblog-deploy 6.将代码提交到远程仓库gitlab的blog仓库中 因为我们是直接克隆的gitlab的仓库下载到本地的所以目录中默认已经有了远程仓库blog的项目地址 查看远程仓库: [root@gitlab blog]#git remote -v origin git@11.0.1.200:dawn/blog.git (fetch) origin git@11.0.1.200:dawn/blog.git (push) 将本地的master推送到远程仓库origin [root@gitlab blog]#git push -u origin master Counting objects: 3, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 1.23 KiB | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git * [new branch] master -\u0026gt; master Branch master set up to track remote branch master from origin. 查看blog项目,代码上传成功\n2.将本地git仓库推送到远程仓库gitlab\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 1.配置一个本地仓库 [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn 2.初始化仓库 [root@gitlab dawn]#git init 3.上传代码 [root@gitlab dawn]#touch a.txt [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;test_v1\u0026#34; [master (root-commit) 815c600] test_v1 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt 4.设置gitlab的blog仓库为本地的远程仓库 [root@gitlab dawn]#git remote add origin git@11.0.1.200:dawn/blog.git 5.将本地仓库的内容提交到远程仓库（由于远程仓库 origin 里已经有内容了，直接推送报错，在这里需要保留远程内容，所以先拉取合并再推送） [root@gitlab dawn]#git pull origin master warning: no common commits remote: Enumerating objects: 3, done. remote: Counting objects: 100% (3/3), done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From 11.0.1.200:dawn/blog * branch master -\u0026gt; FETCH_HEAD Merge made by the \u0026#39;recursive\u0026#39; strategy. blog_state.txt | 110 ++++++++++++++++++++++++++++++++++++ myblog | 1 + myblog-deploy | 1 + 3 files changed, 112 insertions(+) create mode 100644 blog_state.txt create mode 160000 myblog create mode 160000 myblog-deploy [root@gitlab dawn]#git push -u origin master Counting objects: 6, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (5/5), 462 bytes | 0 bytes/s, done. Total 5 (delta 1), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git 5af4246..0242e0a master -\u0026gt; master Branch master set up to track remote branch master from origin. 3.创建普通账号dev\n创建完成后修改dev密码-别退出，点击用户界面的编辑\n完成后使用dev登录gitlab(第一次登录需要修改密码)\n4.将dev用户添加到dawn组\n5.dev的服务器端生成秘钥对\n1 [root@lb01 ~]#ssh-keygen 6.将生成的公钥复制到dev账号下的ssh-key页面中\n7.克隆代码到11.0.1.5 dev\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@lb01 ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. remote: Enumerating objects: 8, done. remote: Counting objects: 100% (8/8), done. remote: Compressing objects: 100% (6/6), done. remote: Total 8 (delta 1), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (8/8), done. Resolving deltas: 100% (1/1), done. 8.修改并上传代码到远程仓库\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 设置11.0.1.5的使用者 [root@lb01 blog]#git config --global user.email \u0026#34;dev@mail.com\u0026#34; [root@lb01 blog]#git config --global user.name \u0026#34;dev\u0026#34; [root@lb01 blog]#vim a.txt 123 [root@lb01 blog]#git commit -am \u0026#34;123\u0026#34; 提交到远程仓库 [root@lb01 blog]#git push -u origin master Counting objects: 5, done. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 257 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: GitLab: You are not allowed to push code to protected branches on this project.To git@11.0.1.200:dawn/blog.git ! [remote rejected] master -\u0026gt; master (pre-receive hook declined) error: failed to push some refs to \u0026#39;git@11.0.1.200:dawn/blog.git\u0026#39; # 不允许推送到master分支 推送到其他分支: 1.创建dev分支 [root@lb01 blog]#git branch dev [root@lb01 blog]#git branch dev * master 2.推送dev分支到远程仓库 [root@lb01 blog]#git push -u origin dev 合并请求\n1.在dev账号发起合并请求\n2.在root账号中同意合并并删除源dev分支\n3.在web服务器下载代码\n1 2 [root@web01 ~]#git clone http://11.0.1.200/dawn/blog.git 输入用户名和密码 ","date":"2026-10-01","description":"","objectID":"/posts/gitlab-practice/","readingTime":6,"tags":["DevOps","GitLab","CI/CD"],"title":"GitLab 实战：私有化部署、SSH 免密与代码仓库管理","url":"/posts/gitlab-practice/","wordCount":1133},{"categories":["DevOps"],"content":"01.安装git并初始化仓库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@gitlab ~]#yum install -y git [root@gitlab ~]#git --version git version 1.8.3.1 [root@gitlab ~]#git config --global user.name \u0026#34;dawn\u0026#34; [root@gitlab ~]#git config --global user.email \u0026#34;dawn@mail.com\u0026#34; [root@gitlab ~]#git config --global color.ui true [root@gitlab ~]#cat .gitconfig [user] name = dawn email = dawn@mail.com [color] ui = true [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn [root@gitlab dawn]#ll total 0 [root@gitlab dawn]#git init Initialized empty Git repository in /root/dawn/.git/ [root@gitlab dawn]#ll total 0 [root@gitlab dawn]#ll -a total 0 drwxr-xr-x 3 root root 18 Jun 11 10:57 . dr-xr-x---. 4 root root 174 Jun 11 10:57 .. drwxr-xr-x 7 root root 119 Jun 11 10:57 .git 02.Git命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 1.查看仓库的状态(时刻保证仓库的干净) [root@gitlab dawn]#git status # On branch master # # Initial commit # nothing to commit (create/copy files and use \u0026#34;git add\u0026#34; to track) 2.创建普通文件 [root@gitlab dawn]#touch a.txt 3.将工作区的内容提交到暂存区 [root@gitlab dawn]#git add a.txt #git add .表示所有工作区的内容提交到暂存区 [root@gitlab dawn]#git add . 4.删除暂存区的内容 [root@gitlab dawn]#git rm --cached a.txt rm \u0026#39;a.txt\u0026#39; 5.提交到暂存区和本地仓库 [root@gitlab dawn]#git add a.txt [root@gitlab dawn]#git status # On branch master # # Initial commit # # Changes to be committed: # (use \u0026#34;git rm --cached \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # new file: a.txt # #使用git commit -m描述 提交到本地仓库 [root@gitlab dawn]#git commit -m \u0026#34;create a.txt\u0026#34; [master (root-commit) b65d348] create a.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt 查看状态: [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 创建文件 touch a.txt 提交到暂存区 git add a.txt 提交到本地仓库 git commit -m \u0026#34;xxx\u0026#34; 2.git删除 [root@gitlab dawn]#git rm a.txt rm \u0026#39;a.txt\u0026#39; [root@gitlab dawn]#git status # On branch master # Changes to be committed: # (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # deleted: a.txt # [root@gitlab dawn]#git commit -m \u0026#34;del a.txt\u0026#34; [master 41fc71b] del a.txt 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 a.txt [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 注意: 如果工作区的文件提交到了暂存区域反悔了,删除暂存区的内容 [root@gitlab dawn]#touch c.txt [root@gitlab dawn]#git add c.txt [root@gitlab dawn]#git rm --cached c.txt rm \u0026#39;c.txt\u0026#39; 3.恢复工作区内容 [root@gitlab dawn]#git reset HEAD b.txt [root@gitlab dawn]#git checkout -- b.txt 4.git比对 git diff 比对的是工作区域和暂存区域的不同 [root@gitlab dawn]#git diff diff --git a/b.txt b/b.txt index e69de29..ccc3e7b 100644 --- a/b.txt +++ b/b.txt @@ -0,0 +1 @@ +aaaaa git diff --cached 比对的是暂存区和本地仓库的不同 [root@gitlab dawn]#git diff --cached diff --git a/b.txt b/b.txt index e69de29..ccc3e7b 100644 --- a/b.txt +++ b/b.txt @@ -0,0 +1 @@ +aaaaa 5.改名 [root@gitlab dawn]#git mv b.txt b.bak [root@gitlab dawn]#git status # On branch master # Changes to be committed: # (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # renamed: b.txt -\u0026gt; b.bak # [root@gitlab dawn]#git commit -m \u0026#34;newname b.txt--\u0026gt;b.bak\u0026#34; [master 55335a7] newname b.txt--\u0026gt;b.bak 1 file changed, 0 insertions(+), 0 deletions(-) rename b.txt =\u0026gt; b.bak (100%) [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 6.指定回滚到哪个快照 显示所有的快照: [root@gitlab dawn]#git log --oneline ae6585c xx 55335a7 newname b.txt--\u0026gt;b.bak 09ffe53 modifile b.txt 043a297 newfile b.txt 41fc71b del a.txt b65d348 create a.txt [root@gitlab dawn]#ll total 8 -rw-r--r-- 1 root root 6 Jun 11 12:10 b.bak -rw-r--r-- 1 root root 6 Jun 11 12:07 b.txt 使用git reset --hard 回滚 [root@gitlab dawn]#git reset --hard b65d348 HEAD is now at b65d348 create a.txt [root@gitlab dawn]#ll total 0 -rw-r--r-- 1 root root 0 Jun 11 12:12 a.txt 7.查看历史所有的提交动作: [root@gitlab dawn]#git reflog b65d348 HEAD@{0}: reset: moving to b65d348 ae6585c HEAD@{1}: commit: xx 55335a7 HEAD@{2}: commit: newname b.txt--\u0026gt;b.bak 09ffe53 HEAD@{3}: commit: modifile b.txt 043a297 HEAD@{4}: commit: newfile b.txt 41fc71b HEAD@{5}: commit: del a.txt b65d348 HEAD@{6}: commit (initial): create a.txt [root@gitlab dawn]#git reset --hard ae6585c HEAD is now at ae6585c xx [root@gitlab dawn]#ll total 8 -rw-r--r-- 1 root root 6 Jun 11 12:16 b.bak -rw-r--r-- 1 root root 6 Jun 11 12:16 b.txt Git分支 1.查看git分支 *表示当前所在的分支 [root@gitlab dawn]#git branch * master 2.创建分支 [root@gitlab dawn]#git branch dev [root@gitlab dawn]#git branch dev * master 3.切换到dev分支 [root@gitlab dawn]#git checkout dev Switched to branch \u0026#39;dev\u0026#39; [root@gitlab dawn]#git branch * dev master 4.分支创建文件并且提交到本地仓库 [root@gitlab dawn]#touch aa.txt [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;newfile aa.txt\u0026#34; [dev 2694c7f] newfile aa.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aa.txt 5.合并分支 先切换回master分支 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master 合并dev分支到master分支 [root@gitlab dawn]#git merge dev Updating ae6585c..2694c7f Fast-forward aa.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aa.txt 6.分支写完功能后立刻删除(因为代码会落后于远程分支) [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 2694c7f). [root@gitlab dawn]#git branch * master 7.冲突合并 ---------------------------------- 修改文件的内容不影响分支的合并 修改主干分支的文件增加bbbbb [root@gitlab dawn]#vim b.txt aaaaa bbbbb 使用一条命令可以直接创建并切换到dev分支 [root@gitlab dawn]#git checkout -b dev Switched to a new branch \u0026#39;dev\u0026#39; [root@gitlab dawn]# [root@gitlab dawn]# [root@gitlab dawn]#git branch * dev master 修改b.txt的内容 [root@gitlab dawn]#vim b.txt aaaaa bbccc 提交到本地仓库 [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;modifle bbbccc\u0026#34; 返回到master分支将dev分支合并 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master [root@gitlab dawn]#cat b.txt aaaaa bbbbb [root@gitlab dawn]#git merge dev Updating f56faba..dc17f6d Fast-forward b.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) [root@gitlab dawn]#cat b.txt aaaaa bbccc ------------------------------------------------------ 影响冲突合并的案例 创建dev分支 [root@gitlab dawn]#git branch dev 在master分支修改b.txt第3行 [root@gitlab dawn]#cat b.txt aaaaa iiiii bbbbb [root@gitlab dawn]#git commit -am \u0026#34;modifile b.txt bbbbb\u0026#34; [master ae4ab87] modifile b.txt bbbbb 1 file changed, 1 insertion(+) 在切换到dev分支修改b.txt第3行 [root@gitlab dawn]#cat b.txt aaaaa iiiii ccccc 提交到本地仓库 [root@gitlab dawn]#git commit -am \u0026#34;modifile b.txt ccccc\u0026#34; [dev 925c06c] modifile b.txt ccccc 1 file changed, 1 insertion(+) 在切换到master分支进行分支合并 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master [root@gitlab dawn]#git merge dev Auto-merging b.txt CONFLICT (content): Merge conflict in b.txt Automatic merge failed; fix conflicts and then commit the result. 查看文件显示代码冲突 [root@gitlab dawn]#cat b.txt aaaaa iiiii \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt; HEAD bbbbb ======= ccccc \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; dev 手动解决代码冲突 留下一行或者全部留下 [root@gitlab dawn]#vim b.txt aaaaa iiiii bbbbb ccccc [root@gitlab dawn]#git commit -am \u0026#34;merge dev\u0026#34; [master 325eef2] merge dev [root@gitlab dawn]# [root@gitlab dawn]# [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean [root@gitlab dawn]#cat b.txt aaaaa iiiii bbbbb ccccc 合并后删除dev分支 [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 925c06c). ","date":"2026-10-01","description":"","objectID":"/posts/git-practice-notes/","readingTime":7,"tags":["DevOps","Git","版本控制"],"title":"Git 实战笔记：安装配置、常用命令与分支管理","url":"/posts/git-practice-notes/","wordCount":1294},{"categories":["Zabbix","监控运维"],"content":"1. 获取系统的登录用户数 1 2 3 4 5 6 7 [root@web02 ~]#w 17:44:18 up 1:17, 2 users, load average: 0.01, 0.02, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 11.0.1.1 16:31 11:46 0.07s 0.07s -bash root pts/1 11.0.1.52 17:07 2.00s 0.04s 0.00s w [root@web02 ~]#w|awk \u0026#39;NR==1{print $4}\u0026#39; 2 2. 定义为 zabbix 的监控项 1 2 3 4 5 6 7 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; # 查看监控项 zabbix_agentd -p 有语法检测功能 [root@web02 ~]#zabbix_agentd -p|grep login_number login_number [t|2] # 重启客户端 [root@web02 ~]# systemctl restart zabbix-agent.service 3. 使用 zabbix 服务器端进行采集监控 服务端通过命令行测试是否通过监控项来获取用户自定义的值\n1 2 3 4 # 安装 zabbix-get 命令 [root@zabbix ~]# yum -y install zabbix-get [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k login_number 2 在 Web 页面添加监控项\n查看监控项\n4. 监控 TCP 22 端口是否存活 键值在选择中选取需要监控的信息\n检查 TCP 端口是否处于侦听状态。返回 0 - 未侦听；1 - 正在侦听\n停止 web02 sshd 服务\n1 [root@web02 ~]# systemctl stop sshd 5. 自定义配置展示值映射 调用值映射\n6. 自定义监控服务器内存百分比 第一步：自定义监控项，获取可用百分比\n1 2 3 4 5 6 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; UserParameter=mem_free,free|awk \u0026#39;NR==2{print $NF/$2*100}\u0026#39; [root@web02 ~]# systemctl restart zabbix-agent.service [root@web02 ~]#zabbix_agentd -p|grep mem_free mem_free [t|79.8753] 第二步：服务端测试获取 key 值并添加监控\n1 2 [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k mem_free 79.9775 7. 为可用内存设置触发器 当内存可用百分比小于 20% 则触发报警规则\n测试：关闭 swap 分区\n1 [root@web02 ~]# swapoff -a 使用 dd 压测消耗内存\n1 [root@web02 ~]# dd if=/dev/zero of=/dev/null bs=1200M count=2048 8. 配置多条件触发器 同时满足两个条件触发告警\n添加 swap 可用百分比\n1 2 3 4 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; UserParameter=mem_free,free|awk \u0026#39;NR==2{print $NF/$2*100}\u0026#39; UserParameter=swap_free,free -m|awk \u0026#39;NR==3{print $3/$2*100}\u0026#39; 修改触发器配置\n完成后在使用 dd 压测查看结果\n","date":"2026-09-23","description":"","objectID":"/posts/zabbix-custom-monitor/","readingTime":2,"tags":["Zabbix","自定义监控","UserParameter","触发器","值映射"],"title":"Zabbix 自定义监控","url":"/posts/zabbix-custom-monitor/","wordCount":237},{"categories":["Zabbix","监控运维"],"content":"企业 zabbix 高并发场景使用\n1. 准备一台数据库服务器 11.0.1.52 安装 MariaDB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@db02 ~]#yum -y install mariadb-server [root@db02 ~]#systemctl enable mariadb --now [root@db02 ~]#mysql Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 2 Server version: 5.5.68-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; create database zabbix character set utf8 collate utf8_bin; Query OK, 1 row affected (0.02 sec) MariaDB [(none)]\u0026gt; create user \u0026#39;zabbix\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#39;zabbix\u0026#39;; Query OK, 0 rows affected (0.10 sec) MariaDB [(none)]\u0026gt; grant all privileges on zabbix.* to \u0026#39;zabbix\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) 2. 导出 Zabbix 服务器 zabbix 数据库、导入到新的数据库 1 2 [root@zabbix ~]# mysqldump -uroot -B zabbix \u0026gt; zabbix.sql [root@zabbix ~]# cat zabbix.sql |mysql -h 172.16.1.52 -uzabbix -pzabbix zabbix 3. 配置 Web 连接数据库信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@zabbix ~]# systemctl stop mariadb [root@zabbix ~]# cat /etc/zabbix/web/zabbix.conf.php \u0026lt;?php // Zabbix GUI configuration file. global $DB; $DB[\u0026#39;TYPE\u0026#39;] = \u0026#39;MYSQL\u0026#39;; $DB[\u0026#39;SERVER\u0026#39;] = \u0026#39;172.16.1.52\u0026#39;; $DB[\u0026#39;PORT\u0026#39;] = \u0026#39;0\u0026#39;; $DB[\u0026#39;DATABASE\u0026#39;] = \u0026#39;zabbix\u0026#39;; $DB[\u0026#39;USER\u0026#39;] = \u0026#39;zabbix\u0026#39;; $DB[\u0026#39;PASSWORD\u0026#39;] = \u0026#39;zabbix\u0026#39;; [root@zabbix ~]# grep ^DB /etc/zabbix/zabbix_server.conf DBHost=172.16.1.52 DBName=zabbix DBUser=zabbix DBPassword=zabbix 4. 重启服务 1 [root@zabbix ~]# systemctl restart zabbix-server httpd ","date":"2026-09-23","description":"","objectID":"/posts/zabbix-split-database/","readingTime":2,"tags":["Zabbix","数据库","拆分","高并发"],"title":"拆分数据库","url":"/posts/zabbix-split-database/","wordCount":232},{"categories":["Zabbix","监控运维"],"content":"1.安装zabbix-agent客户端 准备一台服务器11.0.1.8 1 2 3 4 5 6 7 8 配置仓库: [root@web02 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 将服务端和客户端的yum仓库同步: [root@zabbix ~]#scp /etc/yum.repos.d/CentOS-SCLo-scl.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl.repo [root@zabbix ~]#scp /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl-rh.repo [root@zabbix ~]#scp /etc/yum.repos.d/zabbix.repo root@172.16.1.8:/etc/yum.repos.d/zabbix.repo 如果安装提示软件冲突: [root@web02 ~]# yum -y install centos-release-scl 2.配置zabbix客户端服务器信息 启动客户端 1 2 3 4 5 6 [root@web02 ~]# yum -y install zabbix-agent [root@web02 ~]# vim /etc/zabbix/zabbix_agentd.conf Server=172.16.1.71 ServerActive=172.16.1.71 Hostname=web02 [root@web02 ~]# systemctl enable zabbix-agent --now 3.zabbix监控web02 4.web02服务器完成监控操作 ","date":"2026-09-23","description":"","objectID":"/posts/zabbix-monitor-server/","readingTime":1,"tags":["Zabbix","监控","zabbix-agent","监控配置"],"title":"Zabbix 监控服务器信息","url":"/posts/zabbix-monitor-server/","wordCount":65},{"categories":["Zabbix","监控运维"],"content":"1.配置仓库 1 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 2.安装zabbix-server zabbix-agent 1 yum -y install zabbix-server-mysql zabbix-agent 3.安装scl源（解决版本冲突问题） 1 [root@zabbix ~]# yum -y install centos-release-scl 4.开启前端仓库 1 2 3 4 5 [root@zabbix ~]# vim /etc/yum.repos.d/zabbix.repo [zabbix-frontend] ... enabled=1 ... 5.将CentOS-SCLo-scl.repo和CentOS-SCLo-scl-rh.repo失效的mirrorlist替换为阿里云的baseurl 1 2 3 4 5 6 7 8 9 10 [root@zabbix yum.repos.d]#vim CentOS-SCLo-scl-rh.repo [centos-sclo-rh] ... baseurl=https://mirrors.aliyun.com/centos/7/sclo/x86_64/rh ... [root@zabbix yum.repos.d]#vim CentOS-SCLo-scl.repo [centos-sclo-sclo] ... baseurl=https://mirrors.aliyun.com/centos/7/sclo/x86_64/sclo ... 6.安装 php apache 前端 1 [root@zabbix ~]# yum -y install zabbix-web-mysql-scl zabbix-apache-conf-scl 7.安装mysql数据库 1 2 3 [root@zabbix ~]# yum -y install mariadb-server 启动数据库 [root@zabbix ~]#systemctl enable mariadb --now 8.创建zabbix库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@zabbix ~]#mysql -uroot Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 7 Server version: 5.5.68-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; create database zabbix character set utf8 collate utf8_bin; Query OK, 1 row affected (0.00 sec) MariaDB [(none)]\u0026gt; create user zabbix@localhost identified by \u0026#39;zabbix\u0026#39;; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; grant all privileges on zabbix.* to zabbix@localhost; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 1; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; quit Bye 9.初始化数据(业务运行前需要依赖数据库中的一些数据表格 需要提前导入) 1 2 3 4 5 [root@zabbix ~]# zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix #需要输入zabbix密码 [root@zabbix ~]# mysql -uroot MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 0; quit 10.修改zabbix连接数据的信息 1 2 3 4 [root@zabbix ~]# egrep ^DB /etc/zabbix/zabbix_server.conf DBName=zabbix DBUser=zabbix DBPassword=zabbix # 将注释打开配置密码 11.修改时区 1 2 [root@zabbix ~]# tail -1 /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf php_value[date.timezone] = Asia/Shanghai 12.启动服务 server agent httpd php 加入开机自启 1 [root@zabbix ~]# systemctl enable zabbix-server zabbix-agent httpd rh-php72-php-fpm --now 13.访问zabbix页面 11.0.1.71/zabbix 14.zabbix安装完成 15.调整为中文重新配置字体 查找windows字体复制到zabbix的字体目录下\nwindows字体位置\n上传其中一个字体到并改名\n1 2 3 4 5 6 7 8 9 10 [root@zabbix ~]#cd /usr/share/zabbix/assets/fonts/ [root@zabbix fonts]#ls graphfont.ttf simhei.ttf#上传的字体 [root@zabbix fonts]#rm -f graphfont.ttf #删除原来的字体 [root@zabbix fonts]#ln -s /usr/share/zabbix/assets/fonts/simhei.ttf graphfont.ttf #链接到上传的字体 [root@zabbix fonts]#chmod 644 simhei.ttf [root@zabbix fonts]#ll total 9520 lrwxrwxrwx 1 root root 41 Sep 23 01:19 graphfont.ttf -\u0026gt; /usr/share/zabbix/assets/fonts/simhei.ttf -rw-r--r-- 1 root root 9745792 Sep 23 01:18 simhei.ttf ","date":"2026-09-23","description":"","objectID":"/posts/zabbix-install/","readingTime":2,"tags":["Zabbix","监控","CentOS7","安装部署"],"title":"Zabbix 安装部署","url":"/posts/zabbix-install/","wordCount":361},{"categories":["Kubernetes","运维部署"],"content":"规划如下：\nmaster节点： 计算机名： master IP地址：192.168.8.30\nworker01节点： 计算机名： node1 IP地址： 192.168.8.31\nworker02节点： 计算机名： node2 IP地址： 192.168.8.32\n在三个节点上均完成以下步骤，并且保证三个节点时间均一致。\n1. 关闭防火墙和SELinux 1 2 3 4 [root@master ~]# systemctl disable firewalld.service --now [root@master ~]# vim /etc/selinux/config SELINUX=disabled [root@master ~]# setenforce 0 2. 关闭swap 1 2 [root@master ~]#sed -i \u0026#39;/swap/s/^/#/\u0026#39; /etc/fstab [root@master ~]# swapoff -a 3. 设置系统参数，加载内核模块 1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# cat \u0026gt; /etc/modules-load.d/k8s.conf \u0026lt;\u0026lt; EOF overlay br_netfilter EOF [root@master ~]#modprobe overlay [root@master ~]#modprobe br_netfilter [root@master ~]# cat \u0026gt; /etc/sysctl.d/k8s.conf \u0026lt;\u0026lt; EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF [root@master ~]# sysctl --system 4. 安装容器运行时（Containerd） Kubernetes v1.30 推荐使用 Containerd 作为容器运行时：\n1 [root@master ~]# yum -y remove runc 删除系统自带的yum仓库文件\n1 [root@master ~]# rm -rf /etc/yum.repos.d/Cent*.repo 配置本地yum仓库\n1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# mount /dev/cdrom /media/ [root@master ~]# cat /etc/fstab |grep iso9660 /dev/cdrom\t/media\tiso9660 defaults 0 0 [root@master ~]# cat /etc/yum.repos.d/dvd.repo [BaseOS] name=BaseOS baseurl=file:///media/BaseOS gpgcheck=0 [AppStream] name=AppStream baseurl=file:///media/AppStream gpgcheck=0 配置Containerd\n1 2 [root@master ~]#yum install -y yum-utils [root@master ~]#yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 安装containerd.io\n1 2 3 4 5 6 7 8 9 10 [root@master ~]#yum install -y containerd.io # 生成并修改配置文件 [root@master ~]#mkdir -p /etc/containerd [root@master ~]#containerd config default \u0026gt; /etc/containerd/config.toml # 修改配置使用systemd cgroup驱动 #sed -i \u0026#39;s/SystemdCgroup = false/SystemdCgroup = true/g\u0026#39; /etc/containerd/config.toml # 配置镜像加速（可选） #sed -i \u0026#39;s|registry.k8s.io|registry.aliyuncs.com/google_containers|g\u0026#39; /etc/containerd/config.toml # 启动并设置开机自启 #systemctl enable --now containerd 5. 安装和配置kubernetes 1 2 3 4 5 6 7 [root@master yum.repos.d]# cat kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/ enabled=1 gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/repodata/repomd.xml.key 安装指定版本的kubernetes组件\n1 2 3 [root@master ~]#yum install -y kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0 --disableexcludes=kubernetes # 启动kubelet [root@master ~]#systemctl enable --now kubelet 6. 初始化kubernetes 注意：当所有的节点完成以上配置后，仅需在master节点上执行以下步骤\n1 2 3 4 5 [root@master ~]# kubeadm init --pod-network-cidr=10.244.0.0/16 --service-cidr=172.16.0.0/16 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.30.0 --control-plane-endpoint=\u0026#34;192.168.8.30:6443\u0026#34; --upload-certs # 配置kubectl [root@master ~]#mkdir -p $HOME/.kube [root@master ~]#cp -i /etc/kubernetes/admin.conf $HOME/.kube/config [root@master ~]#chown $(id -u):$(id -g) $HOME/.kube/config 7. 将worker节点加入kubernetes集群 按照以下命令在worker节点上执行，注意：不同的节点token和ca证书可能都会不同，请根据自己系统的提示执行，证书也可能会过期，如果已经过期，可以执行kubeadm token create \u0026ndash;print-join-command重新生成\n1 2 [root@node1 ~]# kubeadm join 192.168.8.30:6443 --token yfjrsi.60lmqgzobp0b8al3 --discovery-token-ca-cert-hash sha256:f4e3c924918545abfd8148e71a377e086d591c6a59856a496b4b33e0987d9c18 [root@node2 ~]# kubeadm join 192.168.8.30:6443 --token yfjrsi.60lmqgzobp0b8al3 --discovery-token-ca-cert-hash sha256:f4e3c924918545abfd8148e71a377e086d591c6a59856a496b4b33e0987d9c18 8. 在master节点上安装calico网络插件 1 2 # 安装Calico网络插件 [root@master ~]#kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml 备注：由于众所周知的原因，可能会出现无法下载calico镜像的问题\n我重新修改了该文件，请下载到master节点上，执行该文件即可\n文件已上传至百度网盘，下载链接：https://pan.baidu.com/s/1BPCFxv0kE0KWb04Qsgy71g （提取码：s44k）\n1 [root@master ~]#kubectl apply -f /root/calico-ucloud.yaml 安装完成后，执行\n1 2 3 4 5 [root@master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready control-plane 65m v1.30.13 node1 Ready \u0026lt;none\u0026gt; 62m v1.30.0 node2 Ready \u0026lt;none\u0026gt; 62m v1.30.0 如果看到以上结果，表示kubernetes已经安装完成。\n9. 扩展内容 在各个节点上执行以下命令\n1 2 3 4 5 kubectl completion bash \u0026gt; /etc/bash_completion.d/kubectl source /etc/bash_completion.d/kubectl vim /root/.bashrc alias docker=\u0026#39;crictl\u0026#39; source /root/.bashrc 说明：\nkubectl 命令补全：上面前两行把 kubectl 的补全脚本写入 /etc/bash_completion.d/。但前提是系统已安装 bash-completion 软件包（如 yum install -y bash-completion 或 apt install -y bash-completion），否则该目录不存在、补全无法生效；此外 kubectl 本身还需执行这两条命令才能真正开启补全（即 k8s 需额外配置才能补全）。 docker 别名：Kubernetes 改用 containerd 容器运行时后，容器管理命令由 docker 变为 crictl。这里把 docker 别名成 crictl，是为了照顾习惯使用 docker 命令的用户，使其沿用原有的操作习惯。 当如果执行#docker images，报错情况：\nk8s出现以下提示，如何解决\nWARN[0000] image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.\n这个警告提示表明你使用的容器运行时客户端（如 crictl 或 kubelet）正在使用默认的端点配置，而这些默认配置已被弃用，需要显式指定端点。以下是解决此问题的详细步骤：\n1 2 3 4 5 6 7 cat \u0026gt; /etc/crictl.yaml \u0026lt;\u0026lt; EOF runtime-endpoint: unix:///run/containerd/containerd.sock # 根据你的容器运行时选择 image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false pull-image-on-create: false EOF 说明：\n若使用 Containerd，端点为 unix:///run/containerd/containerd.sock。\n若使用 CRI-O，端点为 unix:///run/crio/crio.sock。\n备注： 可选\n如果手动安装kubernetes，默认使用的是iptables转发数据，如果大规模的场景，则建议修改为ipvs模式，如果需要在安装完成后，修改为ipvs模式\n1. 在所有的节点上安装ipvsadm软件包 2. 在所有节点上加载模块 1 2 3 4 5 6 7 cat \u0026gt; /etc/modules-load.d/ipvs.conf \u0026lt;\u0026lt; EOF ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack EOF 3. 编辑kube-proxy ConfigMap 1 kubectl edit cm kube-proxy -n kube-system 在打开的编辑器中，找到并修改以下部分：\n1 2 3 4 5 6 7 apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: \u0026#34;ipvs\u0026#34; # 将原来的\u0026#34;iptables\u0026#34;改为\u0026#34;ipvs\u0026#34; ipvs: scheduler: \u0026#34;rr\u0026#34; # 负载均衡算法，可选 rr|wrr|lc 4. 强制重启 kube-proxy Pods 删除所有kube-proxy Pod，触发重建\n1 #kubectl delete pods -n kube-system -l k8s-app=kube-proxy 5. 验证新 Pod 是否正常运行： 1 2 #kubectl get pods -n kube-system -l k8s-app=kube-proxy # 所有Pod状态应为Running 6. 验证 IPVS 配置生效 1 2 3 4 5 [root@master ~]# ipvsadm -L -n TCP 192.168.8.30:30080 rr -\u0026gt; 10.244.104.3:80 Masq 1 0 0 -\u0026gt; 10.244.104.4:80 Masq 1 0 0 -\u0026gt; 10.244.166.132:80 Masq 1 0 0 ","date":"2026-08-07","description":"","objectID":"/posts/k8s-v1.30-manual-deploy/","readingTime":3,"tags":["Kubernetes","kubeadm","containerd","calico","v1.30","集群部署"],"title":"手动部署 Kubernetes v1.30 集群（kubeadm 方式）","url":"/posts/k8s-v1.30-manual-deploy/","wordCount":618},{"categories":null,"content":"KVM 虚拟化入门到实战：部署、管理与网络配置 本文系统介绍 KVM 虚拟化技术，涵盖发展历史、部署方法、虚拟机管理、网络模式配置，以及使用命令行自动化安装虚拟机。所有命令均在 CentOS/RHEL 环境下验证。\n一、KVM 发展历史 KVM（Kernel-based Virtual Machine，基于内核的虚拟机）是当前云计算领域主流的虚拟化技术。了解它的发展脉络，有助于理解为什么业界最终选择了 KVM。\n虚拟化技术大致可分为两类：闭源虚拟化（如 VMware、Microsoft Hyper-V）和开源虚拟化（如 Xen、KVM）。\n早期的开源虚拟化代表是 Xen。AWS 和阿里云早期都采用 Xen 方案；Citrix 的 XenServer（思杰）以及桌面云产品 FusionAccess（对应 Citrix XenDesktop）也基于 Xen。华为的 FusionCompute 在 6.3 版本之前同样使用 Xen。\n关键的时间节点如下：\n2008 年：RedHat 收购 KVM，并在 RHEL 5.4 版本中正式发布 KVM（RHEL 5.0 发布的是 Xen）。 2010 年：OpenStack 项目诞生，红帽联合 IBM、Dell、VMware、Cisco、HP 等厂商共同拥抱 KVM 开源社区，在开源软件基金会下共建生态。同期 Citrix 主推 CloudStack 社区。 2014 年：RHEL 5.11 发布（RHEL 5.x 系列的生命周期终点，提供 7 年有效技术支持，可免费升级到 KVM）。 2017 年前后：AWS 推出基于 KVM 的 Nitro 平台实例，阿里云及全球众多厂商全面拥抱 KVM。 如今，主流云厂商的虚拟化底层基本都是 KVM。华为 FusionCompute 6.3 版本也从 Xen 切换到了 KVM。2019 年 IBM 以约 340 亿美金收购红帽公司，ansible、glusterfs、ceph 等红帽生态项目随之纳入麾下，进一步印证了这条技术路线的价值。\n二、什么是 KVM KVM 全称 Kernel-based Virtual Machine，即基于内核的虚拟机。它的核心特点是：\n无需升级内核：直接复用系统本身的内核，每个虚拟机被当做一个进程来运行。 轻量级虚拟化平台：相比传统方案更轻巧高效。 统一管理：XEN/KVM 虚拟化平台都提供统一的管理平台和命令行工具。 三、部署 KVM 1. 安装 KVM 所需软件包 前提条件：在物理机 BIOS 中开启虚拟化支持 VT-x（大部分电脑默认开启）。如果是在 VMware 中嵌套安装，还需要在 VMware 里开启 VT-x 支持。\n建议资源配置：CPU 4 核，内存 4G。\n1 2 [root@cloud ~]# yum -y groupinstall \u0026#34;Virtualization*\u0026#34; [root@cloud ~]# systemctl restart libvirtd.service 2. 图形化安装虚拟机 本实验使用图形化工具 virt-manager 来创建并安装虚拟机。\n说明：图形化向导的操作过程本文不再截图展示，全文以命令行操作为主，图形界面按提示点击即可完成。另外，宿主机指运行 KVM 虚拟机的那台机器，本实验在 VMware 中进行嵌套虚拟化，因此这里的宿主机是一台 VMware 虚拟机，而非物理机。\n首先准备系统安装镜像：创建 /isos 目录，并将 CentOS 8.4 的 ISO 文件复制进去。\n1 [root@cloud ~]# mkdir /isos 然后启动图形化管理界面：\n1 [root@cloud ~]# virt-manager 在界面中点击新建虚拟机，选择刚才放入 /isos 的 ISO 作为安装源，再按向导依次配置 CPU、内存和磁盘，即可完成安装。安装好的虚拟机磁盘文件默认保存在 /var/lib/libvirt/images/ 目录下，形如 server1.qcow2：\n1 2 3 [root@cloud ~]# cd /var/lib/libvirt/images/ [root@cloud images]# ls server1.qcow2 3. 创建磁盘 上一步图形化安装时 virt-manager 会自动创建磁盘；这里介绍手动创建的方式，后面制作云镜像、给虚拟机添加硬盘都会用到。使用 qemu-img 创建 qcow2 格式磁盘：\n1 [root@cloud ~]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server1.qcow2 10g 提示：如果要用这种方式创建云镜像，容量越小速度越快，可以研究如何把镜像做得最小。通常系统盘性能要求不高，真正的性能瓶颈在数据磁盘。\n4. 创建桥接模式（带上行链路的虚拟交换机） 进入网络配置目录，基于现有网卡复制一份桥接配置：\n1 2 [root@cloud ~]# cd /etc/sysconfig/network-scripts/ [root@cloud network-scripts]# cp ifcfg-ens33 ifcfg-br0 编辑桥接网卡 ifcfg-br0：\n1 [root@cloud network-scripts]# cat ifcfg-br0 1 2 3 4 5 6 7 8 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 GATEWAY=192.168.8.254 DNS1=192.168.8.254 编辑物理网卡 ifcfg-ens33，将其桥接到 br0：\n1 [root@cloud network-scripts]# cat ifcfg-ens33 1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 重新加载网络配置并重启连接：\n1 2 3 [root@cloud ~]# nmcli connection reload [root@cloud ~]# nmcli connection down ens33 ; nmcli connection up ens33 [root@cloud ~]# nmcli connection down br0 ; nmcli connection up br0 5. 制作云主机镜像 需求：创建一个自定义云主机镜像。\n在 KVM 虚拟机部署完成后，进入虚拟机安装一些必要软件：\n配置本地 yum 源 安装必要的软件包： 1 # yum -y install net-tools vim-enhanced bash-completion cloud-init 编辑 /etc/cloud/cloud.cfg 配置文件，按实际需求调整其中的参数 删除 /etc/ssh/ssh_host* 执行 init 0 关机 最后压缩镜像：\n1 [root@cloud images]# qemu-img convert -c -O qcow2 /var/lib/libvirt/images/server1.qcow2 /isos/server1.qcow2 四、管理 KVM 进入 virsh 交互界面：\n1 [root@cloud ~]# virsh 常用的虚拟机管理命令如下。\n列出所有虚拟机：\n1 2 3 4 [root@cloud ~]# virsh list --all Id Name State ------------------------- - server1 shut off 开启 / 关闭 / 强制关闭 / 重启虚拟机：\n1 2 3 4 [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh shutdown server1 [root@cloud ~]# virsh destroy server1 [root@cloud ~]# virsh reboot server1 6. 进入虚拟机 console 接口 进入 KVM 虚拟机，给虚拟机添加虚拟 console，从而实现从宿主机以 console 方式访问虚拟机。\n在虚拟机中编辑 grub 配置：\n1 [root@VM1 ~]# vim /etc/default/grub 在 GRUB_CMDLINE_LINUX 末尾追加 console=ttyS0：\n1 GRUB_CMDLINE_LINUX=\u0026#34;crashkernel=auto resume=UUID=94e88e69-fad6-4d1f-99cf-0ba83cc793b5 rhgb quiet console=ttyS0\u0026#34; 重新生成 grub 配置并重启：\n1 2 [root@VM1 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg [root@VM1 ~]# reboot 在宿主机上连接 console：\n1 [root@cloud ~]# virsh console server1 退出 console 的快捷键是 Ctrl + ]。\n7. 通过配置给虚拟机添加硬盘 （1）创建磁盘文件：\n1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/disk2.qcow2 5g （2）通过配置文件添加硬盘：\n1 [root@cloud ~]# vim /etc/libvirt/qemu/server1.xml 在配置中加入如下 disk 段：\n1 2 3 4 5 6 \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39;/\u0026gt; \u0026lt;source file=\u0026#39;/var/lib/libvirt/images/disk2.qcow2\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vdb\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x07\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; 重启服务并启动虚拟机：\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh console server1 在 KVM 虚拟机中验证磁盘：\n1 2 3 [root@localhost ~]# fdisk -l /dev/vda /dev/vdb 对比：Xen 虚拟化下磁盘设备名为 /dev/xvda、/dev/xvdb。\n8. 快速创建 KVM 虚拟机 通过复制现有虚拟机的 XML 配置来快速创建：\n1 2 3 4 [root@cloud qemu]# cp server1.xml server2.xml [root@cloud qemu]# uuidgen f3ccabc3-ca4c-41f2-8d6f-e99077c24eeb [root@cloud qemu]# vim server2.xml 修改 name、uuid、磁盘路径和 MAC 地址等关键参数：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \u0026lt;domain type=\u0026#39;kvm\u0026#39;\u0026gt; \u0026lt;name\u0026gt;server2\u0026lt;/name\u0026gt; \u0026lt;uuid\u0026gt;f3ccabc3-ca4c-41f2-8d6f-e99077c24eeb\u0026lt;/uuid\u0026gt; \u0026lt;metadata\u0026gt; \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39;/\u0026gt; \u0026lt;source file=\u0026#39;/isos/server1.qcow2\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vda\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x04\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; \u0026lt;interface type=\u0026#39;bridge\u0026#39;\u0026gt; \u0026lt;mac address=\u0026#39;52:54:00:96:f6:a2\u0026#39;/\u0026gt; \u0026lt;source bridge=\u0026#39;br0\u0026#39;/\u0026gt; \u0026lt;model type=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x01\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/interface\u0026gt; 重启服务并启动新虚拟机：\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server2 [root@cloud ~]# virsh list --all 五、KVM 网络配置 KVM 常见有三种网络模式：NAT 模式、仅主机模式（Host-Only）和桥接模式。\n1. NAT 模式 通过 iptables 地址伪装实现 NAT：\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE 或者使用 SNAT 指定源地址：\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j SNAT --to-source 192.168.8.99 查看 NAT 规则：\n1 [root@cloud ~]# iptables -t nat -nL 默认网络配置文件位于 /etc/libvirt/qemu/networks：\n1 2 3 [root@cloud networks]# pwd /etc/libvirt/qemu/networks [root@cloud networks]# vim default.xml 2. 仅主机模式 仅主机模式下，虚拟机只能与宿主机通信，无法访问外部网络。\n3. 让 KVM 中的虚拟机访问 Internet （1）给 VMware 宿主机添加一块可以访问 Internet 的网卡。\n（2）进入宿主机操作系统，配置双桥接。\nifcfg-br0（内网桥接）：\n1 2 3 4 5 6 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 ifcfg-br1（外网桥接，DHCP 获取地址）：\n1 2 3 4 5 TYPE=Bridge NAME=br1 DEVICE=br1 ONBOOT=yes BOOTPROTO=dhcp ifcfg-ens33 桥接到 br0：\n1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 ifcfg-ens37 桥接到 br1：\n1 2 3 4 5 TYPE=Ethernet NAME=ens37 DEVICE=ens37 ONBOOT=yes BRIDGE=br1 重启服务并关闭虚拟机：\n1 2 3 4 5 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh shutdown server1 Domain server1 is being shutdown [root@cloud ~]# virsh shutdown server2 Domain server2 is being shutdown 将 KVM 中 server1 虚拟机网卡修改为桥接到 br1，进入 VM1 把 IP 获取方式改为 DHCP：\n1 2 3 4 5 6 7 8 9 10 11 [root@VM1 ~]# nmcli connection modify enp1s0 ipv4.method auto autoconnect yes [root@VM1 ~]# nmcli connection down enp1s0 ; nmcli connection up enp1s0 [root@VM1 ~]# ifconfig enp1s0: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1500 inet 192.168.31.158 netmask 255.255.255.0 broadcast 192.168.31.255 inet6 fe80::5054:ff:fe96:f6a1 prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether 52:54:00:96:f6:a1 txqueuelen 1000 (Ethernet) RX packets 1253 bytes 279527 (272.9 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 119 bytes 17932 (17.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 测试外网连通性：\n1 2 3 4 5 [root@VM1 ~]# ping www.baidu.com PING www.a.shifen.com (183.2.172.17) 56(84) bytes of data. 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=1 ttl=52 time=23.6 ms 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=2 ttl=52 time=24.3 ms 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=3 ttl=52 time=23.5 ms 六、使用命令行安装 KVM 虚拟机 借助 kickstart 自动化安装脚本，可以无人值守地批量安装虚拟机。\n1. 创建磁盘 1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server3.qcow2 10g 2. 自动化安装 使用 virt-install 指定安装源和 kickstart 文件：\n1 2 3 4 5 [root@cloud ~]# virt-install -n server3 --memory 2048 --vcpu 2 \\ --disk /var/lib/libvirt/images/server3.qcow2 \\ --network bridge=br1 \\ -l http://192.168.31.96/pub \\ -x inst.ks=http://192.168.31.96/ks/ks01.cfg --vnc kickstart 配置文件 ks01.cfg 示例：\n1 # vim /var/www/html/ks/ks01.cfg 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #version=RHEL8 # Use text install for fully unattended text repo --name=\u0026#34;AppStream\u0026#34; --baseurl=http://192.168.0.107/pub/AppStream repo --name=\u0026#34;BaseOS\u0026#34; --baseurl=http://192.168.0.107/pub/BaseOS reboot %packages @^minimal-environment kexec-tools %end # Keyboard layouts keyboard --xlayouts=\u0026#39;us\u0026#39; # System language lang en_US.UTF-8 # Network information network --bootproto=dhcp --activate --hostname=server3 --onboot=yes # Use network installation media url --url=http://192.168.0.107/pub/BaseOS # Run the Setup Agent on first boot firstboot --disable ignoredisk --only-use=vda clearpart --all --initlabel autopart # System timezone timezone Asia/Shanghai --utc # Root password rootpw --iscrypted $6$c31XqEglO8ECIli0$enF8T.LW3OknT.178Vb9m2HHYD6dqRdKF2QZia5.G5Tv/zMD//04S2h61oBOKihJZwPIUmWxLc7oXuWtkHQy10 user --name=admin --password=$6$Ey847S5bcqYg8ktk$sd4BJeFF01IFsGOaFwRoqXoYuZ5H1RV3bLC6n6gQGa1tNAjplFDhTnm3Cjw2pgwltIE.xr27X7E/ro4gF2JSG1 --iscrypted --gecos=\u0026#34;admin\u0026#34; %addon com_redhat_kdump --enable --reserve-mb=\u0026#39;auto\u0026#39; %end %anaconda pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty %end 说明：上面的安装源地址（192.168.0.107）、主机名（server3）以及 rootpw、user 后的密码哈希均为示例，实际使用时请替换为自己环境的地址和生成的密码密文。\n","date":"2026-08-02","description":"","objectID":"/posts/kvm-virtualization-guide/","readingTime":6,"tags":["KVM","虚拟化","Linux","CentOS"],"title":"KVM 虚拟化入门到实战：部署、管理与网络配置","url":"/posts/kvm-virtualization-guide/","wordCount":1149},{"categories":["容器技术"],"content":"1. 选择合适的基础镜像 1 2 3 4 5 6 7 8 9 10 # ❌ 太大 FROM ubuntu:22.04 # ~77MB FROM python:3.11 # ~1GB # ✅ 推荐 FROM python:3.11-alpine # ~50MB FROM python:3.11-slim # ~130MB # ✅ 最小 FROM gcr.io/distroless/python3 # ~20MB 基础镜像 大小 ubuntu:22.04 ~77MB python:3.11 ~1GB python:3.11-alpine ~50MB distroless ~20MB 2. 使用多阶段构建 最有效的优化手段：编译阶段用大镜像，运行阶段只复制产物。\n1 2 3 4 5 6 7 8 9 10 11 # 阶段 1：编译 FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp # 阶段 2：运行 FROM alpine:3.18 WORKDIR /app COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] Go 应用从 ~1GB 缩小到 ~15MB。\n3. 合并 RUN 指令 1 2 3 4 5 6 7 8 9 10 11 12 # ❌ 多个层 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN rm -rf /var/lib/apt/lists/* # ✅ 合并为一个层 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 4. 清理构建缓存 1 2 3 4 5 6 7 8 9 # Python - 禁用 pip 缓存 RUN pip install --no-cache-dir -r requirements.txt # Node.js - 清理 npm 缓存 RUN npm ci --only=production \u0026amp;\u0026amp; \\ npm cache clean --force # Go - 去除调试信息 RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 5. 使用 .dockerignore 1 2 3 4 5 6 7 8 9 10 11 # .dockerignore .git node_modules *.md .env .vscode __pycache__ *.pyc dist build coverage 1 2 # 检查上下文大小 docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; 6. 只安装必要的包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ❌ 运行镜像里装了编译工具 FROM ubuntu:22.04 RUN apt-get install -y build-essential gcc make cmake COPY . . RUN make \u0026amp;\u0026amp; make install CMD [\u0026#34;./myapp\u0026#34;] # ✅ 编译和运行分离 FROM ubuntu:22.04 AS builder RUN apt-get install -y build-essential gcc make COPY . . RUN make \u0026amp;\u0026amp; make install FROM ubuntu:22.04 COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] 7. 使用 \u0026ndash;no-install-recommends 1 2 3 4 5 # ❌ 安装了所有推荐包 RUN apt-get install -y curl # ✅ 只装必须的包（减少约 50% 依赖） RUN apt-get install -y --no-install-recommends curl 8. 压缩静态资源 1 2 3 4 5 6 7 8 9 10 11 # 压缩 JavaScript RUN uglifyjs app.js -o app.min.js # 压缩 CSS RUN cssnano app.css app.min.css # 压缩图片 RUN find . -name \u0026#34;*.png\u0026#34; -exec optipng {} \\; # Go 二进制去除调试信息 RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 9. 使用 Squash 合并层 1 2 # 合并所有层为一层，去除中间层的重复文件 docker build --squash -t myapp:slim . 需要在 /etc/docker/daemon.json 中启用 experimental 功能。\n10. 分析镜像层 1 2 3 4 5 6 7 # 使用 dive 工具分析每层大小和内容 docker run --rm -it \\ -v /var/run/docker.sock:/var/run/docker.sock \\ wagoodman/dive myapp:latest # 或使用 docker history docker history myapp:latest 实战案例：Python 应用 1 2 3 4 5 6 # ❌ 优化前（1.2GB） FROM python:3.11 WORKDIR /app COPY . . RUN pip install -r requirements.txt CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 优化后（150MB，缩小 87%） FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /root/.local /root/.local COPY . . ENV PATH=/root/.local/bin:$PATH CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 实战案例：Node.js 应用 1 2 3 4 5 6 # ❌ 优化前（1.1GB） FROM node:18 WORKDIR /app COPY . . RUN npm install CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 优化后（180MB，缩小 84%） FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM node:18-alpine WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY . . USER node CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 推荐工具 工具 用途 dive 分析镜像每层内容和大小 docker-slim 自动精简镜像 hadolint Dockerfile 静态检查 trivy 镜像安全扫描 ","date":"2026-07-30","description":"10 个经过验证的技巧，把 Docker 镜像体积缩小 90% 以上。","objectID":"/posts/dockerfile-smaller-image/","readingTime":3,"tags":["Docker","Dockerfile","镜像优化","容器化"],"title":"Dockerfile 构建镜像太大？10 个技巧让镜像瘦身 90%","url":"/posts/dockerfile-smaller-image/","wordCount":600},{"categories":["容器技术"],"content":"1. 合理安排指令顺序（利用缓存） 把变化频率低的指令放前面，变化频率高的放后面。一旦某层缓存失效，后续所有层都会重新构建。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 错误：每次改代码都要重新装依赖 FROM node:18-alpine WORKDIR /app COPY . . # 代码经常变 RUN npm ci --production # 依赖很少变 CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # ✅ 正确：依赖变化时才重新安装 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # 依赖定义（很少变） RUN npm ci --production # 安装依赖（很少变） COPY . . # 代码（经常变） CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # 检查缓存命中情况 docker build -t myapp . 2\u0026gt;\u0026amp;1 | grep -E \u0026#34;(CACHED|Using cache)\u0026#34; 2. 使用 .dockerignore 减少上下文 docker build 会把整个目录发送到 Docker daemon，包括 .git、node_modules 等不需要的文件。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # .dockerignore .git .github .vscode .idea node_modules dist build *.md *.log .env .env.local .DS_Store coverage .nyc_output 1 2 3 4 # 检查上下文大小 docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; # 优化前：Sending build context to Docker daemon 1.2GB # 优化后：Sending build context to Docker daemon 50MB 3. 使用轻量级基础镜像 基础镜像 大小 说明 ubuntu:22.04 ~77MB 完整 Ubuntu python:3.11 ~1GB 包含完整 Python python:3.11-slim ~130MB 精简版 python:3.11-alpine ~50MB Alpine 版 1 2 3 4 5 6 7 8 # ❌ 太大 FROM python:3.11 # 1GB # ✅ 推荐 FROM python:3.11-slim # 130MB # ✅ 最小（注意 musl 兼容性问题） FROM python:3.11-alpine # 50MB Alpine 使用 musl 而非 glibc，某些软件可能不兼容。遇到问题选 slim 版本。\n4. 合并 RUN 指令减少层数 每个 RUN 创建一个新层，层越多构建越慢、镜像越大。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 5 个层 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* # ✅ 1 个层 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ apt-get clean \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 1 2 # 检查镜像层数 docker history myapp:latest 5. 使用多阶段构建 在一个阶段构建，在另一个阶段运行，只复制需要的文件。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 阶段 1：构建 FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o app . # 阶段 2：运行（只有 ~15MB） FROM alpine:3.18 RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/app . EXPOSE 8080 CMD [\u0026#34;./app\u0026#34;] 方案 镜像大小 构建时间 单阶段 ~1.2GB 5 分钟 多阶段 ~15MB 2 分钟 6. 使用 BuildKit 加速构建 BuildKit 支持并行构建和高级缓存。\n1 2 3 4 5 6 7 # 启用 BuildKit export DOCKER_BUILDKIT=1 # 或在 /etc/docker/daemon.json 中永久配置 # { # \u0026#34;features\u0026#34;: { \u0026#34;buildkit\u0026#34;: true } # } 1 2 3 4 5 6 7 8 9 10 # 使用缓存挂载（BuildKit 特性） # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # 缓存挂载避免每次重新下载依赖 RUN --mount=type=cache,target=/root/.npm \\ npm ci --production COPY . . CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # 检查 BuildKit 是否启用 docker build --progress=plain -t myapp . 2\u0026gt;\u0026amp;1 | head -5 7. 使用缓存导入导出 在 CI/CD 中，缓存会在每次构建后丢失。使用缓存导入导出可以跨构建保留缓存。\n1 2 3 4 5 6 7 8 9 10 11 # 导出到本地 docker build \\ --cache-from type=local,src=/tmp/cache \\ --cache-to type=local,dest=/tmp/cache \\ -t myapp . # 使用注册表作为缓存 docker build \\ --cache-from type=registry,ref=myregistry/myapp:cache \\ --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \\ -t myapp . 1 2 3 4 5 6 7 8 9 # GitHub Actions 示例 - name: Build and push uses: docker/build-push-action@v5 with: context: . push: true tags: myapp:latest cache-from: type=gha cache-to: type=gha,mode=max 8. 并行安装依赖 1 2 3 4 5 6 7 8 9 10 11 12 # Node.js - npm ci 比 npm install 快 RUN npm ci --production # Python - 禁用缓存 RUN pip install --no-cache-dir -r requirements.txt # Go - 并行下载模块 RUN go mod download # Rust - 使用 cargo-chef COPY --from=chef /app/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json 9. 清理构建缓存和临时文件 在同一个 RUN 指令中清理，避免文件保留在层中。\n1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 正确：同一层中清理 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends build-essential \u0026amp;\u0026amp; \\ make \u0026amp;\u0026amp; make install \u0026amp;\u0026amp; \\ apt-get purge -y --auto-remove build-essential \u0026amp;\u0026amp; \\ apt-get clean \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # ❌ 错误：清理在另一层，文件仍然存在 RUN apt-get update \u0026amp;\u0026amp; apt-get install -y build-essential RUN make \u0026amp;\u0026amp; make install RUN apt-get clean # 太晚了！前面的层已经包含了文件 各语言清理命令：\n1 2 3 4 5 6 7 8 # Node.js RUN npm ci --production \u0026amp;\u0026amp; npm cache clean --force # Python RUN pip install --no-cache-dir -r requirements.txt # Go RUN go build -o app . \u0026amp;\u0026amp; go clean -cache 10. 使用特定版本标签 1 2 3 4 5 6 7 8 # ❌ latest 可能变化，缓存不可预测 FROM node:latest # ✅ 特定版本 FROM node:18.17.0-alpine3.18 # ✅ 更安全：使用 SHA256 摘要 FROM node:18.17.0-alpine3.18@sha256:abc123... 镜像层级详解 1 2 # 查看镜像层历史 docker history \u0026lt;image-name\u0026gt; 创建新层的指令：FROM、RUN、COPY、ADD\n不创建新层的指令（只是元数据）：CMD、ENTRYPOINT、ENV、EXPOSE、WORKDIR、USER、LABEL、ARG、VOLUME\n1 2 3 4 5 6 7 8 9 10 11 # 这会创建 4 个层 FROM node:18-alpine # 层 1 RUN apk add --no-cache curl # 层 2 COPY package.json . # 层 3 RUN npm install # 层 4 # 这些指令不增加层 WORKDIR /app # 无层 ENV NODE_ENV=production # 无层 EXPOSE 3000 # 无层 CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # 无层 优化效果对比 1 2 3 4 5 6 7 8 9 # ❌ 优化前（8 分钟，1.2GB） FROM ubuntu:22.04 RUN apt-get update RUN apt-get install -y python3 python3-pip RUN apt-get install -y build-essential WORKDIR /app COPY . . RUN pip3 install -r requirements.txt CMD [\u0026#34;python3\u0026#34;, \u0026#34;app.py\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ✅ 优化后（1.5 分钟，150MB） # syntax=docker/dockerfile:1 FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN --mount=type=cache,target=/root/.cache/pip \\ pip install --no-cache-dir -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY . . EXPOSE 8000 CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 指标 优化前 优化后 提升 构建时间 8 分钟 1.5 分钟 81% ⬇️ 镜像大小 1.2GB 150MB 87% ⬇️ 上下文大小 500MB 50MB 90% ⬇️ 缓存命中率 10% 90% 80% ⬆️ 监控和调试 1 2 3 4 5 6 7 8 9 10 11 # 显示详细构建时间 time docker build -t myapp . # BuildKit 详细进度 DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp . # 分析镜像层 docker history myapp:latest # 使用 dive 工具深入分析 dive myapp:latest ","date":"2026-07-30","description":"10 个经过验证的技巧，从缓存策略到多阶段构建，大幅缩短 Docker 构建时间。","objectID":"/posts/dockerfile-faster-build/","readingTime":5,"tags":["Docker","Dockerfile","性能优化","CI/CD"],"title":"Dockerfile 构建镜像如何更快？10 个实用优化技巧","url":"/posts/dockerfile-faster-build/","wordCount":966},{"categories":["容器技术"],"content":"1. 基本语法 两者语法几乎一样，但行为差异很大：\n1 2 3 4 5 # COPY - 纯粹的文件复制，不做任何额外处理 COPY [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; # ADD - 支持自动解压和远程 URL，但行为不可预测 ADD [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; 2. 自动解压压缩文件 ADD 会自动解压本地压缩包，COPY 不会：\n1 2 3 4 5 6 7 # ADD 自动解压 .tar.gz ADD app.tar.gz /opt/app/ # 结果：/opt/app/ 下是解压后的内容 # COPY 只复制文件本身 COPY app.tar.gz /opt/app/ # 结果：/opt/app/app.tar.gz 是原始压缩包 支持的压缩格式：.tar、.tar.gz/.tgz、.tar.bz2、.tar.xz\n如果你想复制压缩包本身而不解压，必须用 COPY。\n3. 远程 URL 下载 ADD 可以直接从 URL 下载文件，COPY 不支持：\n1 2 3 4 5 # ADD 可以下载远程文件（但不推荐） ADD https://example.com/app.tar.gz /opt/ # COPY 不支持 URL COPY https://example.com/app.tar.gz /opt/ # 报错！ 推荐用 RUN + curl 替代 ADD 下载远程文件，因为 ADD 下载无法利用构建缓存、无法做错误处理、无法验证完整性。\n1 2 3 4 # 推荐：RUN + curl RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \\ \u0026amp;\u0026amp; tar -xzf /tmp/app.tar.gz -C /opt/ \\ \u0026amp;\u0026amp; rm /tmp/app.tar.gz 4. 自动创建目录 两者都会自动创建不存在的中间目录：\n1 2 3 4 5 6 # 如果 /etc/myapp/config/ 不存在，会自动创建 COPY app.conf /etc/myapp/config/ ADD app.conf /etc/myapp/config/ # 不需要手动 mkdir # RUN mkdir -p /etc/myapp/config ← 多余 5. 构建缓存行为 1 2 3 4 5 # COPY - 严格匹配文件内容，内容变了才缓存失效 COPY config/app.conf /etc/app/ # ADD - 压缩文件内容相同时缓存仍然有效 ADD app.tar.gz /opt/app/ 场景 COPY ADD 文件内容变化 缓存失效 缓存失效 文件元数据变化 缓存失效 缓存失效 压缩文件内容相同 N/A 缓存有效 6. 功能对比 特性 COPY ADD 复制本地文件 ✅ ✅ 复制目录 ✅ ✅ 支持通配符 ✅ ✅ 自动创建目录 ✅ ✅ 自动解压压缩文件 ❌ ✅ 支持远程 URL ❌ ✅ 语义清晰度 ✅ 高 ❌ 低 推荐使用 ✅ 首选 ⚠️ 特定场景 7. 使用场景 COPY 适用于：\n1 2 3 4 5 6 7 8 9 # 复制配置文件 COPY nginx.conf /etc/nginx/nginx.conf # 复制应用代码 COPY src/ /app/src/ COPY package.json /app/ # 多阶段构建中复制二进制 COPY --from=builder /app/build/app /usr/local/bin/ ADD 仅适用于需要自动解压的场景：\n1 2 3 4 5 # 合理使用 ADD ADD node-v18.17.0-linux-x64.tar.gz /usr/local/ # 普通文件不要用 ADD ADD app.conf /etc/app/ # ← 用 COPY 代替 8. 最佳实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 规则 1：默认用 COPY COPY requirements.txt /app/ # ✅ ADD requirements.txt /app/ # ❌ 没必要 # 规则 2：只在需要解压时用 ADD ADD node-v18.tar.gz /usr/local/ # ✅ 需要解压 # 规则 3：远程文件用 RUN + curl RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \\ \u0026amp;\u0026amp; tar -xzf /tmp/app.tar.gz -C /opt/ \\ \u0026amp;\u0026amp; rm /tmp/app.tar.gz # ✅ ADD https://example.com/app.tar.gz /opt/ # ❌ # 规则 4：用 .dockerignore 减少上下文 # .dockerignore # .git # node_modules # *.md # .env # 规则 5：合理安排 COPY 顺序利用缓存 COPY package.json package-lock.json ./ # 先复制依赖（变化少） RUN npm ci --production COPY . . # 再复制代码（变化多） 9. 常见陷阱 1 2 3 4 5 6 7 8 9 10 # 陷阱 1：意外的自动解压 ADD app.tar.gz /opt/app/ # 你以为复制了文件，实际解压成了目录 # 陷阱 2：远程文件无法缓存 ADD https://github.com/user/repo/releases/download/v1.0/app.tar.gz /tmp/ # 每次构建都重新下载 # 陷阱 3：权限问题 COPY --chown=app:app src/ /app/src/ # 指定所有者 10. 选择决策 1 2 3 4 5 6 7 需要复制文件？ ├── 本地压缩文件且需要解压？ │ └── 用 ADD ├── 本地普通文件？ │ └── 用 COPY └── 远程文件？ └── 用 RUN + curl ","date":"2026-07-30","description":"COPY 和 ADD 都能复制文件，但行为差异很大。本文通过对比和示例讲清两者区别。","objectID":"/posts/dockerfile-copy-vs-add/","readingTime":3,"tags":["Docker","Dockerfile","容器化"],"title":"Dockerfile 中 COPY 与 ADD 的区别详解","url":"/posts/dockerfile-copy-vs-add/","wordCount":456},{"categories":["技术教程"],"content":"1. 环境要求 操作系统: CentOS 7/8/Stream 9 Node.js: v18 或更高 内存: 建议 2GB 以上 网络: 需要访问 Anthropic API 2. 安装 Node.js 1 2 3 4 5 6 7 8 9 # 安装 NodeSource 仓库 curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - # 安装 Node.js sudo yum install -y nodejs # 检查 node -v npm -v 如果 Node.js 版本过低，使用 nvm 管理版本：\n1 2 3 4 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash source ~/.bashrc nvm install 20 nvm use 20 3. 安装 Claude Code 1 2 3 4 5 # 全局安装 npm install -g @anthropic-ai/claude-code # 检查 claude --version 遇到权限问题有两种解决方案：\n1 2 3 4 5 6 7 8 # 方案一：sudo sudo npm install -g @anthropic-ai/claude-code # 方案二：修改 npm 全局目录（推荐） mkdir -p ~/.npm-global npm config set prefix \u0026#34;~/.npm-global\u0026#34; echo \u0026#39;export PATH=~/.npm-global/bin:$PATH\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 4. 配置 API Key 1 2 3 4 5 6 # 方式一：环境变量（推荐） echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;your-api-key-here\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc # 检查 echo $ANTHROPIC_API_KEY 方式二：直接运行 claude，首次启动时会提示输入 API Key。\n5. 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 启动交互式会话 cd /path/to/your/project claude # 直接提问（非交互模式） claude -p \u0026#34;写一个 Python 脚本，统计当前目录下所有文件的数量\u0026#34; # 分析代码 cat main.py | claude -p \u0026#34;分析这段代码的潜在问题\u0026#34; # 查看/修改配置 claude config # 更新到最新版本 claude update 命令 说明 claude 启动交互式会话 claude -p \u0026quot;问题\u0026quot; 直接提问，非交互模式 claude config 查看/修改配置 claude update 更新到最新版本 6. 网络问题排查 如果连接 Anthropic API 超时：\n1 2 3 4 5 6 # 配置代理 export https_proxy=\u0026#34;http://your-proxy:port\u0026#34; export http_proxy=\u0026#34;http://your-proxy:port\u0026#34; # 测试连通性 curl -s https://api.anthropic.com/ | head -5 7. 安全建议 1 2 3 4 5 6 7 8 9 # 不要硬编码 API Key，始终用环境变量 echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;sk-ant-xxx\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc # ✅ # ANTHROPIC_API_KEY=sk-ant-xxx 写在代码里 # ❌ # 限制文件权限 chmod 600 ~/.bashrc # 检查是否泄露 grep -r \u0026#34;ANTHROPIC_API_KEY\u0026#34; --include=\u0026#34;*.py\u0026#34; --include=\u0026#34;*.js\u0026#34; . ","date":"2026-07-04","description":"在 CentOS 上从零部署 Claude Code，涵盖 Node.js 安装、API Key 配置、常见问题排查。","objectID":"/posts/centos-deploy-claude-code/","readingTime":2,"tags":["Claude Code","CentOS","AI","部署"],"title":"CentOS 部署 Claude Code 全流程指南","url":"/posts/centos-deploy-claude-code/","wordCount":282},{"categories":null,"content":"flannel 实战：从零搭建 Docker 跨主机容器网络 一、网络环境规划 etcd 主机 IP：192.168.8.88 主机名：docker-network Host1 IP：192.168.8.188 主机名：web01 Host2 IP：192.168.8.99 主机名：web02 二、etcd 主机配置 步骤 1：下载并解压 etcd 从github.com 下载etcd-v3.6.0-linux-amd64.tar.gz 软件包 将软件包复制到 etcd 主机/root 目录并解压： 1 [root@docker-network ~]# tar -zxvf etcd-v3.6.0-linux-amd64.tar.gz 步骤 2：安装 etcd 可执行文件 1 2 [root@docker-network ~]# cd etcd-v3.6.0-linux-amd64 [root@docker-network etcd-v3.6.0-linux-amd64]# cp etcd* /usr/local/bin/ 步骤 3：创建数据目录 1 [root@docker-network ~]# mkdir /var/lib/etcd 步骤 4：配置并启动 etcd 服务 创建etcd.service 文件： 1 [root@docker-network ~]# vim /usr/lib/systemd/system/etcd.service 1 2 3 4 5 6 [Unit] Description=etcd_service [Service] ExecStart=/usr/local/bin/etcd --name etcd1 --data-dir /var/lib/etcd --listen-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 [Install] WantedBy=multi-user.target 重载系统服务配置并启动 etcd： 1 2 3 [root@docker-network ~]# systemctl daemon-reload [root@docker-network ~]# systemctl enable etcd.service [root@docker-network ~]# systemctl restart etcd.service 验证 etcd 监听状态： 1 2 3 [root@docker-network ~]# netstat -tulnp |grep :2379 tcp 0 0 192.168.8.88:2379 0.0.0.0:* LISTEN 1912/etcd tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 1912/etcd 步骤 5：向 etcd 添加 flannel 网络配置 1 etcdctl --endpoints http://127.0.0.1:2379 put /coreos.com/network/config \u0026#39;{\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}}\u0026#39; 1 etcdctl --endpoints http://192.168.8.88:2379 get /coreos.com/network/config 配置说明：\nNetwork：Flannel 地址池 SubnetLen：分配给单个宿主机的 docker0 子网掩码长度（24 位即255.255.255.0） SubnetMin/SubnetMax：可分配的子网范围（示例为172.16.1.0/24 到172.16.10.0/24） Backend：数据转发模式（vxlan 支持跨主机通信） 步骤 6：关闭防火墙 1 2 [root@docker-network ~]# systemctl disable firewalld [root@docker-network ~]# systemctl stop firewalld 三、web01 主机配置（Host1: 192.168.8.188） 步骤 1：下载并解压 flannel 下载flannel-v0.26.7-linux-amd64.tar.gz 到主机 解压文件： 1 [root@web01 ~]# tar -zxvf flannel-v0.26.7-linux-amd64.tar.gz 步骤 2：安装 flannel 可执行文件 1 [root@web01 ~]# cp -p flanneld mk-docker-opts.sh /usr/local/bin/ 步骤 3：配置并启动 flannel 服务 创建flanneld.service 文件： 1 [root@web01 ~]# vim /usr/lib/systemd/system/flanneld.service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=Flanneld Documentation=https://github.com/coreos/flannel After=network.target Before=docker.service [Service] User=root ExecStartPost=/usr/local/bin/mk-docker-opts.sh ExecStart=/usr/local/bin/flanneld --etcd-endpoints=http://192.168.8.88:2379 --iface=192.168.8.188 --ip-masq=true --etcd-prefix=/coreos.com/network Restart=on-failure Type=notify LimitNOFILE=65536 [Install] WantedBy=multi-user.target 参数说明：\n--iface=192.168.8.188：指定本机连接 etcd 的 IP --etcd-endpoints：etcd 服务器地址 重载服务配置并启动 flannel：\n1 2 3 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl enable flanneld.service [root@web01 ~]# systemctl restart flanneld.service 步骤 4：验证 flannel 网络接口 1 [root@web01 ~]# ifconfig 应显示flannel.1 接口（示例输出）：\n1 2 3 4 5 6 7 8 flannel.1: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1450 inet 172.16.5.0 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::f06c:96ff:fefe:b7ab prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether f2:6c:96:fe:b7:ab txqueuelen 0 (Ethernet) RX packets 315 bytes 26460 (25.8 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 314 bytes 34636 (33.8 KiB) TX errors 0 dropped 29 overruns 0 carrier 0 collisions 0 步骤 5：修改 docker 配置文件 查看 flannel 生成的子网环境变量： 1 [root@web01 ~]# cat /run/flannel/subnet.env 输出示例：\n1 2 3 4 FLANNEL_NETWORK=172.16.0.0/16 FLANNEL_SUBNET=172.16.5.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true 1 [root@web01 ~]# cat /run/docker_opts.env 输出示例：\n1 2 3 4 DOCKER_OPT_BIP=\u0026#34;--bip=172.16.5.1/24\u0026#34; DOCKER_OPT_IPMASQ=\u0026#34;--ip-masq=false\u0026#34; DOCKER_OPT_MTU=\u0026#34;--mtu=1450\u0026#34; DOCKER_OPTS=\u0026#34; --bip=172.16.5.1/24 --ip-masq=false --mtu=1450\u0026#34; 修改 docker 服务配置： 1 [root@web01 ~]# vim /usr/lib/systemd/system/docker.service 在[Service]部分添加：\n1 2 3 ExecStart=/usr/bin/dockerd $DOCKER_OPTS -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID EnvironmentFile=-/run/docker_opts.env 重启 docker 服务： 1 2 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl restart docker.service 步骤 6：验证 docker 网络 创建 nginx 容器并进入： 1 2 3 4 5 6 7 8 9 [root@web01 ~]# ifconfig docker0: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1450 inet 172.16.5.1 netmask 255.255.255.0 broadcast 172.16.5.255 inet6 fe80::42:f6ff:fe5d:5423 prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether 02:42:f6:5d:54:23 txqueuelen 0 (Ethernet) RX packets 10073 bytes 407167 (397.6 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 48185 bytes 38083646 (36.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 1 2 [root@web01 ~]# docker run -d -p 8800:80 nginx [root@web01 ~]# docker exec -it \u0026lt;容器ID\u0026gt; /bin/bash 查看容器 IP（示例为172.16.5.3）： 1 2 root@容器ID:/# ifconfig eth0: inet 172.16.5.3 netmask 255.255.255.0 四、web02 主机配置（Host2: 192.168.0.208） 步骤说明 配置步骤与 web01 完全一致，仅需修改以下参数： --iface=192.168.8.99（本机 IP） docker 容器 IP 示例：172.16.5.2（由 flannel 自动分配） 五、实验验证 在 web01 容器中 ping web02 容器 IP（示例：172.16.9.2）：\n在web02 中创建容器，查看容器IP 地址\n结果：容器间通信成功，flannel 网络配置完成。\n附录：etcd 网络配置查询 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/config /coreos.com/network/config {\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}} 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/subnets --prefix --keys-only /coreos.com/network/subnets/172.16.5.0-24 /coreos.com/network/subnets/172.16.9.0-24 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/subnets/172.16.5.0-24 /coreos.com/network/subnets/172.16.5.0-24 {\u0026#34;PublicIP\u0026#34;:\u0026#34;192.168.8.188\u0026#34;,\u0026#34;PublicIPv6\u0026#34;:null,\u0026#34;BackendType\u0026#34;:\u0026#34;vxlan\u0026#34;,\u0026#34;BackendData\u0026#34;:{\u0026#34;VNI\u0026#34;:1,\u0026#34;VtepMAC\u0026#34;:\u0026#34;5a:67:18:61:6a:5d\u0026#34;}} ","date":"2025-05-21","description":"","objectID":"/posts/flannel-network-lab/","readingTime":3,"tags":["flannel","Docker","网络","etcd"],"title":"flannel 实战：从零搭建 Docker 跨主机容器网络","url":"/posts/flannel-network-lab/","wordCount":603},{"content":"Harbor 主备部署 环境准备 操作系统： CentOS 8.4 64bit\nHarbor 版本： harbor-offline-installer-v2.11.1.tgz\nDocker Compose： docker-compose-linux-x86_64\n角色 主机名 IP 地址 主 Harbor Harbor01 192.168.8.161 备 Harbor Harbor02 192.168.8.162 Docker 主机 web01 192.168.8.188 Harbor 是 VMware 公司开源的企业级 Docker Registry 项目，以 Docker 官方 Registry 为基础，提供了管理 UI、基于角色的访问控制（RBAC）、AD/LDAP 集成和审计日志等企业功能，同时原生支持中文。Harbor 的每个组件都以 Docker 容器形式构建，使用 Docker Compose 进行部署。\n一、部署主 Harbor（HTTP 模式，不使用证书） 此方式适用于内网环境，通过 HTTP 直接访问 Harbor，无需配置 SSL 证书。\n1. 安装 Docker 1 2 3 4 5 6 7 8 9 10 11 12 13 # 删除所有已存在的 yum 仓库文件 rm -rf /etc/yum.repos.d/* # 从阿里云镜像站下载 yum 仓库文件 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo # 安装 Docker CE yum install -y yum-utils yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 启动 Docker 并设置开机自启 systemctl enable docker.service --now 2. 配置 Docker 加速器 1 vim /etc/docker/daemon.json 写入以下内容：\n1 2 3 4 5 6 { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://37564ea7e7cc4824a5049fd666807c27.mirror.swr.myhuaweicloud.com\u0026#34; ], \u0026#34;insecure-registries\u0026#34;: [\u0026#34;http://192.168.8.161\u0026#34;] } 1 2 systemctl daemon-reload systemctl restart docker.service 3. 安装 Docker Compose 和 Harbor 1 2 3 4 5 6 7 8 9 10 # 将 docker-compose 和 harbor 离线包放到 /data 目录 ls /data # docker-compose-linux-x86_64 harbor-offline-installer-v2.11.1.tgz # 安装 docker-compose cp /data/docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod a+x /usr/local/bin/docker-compose # 解压 harbor tar -zxvf /data/harbor-offline-installer-v2.11.1.tgz -C /usr/local/ 4. 修改 Harbor 配置 1 2 3 cd /usr/local/harbor/ cp harbor.yml.tmpl harbor.yml vim harbor.yml 修改以下关键配置：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 hostname: 192.168.8.161 http: port: 80 # 将 https 段落全部注释掉（HTTP 模式不需要证书） #https: # port: 443 # certificate: /your/certificate/path # private_key: /your/private/key/path harbor_admin_password: Harbor12345 data_volume: /images 其余配置保持默认即可。\n5. 执行安装 1 ./install.sh 安装完成后，浏览器访问 http://192.168.8.161，使用 admin / Harbor12345 登录。\n6. 创建用户和项目 在 Harbor Web 界面中创建用户 user1：\n使用 user1 身份登录：\n以user1身份新建项目（如 openssh）：\n7. 测试镜像推送和拉取 在 Docker 主机（web01）上操作：\n1 2 3 4 5 6 7 8 9 10 11 # 登录 Harbor docker login 192.168.8.161 Username: user1 Password: # Login Succeeded # 给镜像打标签 docker tag nginx:latest 192.168.8.161/openssh/nginx:latest # 推送到 Harbor docker push 192.168.8.161/openssh/nginx:latest 推送成功后可在 Harbor 中看到该镜像：\n1 2 3 # 删除本地镜像后重新拉取 docker image rm -f 192.168.8.161/openssh/nginx:latest docker pull 192.168.8.161/openssh/nginx:latest 拉取后 Harbor 会显示下载次数 +1：\n二、部署备 Harbor 备端部署方法与主端完全一致，只需将 hostname 改为备端 IP。\n1 vim /usr/local/harbor/harbor.yml 1 hostname: 192.168.8.162 部署完成后访问 http://192.168.8.162，创建 user2 用户：\n使用 user2 登录备端 Harbor：\n三、配置主备复制 1. 在主端新建复制目标 使用 admin 登录主端 Harbor，进入 系统管理 → 仓库管理，新建目标：\n填写备端信息：\n端点 URL：http://192.168.8.162 访问 ID / 访问密钥：user2 的账号密码 创建成功：\n2. 创建复制策略 进入 系统管理 → 复制管理，新建复制策略：\n目标仓库：选择刚创建的 Harbor02 模式：事件驱动（也可选其他） 3. 验证同步结果 在 Docker 主机上推送更多镜像到主端：\n1 2 3 4 5 docker tag httpd:2.4 192.168.8.161/openssh/httpd:2.4 docker push 192.168.8.161/openssh/httpd:2.4 docker tag mysql:5.7 192.168.8.161/openssh/mysql:5.7 docker push 192.168.8.161/openssh/mysql:5.7 查看主端镜像：\n查看备端镜像（已自动同步）：\n备端能看到主端同步过来的镜像，说明主备复制配置成功。\n注：同步之前主端已经存在的镜像需要手动同步。\n四、启用 HTTPS（自签名证书方式） 如果需要在生产环境或跨公网使用 Harbor，建议启用 HTTPS。以下为使用 OpenSSL 自签名证书的完整流程。\n1. 生成 CA 证书和服务器证书 在 Harbor 服务器上执行：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 mkdir -p /data/cert \u0026amp;\u0026amp; cd /data/cert # 生成 CA 私钥 openssl genrsa -out ca.key 4096 # 生成 CA 证书（有效期 10 年） openssl req -x509 -new -nodes -sha512 -days 3650 \\ -subj \u0026#34;/C=CN/CN=HarborRootCA\u0026#34; \\ -key ca.key \\ -out ca.crt # 生成服务器私钥 openssl genrsa -out harbor.key 4096 # 生成服务器证书请求（CN 填写 Harbor 的 IP 或域名） openssl req -sha512 -new \\ -subj \u0026#34;/C=CN/CN=192.168.8.161\u0026#34; \\ -key harbor.key \\ -out harbor.csr 2. 配置证书扩展信息 创建 v3.ext 文件，指定 SAN（Subject Alternative Name）：\n1 2 3 4 5 6 7 8 9 10 cat \u0026gt; v3.ext \u0026lt;\u0026lt;-EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] IP.1=192.168.8.161 EOF IP.1 必须与 Harbor 的实际访问地址一致，否则 Docker 客户端登录时会报证书域名不匹配的错误。\n3. 签发服务器证书 1 2 3 4 5 openssl x509 -req -sha512 -days 3650 \\ -extfile v3.ext \\ -CA ca.crt -CAkey ca.key -CAcreateserial \\ -in harbor.csr \\ -out harbor.crt 4. 修改 Harbor 配置启用 HTTPS 1 vim /usr/local/harbor/harbor.yml 取消 https 部分的注释，并修改证书路径：\n1 2 3 4 5 6 hostname: 192.168.8.161 https: port: 443 certificate: /data/cert/harbor.crt private_key: /data/cert/harbor.key hostname 必须和 v3.ext 中的 IP.1 保持一致。\n5. 重新部署 Harbor 1 2 3 4 cd /usr/local/harbor/ docker-compose down ./prepare docker-compose up -d 注意：不要使用 docker-compose down -v，-v 会删除 named volumes，导致 Harbor 数据库和用户数据丢失。\n部署完成后通过 https://192.168.8.161 访问。\n6. 配置 Docker 客户端信任证书 每台需要拉取/推送镜像的机器都要执行：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 创建证书目录（目录名必须是 Harbor 的 IP 或域名） mkdir -p /etc/docker/certs.d/192.168.8.161 # 将 CA 证书复制到该目录 # 方式一：从 Harbor 服务器 scp scp root@192.168.8.161:/data/cert/ca.crt /etc/docker/certs.d/192.168.8.161/ # 方式二：手动复制，将 /data/cert/ca.crt 文件内容写入上述目录 # 重启 Docker systemctl restart docker # 测试登录 docker login 192.168.8.161 # 输入 admin 和密码，显示 Login Succeeded 即成功 五、常见问题 Q：HTTP 模式和 HTTPS 模式能否切换？\n可以。修改 harbor.yml 中的 https 配置段落后，重新执行 ./install.sh 即可。从 HTTP 切换到 HTTPS 需要先生成证书；从 HTTPS 切回 HTTP 则需要注释掉 https 段。\nQ：Docker 客户端登录 HTTPS Harbor 报 \u0026ldquo;x509: certificate relies on legacy Common Name field\u0026rdquo; 怎么办？\n这是因为证书没有配置 SAN（Subject Alternative Name）。重新生成证书时确保 v3.ext 中包含 IP.1=\u0026lt;Harbor的IP\u0026gt;，并用 -extfile v3.ext 参数签发。\nQ：主备复制失败怎么排查？\n检查主备两端的网络连通性、目标端仓库配置中的用户名密码是否正确、以及目标端对应用户是否有项目写入权限。\n","permalink":"https://LMDAWN1127.github.io/posts/harbor-master-slave/","summary":"完整记录 Harbor 企业级镜像仓库的主备部署过程，涵盖 HTTP/HTTPS 双模式安装、自签名证书配置、跨主机主备复制等核心运维技能。","title":"Harbor 主备部署实战：从安装到 HTTPS 与主备复制"},{"content":"ES集群的API查看 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 root@elk02:~# apt -y install jq root@elk02:~# curl -s -u elastic:123456 11.0.1.111:9200/_cluster/health | jq { \u0026#34;cluster_name\u0026#34;: \u0026#34;dawn\u0026#34;, \u0026#34;status\u0026#34;: \u0026#34;green\u0026#34;, \u0026#34;timed_out\u0026#34;: false, \u0026#34;number_of_nodes\u0026#34;: 3, \u0026#34;number_of_data_nodes\u0026#34;: 3, \u0026#34;active_primary_shards\u0026#34;: 103, \u0026#34;active_shards\u0026#34;: 286, \u0026#34;relocating_shards\u0026#34;: 0, \u0026#34;initializing_shards\u0026#34;: 0, \u0026#34;unassigned_shards\u0026#34;: 0, \u0026#34;delayed_unassigned_shards\u0026#34;: 0, \u0026#34;number_of_pending_tasks\u0026#34;: 0, \u0026#34;number_of_in_flight_fetch\u0026#34;: 0, \u0026#34;task_max_waiting_in_queue_millis\u0026#34;: 0, \u0026#34;active_shards_percent_as_number\u0026#34;: 100 } root@elk02:~# curl -s -u elastic:123456 11.0.1.111:9200/_cluster/health | jq \u0026#34;.status\u0026#34; \u0026#34;green\u0026#34; root@elk02:~# curl -s -u elastic:123456 11.0.1.111:9200/_cluster/health | jq \u0026#34;.active_shards_percent_as_number\u0026#34; 100 相关参数说明: cluster_name 集群的名称。\nstatus 集群的健康状态，基于其主分片和副本分片的状态。 ES集群有以下三种状态: green 所有分片都已分配。 yellow 所有主分片都已分配，但一个或多个副本分片未分配。 如果集群中的某个节点发生故障，则在修复该节点之前，某些数据可能不可用。 red 一个或多个主分片未分配，因此某些数据不可用。这可能会在集群启动期间短暂发生，因为分配了主分片。\ntimed_out 是否在参数false指定的时间段内返回响应（默认情况下30秒）。\nnumber_of_nodes 集群内的节点数。\nnumber_of_data_nodes 作为专用数据节点的节点数。\nactive_primary_shards 可用主分片的数量。\nactive_shards 可用主分片和副本分片的总数。\nrelocating_shards 正在重定位的分片数。\ninitializing_shards 正在初始化的分片数。\nunassigned_shards 未分配的分片数。\ndelayed_unassigned_shards 分配因超时设置而延迟的分片数。\nnumber_of_pending_tasks 尚未执行的集群级别更改的数量。\nnumber_of_in_flight_fetch 未完成的提取次数。\ntask_max_waiting_in_queue_millis 自最早启动的任务等待执行以来的时间（以毫秒为单位）。\nactive_shards_percent_as_number 集群中活动分片的比率，以百分比表示。\n官方文档： https://www.elastic.co/guide/en/elasticsearch/reference/7.17/cluster-health.html\n","permalink":"https://LMDAWN1127.github.io/posts/es-cluster-health/","summary":"通过 _cluster/health API 查看 ES 集群健康状态：curl + jq 的两种查询姿势（整包 JSON 与单字段提取），以及 status/timed_out/active_shards 等全部返回参数的逐项解读。","title":"ES 集群健康状态查看实战：_cluster/health 全参数解析"},{"content":"kibana的RBAC 内置用户：\n用户 角色 用途 elastic superuser 超级管理员，本环境密码为 123456 kibana_system kibana_system Kibana 服务连接 ES 专用 logstash_system logstash_system logstash 集中写 ES beats_system beats_system filebeat 等 beats 写 ES apm_system apm_system APM Server 1. 创建角色（RBAC 的核心） 角色由三段权限组成：\n段 控制对象 典型内容 cluster 集群级操作 monitor、manage_index_template、manage_security indices 索引级数据操作 names（索引名模式，支持通配）+ privileges（read/write/all 等） applications Kibana 等应用功能 feature_xxx.read / .write / .all + resources（空间） 创建\u0026quot;日志只读查看员\u0026quot;角色，全部在界面点：\n左侧导航最下方 Stack Management（齿轮图标）→ 左侧「安全」分区 → 角色 → 右上角创建角色 角色名称填 dawn_log_reader 集群权限：勾选 monitor（集群只读监控） 索引权限：点添加索引权限 → 「索引」填 dawn-log*，「权限」勾选 read 和 view_index_metadata Kibana 权限：点添加 Kibana 权限 → 「工作区」下拉选默认 → 在功能权限表里把 Discover、仪表板、可视化、地图 四行设为只读，其余保持无 保存 权限设计要点：\nindices 的索引模式用通配 dawn-log*，覆盖 dawn-log-nginx、dawn-log-filestream-、dawn-logstash- 等所有日志索引 privileges 最小集：view_index_metadata（Kibana 列索引、查字段必需）+ read（检索）；不给 write 就是只读用户 Kibana 权限控制到功能按钮级：Discover 只给只读就只能看不能存检索，不给「开发工具」就用不了 Dev Tools 「工作区」控制空间：选「* 所有工作区」可进所有空间，只选「默认」就进不了别的空间 2. 创建用户并绑定角色 Stack Management → 安全 → 用户 → 右上角创建用户 表单填写： 用户名：elk_viewer 密码 / 确认密码：Dawn123456 全名：ELK 日志只读查看员 电子邮件地址：elk_viewer@dawn.com 角色：下拉选择 dawn_log_reader 保存 用户与角色是多对多，一个用户可以配多个角色，最终权限取并集，我演示的是一个用户绑一个角色\n3. 准备索引模式 Stack Management →（Kibana 分组下）索引模式，确认列表里有 dawn-log-tomcat-errorlog（149 条 tomcat 错误日志，elk_viewer 授权范围内）和 dawn-apps-*（授权范围外，作对照） 列表里没有的，点创建索引模式补建：名称填索引模式名（如 dawn-log-tomcat-errorlog），下一步选时间字段 @timestamp，保存 4. 用 elk_viewer 登录验证 开一个无痕窗口，用 elk_viewer / Dawn123456 登录\n验证 1：空间切换器只剩「默认」。 看页面左上角的空间切换器，elk_viewer 只能看到「默认」；elastic 视角是「默认」+「运维空间」两个。原因是 dawn_log_reader 的 Kibana 权限只授权了「默认」工作区\n验证 2：授权范围内有数据。 进 Discover → 左上角索引模式选 dawn-log-tomcat-errorlog → 文档列表正常展示\n验证 3：授权范围外看得见对象、读不出数据。 在 Discover 左上角把索引模式切换成 dawn-apps-*，会报错或查不出数据。默认空间里能看到 dawn-apps-* 的索引模式对象名，但读数据仍然按角色里的索引权限卡\n验证 4：界面是只读的。\nDiscover 里改完检索条件，没有可用的「保存」入口（或提示无权限）——Discover 只授了只读 左侧导航没有 Stack Management 入口——本角色没有授予任何管理类功能 地址栏直接输入 http://11.0.1.114:5601/app/dev_tools 也进不去（返回 403）——没给「开发工具」功能权限 验证 5：elk_viewer 自己建不了索引模式。 即使索引模式名指向授权索引（如 dawn-log-es*），elk_viewer 尝试创建索引模式同样会被拒，所以索引模式只能由 elastic 建好给大家用\n5. Kibana 空间 Kibana 用\u0026quot;空间\u0026quot;给索引模式、仪表板、可视化分组，角色 Kibana 权限里的「工作区」决定用户能进哪些空间。\n创建空间：\n点左上角空间切换器 → 管理空间 点创建空间，ID 填 elk_ops，名称填「运维空间」 保存 ","permalink":"https://LMDAWN1127.github.io/posts/kibana-rbac/","summary":"Kibana RBAC 实操：内置五大系统用户、创建 dawn_log_reader 只读角色（cluster/indices/applications 三段权限）、创建 elk_viewer 用户并五项登录验证（空间隔离、索引授权边界、只读界面、禁 Dev Tools、禁建索引模式），以及 Kibana 空间管理。","title":"Kibana RBAC 实战：角色创建、用户授权与空间隔离"},{"content":"ES集群加密认证 1. 生成证书文件 1 2 3 4 root@elk01:~# /usr/share/elasticsearch/bin/elasticsearch-certutil cert --days 3650 -out /etc/elasticsearch/config/elastic-certtes.p12 -pass \u0026#34;\u0026#34; ... Directory /etc/elasticsearch/config does not exist. Do you want to create it? [Y/n]Y ... 2. 查看证书文件属主和属组 1 2 3 4 5 6 7 8 root@elk01:~# ll /etc/elasticsearch/config/ total 12 drwxr-sr-x 2 root elasticsearch 4096 Oct 21 13:36 ./ drwxr-s--- 4 root elasticsearch 4096 Oct 21 13:36 ../ -rw------- 1 root elasticsearch 3596 Oct 21 13:36 elastic-certificates.p12 root@elk01:~# chmod +r /etc/elasticsearch/config/elastic-certificates.p12 root@elk01:~# ll /etc/elasticsearch/config/elastic-certificates.p12 -rw-r--r-- 1 root elasticsearch 3596 Oct 21 13:36 /etc/elasticsearch/config/elastic-certificates.p12 3. 同步证书文件到其他节点 1 2 3 4 5 6 root@elk01:~# scp -r /etc/elasticsearch/config/ 11.0.1.112:/etc/elasticsearch/ root@11.0.1.112\u0026#39;s password: elastic-certificates.p12 100% 3596 2.9MB/s 00:00 root@elk01:~# scp -r /etc/elasticsearch/config/ 11.0.1.113:/etc/elasticsearch/ root@11.0.1.113\u0026#39;s password: elastic-certificates.p12 100% 3596 5.0MB/s 00:00 4. 修改ES集群的配置文件 1 root@elk01:~# vim /etc/elasticsearch/elasticsearch.yml 1 2 3 4 5 6 7 ... # 在最后一行添加以下内容 xpack.security.enabled: true xpack.security.transport.ssl.enabled: true xpack.security.transport.ssl.verification_mode: certificate xpack.security.transport.ssl.keystore.path: config/elastic-certificates.p12 xpack.security.transport.ssl.truststore.path: config/elastic-certificates.p12 5. 将配置文件同步到其他节点 1 2 3 4 5 6 root@elk01:~# scp /etc/elasticsearch/elasticsearch.yml 11.0.1.112:/etc/elasticsearch/elasticsearch.yml root@11.0.1.112\u0026#39;s password: elasticsearch.yml 100% 3928 1.0MB/s 00:00 root@elk01:~# scp /etc/elasticsearch/elasticsearch.yml 11.0.1.113:/etc/elasticsearch/elasticsearch.yml root@11.0.1.113\u0026#39;s password: elasticsearch.yml 100% 3928 2.9MB/s 00:00 6. 所有节点重启ES集群 1 2 3 root@elk01:~# systemctl restart elasticsearch.service root@elk02:~# systemctl restart elasticsearch.service root@elk03:~# systemctl restart elasticsearch.service 7. 检查集群节点端口是否监听 1 2 3 4 5 6 7 8 9 root@elk01:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* root@elk02:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* root@elk03:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* 8. 测试访问ES集群 1 2 3 root@elk01:~# curl 11.0.1.113:9200 {\u0026#34;error\u0026#34;:{\u0026#34;root_cause\u0026#34;:[{\u0026#34;type\u0026#34;:\u0026#34;security_exception\u0026#34;,\u0026#34;reason\u0026#34;:\u0026#34;missing authentication credentials for REST request [/]\u0026#34;,\u0026#34;header\u0026#34;:{\u0026#34;WWW-Authenticate\u0026#34;:\u0026#34;Basic realm=\\\u0026#34;security\\\u0026#34; charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026#34;}}],\u0026#34;type\u0026#34;:\u0026#34;security_exception\u0026#34;,\u0026#34;reason\u0026#34;:\u0026#34;missing authentication credentials for REST request [/]\u0026#34;,\u0026#34;header\u0026#34;:{\u0026#34;WWW-Authenticate\u0026#34;:\u0026#34;Basic realm=\\\u0026#34;security\\\u0026#34; charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026#34;}},\u0026#34;status\u0026#34;:401}root@elk01:~# curl 11.0.1.113:9200/_cat/nodes {\u0026#34;error\u0026#34;:{\u0026#34;root_cause\u0026#34;:[{\u0026#34;type\u0026#34;:\u0026#34;security_exception\u0026#34;,\u0026#34;reason\u0026#34;:\u0026#34;missing authentication credentials for REST request [/_cat/nodes]\u0026#34;,\u0026#34;header\u0026#34;:{\u0026#34;WWW-Authenticate\u0026#34;:\u0026#34;Basic realm=\\\u0026#34;security\\\u0026#34; charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026#34;}}],\u0026#34;type\u0026#34;:\u0026#34;security_exception\u0026#34;,\u0026#34;reason\u0026#34;:\u0026#34;missing authentication credentials for REST request [/_cat/nodes]\u0026#34;,\u0026#34;header\u0026#34;:{\u0026#34;WWW-Authenticate\u0026#34;:\u0026#34;Basic realm=\\\u0026#34;security\\\u0026#34; charset=\\\u0026#34;UTF-8\\\u0026#34;\u0026#34;}},\u0026#34;status\u0026#34;:401} ES集群加密成功，用户无法直接访问，直接会拒绝，状态码为401\n9. 生成ES的随机密码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 root@elk01:~# /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME Initiating the setup of passwords for reserved users elastic,apm_system,kibana,kibana_system,logstash_system,beats_system,remote_monitoring_user. The passwords will be randomly generated and printed to the console. Please confirm that you would like to continue [y/N]y Changed password for user apm_system PASSWORD apm_system = iU3hx3Md2e1HoKNwAXpc Changed password for user kibana_system PASSWORD kibana_system = Apxpi9MYK5nK0yfQ5oVC Changed password for user kibana PASSWORD kibana = Apxpi9MYK5nK0yfQ5oVC Changed password for user logstash_system PASSWORD logstash_system = FLLwK9WLan5fgtTsoFnM Changed password for user beats_system PASSWORD beats_system = XOiZBPaNfwVt4WoDmlpd Changed password for user remote_monitoring_user PASSWORD remote_monitoring_user = 9LYltf3G7dnJ74m9Amb6 Changed password for user elastic PASSWORD elastic = vM2bNP9zylFUyoOLlR3W 10. 使用密码访问ES集群 1 2 3 4 root@elk01:~# curl -u elastic:vM2bNP9zylFUyoOLlR3W 11.0.1.113:9200/_cat/nodes 11.0.1.112 69 76 4 0.00 0.06 0.09 cdfhilmrstw * elk02 11.0.1.113 74 75 3 0.02 0.04 0.05 cdfhilmrstw - elk03 11.0.1.111 61 50 4 0.00 0.05 0.09 cdfhilmrstw - elk01 kibana页面登录认证及elastic密码修改 1. 修改kibana的配置文件 1 root@kibana:~# vim /etc/kibana/kibana.yml 1 2 3 ... elasticsearch.username: \u0026#34;kibana_system\u0026#34; elasticsearch.password: \u0026#34;Apxpi9MYK5nK0yfQ5oVC\u0026#34; 2. 重启kibana服务 1 root@kibana:~# systemctl restart kibana.service 3. 检查kibana服务是否监听 1 2 root@kibana:~# ss -ntl | grep 5601 LISTEN 0 511 0.0.0.0:5601 0.0.0.0:* 4. 访问kibana的WebUI使用elastic用户登录 http://11.0.1.114:5601/（需要使用elastic用户登录）\n5. 修改elastic的初始密码 postman访问ES加密集群 es-head访问ES加密集群 filebeat访问ES加密集群 1. 编写filebeat配置文件 1 root@elk01:/etc/filebeat# vim config/17-tcp-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 filebeat.inputs: - type: tcp host: \u0026#34;0.0.0.0:9000\u0026#34; output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-es-tls\u0026#34; username: \u0026#34;elastic\u0026#34; password: \u0026#34;123456\u0026#34; setup.ilm.enabled: false setup.template.name: \u0026#34;dawn-modules\u0026#34; setup.template.pattern: \u0026#34;dawn-log*\u0026#34; setup.template.overwrite: false 1 root@elk01:/etc/filebeat# filebeat -e -c config/17-tcp-to-es.yaml 2. 发送测试数据 1 root@elk02:~# echo 123456 | nc 11.0.1.111 9000 3. kibana查看数据 logstash访问ES加密集群 1. 编写配置文件 1 root@elk03:/etc/logstash# vim config/09-tcp-to-es_tls.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 input { tcp { port =\u0026gt; 8888 } } output { stdout {} elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-es-tls-logstash-%{+yyyy.MM.dd}\u0026#34; user =\u0026gt; \u0026#34;elastic\u0026#34; password =\u0026gt; \u0026#34;123456\u0026#34; } } 1 root@elk03:/etc/logstash# logstash -f config/09-tcp-to-es_tls.conf 2. 发送测试数据 1 root@elk02:~# echo \u0026#34;7891011\u0026#34; | nc 11.0.1.113 8888 3. kibana查看数据 ","permalink":"https://LMDAWN1127.github.io/posts/es-cluster-encryption-auth/","summary":"ES 集群安全实战：生成 TLS 证书并同步各节点、修改 elasticsearch.yml 启用传输加密与认证、Kibana 登录认证与 elastic 密码修改，以及 postman/es-head/filebeat/logstash 四类客户端如何访问加密集群。","title":"ES 集群加密认证实战：TLS 证书、密码登录与客户端接入"},{"content":"logstash的多分支语句、多实例与pipeline语法 一、logstash的多分支语句 1. 自定义模式文件 1 2 3 4 5 root@elk03:/etc/logstash/config# vim /etc/logstash/dawn-patterns/dawn SCHOOL (dawn|devops|k8s) CLAss [a-z0-9]+[0-9]{2} YEAR4 (19|20)[0-9]{2} year (19|20)[0-9]{2} 2. 测试数据 xixi 分支：\n1 2 3 123.112.21.112 - - [20/Oct/2026:21:20:00 +0800] \u0026#34;GET /index.html HTTP/1.1\u0026#34; 200 1024 \u0026#34;https://www.dawn.com/\u0026#34; \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36\u0026#34; 202.96.134.133 - - [20/Oct/2026:21:20:05 +0800] \u0026#34;POST /api/login HTTP/1.1\u0026#34; 404 512 \u0026#34;-\u0026#34; \u0026#34;Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148\u0026#34; 8.8.8.8 - - [20/Oct/2026:21:20:10 +0800] \u0026#34;GET /static/app.js HTTP/1.1\u0026#34; 200 20480 \u0026#34;https://www.dawn.com/index.html\u0026#34; \u0026#34;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/119.0.0.0 Safari/537.36\u0026#34; haha 分支：\n1 2 3 4 5 6 7 8 9 filebeat.inputs: - type: log paths: [\u0026#34;/tmp/haha.log\u0026#34;] fields: type: haha fields_under_root: true output.logstash: hosts: [\u0026#34;127.0.0.1:9999\u0026#34;] hehe 分支：\n1 2 3 4 5 exec 3\u0026lt;\u0026gt;/dev/tcp/127.0.0.1/8888 printf \u0026#39;%s\\n\u0026#39; \u0026#34;dawn haha devops01 xixi 2024 123.112.21.112\u0026#34; \u0026gt;\u0026amp;3 printf \u0026#39;%s\\n\u0026#39; \u0026#34;devops haha k8s07 xixi 2025 8.8.8.8\u0026#34; \u0026gt;\u0026amp;3 printf \u0026#39;%s\\n\u0026#39; \u0026#34;这一行没有IP 应该grok失败\u0026#34; \u0026gt;\u0026amp;3 exec 3\u0026gt;\u0026amp;- 3. 配置文件 1 root@elk03:/etc/logstash# vim config/08-input_multiple-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 input { file { path =\u0026gt; [\u0026#34;/tmp/xixi.log\u0026#34;] start_position =\u0026gt; \u0026#34;beginning\u0026#34; type =\u0026gt; \u0026#34;xixi\u0026#34; } beats { port =\u0026gt; 9999 type =\u0026gt; \u0026#34;haha\u0026#34; } tcp { port =\u0026gt; 8888 type =\u0026gt; \u0026#34;hehe\u0026#34; } } filter { # 如果类型是\u0026#34;xixi\u0026#34;，则遵循if语句代码块的处理逻辑。 if [type] == \u0026#34;xixi\u0026#34; { grok { match =\u0026gt; { message =\u0026gt; \u0026#34;%{HTTPD_COMMONLOG}\u0026#34; } } geoip { source =\u0026gt; \u0026#34;clientip\u0026#34; # 不写 database 插件会自己去下载 mmdb，且每次新 path.data 都要重下 database =\u0026gt; \u0026#34;/etc/logstash/GeoLite2-City.mmdb\u0026#34; } useragent { source =\u0026gt; \u0026#34;message\u0026#34; target =\u0026gt; \u0026#34;dawn-device\u0026#34; } date { match =\u0026gt; [\u0026#34;timestamp\u0026#34;,\u0026#34;dd/MMM/yyyy:HH:mm:ss Z\u0026#34;] } mutate { remove_field =\u0026gt; [ \u0026#34;ecs\u0026#34;,\u0026#34;agent\u0026#34;,\u0026#34;tags\u0026#34;,\u0026#34;input\u0026#34;,\u0026#34;@version\u0026#34;,\u0026#34;log\u0026#34;,\u0026#34;host\u0026#34; ] } } else if [type] == \u0026#34;haha\u0026#34; { mutate { remove_field =\u0026gt; [ \u0026#34;ecs\u0026#34;,\u0026#34;agent\u0026#34;,\u0026#34;tags\u0026#34;,\u0026#34;input\u0026#34;,\u0026#34;@version\u0026#34;,\u0026#34;log\u0026#34;, \u0026#34;host\u0026#34;] } } else { grok { patterns_dir =\u0026gt; [\u0026#34;/etc/logstash/dawn-patterns/\u0026#34;] match =\u0026gt; { message =\u0026gt; \u0026#34;%{SCHOOL:dawn-school} haha %{CLAss:dawn-class} xixi %{year:dawn-year} %{IPV4:dawn-ip}\u0026#34; } tag_on_failure =\u0026gt; [\u0026#34;_grokparsefailure\u0026#34;] } mutate { remove_field =\u0026gt; [ \u0026#34;ecs\u0026#34;,\u0026#34;agent\u0026#34;,\u0026#34;tags\u0026#34;,\u0026#34;input\u0026#34;,\u0026#34;@version\u0026#34;,\u0026#34;log\u0026#34;,\u0026#34;host\u0026#34; ] } } } output { stdout {} if [type] == \u0026#34;xixi\u0026#34; { elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-if-xixi-%{+yyyy.MM.dd}\u0026#34; } } else if [type] == \u0026#34;haha\u0026#34; { elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-if-haha-%{+yyyy.MM.dd}\u0026#34; } }else { elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-if-hehe-%{+yyyy.MM.dd}\u0026#34; } } } 4. 启动与验证 1 logstash -f /etc/logstash/config/08-input_multiple-to-es.yaml --path.data=/tmp/logstash-08f --path.logs=/tmp/logstash-08f/logs 查询\n1 curl -s \u0026#34;http://11.0.1.111:9200/dawn-logstash-if-hehe-2026.10.20/_count\u0026#34; 二、logstash的多实例 1. 创建测试数据 1 2 3 4 5 6 7 cat \u0026gt; /tmp/apps.log \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; INFO 2026-10-20 09:15:32|1001|login|svip1|99.50 INFO 2026-10-20 09:16:05|1002|logout|svip2|0.00 WARN 2026-10-20 10:20:11|1003|buy|svip3|199.00 ERROR 2026-10-20 11:05:47|1004|refund|svip4|59.90 INFO 2026-10-20 12:30:00|1005|login|svip5|128.00 EOF 2. 启动三个实例 1 2 3 4 5 6 7 8 # 实例1 logstash -rf config/02-file-to-stdout.conf --path.data=/tmp/logstash-haha # 实例2 logstash -rf config/03-file-filter-elasticsearch.conf --path.data=/tmp/logstash-haha2 # 实例3 logstash -rf config/04-file-filter-elasticsearch_custom_index.conf --path.data=/tmp/logstash-xixi 3. 验证 1 2 3 curl -s \u0026#34;http://127.0.0.1:9200/dawn-apps-2026.10.20/_count\u0026#34; curl -s \u0026#34;http://127.0.0.1:9200/_cat/indices/logstash-*?v\u0026amp;h=index,docs.count\u0026#34; 三、logstash的pipeline语法 1. pipelines.yml 1 2 mkdir -pv /usr/share/logstash/config/ vim /usr/share/logstash/config/pipelines.yml 1 2 3 4 5 - pipeline.id: dawn_xixi path.config: \u0026#34;/etc/logstash/config/02-file-to-stdout.conf\u0026#34; - pipeline.id: dawn_haha path.config: \u0026#34;/etc/logstash/config/03-file-filter-elasticsearch.conf\u0026#34; 2. 启动验证 1 logstash -r 日志确认两个 pipeline 都加载（一个进程，多个 pipeline）：\n1 [INFO ] agent - Pipelines running {:count=\u0026gt;2, :running_pipelines=\u0026gt;[:dawn_xixi, :dawn_haha], :non_running_pipelines=\u0026gt;[]} 用 -f 时自动忽略 pipelines.yml：\n1 [WARN ] [LogStash::Runner] multilocal - Ignoring the \u0026#39;pipelines.yml\u0026#39; file because modules or command line options are specified 3. sincedb 1 2 rm -f /usr/share/logstash/data/plugins/inputs/file/.sincedb_* # 裸命令方式 rm -f /var/lib/logstash/plugins/inputs/file/.sincedb_* # --path.settings /etc/logstash 方式 4. 三者的关系 pipeline 和多分支语句的关系：在每个 pipeline 中，都可以使用多分支语法 logstash 实例和 pipeline 的关系：一个 logstash 可以启动多个 pipeline，本质上就是一个进程加载了多个配置文件 logstash 多实例：启动多个 logstash 进程，每个进程都有一个 main pipeline，本质上就是每个进程加载一个配置文件 ","permalink":"https://LMDAWN1127.github.io/posts/logstash-multi-branch-instance-pipeline/","summary":"Logstash 进阶第二篇：if 多分支语句（file/beats/tcp 三种输入分别走 grok+geoip+useragent）、多实例部署（多进程各加载一个配置）与 pipelines.yml 多 pipeline 语法，以及三者的关系与 sincedb 注意事项。","title":"Logstash 进阶实战：多分支语句、多实例与 pipeline 语法"},{"content":"一、Logstash file 输入的 offset 与重新采集 1.1 offset 记录在 sincedb 文件中 Logstash 会在 /usr/share/logstash/data/plugins/inputs/file/ 目录下生成 .sincedb_\u0026lt;文件路径哈希\u0026gt; 文件，记录每个被采集文件当前的读取位置（offset）。\n下次启动时从这个位置继续采集，不会把整个文件重读一遍。\n1.2 重新导入旧数据的完整步骤 1 停止 logstash\n2 清除 sincedb 文件\n1 root@elk03:/etc/logstash# rm -f /usr/share/logstash/data/plugins/inputs/file/.sincedb_c3e21f60a15f19878632de9b335e4596 .sincedb_ 后的哈希由被采集文件的路径决定，不同文件文件名不同；删之前先 ls /usr/share/logstash/data/plugins/inputs/file/ 确认。\n3 重启 logstash\n要指定采集文件的起始位置。\n1 root@elk03:/etc/logstash# vim config/03-file-filter-elasticsearch.conf 1 2 3 4 5 6 input { file { path =\u0026gt; [\u0026#34;/tmp/apps.log\u0026#34;] start_position =\u0026gt; \u0026#34;beginning\u0026#34; } } 本节只关注 input 的读取位置行为，filter 拆分解法与完整配置见 2.2 节。\n演示用日志 /tmp/apps.log，每行以 | 分隔 5 段，首段为「程序名 + 日期 + 时间」：\n1 apps 2026-10-19 10:00:00|1001|login|svip01|9.9 1 root@elk03:/etc/logstash# logstash -rf config/03-file-filter-elasticsearch.conf 二、Logstash 处理日志的访问时间（date 插件） 2.1 date 插件 专门用于处理日志的日期模块，将其作为时间戳可以替换\u0026quot;@timestamp\u0026quot;字段。\n将来kibana基于该字段进行时间查询。\n2.2 编写 logstash 配置文件 1 root@elk03:/etc/logstash# vim config/03-file-filter-elasticsearch.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 input { # 输入类型是一个file，代表的是文本文件 file { # 指定的文件的路径，可以指定多个 path =\u0026gt; [\u0026#34;/tmp/apps.log\u0026#34;] # 指定从源文件采集的位置，有效值为: beginning, end(默认值)。 # 该参数仅在首次采集(没有在\u0026#34;.sincedb*\u0026#34;文件中记录)新文件时生效，第二次采集则无视此参数。 start_position =\u0026gt; \u0026#34;beginning\u0026#34; } } filter { mutate { # 对指定字段进行切分，本案例是将message字段按照\u0026#34;|\u0026#34;进行切分 split =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;|\u0026#34; } # 添加字段 add_field =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34;%{[message][0]}\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;%{[message][1]}\u0026#34; \u0026#34;action\u0026#34; =\u0026gt; \u0026#34;%{[message][2]}\u0026#34; \u0026#34;svip\u0026#34; =\u0026gt; \u0026#34;%{[message][3]}\u0026#34; \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;%{[message][4]}\u0026#34; } } mutate { split =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34; \u0026#34;} add_field =\u0026gt; { datetime =\u0026gt; \u0026#34;%{[other][1]} %{[other][2]}\u0026#34; } # 移除字段 remove_field =\u0026gt; [ \u0026#34;message\u0026#34; , \u0026#34;other\u0026#34;, \u0026#34;@version\u0026#34;] } mutate { # 对字段进行数据转换 convert =\u0026gt; { \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;float\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } } # 处理时间相关的模块 date { # 匹配日期字段，将\u0026#34;datetime\u0026#34; 转换为日期格式 match =\u0026gt; [ \u0026#34;datetime\u0026#34;, \u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34; ] # 使用match匹配到的时间数据类型存储在哪个字段中，若不指定，则默认使用覆盖\u0026#34;@timestamp\u0026#34; target =\u0026gt; \u0026#34;dawn-datetime\u0026#34; } } output { stdout {} elasticsearch {} } 注意：date 插件不指定 timezone 时默认按 UTC 解析，本地时间（+08:00）解析后在 Kibana 里查会差 8 小时；需要与本地时间一致时加上 timezone =\u0026gt; \u0026quot;Asia/Shanghai\u0026quot;。\n2.3 启动 logstash 实例 1 root@elk03:/etc/logstash# logstash -rf config/03-file-filter-elasticsearch.conf 2.4 Kibana 查询验证 三、Logstash 写入 ES 自定义索引名称 1 root@elk03:/etc/logstash# vim config/04-file-filter-elasticsearch_custom_index.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 input { # 输入类型是一个file，代表的是文本文件 file { # 指定的文件的路径，可以指定多个 path =\u0026gt; [\u0026#34;/tmp/apps.log\u0026#34;] # 指定从源文件采集的位置，有效值为: beginning, end(默认值)。 # 该参数仅在首次采集(没有在\u0026#34;.sincedb*\u0026#34;文件中记录)新文件时生效，第二次采集则无视此参数。 start_position =\u0026gt; \u0026#34;beginning\u0026#34; } } filter { mutate { # 对指定字段进行切分，本案例是将message字段按照\u0026#34;|\u0026#34;进行切分 split =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;|\u0026#34; } # 添加字段 add_field =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34;%{[message][0]}\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;%{[message][1]}\u0026#34; \u0026#34;action\u0026#34; =\u0026gt; \u0026#34;%{[message][2]}\u0026#34; \u0026#34;svip\u0026#34; =\u0026gt; \u0026#34;%{[message][3]}\u0026#34; \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;%{[message][4]}\u0026#34; } } mutate { split =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34; \u0026#34;} add_field =\u0026gt; { datetime =\u0026gt; \u0026#34;%{[other][1]} %{[other][2]}\u0026#34; } # 移除字段 remove_field =\u0026gt; [ \u0026#34;message\u0026#34; , \u0026#34;other\u0026#34;, \u0026#34;@version\u0026#34;] } mutate { # 对字段进行数据转换 convert =\u0026gt; { \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;float\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } } # 处理时间相关的模块 date { # 匹配日期字段，将\u0026#34;datetime\u0026#34; 转换为日期格式 match =\u0026gt; [ \u0026#34;datetime\u0026#34;, \u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34; ] # 使用match匹配到的时间数据类型存储在哪个字段中，若不指定，则默认使用覆盖\u0026#34;@timestamp\u0026#34; target =\u0026gt; \u0026#34;dawn-datetime\u0026#34; } } output { stdout {} elasticsearch { # 指定ES集群地址 hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] # 指定ES自定义索引的名称 index =\u0026gt; \u0026#34;dawn-apps-%{+yyyy.MM.dd}\u0026#34; } } 1 root@elk03:/etc/logstash# logstash -rf config/04-file-filter-elasticsearch_custom_index.conf 四、Logstash 的 grok 自定义正则与常用 filter 插件 4.1 grok 的语法 把正则封装成可复用的模式，再给捕获组起字段名。\n1 2 %{SYNTAX:SEMANTIC} # 模式名:字段名 %{SYNTAX:SEMANTIC:TYPE} # 可加类型转换 int/float，如 %{NUMBER:bytes:int} 字段名不要带.，会被当成嵌套层级。\n4.2 自定义模式文件 1 2 root@elk03:/etc/logstash# mkdir -pv /etc/logstash/dawn-patterns/ root@elk03:/etc/logstash# vim /etc/logstash/dawn-patterns/dawn 1 2 3 SCHOOL (dawn|devops|k8s) CLASS [a-z0-9]+[0-9]{2} YEAR4 (19|20)[0-9]{2} 1 root@elk03:/etc/logstash# vim /etc/logstash/dawn-patterns/school.yml 1 2 3 \u0026#34;dawn\u0026#34;: \u0026#34;黎明实验室\u0026#34; \u0026#34;devops\u0026#34;: \u0026#34;DevOps 学院\u0026#34; \u0026#34;k8s\u0026#34;: \u0026#34;云原生学院\u0026#34; yml 字典的 key 要加引号，否则数字 key 会和字段里的字符串对不上。\n4.3 编写 07 完整配置 1 root@elk03:/etc/logstash# vim config/07-grok-custom-filters-es.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 input { # filebeat 走 beats 协议 beats { port =\u0026gt; 9999 } # 调试链路，nc/bash 直接送纯文本，省掉 filebeat tcp { port =\u0026gt; 9998 } } filter { # 自定义 grok，时间用模式抽 grok { patterns_dir =\u0026gt; [\u0026#34;/etc/logstash/dawn-patterns/\u0026#34;] match =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;%{TIMESTAMP_ISO8601:dawn_datetime} %{SCHOOL:dawn_school} haha %{CLASS:dawn_class} xixi %{YEAR4:dawn_year} %{IP:dawn_ip} %{GREEDYDATA:dawn_ua}\u0026#34; } tag_on_failure =\u0026gt; [\u0026#34;_grokparsefailure\u0026#34;, \u0026#34;dawn_grok_fail\u0026#34;] } # date，把日志真实时间写进 @timestamp date { match =\u0026gt; [\u0026#34;dawn_datetime\u0026#34;, \u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34;, \u0026#34;ISO8601\u0026#34;] target =\u0026gt; \u0026#34;@timestamp\u0026#34; } # geoip，指定本地库 geoip { source =\u0026gt; \u0026#34;dawn_ip\u0026#34; database =\u0026gt; \u0026#34;/etc/logstash/GeoLite2-City.mmdb\u0026#34; } useragent { source =\u0026gt; \u0026#34;dawn_ua\u0026#34; target =\u0026gt; \u0026#34;dawn_device\u0026#34; } # translate，字典富化 translate { source =\u0026gt; \u0026#34;dawn_school\u0026#34; target =\u0026gt; \u0026#34;dawn_school_cn\u0026#34; dictionary_path =\u0026gt; \u0026#34;/etc/logstash/dawn-patterns/school.yml\u0026#34; fallback =\u0026gt; \u0026#34;未知\u0026#34; } mutate { rename =\u0026gt; { \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;dawn-host\u0026#34; } convert =\u0026gt; { \u0026#34;dawn_year\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } add_field =\u0026gt; { \u0026#34;dawn_label\u0026#34; =\u0026gt; \u0026#34;%{dawn_school}-%{dawn_class}\u0026#34; } } # ruby，把 geoip 的 {lat,lon} 对象转成 [经度,纬度] 数组 ruby { code =\u0026gt; \u0026#39; loc = event.get(\u0026#34;[geoip][location]\u0026#34;) if loc event.set(\u0026#34;dawn_geo\u0026#34;, [loc[\u0026#34;lon\u0026#34;], loc[\u0026#34;lat\u0026#34;]]) end \u0026#39; } # 收尾清理 mutate { remove_field =\u0026gt; [\u0026#34;ecs\u0026#34;, \u0026#34;input\u0026#34;, \u0026#34;@version\u0026#34;, \u0026#34;log\u0026#34;, \u0026#34;message\u0026#34;] } } output { stdout { codec =\u0026gt; rubydebug } elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-grok-%{+yyyy.MM.dd}\u0026#34; } } 4.4 启动 logstash 1 root@elk03:/etc/logstash# logstash -rf config/07-grok-custom-filters-es.conf 日志出现 Pipelines running 才算就绪\n4.5 发送测试数据 1 2 3 4 5 6 7 8 9 10 11 12 13 root@elk03:/tmp# cat \u0026gt; send_testdata.sh \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; #!/bin/bash exec 3\u0026lt;\u0026gt;/dev/tcp/127.0.0.1/9998 || { echo \u0026#34;CONNECT_FAIL\u0026#34;; exit 1; } sent=0 while IFS= read -r line; do [ -z \u0026#34;$line\u0026#34; ] \u0026amp;\u0026amp; continue printf \u0026#39;%s\\n\u0026#39; \u0026#34;$line\u0026#34; \u0026gt;\u0026amp;3 sent=$((sent+1)) done \u0026lt; /tmp/grok-exp-testdata.txt exec 3\u0026gt;\u0026amp;- echo \u0026#34;SENT_TOTAL=$sent\u0026#34; EOF root@elk03:/tmp# bash send_testdata.sh 测试数据，5 行覆盖 3 个学校、4 种 UA，境内外 IP，最后一行故意写坏用来验证失败标签。\n1 2 3 4 5 2026-10-20 17:20:00 dawn haha linux92 xixi 2024 123.112.21.112 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36 2026-10-20 17:20:05 devops haha k8s07 xixi 2025 8.8.8.8 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Version/17.0 Safari/605.1.15 2026-10-20 17:20:10 dawn haha python88 xixi 2023 202.96.134.133 Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/119.0.0.0 Safari/537.36 2026-10-20 17:20:15 k8s haha docker33 xixi 2026 1.1.1.1 Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148 2026-10-20 17:20:20 dawn haha java55 xixi # 这一行故意写坏 用来触发_grokparsefailure 4.6 Kibana 查询验证 Stack Management → Index Patterns → Create index pattern，索引 dawn-logstash-grok-*，时间字段 @timestamp Discover 里确认字段：dawn_school、dawn_school_cn、dawn_class、dawn_year、dawn_label、dawn_ip、dawn_device.*、geoip.*、dawn_geo 地图用 Maps → Add layer → Documents，地理字段选 dawn_geo 时间过滤器默认过去 15 分钟，历史数据要把右上角时间范围改到覆盖数据的时间段 控制台每条事件的输出：\n1 2 3 4 5 6 7 8 9 10 11 12 { \u0026#34;@timestamp\u0026#34; =\u0026gt; 2026-10-20T17:20:00.000Z, \u0026#34;dawn_school\u0026#34; =\u0026gt; \u0026#34;dawn\u0026#34;, \u0026#34;dawn_school_cn\u0026#34; =\u0026gt; \u0026#34;黎明实验室\u0026#34;, \u0026#34;dawn_class\u0026#34; =\u0026gt; \u0026#34;linux92\u0026#34;, \u0026#34;dawn_year\u0026#34; =\u0026gt; 2024, \u0026#34;dawn_label\u0026#34; =\u0026gt; \u0026#34;dawn-linux92\u0026#34;, \u0026#34;dawn_ip\u0026#34; =\u0026gt; \u0026#34;123.112.21.112\u0026#34;, \u0026#34;dawn_device\u0026#34; =\u0026gt; { \u0026#34;name\u0026#34; =\u0026gt; \u0026#34;Chrome\u0026#34;, \u0026#34;os_name\u0026#34; =\u0026gt; \u0026#34;Windows\u0026#34;, \u0026#34;device\u0026#34; =\u0026gt; \u0026#34;Other\u0026#34; }, \u0026#34;geoip\u0026#34; =\u0026gt; { \u0026#34;country_name\u0026#34; =\u0026gt; \u0026#34;China\u0026#34;, \u0026#34;city_name\u0026#34; =\u0026gt; \u0026#34;Beijing\u0026#34; }, \u0026#34;dawn_geo\u0026#34; =\u0026gt; [116.3889, 39.9288] } 故意写坏那条的 tags 是 _grokparsefailure + dawn_grok_fail + _geoip_lookup_failure，后一个是连带的（没抽出 IP，geoip 自然失败），看到两个一起出现先查 grok。\n1 2 root@elk03:/etc/logstash# curl -s \u0026#39;http://11.0.1.111:9200/dawn-logstash-grok-2026.10.20/_count\u0026#39; {\u0026#34;count\u0026#34;:5,\u0026#34;_shards\u0026#34;:{\u0026#34;total\u0026#34;:1,\u0026#34;successful\u0026#34;:1,\u0026#34;skipped\u0026#34;:0,\u0026#34;failed\u0026#34;:0}} 4.7 常用 filter 插件速查 1. mutate 字段增删改、类型转换，出场率最高。注意 mutate 内部各操作是固定顺序（coerce→rename→update→replace→convert→gsub→uppercase→capitalize→lowercase→strip→remove→split→join→merge→copy），跟书写顺序无关，有先后依赖要拆成多个 mutate 块。\n1 2 3 4 5 6 7 8 mutate { split =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;|\u0026#34; } add_field =\u0026gt; { \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;%{[message][1]}\u0026#34; } convert =\u0026gt; { \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;float\u0026#34;, \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } rename =\u0026gt; { \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;dawn-host\u0026#34; } gsub =\u0026gt; [ \u0026#34;request\u0026#34;, \u0026#34;\\?.*$\u0026#34;, \u0026#34;\u0026#34; ] remove_field =\u0026gt; [ \u0026#34;message\u0026#34;, \u0026#34;@version\u0026#34; ] } 2. date 1 2 3 4 date { match =\u0026gt; [\u0026#34;timestamp\u0026#34;, \u0026#34;dd/MMM/yyyy:HH:mm:ss Z\u0026#34;, \u0026#34;ISO8601\u0026#34;] target =\u0026gt; \u0026#34;@timestamp\u0026#34; } 不写 target 就覆盖 @timestamp。MMM 匹配英文月份缩写，依赖 JVM locale，中文 locale 下要加 -Duser.language=en -Duser.country=US 或改用数字月份。\n3. geoip 1 2 3 4 5 geoip { source =\u0026gt; \u0026#34;clientip\u0026#34; database =\u0026gt; \u0026#34;/etc/logstash/GeoLite2-City.mmdb\u0026#34; target =\u0026gt; \u0026#34;geoip\u0026#34; } 不写 database 会联网下库，内网首次卡 90s+ 再失败。内网 IP 查不到地理信息，字段为空是正常的。\n4. useragent 1 2 3 4 useragent { source =\u0026gt; \u0026#34;agent\u0026#34; target =\u0026gt; \u0026#34;dawn_device\u0026#34; } 产出 dawn_device.name（浏览器）、os_name（系统）、device（设备类型）。\n5. translate 1 2 3 4 5 6 7 translate { source =\u0026gt; \u0026#34;dawn_school\u0026#34; target =\u0026gt; \u0026#34;dawn_school_cn\u0026#34; dictionary_path =\u0026gt; \u0026#34;/etc/logstash/dawn-patterns/school.yml\u0026#34; fallback =\u0026gt; \u0026#34;未知\u0026#34; refresh_interval =\u0026gt; 300 } 6. kv 1 2 3 4 5 6 7 kv { source =\u0026gt; \u0026#34;request\u0026#34; field_split =\u0026gt; \u0026#34;\u0026amp;\u0026#34; value_split =\u0026gt; \u0026#34;=\u0026#34; trim_key =\u0026gt; \u0026#34;?\u0026#34; target =\u0026gt; \u0026#34;dawn_params\u0026#34; } 不写 target 会平铺到事件顶层，容易和别的字段打架。\n7. json 1 2 3 4 json { source =\u0026gt; \u0026#34;message\u0026#34; tag_on_failure =\u0026gt; [\u0026#34;_jsonparsefailure\u0026#34;] } 日志本身就是 JSON 的话，优先在 filebeat 端用 json.keys_under_root: true 解析，省掉 Logstash 这一步。\n8. csv 1 2 3 4 5 csv { source =\u0026gt; \u0026#34;message\u0026#34; columns =\u0026gt; [\u0026#34;time\u0026#34;, \u0026#34;level\u0026#34;, \u0026#34;msg\u0026#34;, \u0026#34;cost\u0026#34;] convert =\u0026gt; { \u0026#34;cost\u0026#34; =\u0026gt; \u0026#34;float\u0026#34; } } 9. dissect 固定分隔符的日志优先用它，比 grok 快数倍，不做正则回溯。\n1 2 3 4 5 6 dissect { mapping =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;%{clientip} - %{ident} [%{timestamp}] \\\u0026#34;%{verb} %{request}\\\u0026#34; %{response} %{bytes}\u0026#34; } remove_field =\u0026gt; [\u0026#34;message\u0026#34;] } 10. ruby 1 2 # 代码同 4.3 节 07 配置中的 ruby 块：把 geoip.location 的 {lat,lon} 对象拼成 [经度,纬度] 数组。 # dawn-geo 在 ES 侧映射为 geo_point 后，Kibana Maps 才能直接选它做地理图层。 同步阻塞，高吞吐场景能不用就不用。\n","permalink":"https://LMDAWN1127.github.io/posts/logstash-sincedb-date-index/","summary":"记录 Logstash 四段进阶实践：file 输入 sincedb 的 offset 机制与旧数据重新采集、date 插件处理日志时间、ES 自定义索引名，以及 grok 自定义正则配合 geoip/useragent/translate 等 filter 的完整案例。","title":"Logstash 进阶实战：sincedb 重新采集、date 时间处理与自定义索引"},{"content":"基于ELFK架构分析nginx访问日志 1. logstash代码 1 root@elk03:/etc/logstash# vim config/05-beats-filter-es.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 input { # 接收beats组件的数据，此处指filebeat beats { port =\u0026gt; 9999 } } filter { mutate { remove_field =\u0026gt; [ \u0026#34;ecs\u0026#34;,\u0026#34;agent\u0026#34;,\u0026#34;tags\u0026#34;,\u0026#34;input\u0026#34;,\u0026#34;@version\u0026#34; ] } # 基于正则匹配nginx的访问日志并解析相应格式，提取任意文本类型。 # nginx 日志为 combined 格式，改用 HTTPD_COMBINEDLOG（= COMMONLOG + referrer + agent） grok { match =\u0026gt; { message =\u0026gt; \u0026#34;%{HTTPD_COMBINEDLOG}\u0026#34; } } # 基于公网地址分析经纬度，城市的名称等信息，指定本地 geoip 数据库 # 注意: 内网 IP（如 11.0.1.1）不会生成 geoip 字段，仅对公网 IP 生效 geoip { source =\u0026gt; \u0026#34;clientip\u0026#34; database =\u0026gt; \u0026#34;/etc/logstash/GeoLite2-City.mmdb\u0026#34; } # geoip 插件的 location 是 {lat,lon} 对象，ES 动态映射不会将其识别为 geo_point，（只有 [lon,lat] 数组/GeoJSON/geohash 字符串才会） # 用 ruby 过滤器把经纬度拼成 [lon,lat] 数组字段 dawn-geo，动态映射为 geo_point ruby { code =\u0026gt; \u0026#34;event.set(\u0026#39;[dawn-geo]\u0026#39;, [event.get(\u0026#39;[geoip][longitude]\u0026#39;), event.get(\u0026#39;[geoip][latitude]\u0026#39;)]) if event.get(\u0026#39;[geoip][longitude]\u0026#39;)\u0026#34; } # 基于agent字段分析用户的设备类型,将分析的结果存储在\u0026#34;dawn-device\u0026#34;字段中 useragent { source =\u0026gt; \u0026#34;agent\u0026#34; target =\u0026gt; \u0026#34;dawn-device\u0026#34; } mutate { rename =\u0026gt; { \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;dawn-host\u0026#34; \u0026#34;agent\u0026#34; =\u0026gt; \u0026#34;dawn-agent\u0026#34; } } date { match =\u0026gt; [\u0026#34;timestamp\u0026#34;,\u0026#34;dd/MMM/yyyy:HH:mm:ss Z\u0026#34;] } } output { # stdout {} elasticsearch { hosts =\u0026gt; [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] index =\u0026gt; \u0026#34;dawn-logstash-nginx-%{+yyyy.MM.dd}\u0026#34; } } 1 root@elk03:/etc/logstash# logstash -rf config/05-beats-filter-es.conf 2. 启动filebeat 1 root@elk01:/etc/filebeat# vim config/14-modules_nginx-to-logstash.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 filebeat.config.modules: # 此处的\u0026#34;${path.config}\u0026#34;对应的路径是/etc/filebeat目录 path: ${path.config}/modules.d/*.yml # 支持热加载 reload.enabled: true # 指定每间隔多长时间检测一次\u0026#34;${path.config}/modules.d/*.yml\u0026#34; reload.period: 5s #output.console: # pretty: true # 将数据推送logstash output.logstash: hosts: [\u0026#34;11.0.1.113:9999\u0026#34;] 1 2 root@elk01:/etc/filebeat# rm -rf /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/14-modules_nginx-to-logstash.yaml 3. Kibana 索引模式与可视化 3.1 建索引模式 Stack Management → Index Patterns → Create：dawn-logstash-nginx-*，Time field 选 @timestamp。\n3.2 可视化（3 个）+ 地理分布（Maps 1 个） 可视化 类型 聚合字段 ELFK-nginx 访客地理分布 Maps 应用对象（面板类型 map） dawn-geo，ES_SEARCH 文档图层 ELFK-nginx 设备类型分布 Pie dawn-device.name，terms ELFK-nginx 操作系统分布 Pie dawn-device.os，terms ELFK-nginx 响应状态码分布 Vertical Bar（类型名 histogram） response，terms 3.3 仪表板 把 3 个可视化和 1 个 Maps 面板拖进同一个 Dashboard 即可，布局：地图通栏，下面三张图各占 1/3。\n仪表板地址：http://11.0.1.114:5601/app/dashboards#/view/788cabb0-cbda-11f1-9207-335bca41b28c Maps 对象地址：http://11.0.1.114:5601/app/maps#/map/a78a0830-cbe0-11f1-9207-335bca41b28c\n3.4 效果字段速查 geoip：country_name / country_code2 / continent_code / timezone / latitude / longitude / city_name / location dawn-device：name（浏览器）/ os（操作系统）/ os_name / os_version / device（设备） nginx：clientip / request / response / bytes / referrer / timestamp 4. 项目整合: ELFK架构分析出图展示 filebeat（采集）→ logstash（grok 解析 + geoip + useragent + date）→ elasticsearch（三节点存储）→ kibana（索引模式 + 地图/饼图/柱状图仪表板）\n","permalink":"https://LMDAWN1127.github.io/posts/elfk-nginx-access-log/","summary":"完整演示基于 ELFK 架构分析 Nginx 访问日志的全流程：Logstash 编写 05-beats-filter-es.conf（beats 输入 9999、grok 用 HTTPD_COMBINEDLOG 解析 combined 格式、geoip 指定本地 GeoLite2-City.mmdb、ruby 将经纬度拼成 dawn-geo 的 [lon,lat] 数组、useragent 解析 dawn-device、rename 规避字段冲突、date 对齐 @timestamp），输出到 Elasticsearch 的 dawn-logstash-nginx 索引；Filebeat 启用 nginx 模块并将数据推送至 Logstash；最后在 Kibana 建立索引模式，制作访客地理分布 Maps、设备类型/操作系统饼图、响应状态码柱状图，并整合进统一 Dashboard。","title":"ELFK 架构分析 Nginx 访问日志实战：Filebeat + Logstash + Kibana 全流程"},{"content":"ES的JVM基础环境优化 1. 修改ES的JVM大小 1 root@elk01:/etc/filebeat# vim /etc/elasticsearch/jvm.options 1 2 3 4 ... # 学习环境推荐为256M，生产环境建议设置为物理内存一半，但不建议超过32GB -Xms256m -Xmx256m 2. 拷贝配置文件 1 2 3 4 5 6 root@elk01:/etc/filebeat# scp /etc/elasticsearch/jvm.options 11.0.1.112:/etc/elasticsearch root@11.0.1.112\u0026#39;s password: jvm.options 100% 3484 895.4KB/s 00:00 root@elk01:/etc/filebeat# scp /etc/elasticsearch/jvm.options 11.0.1.113:/etc/elasticsearch root@11.0.1.113\u0026#39;s password: jvm.options 100% 3484 2.0MB/s 00:00 3. 重启服务 1 2 3 root@elk01:/etc/filebeat# systemctl restart elasticsearch.service root@elk02:~# systemctl restart elasticsearch.service root@elk03:~# systemctl restart elasticsearch.service logstash环境搭建 1. 概述 用于处理日志，聚合日志，分析的功能。功能丰富但比较笨重，目前ELK的市场逐渐被EFK替代。\nfilebeat很多功能基本是都是基于logstash为原型使用Golang编程语言重写的。轻量级的日志采集工具。\nLogstash是免费且开放的服务器端数据处理管道，能够从多个来源采集数据，转换数据，然后将数据发送到您最喜欢的“存储库”中。\n2. 下载logstash 1 root@elk03:~# wget https://artifacts.elastic.co/downloads/logstash/logstash-7.17.22-amd64.deb 3. 安装logstash 1 root@elk03:~# dpkg -i logstash-7.17.22-amd64.deb 4. 添加logstash命令到PATH环境变量 1 2 root@elk03:~# ln -svf /usr/share/logstash/bin/logstash /usr/local/sbin/ \u0026#39;/usr/local/sbin/logstash\u0026#39; -\u0026gt; \u0026#39;/usr/share/logstash/bin/logstash\u0026#39; 5. 编写logstash的配置文件 1 2 3 4 5 6 7 8 9 10 11 root@elk03:~# cd /etc/logstash/ root@elk03:/etc/logstash# mkdir config root@elk03:/etc/logstash# vim config/01-stdin-to-stdout.conf input { stdin {} } output { stdout {} } 6. 启动logstash实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@elk03:/etc/logstash# logstash -f config/01-stdin-to-stdout.conf ...\t{ \u0026#34;@version\u0026#34; =\u0026gt; \u0026#34;1\u0026#34;, \u0026#34;@timestamp\u0026#34; =\u0026gt; 2026-10-18T16:37:36.202Z, \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;\u0026#34;, \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;elk03\u0026#34; } 111111111 { \u0026#34;@version\u0026#34; =\u0026gt; \u0026#34;1\u0026#34;, \u0026#34;@timestamp\u0026#34; =\u0026gt; 2026-10-18T16:37:55.092Z, \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;111111111\u0026#34;, \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;elk03\u0026#34; } logstash处理自定义业务的日志 1. 准备Python脚本生成数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 root@elk03:~# vim generate_log.py #!/usr/bin/env python # -*- coding: UTF-8 -*- # @author : Jason Yin import datetime import random import logging import time import sys LOG_FORMAT = \u0026#34;%(levelname)s %(asctime)s [com.dawn.%(module)s] - %(message)s \u0026#34; DATE_FORMAT = \u0026#34;%Y-%m-%d %H:%M:%S\u0026#34; # 配置root的logging.Logger实例的基本配置 logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, datefmt=DATE_FORMAT, filename=sys.argv[1] , filemode=\u0026#39;a\u0026#39;,) actions = [\u0026#34;浏览页面\u0026#34;, \u0026#34;评论商品\u0026#34;, \u0026#34;加入收藏\u0026#34;, \u0026#34;加入购物车\u0026#34;, \u0026#34;提交订单\u0026#34;, \u0026#34;使用优惠券\u0026#34;, \u0026#34;领取优惠券\u0026#34;, \u0026#34;搜索\u0026#34;, \u0026#34;查看订单\u0026#34;, \u0026#34;付款\u0026#34;, \u0026#34;清空购物车\u0026#34;] while True: time.sleep(random.randint(1, 5)) user_id = random.randint(1, 10000) # 对生成的浮点数保留2位有效数字. price = round(random.uniform(15000, 30000),2) action = random.choice(actions) svip = random.choice([0,1]) logging.info(\u0026#34;DAU|{0}|{1}|{2}|{3}\u0026#34;.format(user_id, action,svip,price)) 1 2 3 root@elk03:~# apt update root@elk03:~# apt install python-is-python3 root@elk03:~# python generate_log.py /tmp/apps.log 2. 编写配置文件 1 root@elk03:/etc/logstash# vim config/02-file-to-stdout.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 input { # 输入类型是一个file，代表的是文本文件 file { # 指定的文件的路径，可以指定多个 path =\u0026gt; [\u0026#34;/tmp/apps.log\u0026#34;] # 指定从源文件采集的位置，有效值为: beginning, end(默认值)。 start_position =\u0026gt; \u0026#34;beginning\u0026#34; } } filter { mutate { # 对指定字段进行切分，本案例是将message字段按照\u0026#34;|\u0026#34;进行切分 split =\u0026gt; { \u0026#34;message\u0026#34; =\u0026gt; \u0026#34;|\u0026#34; } # 添加字段 add_field =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34;%{[message][0]}\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;%{[message][1]}\u0026#34; \u0026#34;action\u0026#34; =\u0026gt; \u0026#34;%{[message][2]}\u0026#34; \u0026#34;svip\u0026#34; =\u0026gt; \u0026#34;%{[message][3]}\u0026#34; \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;%{[message][4]}\u0026#34; } } mutate { split =\u0026gt; { \u0026#34;other\u0026#34; =\u0026gt; \u0026#34; \u0026#34;} add_field =\u0026gt; { datetime =\u0026gt; \u0026#34;%{[other][1]} %{[other][2]}\u0026#34; } # 移除字段 remove_field =\u0026gt; [ \u0026#34;message\u0026#34; , \u0026#34;other\u0026#34;, \u0026#34;@version\u0026#34;] } mutate { convert =\u0026gt; { \u0026#34;price\u0026#34; =\u0026gt; \u0026#34;float\u0026#34; \u0026#34;userId\u0026#34; =\u0026gt; \u0026#34;integer\u0026#34; } } } output { # stdout {} elasticsearch {} } 3. 启动实例 1 root@elk03:/etc/logstash# logstash -r -f config/02-file-to-stdout.conf ","permalink":"https://LMDAWN1127.github.io/posts/es-jvm-logstash/","summary":"围绕 ElasticStack 中 ES 与 Logstash 两个组件展开实战：一是 ES 的 JVM 基础环境优化，调整堆内存（-Xms/-Xmx，生产建议物理内存一半且不超 32GB），将 jvm.options 经 scp 分发至 11.0.1.112/113 并三节点重启生效；二是 Logstash 环境搭建，下载安装 logstash-7.17.22、加入 PATH、用 stdin-to-stdout 配置跑通管道；三是用 Logstash 处理自定义业务日志，编写 generate_log.py 造数，借助 mutate 过滤器（split 切分、add_field 加字段、convert 类型转换）将文本日志结构化后写入 Elasticsearch。","title":"ElasticStack 实战：ES JVM 优化与 Logstash 环境搭建及日志处理"},{"content":"filebeat模块使用 1. filebeat模块概述 filebeat模块是官方提供的一种对各种中间件进行日志采集的解决方案。\n2. 还原nginx的访问日志格式 1 root@elk01:/etc/filebeat# vim /etc/nginx/nginx.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ... #log_format dawn_nginx_json \u0026#39;{\u0026#34;@timestamp\u0026#34;:\u0026#34;$time_iso8601\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;host\u0026#34;:\u0026#34;$server_addr\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;clientip\u0026#34;:\u0026#34;$remote_addr\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;SendBytes\u0026#34;:$body_bytes_sent,\u0026#39; # \u0026#39;\u0026#34;responsetime\u0026#34;:$request_time,\u0026#39; # \u0026#39;\u0026#34;upstreamtime\u0026#34;:\u0026#34;$upstream_response_time\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;upstreamhost\u0026#34;:\u0026#34;$upstream_addr\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;http_host\u0026#34;:\u0026#34;$host\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;uri\u0026#34;:\u0026#34;$uri\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;domain\u0026#34;:\u0026#34;$host\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;xff\u0026#34;:\u0026#34;$http_x_forwarded_for\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;referer\u0026#34;:\u0026#34;$http_referer\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;tcp_xff\u0026#34;:\u0026#34;$proxy_protocol_addr\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;http_user_agent\u0026#34;:\u0026#34;$http_user_agent\u0026#34;,\u0026#39; # \u0026#39;\u0026#34;status\u0026#34;:\u0026#34;$status\u0026#34;}\u0026#39;; #access_log /var/log/nginx/access.log dawn_nginx_json; access_log /var/log/nginx/access.log; ... 1 2 3 root@elk01:/etc/filebeat# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful 3. 重启nginx的配置 1 2 3 4 5 root@elk01:/etc/filebeat# \u0026gt;/var/log/nginx/access.log root@elk01:/etc/filebeat# systemctl reload nginx root@elk01:/etc/filebeat# tail -f /var/log/nginx/access.log 11.0.1.1 - - [18/Oct/2026:20:46:20 +0800] \u0026#34;GET / HTTP/1.1\u0026#34; 200 266 \u0026#34;-\u0026#34; \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0\u0026#34; 11.0.1.1 - - [18/Oct/2026:20:46:20 +0800] \u0026#34;GET /phone.jpg HTTP/1.1\u0026#34; 200 49378 \u0026#34;http://11.0.1.111/\u0026#34; \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0\u0026#34; 4. filebeat启用，禁用和查看模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 root@elk01:/etc/filebeat# ll modules.d/ | egrep \u0026#34;tomcat|nginx\u0026#34; -rw-r--r-- 1 root root 784 May 30 2024 nginx.yml.disabled -rw-r--r-- 1 root root 623 May 30 2024 tomcat.yml.disabled root@elk01:/etc/filebeat# filebeat modules enable nginx tomcat Enabled nginx Enabled tomcat root@elk01:/etc/filebeat# ll modules.d/ | egrep \u0026#34;tomcat|nginx\u0026#34; -rw-r--r-- 1 root root 784 May 30 2024 nginx.yml -rw-r--r-- 1 root root 623 May 30 2024 tomcat.yml root@elk01:/etc/filebeat# filebeat modules list # 查看启用的列表 Enabled: nginx tomcat Disabled: activemq apache auditd aws awsfargate azure barracuda bluecoat cef checkpoint cisco coredns crowdstrike cyberark cyberarkpas cylance elasticsearch envoyproxy f5 fortinet gcp google_workspace googlecloud gsuite haproxy ibmmq icinga iis imperva infoblox iptables juniper kafka kibana logstash microsoft misp mongodb mssql mysql mysqlenterprise nats netflow netscout o365 okta oracle osquery panw pensando postgresql proofpoint rabbitmq radware redis santa snort snyk sonicwall sophos squid suricata system threatintel traefik zeek zookeeper zoom zscaler 1 2 3 4 5 6 root@elk01:/etc/filebeat# filebeat modules disable nginx tomcat Disabled nginx Disabled tomcat root@elk01:/etc/filebeat# ll modules.d/ | egrep \u0026#34;tomcat|nginx\u0026#34; -rw-r--r-- 1 root root 784 May 30 2024 nginx.yml.disabled -rw-r--r-- 1 root root 623 May 30 2024 tomcat.yml.disabled 所谓的启用模块，本质上是将\u0026quot;/etc/filebeat/modules.d/\u0026ldquo;目录下的\u0026rdquo;.yml.disabled\u0026quot;文件更名为\u0026quot;.yml\u0026quot;\n5. 使用模块采集nginx日志 启用nginx模块并修改配置文件\n1 2 3 4 5 6 7 8 9 10 root@elk01:/etc/filebeat# vim modules.d/nginx.yml - module: nginx access: enabled: true var.paths: [\u0026#34;/var/log/nginx/access.log*\u0026#34;] error: enabled: true var.paths: [\u0026#34;/var/log/nginx/error.log*\u0026#34;] ingress_controller: enabled: false 编写filebeat配置文件\n1 root@elk01:/etc/filebeat# vim config/13-modules_nginx-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 filebeat.config.modules: # 注意，此处的\u0026#34;${path.config}\u0026#34;对应的路径是/etc/filebeat目录 path: ${path.config}/modules.d/*.yml # 支持热加载 reload.enabled: true # 指定每间隔多长时间检测一次“${path.config}/modules.d/*.yml” reload.period: 5s #output.console: # pretty: true output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-modules-nginx\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称,和ES最好别冲突 setup.template.name: \u0026#34;dawn-modules\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false 启动filebeat实例\n1 2 3 4 root@elk01:/etc/filebeat# filebeat test config -c config/13-modules_nginx-to-es.yaml Config OK root@elk01:/etc/filebeat# rm -rf /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/13-modules_nginx-to-es.yaml kibana查看数据\n","permalink":"https://LMDAWN1127.github.io/posts/filebeat-modules/","summary":"围绕 Filebeat 官方 modules 模块展开实战：先介绍模块概念，演示还原 Nginx 访问日志格式并热加载生效；再通过 filebeat modules enable/disable/list 启用 nginx、tomcat 模块（本质是 modules.d 下 *.yml.disabled 重命名为 *.yml）；最后用 nginx 模块采集访问/错误日志写入 Elasticsearch（索引 dawn-log-modules-nginx，模板名 dawn-modules），并在 Kibana 查看数据。","title":"Filebeat 模块（modules）使用实战：启用与 nginx 日志采集"},{"content":"filebeat的filestream解析json格式日志 1. 编写配置文件 1 root@elk01:/etc/filebeat# vim config/10-filestream-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 filebeat.inputs: - type: filestream paths: - /var/log/nginx/access.log # 配置解析 parsers: # 解析json格式 - ndjson: # 将解析的数据放在哪个字段，若为\u0026#34;\u0026#34;代表放在顶级字段中 # target: \u0026#34;zlm\u0026#34; target: \u0026#34;\u0026#34; # 对哪个字段进行解析，若不指定，则默认会对message字段进行解析，并删除该字段。 message_key: message output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-filestream-nginx\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 2. 启动filebeat实例 1 2 root@elk01:/etc/filebeat# rm -fr /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/10-filestream-to-es.yaml filebeat的filestream实现多行匹配 1. 编写配置文件 1 root@elk01:/etc/filebeat# vim config/11-filestream_multiple-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 filebeat.inputs: - type: filestream paths: - /dawn/softwares/apache-tomcat-10.1.25/logs/catalina.out parsers: - multiline: type: pattern pattern: \u0026#39;^\\d{2}\u0026#39; negate: true match: after output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-filestream-tomcat-errorlog\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 2. 启动filebeat实例 1 2 root@elk01:/etc/filebeat# rm -rf /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/11-filestream_multiple-to-es.yaml filestream综合案例 1. 测试文件 1 2 3 4 5 6 7 8 9 root@elk01:/etc/filebeat# vim /tmp/apps.json { \u0026#34;name\u0026#34;: \u0026#34;张三\u0026#34;, \u0026#34;hobby\u0026#34;: [\u0026#34;111\u0026#34;,\u0026#34;222\u0026#34;,\u0026#34;333\u0026#34;] } { \u0026#34;name\u0026#34;: \u0026#34;李四\u0026#34;, \u0026#34;hobby\u0026#34;: [\u0026#34;444\u0026#34;,\u0026#34;555\u0026#34;,\u0026#34;666\u0026#34;] } 2. 编写配置文件 1 root@elk01:/etc/filebeat# vim config/12-filestream_json_multiple_line-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 filebeat.inputs: - type: filestream paths: - /tmp/apps.json parsers: - multiline: type: count count_lines: 4 - ndjson: target: \u0026#34;\u0026#34; message_key: message #output.console: # pretty: true output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-filestream-case-demo\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 3. 启动filebeat实例 1 2 root@elk01:/etc/filebeat# rm -rf /var/lib/filebeat/ root@elk01:/etc/filebeat# filebeat -e -c config/12-filestream_json_multiple_line-to-es.yaml ","permalink":"https://LMDAWN1127.github.io/posts/filebeat-filestream/","summary":"围绕 Filebeat 新版 filestream 输入类型展开实战：一是用 ndjson parser 解析 JSON 格式日志并写入 Elasticsearch（索引 dawn-log-filestream-nginx）；二是用 multiline pattern 正则实现多行错误日志匹配（索引 dawn-log-filestream-tomcat-errorlog）；三是综合案例，组合 multiline count_lines 与 ndjson 解析一段多行 JSON 日志（索引 dawn-log-filestream-case-demo）。","title":"Filebeat filestream 采集实战：ndjson 解析、multiline 多行匹配与综合案例"},{"content":"EFK架构采集tomcat日志 1. 部署tomcat 1 root@elk01:~# wget https://archive.apache.org/dist/tomcat/tomcat-10/v10.1.25/bin/apache-tomcat-10.1.25.tar.gz 2. 创建工作目录 1 root@elk01:~# mkdir /dawn/softwares -pv 3. 解压软件包 1 root@elk01:~# tar xf apache-tomcat-10.1.25.tar.gz -C /dawn/softwares/ 4. 配置tomcat的环境变量 1 root@elk01:~# vim /etc/profile.d/tomcat.sh 1 2 3 4 5 #!/bin/bash export JAVA_HOME=/usr/share/elasticsearch/jdk export TOMCAT_HOME=/dawn/softwares/apache-tomcat-10.1.25 export PATH=$PATH:$JAVA_HOME/bin:$TOMCAT_HOME/bin 1 root@elk01:~# source /etc/profile.d/tomcat.sh 5. 启动tomcat服务 1 2 3 4 5 6 7 8 9 10 root@elk01:~# catalina.sh start Using CATALINA_BASE: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_HOME: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_TMPDIR: /dawn/softwares/apache-tomcat-10.1.25/temp Using JRE_HOME: /usr/share/elasticsearch/jdk Using CLASSPATH: /dawn/softwares/apache-tomcat-10.1.25/bin/bootstrap.jar:/dawn/softwares/apache-tomcat-10.1.25/bin/tomcat-juli.jar Using CATALINA_OPTS: Tomcat started. root@elk01:~# ss -ntl | grep 8080 LISTEN 0 100 *:8080 *:* 6. 访问tomcat服务 http://11.0.1.111:8080/\n7. 查看访问日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 root@elk01:~# cat /dawn/softwares/apache-tomcat-10.1.25/logs/localhost_access_log.2026-10-17.txt 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET / HTTP/1.1\u0026#34; 200 11239 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /tomcat.svg HTTP/1.1\u0026#34; 200 67795 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /tomcat.css HTTP/1.1\u0026#34; 200 5584 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /bg-nav.png HTTP/1.1\u0026#34; 200 1401 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /bg-middle.png HTTP/1.1\u0026#34; 200 1918 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /bg-button.png HTTP/1.1\u0026#34; 200 713 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /asf-logo-wide.svg HTTP/1.1\u0026#34; 200 27235 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /bg-upper.png HTTP/1.1\u0026#34; 200 3103 11.0.1.1 - - [17/Oct/2026:10:35:18 +0000] \u0026#34;GET /favicon.ico HTTP/1.1\u0026#34; 200 21630 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET / HTTP/1.1\u0026#34; 200 11239 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /tomcat.css HTTP/1.1\u0026#34; 200 5584 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /tomcat.svg HTTP/1.1\u0026#34; 200 67795 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /bg-nav.png HTTP/1.1\u0026#34; 200 1401 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /asf-logo-wide.svg HTTP/1.1\u0026#34; 200 27235 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /bg-upper.png HTTP/1.1\u0026#34; 200 3103 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /bg-button.png HTTP/1.1\u0026#34; 200 713 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /bg-middle.png HTTP/1.1\u0026#34; 200 1918 11.0.1.1 - - [17/Oct/2026:10:36:39 +0000] \u0026#34;GET /favicon.ico HTTP/1.1\u0026#34; 200 21630 自定义tomcat日志并使用filebeat采集 1. 自定义日志格式 1 root@elk01:~# vim /dawn/softwares/apache-tomcat-10.1.25/conf/server.xml 1 2 3 4 5 6 7 8 9 ...(143行左右) \u0026lt;Host name=\u0026#34;tomcat.dawn.com\u0026#34; appBase=\u0026#34;webapps\u0026#34; unpackWARs=\u0026#34;true\u0026#34; autoDeploy=\u0026#34;true\u0026#34;\u0026gt; \u0026lt;Valve className=\u0026#34;org.apache.catalina.valves.AccessLogValve\u0026#34; directory=\u0026#34;logs\u0026#34; prefix=\u0026#34;tomcat.dawn.com_access_log\u0026#34; suffix=\u0026#34;.json\u0026#34; pattern=\u0026#34;{\u0026amp;quot;clientip\u0026amp;quot;:\u0026amp;quot;%h\u0026amp;quot;,\u0026amp;quot;ClientUser\u0026amp;quot;:\u0026amp;quot;%l\u0026amp;quot;,\u0026amp;quot;authenticated\u0026amp;quot;:\u0026amp;quot;%u\u0026amp;quot;,\u0026amp;quot;AccessTime\u0026amp;quot;:\u0026amp;quot;%t\u0026amp;quot;,\u0026amp;quot;request\u0026amp;quot;:\u0026amp;quot;%r\u0026amp;quot;,\u0026amp;quot;status\u0026amp;quot;:\u0026amp;quot;%s\u0026amp;quot;,\u0026amp;quot;SendBytes\u0026amp;quot;:\u0026amp;quot;%b\u0026amp;quot;,\u0026amp;quot;Query?string\u0026amp;quot;:\u0026amp;quot;%q\u0026amp;quot;,\u0026amp;quot;partner\u0026amp;quot;:\u0026amp;quot;%{Referer}i\u0026amp;quot;,\u0026amp;quot;http_user_agent\u0026amp;quot;:\u0026amp;quot;%{User-Agent}i\u0026amp;quot;}\u0026#34;/\u0026gt; \u0026lt;/Host\u0026gt; 2. 重启tomcat服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@elk01:~# catalina.sh stop Using CATALINA_BASE: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_HOME: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_TMPDIR: /dawn/softwares/apache-tomcat-10.1.25/temp Using JRE_HOME: /usr/share/elasticsearch/jdk Using CLASSPATH: /dawn/softwares/apache-tomcat-10.1.25/bin/bootstrap.jar:/dawn/softwares/apache-tomcat-10.1.25/bin/tomcat-juli.jar Using CATALINA_OPTS: root@elk01:~# catalina.sh start Using CATALINA_BASE: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_HOME: /dawn/softwares/apache-tomcat-10.1.25 Using CATALINA_TMPDIR: /dawn/softwares/apache-tomcat-10.1.25/temp Using JRE_HOME: /usr/share/elasticsearch/jdk Using CLASSPATH: /dawn/softwares/apache-tomcat-10.1.25/bin/bootstrap.jar:/dawn/softwares/apache-tomcat-10.1.25/bin/tomcat-juli.jar Using CATALINA_OPTS: Tomcat started. 3. 验证日志格式 1 使用curl工具测试 1 root@elk03:/etc/filebeat# curl -H \u0026#39;HOST: tomcat.dawn.com\u0026#39; 11.0.1.111:8080 2 查看日志 1 2 3 4 5 6 root@elk01:~# cat /dawn/softwares/apache-tomcat-10.1.25/logs/ catalina.2026-10-17.log localhost_access_log.2026-10-17.txt catalina.out tomcat.dawn.com_access_log.2026-10-17.json localhost.2026-10-17.log root@elk01:~# cat /dawn/softwares/apache-tomcat-10.1.25/logs/tomcat.dawn.com_access_log.2026-10-17.json {\u0026#34;clientip\u0026#34;:\u0026#34;11.0.1.113\u0026#34;,\u0026#34;ClientUser\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;authenticated\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;AccessTime\u0026#34;:\u0026#34;[17/Oct/2026:11:06:28 +0000]\u0026#34;,\u0026#34;request\u0026#34;:\u0026#34;GET / HTTP/1.1\u0026#34;,\u0026#34;status\u0026#34;:\u0026#34;200\u0026#34;,\u0026#34;SendBytes\u0026#34;:\u0026#34;11239\u0026#34;,\u0026#34;Query?string\u0026#34;:\u0026#34;\u0026#34;,\u0026#34;partner\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;http_user_agent\u0026#34;:\u0026#34;curl/7.81.0\u0026#34;} 4. 编写filebeat采集tomcat日志 1 root@elk01:/etc/filebeat# vim config/07-tomcat-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 filebeat.inputs: - type: log paths: - /dawn/softwares/apache-tomcat-10.1.25/logs/tomcat.dawn.com_access_log*.json # 将message的json格式解析后放在顶级字段中，并删除message字段。 json.keys_under_root: true output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-tomcat\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 5. 启动filebeat 1 root@elk01:/etc/filebeat# filebeat -e -c config/07-tomcat-to-es.yaml filebeat多行匹配采集tomcat启动日志 1. 编写filebeat配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 root@elk01:/etc/filebeat# vim config/08-tomcat_errlog-to-es.yaml filebeat.inputs: - type: log paths: - /dawn/softwares/apache-tomcat-10.1.25/logs/catalina.out multiline: # 指定多行匹配的模式，支持pattern模式 type: pattern # 指定匹配模式 pattern: \u0026#39;^\\d{2}\u0026#39; # 参考链接: https://www.elastic.co/guide/en/beats/filebeat/7.17/multiline-examples.html negate: true match: after output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-tomcat-errorlog\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 2. 启动实例 1 root@elk01:/etc/filebeat# filebeat -e -c config/08-tomcat_errlog-to-es.yaml 3. kibana查看数据 ","permalink":"https://LMDAWN1127.github.io/posts/filebeat-tomcat-log-collection/","summary":"围绕 EFK 采集 Tomcat 日志展开：部署 Tomcat 并自定义 JSON 格式访问日志（server.xml AccessLogValve），用 Filebeat 采集 JSON 访问日志写入 Elasticsearch（索引 dawn-log-tomcat，json.keys_under_root 关键配置），以及用 multiline 多行匹配采集 catalina.out 启动/错误日志（索引 dawn-log-tomcat-errorlog），最后在 Kibana 查看数据。","title":"Filebeat 采集 Tomcat 日志实战：自定义 JSON 访问日志与多行错误日志匹配"},{"content":"filebeat配置多个input写入到不同的ES索引 1. 编写配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 root@elk01:/etc/filebeat# vim config/09-input_multiple-to-es.yaml # ============================== Filebeat inputs =============================== filebeat.inputs: # 输入 1：采集本地日志文件 /tmp/zlm.log - type: log enabled: true paths: - /tmp/zlm.log # tags 规范为字符串数组，便于 when.contains 精确匹配 tags: [\u0026#34;zlm\u0026#34;] # 多行合并：每 4 行合并为一个事件 multiline: type: count count_lines: 4 # 输入 2：接收 TCP 日志 - type: tcp enabled: true host: \u0026#34;0.0.0.0:9000\u0026#34; tags: [\u0026#34;lmy\u0026#34;] # ====================== Elasticsearch output ====================== output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; # 根据 tags 字段将事件分流到不同索引 indices: - index: \u0026#34;dawn-log-zlm-%{+yyyy.MM.dd}\u0026#34; when: contains: tags: \u0026#34;zlm\u0026#34; - index: \u0026#34;dawn-log-lmy-%{+yyyy.MM.dd}\u0026#34; when: contains: tags: \u0026#34;lmy\u0026#34; # ====================== 索引模板设置 ====================== # 禁用 ILM，否则自定义索引名会被 Filebeat 默认的 ILM 策略覆盖 setup.ilm.enabled: false # 自定义索引模板名称，匹配所有 dawn-log* 索引 setup.template.name: \u0026#34;dawn\u0026#34; setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果模板已存在，是否覆盖。生产环境建议保持 false，避免多实例并发更新模板 setup.template.overwrite: false # 索引分片与副本设置 setup.template.settings: index.number_of_shards: 5 index.number_of_replicas: 0 2. 启动filebeat 1 root@elk01:/etc/filebeat# filebeat -e -c config/09-input_multiple-to-es.yaml 3. 查看kibana数据 filebeat的多实例案例 1. 启动实例1 1 root@elk01:/etc/filebeat# filebeat -e -c config/02-stdin-to-es.yaml 2. 启动实例2 1 root@elk01:~# filebeat -e -c config/03-tcp-to-es.yaml --path.data /tmp/data 3. 查看主机的filebeat进程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 root@elk01:~# ps -ef | grep filebeat | grep -v grep root 18713 18699 0 00:41 pts/1 00:00:00 /usr/share/filebeat/bin/filebeat --path.home /usr/share/filebeat --path.config /etc/filebeat --path.data /var/lib/filebeat --path.logs /var/log/filebeat -e -c config/03-tcp-to-es.yaml --path.data /tmp/data root 18726 1617 0 00:42 pts/0 00:00:00 /usr/share/filebeat/bin/filebeat --path.home /usr/share/filebeat --path.config /etc/filebeat --path.data /var/lib/filebeat --path.logs /var/log/filebeat -e -c config/02-stdin-to-es.yaml root@elk01:~# ll /tmp/data total 16 drwxr-x--- 3 root root 4096 Oct 18 00:41 ./ drwxrwxrwt 17 root root 4096 Oct 18 00:41 ../ -rw------- 1 root root 0 Oct 18 00:41 filebeat.lock -rw------- 1 root root 100 Oct 18 00:41 meta.json drwxr-x--- 3 root root 4096 Oct 18 00:41 registry/ root@elk01:~# ll /var/lib/filebeat/ total 16 drwxr-x--- 3 root root 4096 Oct 18 00:42 ./ drwxr-xr-x 45 root root 4096 Oct 16 17:11 ../ -rw------- 1 root root 0 Oct 18 00:42 filebeat.lock -rw------- 1 root root 100 Oct 16 17:11 meta.json drwxr-x--- 3 root root 4096 Oct 16 17:11 registry/ 使用dpkg安装的包，启动filebeat会自动携带参数，如果我们定义了相同的参数会直接覆盖原来的配置\n","permalink":"https://LMDAWN1127.github.io/posts/filebeat-multi-input-multi-instance/","summary":"实战演示 Filebeat 两个核心进阶用法：一是配置多个 input 按 tags 将事件分流写入不同 Elasticsearch 索引（dawn-log-zlm、dawn-log-lmy），含 multiline 多行合并与关闭 ILM、自定义 dawn-log* 模板；二是 Filebeat 多实例部署，通过 \u0026ndash;path.data 隔离数据目录实现同一主机并行运行多个采集进程。","title":"Filebeat 多 Input 写入不同 ES 索引与多实例部署实战"},{"content":"kibana查询和dashboard制作 1.kibana查询PV统计 2.kibana查询IP统计 3.kibana查询带宽统计 4.kibana查询网站状态码统计 5.kibana查询网站站点访问统计 6.kibana的dashboard制作 选好后拖拽形成一张图，然后保存\n","permalink":"https://LMDAWN1127.github.io/posts/kibana-query-dashboard/","summary":"基于已采集进 Elasticsearch 的 Nginx 日志，手把手演示 Kibana 可视化：PV 统计、IP 统计、带宽统计、网站状态码统计、站点访问统计多维查询，以及如何将多个可视化拖拽组合成一张 Dashboard 面板并保存。","title":"Kibana 查询与 Dashboard 制作实战：PV、IP、带宽、状态码统计与可视化面板"},{"content":"自定义nginx的日志格式 1. 安装nginx服务 1 root@elk01:~# apt -y install nginx 2. 修改nginx的日志格式 1 root@elk01:~# vim /etc/nginx/nginx.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... log_format dawn_nginx_json \u0026#39;{\u0026#34;@timestamp\u0026#34;:\u0026#34;$time_iso8601\u0026#34;,\u0026#39; \u0026#39;\u0026#34;host\u0026#34;:\u0026#34;$server_addr\u0026#34;,\u0026#39; \u0026#39;\u0026#34;clientip\u0026#34;:\u0026#34;$remote_addr\u0026#34;,\u0026#39; \u0026#39;\u0026#34;SendBytes\u0026#34;:$body_bytes_sent,\u0026#39; \u0026#39;\u0026#34;responsetime\u0026#34;:$request_time,\u0026#39; \u0026#39;\u0026#34;upstreamtime\u0026#34;:\u0026#34;$upstream_response_time\u0026#34;,\u0026#39; \u0026#39;\u0026#34;upstreamhost\u0026#34;:\u0026#34;$upstream_addr\u0026#34;,\u0026#39; \u0026#39;\u0026#34;http_host\u0026#34;:\u0026#34;$host\u0026#34;,\u0026#39; \u0026#39;\u0026#34;uri\u0026#34;:\u0026#34;$uri\u0026#34;,\u0026#39; \u0026#39;\u0026#34;domain\u0026#34;:\u0026#34;$host\u0026#34;,\u0026#39; \u0026#39;\u0026#34;xff\u0026#34;:\u0026#34;$http_x_forwarded_for\u0026#34;,\u0026#39; \u0026#39;\u0026#34;referer\u0026#34;:\u0026#34;$http_referer\u0026#34;,\u0026#39; \u0026#39;\u0026#34;tcp_xff\u0026#34;:\u0026#34;$proxy_protocol_addr\u0026#34;,\u0026#39; \u0026#39;\u0026#34;http_user_agent\u0026#34;:\u0026#34;$http_user_agent\u0026#34;,\u0026#39; \u0026#39;\u0026#34;status\u0026#34;:\u0026#34;$status\u0026#34;}\u0026#39;; access_log /var/log/nginx/access.log dawn_nginx_json; 1 2 # 将默认的日志格式注释掉 # access_log /var/log/nginx/access.log; 3. 检查nginx的配置文件语法 1 2 3 root@elk01:~# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful 4. 启动nginx服务 1 root@elk01:~# systemctl restart nginx 5. 访问测试 1 2 root@elk01:~# tail -f /var/log/nginx/access.log {\u0026#34;@timestamp\u0026#34;:\u0026#34;2026-10-16T15:53:58+08:00\u0026#34;,\u0026#34;host\u0026#34;:\u0026#34;11.0.1.111\u0026#34;,\u0026#34;clientip\u0026#34;:\u0026#34;11.0.1.1\u0026#34;,\u0026#34;SendBytes\u0026#34;:396,\u0026#34;responsetime\u0026#34;:0.000,\u0026#34;upstreamtime\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;upstreamhost\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;http_host\u0026#34;:\u0026#34;11.0.1.111\u0026#34;,\u0026#34;uri\u0026#34;:\u0026#34;/index.nginx-debian.html\u0026#34;,\u0026#34;domain\u0026#34;:\u0026#34;11.0.1.111\u0026#34;,\u0026#34;xff\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;referer\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;tcp_xff\u0026#34;:\u0026#34;-\u0026#34;,\u0026#34;http_user_agent\u0026#34;:\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0\u0026#34;,\u0026#34;status\u0026#34;:\u0026#34;200\u0026#34;} Ubuntu基于nfs实现filebeat配置文件共享 1.另外两个节点也安装filebeat 1 2 root@elk01:~# dpkg -i filebeat-7.17.22-amd64.deb root@elk02:~# dpkg -i filebeat-7.17.22-amd64.deb 2. 所有节点安装nfs工具 1 2 3 4 5 6 7 8 root@elk01:~# apt -y remove needrestart root@elk01:~# apt -y install nfs-kernel-server root@elk02:~# apt -y remove needrestart root@elk02:~# apt -y install nfs-kernel-server root@elk03:~# apt -y remove needrestart root@elk03:~# apt -y install nfs-kernel-server 3. 配置nfs的服务端 1 2 3 4 root@elk01:~# mkdir /etc/filebeat/config root@elk01:~# cat \u0026gt; /etc/exports \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; /etc/filebeat/config *(rw,no_root_squash) EOF 3. 启动nfs服务 1 2 3 4 5 6 7 root@elk01:~# systemctl enable --now nfs-kernel-server Synchronizing state of nfs-kernel-server.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable nfs-kernel-server root@elk01:~# systemctl restart nfs-kernel-server root@elk01:~# exportfs /etc/filebeat/config \u0026lt;world\u0026gt; 4. 客户端测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@elk02:~# mount 11.0.1.111:/etc/filebeat/config/ /mnt/ root@elk02:~# umount /mnt root@elk03:~# mount 11.0.1.111:/etc/filebeat/config/ /mnt/ root@elk03:~# mv config/* /mnt/ root@elk03:~# ll /mnt/ total 28 drwxr-xr-x 2 root root 4096 Oct 16 16:33 ./ drwxr-xr-x 20 root root 4096 Oct 14 15:44 ../ -rw-r--r-- 1 root root 63 Oct 16 13:14 01-stdin-to-stdout.yaml -rw-r--r-- 1 root root 141 Oct 16 13:21 02-stdin-to-es.yaml -rw-r--r-- 1 root root 161 Oct 16 13:35 03-tcp-to-es.yaml -rw-r--r-- 1 root root 729 Oct 16 14:26 04-tcp-to-es-custom-index.yaml -rw-r--r-- 1 root root 772 Oct 16 14:43 05-log_ubuntu_system-to-es.yaml root@elk03:~# umount /mnt 5. 所有客户端挂载 1 2 3 4 5 6 7 8 9 root@elk02:~# mkdir /etc/filebeat/config root@elk02:~# mount 11.0.1.111:/etc/filebeat/config/ /etc/filebeat/config root@elk02:~# cd /etc/filebeat/ root@elk02:/etc/filebeat# root@elk03:~# mkdir /etc/filebeat/config root@elk03:~# mount 11.0.1.111:/etc/filebeat/config/ /etc/filebeat/config root@elk03:~# cd /etc/filebeat/ root@elk03:/etc/filebeat# EFK架构采集nginx日志; 1. 进入到filebeat的配置文件目录 1 2 root@elk01:~# cd /etc/filebeat/ root@elk01:/etc/filebeat# 2. 编写filebeat采集nginx日志 1 root@elk01:/etc/filebeat# vim config/06-nginx-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 filebeat.inputs: - type: log paths: - /var/log/nginx/access.log output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-nginx\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 3. 启动filebeat 1 root@elk01:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml filebeat采集json格式日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 root@elk01:/etc/filebeat# vim config/06-nginx-to-es.yaml filebeat.inputs: - type: log paths: - /var/log/nginx/access.log # 将message的json格式解析后放在顶级字段中，并删除message字段。 json.keys_under_root: true output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-nginx\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 1 root@elk01:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml 基于Nginx七层分流的Web集群部署与Filebeat日志采集 1. elk02节点环境准备 1 安装nginx 1 root@elk02:~# apt -y install nginx 2 修改nginx的主配置文件 1 root@elk01:/etc/filebeat# scp /etc/nginx/nginx.conf 11.0.1.112:/etc/nginx/nginx.conf 3 修改nginx的子配置文件 1 root@elk02:~# vim /etc/nginx/sites-enabled/default 4 准备测试页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 root@elk02:~# vim /var/www/html/index.html \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;/\u0026gt; \u0026lt;title\u0026gt;DAWN’s phone\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; div img { width: 900px; height: 600px; margin: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1 style=\u0026#34;color: red\u0026#34;\u0026gt;手机测试页面 \u0026lt;/h1\u0026gt; \u0026lt;div\u0026gt; \u0026lt;img src=\u0026#34;phone.jpg\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; root@elk02:/var/www/html# ls index.html index.nginx-debian.html phone.jpg 5 重启nginx服务 1 2 3 4 5 6 7 8 9 10 11 root@elk02:~# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful root@elk02:~# systemctl reload nginx root@elk02:~# systemctl restart nginx root@elk02:~# systemctl enable nginx Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable nginx root@elk02:~# ss -ntl | grep 81 LISTEN 0 511 0.0.0.0:81 0.0.0.0:* LISTEN 0 4096 [::]:42815 [::]:* 6 测试访问http://11.0.1.112:81/\n2. elk03节点环境准备 1 安装nginx 1 root@elk03:~# apt -y install nginx 2 修改nginx的主配置文件 1 root@elk01:/etc/filebeat# scp /etc/nginx/nginx.conf 11.0.1.113:/etc/nginx/nginx.conf 3 修改nginx的子配置文件 1 root@elk03:~# vim /etc/nginx/sites-enabled/default 4 准备测试页面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 root@elk03:~# vim /var/www/html/index.html \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;/\u0026gt; \u0026lt;title\u0026gt;DAWN’s pc\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; div img { width: 900px; height: 600px; margin: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1 style=\u0026#34;color: green\u0026#34;\u0026gt;电脑测试页面 \u0026lt;/h1\u0026gt; \u0026lt;div\u0026gt; \u0026lt;img src=\u0026#34;pc.jpg\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; root@elk03:/var/www/html# ls index.html index.nginx-debian.html pc.jpg 5 重启nginx服务 1 2 3 4 5 6 7 8 9 10 root@elk03:~# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful root@elk03:~# systemctl reload nginx root@elk03:~# systemctl restart nginx root@elk03:~# systemctl enable nginx Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable nginx root@elk03:~# ss -ntl | grep 82 LISTEN 0 511 0.0.0.0:82 0.0.0.0:* 6 测试访问http://11.0.1.113:82/\n3. elk01负载均衡器配置 1 编写配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 root@elk01:~# vim /etc/nginx/sites-enabled/default upstream phone { server 11.0.1.112:81; } upstream pc { server 11.0.1.113:82; } server { listen 80 ; server_name phone.dawn.com; location / { proxy_pass http://phone; } } server { listen 80 ; server_name pc.dawn.com; location / { proxy_pass http://pc; } } 2 重新加载nginx服务 1 2 3 4 root@elk01:~# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful root@elk01:~# systemctl reload nginx 3 测试访问 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 root@elk03:~# curl -H \u0026#39;host: pc.dawn.com\u0026#39; 11.0.1.111 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;/\u0026gt; \u0026lt;title\u0026gt;DAWN’s pc\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; div img { width: 900px; height: 600px; margin: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1 style=\u0026#34;color: green\u0026#34;\u0026gt;电脑测试页面 \u0026lt;/h1\u0026gt; \u0026lt;div\u0026gt; \u0026lt;img src=\u0026#34;pc.jpg\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 root@elk03:~# curl -H \u0026#39;host: phone.dawn.com\u0026#39; 11.0.1.111 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;/\u0026gt; \u0026lt;title\u0026gt;DAWN’s phone\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; div img { width: 900px; height: 600px; margin: 0; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1 style=\u0026#34;color: red\u0026#34;\u0026gt;手机测试页面 \u0026lt;/h1\u0026gt; \u0026lt;div\u0026gt; \u0026lt;img src=\u0026#34;phone.jpg\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 4.filebeat采集Web集群日志\n1 2 3 root@elk01:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml root@elk02:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml root@elk03:/etc/filebeat# filebeat -e -c config/06-nginx-to-es.yaml 5.windows测试 在**C:\\Windows\\System32\\drivers\\etc/hosts**添加hosts解析。\n1 11.0.1.111 phone.dawn.com pc.dawn.com ","permalink":"https://LMDAWN1127.github.io/posts/filebeat-nginx-log-collection/","summary":"围绕 ELK/EFK 日志采集实验展开：自定义 Nginx JSON 日志格式（dawn_nginx_json），基于 NFS 实现三节点 Filebeat 配置共享，EFK 架构采集 Nginx 日志至 Elasticsearch（索引 dawn-log-nginx），Filebeat 解析 JSON 格式日志（json.keys_under_root 关键配置），以及基于 Nginx 七层分流的 Web 集群部署与 Filebeat 日志采集实战。","title":"Filebeat 采集 Nginx 日志实战：自定义 JSON 日志格式、NFS 配置共享与七层 Web 集群"},{"content":"filebeat环境搭建 1. 下载filebeat 希望采集哪个节点数据就在哪个节点部署\n1 root@elk03:~# wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.17.22-amd64.deb 2. 安装filebeat 1 root@elk03:~# dpkg -i filebeat-7.17.22-amd64.deb 3. 查看filebeat的帮助信息 1 root@elk03:~# filebeat -h filebeat的架构 1. 作用 能够采集任意文本类型日志，都可以进行分析。\n2. filebeat的组成部分 3. 案例 1 2 root@elk03:~# mkdir config root@elk03:~# vim config/01-stdin-to-stdout.yaml 1 2 3 4 5 filebeat.inputs: - type: stdin output.console: pretty: true 1 root@elk03:~# filebeat -e -c /root/config/01-stdin-to-stdout.yaml EFK架构测试样例 1. 输入端为stdin 1 root@elk03:~# vim config/02-stdin-to-es.yaml 1 2 3 4 5 filebeat.inputs: - type: stdin output.elasticsearch: hosts: [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] 1 root@elk03:~# filebeat -e -c /root/config/02-stdin-to-es.yaml 2. 输入端为tcp 1 root@elk03:~# vim config/03-tcp-to-es.yaml 1 2 3 4 5 6 filebeat.inputs: - type: tcp host: \u0026#34;0.0.0.0:9000\u0026#34; output.elasticsearch: hosts: [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] 1 root@elk03:~# filebeat -e -c /root/config/03-tcp-to-es.yaml 发送测试数据\n1 root@elk01:~# echo \u0026#34;欢迎来龙餐馆\u0026#34; | nc 11.0.1.113 9000 kibana查看数据\nfilebeat的后端运行的两种方案 方式一：\n1 2 root@elk03:~# filebeat -e -c /root/config/03-tcp-to-es.yaml \u0026amp; root@elk03:~# nohup filebeat -e -c /root/config/03-tcp-to-es.yaml \u0026gt; filebeat.log \u0026amp; 方式二:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cat \u0026gt; /usr/lib/systemd/system/filebeat-tcp-to-es.service \u0026lt;\u0026lt;EOF [Unit] Description=filebeat After=network.target [Service] ExecStart=filebeat -e -c /root/config/03-tcp-to-es.yaml Type=simple [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl start filebeat-tcp-to-es.service filebeat写入ES自定义索引和分片和副本案例 1. 移动到默认的配置文件目录或者创建符号链接（仅仅是为了方便，可选非必要） 1 2 3 root@elk03:~# mv config/ /etc/filebeat/ 或 root@elk03:~# ln -svf /etc/filebeat/config config 2. 编写配置文件并启动filebeat 1 root@elk03:~# vim config/04-tcp-to-es-custom-index.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 filebeat.inputs: - type: tcp host: \u0026#34;0.0.0.0:9000\u0026#34; output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-tcp\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-tcp*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 3 # 设置索引的副本数量 index.number_of_replicas: 0 1 root@elk03:~# filebeat -e -c /root/config/04-tcp-to-es-custom-index.yaml 3. 发送测试数据 1 root@elk01:~# echo 你好，李焕英 | nc 11.0.1.113 9000 4. kibana查看数据 filebeat采集ubuntu系统日志到ES集群 1. 分析 Centos的系统日志： /var/log/message\nUbuntu的系统日志: /var/log/syslog\n其他日志: /var/log/alternatives.log /var/log/auth.log /var/log/bootstrap.log /var/log/cloud-init.log /var/log/cloud-init-output.log /var/log/dpkg.log /var/log/kern.log /var/log/ubuntu-advantage.log /var/log/vmware-network.log /var/log/vmware-vmsvc-root.1.log /var/log/vmware-vmsvc-root.2.log /var/log/vmware-vmsvc-root.3.log /var/log/vmware-vmsvc-root.log /var/log/vmware-vmtoolsd-root.log\n2. 编写配置文件并启动filebeat 1 root@elk03:~# vim config/05-log_ubuntu_system-to-es.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 filebeat.inputs: - type: log paths: - /var/log/syslog - /var/log/*.log output.elasticsearch: hosts: - \u0026#34;http://11.0.1.111:9200\u0026#34; - \u0026#34;http://11.0.1.112:9200\u0026#34; - \u0026#34;http://11.0.1.113:9200\u0026#34; index: \u0026#34;dawn-log-ubuntu-syslog\u0026#34; # 禁用索引的生命周期(Index Lifecycle Management,简称\u0026#34;ilm\u0026#34;)，如果不禁用则忽略自定义索引名称 setup.ilm.enabled: false # ES的索引模板名称 setup.template.name: \u0026#34;dawn\u0026#34; # ES索引模板的匹配模式 setup.template.pattern: \u0026#34;dawn-log*\u0026#34; # 如果索引模板已经存在，是否覆盖，推荐设置为false setup.template.overwrite: false # 设置索引模板 setup.template.settings: # 设置索引的分片数量 index.number_of_shards: 5 # 设置索引的副本数量 index.number_of_replicas: 0 1 root@elk03:~# filebeat -e -c /root/config/05-log_ubuntu_system-to-es.yaml 3. kibana查看数据 filebeat的log类型是按行读取数据;\nfilebeat会在本地记录采集文本日志的offset;\n1 2 3 4 5 6 7 8 dpkg包部署存储路径： /var/lib/filebeat/ 二进制部署: 在 \u0026#34;./data\u0026#34; 自定义数据路径： 启动时使用\u0026#34;--path.data \u0026#34;指定即可。 ","permalink":"https://LMDAWN1127.github.io/posts/filebeat-deployment/","summary":"围绕 Filebeat 日志采集器展开：环境搭建（deb 下载安装）、架构组成，以及 stdin→ES、tcp→ES、log→ES 三类输入实战，并涵盖自定义索引名称、关闭 ILM、自定义分片/副本，以及采集 Ubuntu 系统日志写入 ES 集群的完整操作。","title":"Filebeat 部署与日志采集实战：安装、架构与 stdin/tcp/log 输入"},{"content":"IP案例 1. 自定义数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 PUT 11.0.1.111:9200/dawn-ip { \u0026#34;mappings\u0026#34;: { \u0026#34;properties\u0026#34;: { \u0026#34;ip_addr\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;ip\u0026#34; } } }, \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 3, \u0026#34;number_of_replicas\u0026#34;: 0 } } 2. 查看自定义类型索引 1 GET 11.0.1.111:9200/dawn-ip 3. 创建测试数据 1 2 3 4 5 6 7 8 9 10 11 12 13 POST 11.0.1.111:9200/_bulk { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;192.168.10.101\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;192.168.10.201\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;172.31.10.100\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;11.0.1.222\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;192.168.20.91\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-ip\u0026#34;} } { \u0026#34;ip_addr\u0026#34;: \u0026#34;11.0.1.113\u0026#34; } 4. 查看IP的网段 1 2 3 4 5 6 7 8 GET 11.0.1.111:9200/dawn-ip/_search { \u0026#34;query\u0026#34;: { \u0026#34;match\u0026#34; : { \u0026#34;ip_addr\u0026#34;: \u0026#34;192.168.0.0/16\u0026#34; } } } 5. 删除索引 1 DELETE 11.0.1.111:9200/dawn-ip date案例 1. 创建索引 1 2 3 4 5 6 7 8 9 10 11 PUT 11.0.1.111:9200/dawn-date { \u0026#34;mappings\u0026#34;: { \u0026#34;properties\u0026#34;: { \u0026#34;birthday\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;date\u0026#34;, \u0026#34;format\u0026#34;: \u0026#34;yyyy-MM-dd\u0026#34; } } } } 2. 创建测试数据 1 2 3 4 5 6 7 8 POST 11.0.1.111:9200/_bulk { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-date\u0026#34;} } { \u0026#34;name\u0026#34;: \u0026#34;father\u0026#34;,\u0026#34;birthday\u0026#34;: \u0026#34;1980-11-01\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-date\u0026#34;} } { \u0026#34;name\u0026#34;: \u0026#34;mother\u0026#34;,\u0026#34;birthday\u0026#34;: \u0026#34;1982-08-01\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-date\u0026#34;} } { \u0026#34;name\u0026#34;: \u0026#34;brother\u0026#34;,\u0026#34;birthday\u0026#34;: \u0026#34;2010-06-01\u0026#34; } { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-date\u0026#34;} } 3. 查看年龄，从大到小(比较的是数字，数字大的在下面) 1 2 3 4 GET 11.0.1.111:9200/dawn-date/_search { \u0026#34;sort\u0026#34;: { \u0026#34;birthday\u0026#34;: \u0026#34;asc\u0026#34;} } geo_point案例 1. 创建索引 1 2 3 4 5 6 7 8 9 10 PUT 11.0.1.113:9200/dawn-map { \u0026#34;mappings\u0026#34;: { \u0026#34;properties\u0026#34;: { \u0026#34;location\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;geo_point\u0026#34; } } } } 2. 写入地理位置（lat代表纬度,lon代表经度） 1 2 3 4 5 6 7 8 9 10 11 POST 11.0.1.113:9200/_bulk { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 39.914,\u0026#34;lon\u0026#34;: 116.386}} { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 42.210,\u0026#34;lon\u0026#34;: 101.666}} { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 32.850,\u0026#34;lon\u0026#34;: 98.124}} { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 79.850,\u0026#34;lon\u0026#34;: 128.124}} { \u0026#34;create\u0026#34;: { \u0026#34;_index\u0026#34;: \u0026#34;dawn-map\u0026#34;}} { \u0026#34;location\u0026#34;: { \u0026#34;lat\u0026#34;: 63.850,\u0026#34;lon\u0026#34;: 179.124}} 3. kibana的map展示 ","permalink":"https://LMDAWN1127.github.io/posts/elasticsearch-data-mapping/","summary":"围绕 Elasticsearch 字段映射（mapping）展开三个实战案例：ip 类型字段配合 CIDR 网段查询、date 类型字段按生日排序、geo_point 类型字段写入经纬度并经 Kibana Maps 在地图上可视化展示。","title":"Elasticsearch 数据映射（ip/date/geo_point）"},{"content":"1. 下载kibana 1 root@kibana:~# wget https://artifacts.elastic.co/downloads/kibana/kibana-7.17.22-amd64.deb 2. 安装kibana 1 root@kibana:~# dpkg -i kibana-7.17.22-amd64.deb 3. 修改kibana的配置文件 1 root@kibana:~# vim /etc/kibana/kibana.yml 1 2 3 4 5 6 7 8 # 监听的端口 server.port: 5601 # 监听的地址 server.host: \u0026#34;0.0.0.0\u0026#34; # 指定ES集群地址 elasticsearch.hosts: [\u0026#34;http://11.0.1.111:9200\u0026#34;,\u0026#34;http://11.0.1.112:9200\u0026#34;,\u0026#34;http://11.0.1.113:9200\u0026#34;] # 国际化语言 i18n.locale: \u0026#34;zh-CN\u0026#34; 4. 启动kibana 1 2 3 root@kibana:~# systemctl enable --now kibana.service root@kibana:~# ss -ntl | grep 5601 LISTEN 0 511 0.0.0.0:5601 0.0.0.0:* 5. 访问kibana的WebUI http://11.0.1.114:5601/\n","permalink":"https://LMDAWN1127.github.io/posts/kibana-setup/","summary":"记录 Kibana 7.17.22 的离线环境搭建全流程：通过 deb 包下载与 dpkg 安装，修改 kibana.yml 接入 Elasticsearch 集群（配置 server.port/host、elasticsearch.hosts、i18n.locale 中文），使用 systemctl 启动并验证 5601 端口监听，最后通过 WebUI 访问。","title":"Kibana 环境搭建与配置：从 deb 安装到接入 Elasticsearch 集群"},{"content":"1. 核心配置项 action.destructive_requires_name\n这个配置项决定了删除索引时是否必须指定明确的索引名称。\ntrue（推荐，ES 7.x 及以后版本的默认值）：必须指定明确索引名，禁止使用通配符（*）或 _all 删除。\nfalse 允许使用通配符或 _all 删除。\n2. 动态配置方式（推荐，无需重启） 通过 API 动态修改集群设置，立即生效：\n1 2 3 4 5 6 PUT 11.0.1.111:9200/_cluster/settings { \u0026#34;persistent\u0026#34;: { \u0026#34;action.destructive_requires_name\u0026#34;: true } } persistent：持久生效，重启 ES 后依然有效\ntransient：临时生效，集群重启后失效\n3. 静态配置方式（修改配置文件） 在每个节点的/etc/elasticsearch/elasticsearch.yml中添加：\n1 action.destructive_requires_name: true 修改后需要滚动重启整个集群生效，这种方法比动态设置更底层、更安全\n4. 验证配置效果 尝试通配符删除（报错）\n1 DELETE 11.0.1.111:9200/dawn* 尝试明确名称删除（成功）\n1 DELETE 11.0.1.111:9200/dawn ","permalink":"https://LMDAWN1127.github.io/posts/elasticsearch-destructive-requires-name/","summary":"围绕 Elasticsearch 集群级安全设置 action.destructive_requires_name 展开：该配置决定删除索引时是否必须指定明确名称（禁用通配符与 _all）。涵盖动态 API（_cluster/settings，persistent/transient）与静态 elasticsearch.yml 两种配置方式，以及如何验证通配符删除被拦截、明确名称删除成功。","title":"Elasticsearch 集群安全配置：action.destructive_requires_name 防误删保护"},{"content":"ES单个文档管理 文档创建 1. 文档的创建(推荐) 1 2 3 4 5 6 7 8 9 POST 11.0.1.113:9200/dawn/doc { \u0026#34;name\u0026#34;: \u0026#34;zlm\u0026#34;, \u0026#34;hobby\u0026#34;: [ \u0026#34;吃饭\u0026#34;, \u0026#34;睡觉\u0026#34;, \u0026#34;上课\u0026#34; ] } 2. 文档的创建指定文档id(不推荐，可能存在id冲突问题) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 POST 11.0.1.113:9200/dawn/doc/2300160645 { \u0026#34;school\u0026#34;: \u0026#34;czu\u0026#34;, \u0026#34;class\u0026#34;: \u0026#34;计算机001\u0026#34;, \u0026#34;student\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;dark\u0026#34;, \u0026#34;hobby\u0026#34;: [ \u0026#34;打球\u0026#34;, \u0026#34;看书\u0026#34; ] }, { \u0026#34;name\u0026#34;: \u0026#34;ocean\u0026#34;, \u0026#34;hobby\u0026#34;: [ \u0026#34;唱歌\u0026#34;, \u0026#34;rap\u0026#34;, \u0026#34;篮球\u0026#34; ] } ] } 文档查看 1. 查看所有的文档 1 GET 11.0.1.113:9200/dawn/_search 2. 查看指定的文档 1 GET 11.0.1.113:9200/dawn/doc/2300160645 3. 基于DSL语句查询 1 2 3 4 5 6 7 8 POST 11.0.1.113:9200/dawn/_search { \u0026#34;query\u0026#34;: { \u0026#34;match\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;zlm\u0026#34; } } } 文档修改和删除 1. 修改文档 1 2 3 4 5 6 POST 11.0.1.113:9200/dawn/doc/2300160645/_update { \u0026#34;doc\u0026#34;:{ \u0026#34;school\u0026#34; : \u0026#34;thu\u0026#34; } } 2. 删除文档 1 DELETE 11.0.1.113:9200/dawn/doc/2300160645 ES文档批量操作 文档批量创建（注意最后有一个换行） 只能采用扁平化的方式去写，不能采用Beautify的方式去写\n1 2 3 4 5 6 7 POST 11.0.1.113:9200/_bulk { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;张三\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;1\u0026#34;,\u0026#34;2\u0026#34;,\u0026#34;3\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;李四\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;4\u0026#34;,\u0026#34;5\u0026#34;,\u0026#34;6\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;王五\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;7\u0026#34;,\u0026#34;8\u0026#34;,\u0026#34;9\u0026#34;]} 文档批量查看 1 2 3 4 5 6 7 8 9 10 11 12 13 POST 11.0.1.113:9200/_mget { \u0026#34;docs\u0026#34;: [ { \u0026#34;_index\u0026#34;: \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34;: \u0026#34;854sPqEBMns9ROCbUcEr\u0026#34; }, { \u0026#34;_index\u0026#34;: \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34;: \u0026#34;8p4sPqEBMns9ROCbUcEq\u0026#34; } ] } 文档批量修改（注意最后有一个换行） 1 2 3 POST 11.0.1.113:9200/_bulk { \u0026#34;update\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;,\u0026#34;_id\u0026#34; : \u0026#34;9J4sPqEBMns9ROCbUcEr\u0026#34;} } { \u0026#34;doc\u0026#34; : {\u0026#34;age\u0026#34; : 30, \u0026#34;hobby\u0026#34;: [\u0026#34;10\u0026#34;,\u0026#34;11\u0026#34;,\u0026#34;12\u0026#34;]} } 文档批量删除（注意最后有一个换行） 1 2 3 4 POST 11.0.1.113:9200/_bulk { \u0026#34;delete\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34; : \u0026#34;854sPqEBMns9ROCbUcEr\u0026#34; } } { \u0026#34;delete\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34; : \u0026#34;8p4sPqEBMns9ROCbUcEq\u0026#34; } } { \u0026#34;delete\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;dawn05\u0026#34;, \u0026#34;_id\u0026#34; : \u0026#34;9J4sPqEBMns9ROCbUcEr\u0026#34; } } 对于文档的批量操作而言，使用\u0026quot;_bulk\u0026quot;的API实现数据的写操作(创建，修改，删除)，使用\u0026quot;_mget\u0026quot;实现读取操作。\n","permalink":"https://LMDAWN1127.github.io/posts/elasticsearch-document-api-management/","summary":"围绕 Elasticsearch 文档的 REST API 展开：单文档的创建（指定/不指定 id）、查看（全量/指定/DSL 查询）、修改与删除，以及批量写入（_bulk）与批量读取（_mget）等常见文档级运维操作。","title":"Elasticsearch 文档 API 管理：单文档与批量操作"},{"content":"索引创建和查看 1. 创建索引 1 PUT 11.0.1.111:9200/dawn 默认创建一个分片和一个副本\n2. 查看索引 查看单个索引\n1 GET 11.0.1.111:9200/dawn 查看所有索引\n1 GET 11.0.1.111:9200/_cat/indices?v 3. 创建索引自定义分片数量 1 2 3 4 5 6 PUT 11.0.1.111:9200/dawn-01 { \u0026#34;settings\u0026#34;:{ \u0026#34;number_of_shards\u0026#34;: 3 } } 4. 创建索引自定义分片副本数量 1 2 3 4 5 6 7 PUT 11.0.1.111:9200/dawn-02 { \u0026#34;settings\u0026#34;:{ \u0026#34;number_of_shards\u0026#34;: 5, \u0026#34;number_of_replicas\u0026#34;: 0 } } 索引修改和删除 1. 修改索引的副本数量 1 2 3 4 PUT 11.0.1.111:9200/dawn-02/_settings { \u0026#34;number_of_replicas\u0026#34;: 2 } 2. 删除指定索引 1 DELETE\t11.0.1.111:9200/dawn-02 3. 批量删除索引数据 1 DELETE 11.0.1.111:9200/dawn* 一旦删除了索引，意味着该索引下的所有文档全部删除！\n索引开启和关闭 1. 关闭索引 1 POST 11.0.1.111:9200/dawn/_close 2. 开启索引 1 POST 11.0.1.111:9200/dawn/_open 一旦一个索引被关闭，则意味着该索引不可访问;\n被关闭的索引数据并不会丢失;\n默认情况下，ES的索引都是处于开启状态。\n索引模板 1. 索引模板介绍 索引模板就是创建索引时要遵循的模板规则。\n索引模板仅对新创建的索引有效，已经创建的索引并不受索引模板的影响。\n2. 查看所有的索引模板 1 GET 11.0.1.111:9200/_index_template 3. 创建自定义索引模板 1 2 3 4 5 6 7 8 9 10 11 12 POST 11.0.1.111:9200/_index_template/dawn { \u0026#34;index_patterns\u0026#34;: [ \u0026#34;dawn*\u0026#34; ], \u0026#34;template\u0026#34;: { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 5, \u0026#34;number_of_replicas\u0026#34;: 0 } } } 4. 查看单个索引模板 1 GET 11.0.1.111:9200/_index_template/dawn 5. 创建测试的索引 1 PUT 11.0.1.111:9200/dawn-01 6. 修改索引模板 1 2 3 4 5 6 7 8 9 10 11 12 PUT 11.0.1.111:9200/_index_template/dawn { \u0026#34;index_patterns\u0026#34;: [ \u0026#34;dawn*\u0026#34; ], \u0026#34;template\u0026#34;: { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 3, \u0026#34;number_of_replicas\u0026#34;: 2 } } } 7. 再次创建索引 1 PUT 11.0.1.111:9200/dawn-02 8. 删除索引模板 1 DELETE 11.0.1.111:9200/_index_template/dawn 组件模板和索引模板联合使用 1. 查看现有的组件模板 1 GET 11.0.1.111:9200/_component_template/ 2. 创建自定义的组件模板 1 2 3 4 5 6 7 8 POST 11.0.1.111:9200/_component_template/dark { \u0026#34;template\u0026#34;: { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 10 } } } 3. 查看单个组件模板 1 GET 11.0.1.111:9200/_component_template/dark 4. 创建索引模板指定组件模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST 11.0.1.111:9200/_index_template/dawn { \u0026#34;index_patterns\u0026#34;: [ \u0026#34;dawn*\u0026#34; ], \u0026#34;composed_of\u0026#34;: [ \u0026#34;dark\u0026#34; ], \u0026#34;template\u0026#34;: { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_replicas\u0026#34;: 2 } } } 5. 创建测试索引 1 PUT 11.0.1.111:9200/dawn-03 索引别名的创建 1. 索引别名介绍 所谓的索引别名，其实就是给索引起名字。\n其应用场景可以将多个不同的索引打上相同的别名，将来基于别名进行查询，就可以将这些不同索引的数据一起查询。\n2. 准备测试数据 1 2 3 4 5 6 7 8 9 10 11 12 13 POST 11.0.1.111:9200/_bulk { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-235\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小张\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;1\u0026#34;,\u0026#34;2\u0026#34;,\u0026#34;3\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-235\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小王\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;4\u0026#34;,\u0026#34;5\u0026#34;,\u0026#34;6\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-236\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小赵\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;7\u0026#34;,\u0026#34;8\u0026#34;,\u0026#34;9\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-231\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小李\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;10\u0026#34;,\u0026#34;11\u0026#34;,\u0026#34;12\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-232\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小黄\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;13\u0026#34;,\u0026#34;14\u0026#34;,\u0026#34;15\u0026#34;]} { \u0026#34;index\u0026#34; : { \u0026#34;_index\u0026#34; : \u0026#34;czu-234\u0026#34;} } {\u0026#34;name\u0026#34;: \u0026#34;小陈\u0026#34;,\u0026#34;hobby\u0026#34;: [\u0026#34;16\u0026#34;,\u0026#34;17\u0026#34;,\u0026#34;18\u0026#34;]} 3. 添加别名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 POST 11.0.1.111:9200/_aliases { \u0026#34;actions\u0026#34;: [ { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-235\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;czu\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-234\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;czu\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-231\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;ok\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-232\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;ok\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-236\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;ok\u0026#34; } } ] } 4. kibana基于别名查询数据 索引别名的查看、删除和修改 1. 查看所有索引的别名信息 1 GET 11.0.1.111:9200/_alias 2. 查看某个索引的别名信息 1 GET 11.0.1.111:9200/czu-235/_alias 3. 删除索引别名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST 11.0.1.111:9200/_aliases { \u0026#34;actions\u0026#34;: [ { \u0026#34;remove\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-235\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;czu\u0026#34; } }, { \u0026#34;remove\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-234\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;czu\u0026#34; } } ] } 删除前\n删除后\n4. 修改别名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST 11.0.1.111:9200/_aliases { \u0026#34;actions\u0026#34;: [ { \u0026#34;remove\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-231\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;ok\u0026#34; } }, { \u0026#34;add\u0026#34;: { \u0026#34;index\u0026#34;: \u0026#34;czu-231\u0026#34;, \u0026#34;alias\u0026#34;: \u0026#34;no\u0026#34; } } ] } 修改前\n修改后\n","permalink":"https://LMDAWN1127.github.io/posts/elasticsearch-index-api-management/","summary":"围绕 Elasticsearch 索引的 REST API 管理展开：索引的创建、查看、自定义分片与副本数量，修改副本数量、删除指定/批量删除索引数据，索引开启与关闭，索引模板与组件模板的创建/查看/修改/删除，以及索引别名的增删改查与实际查询应用。","title":"Elasticsearch 索引 API 管理"},{"content":"ElasticStack ElasticSearch，简称:\u0026ldquo;es\u0026rdquo;\n数据存储 数据查询 filebeat:\n采集数据 kibana:\n用于出图展示 logstash:\n日志分析处理。 zookeeper:\n分布式协调服务，用于协调其他服务组件。 kafka:\n分布式消息队列，可以缓存数据。依赖zk实现元数据管理。 ElasticSearch单点部署 1. 下载软件包 1 root@elk01:~# wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.22-amd64.deb 2. 安装ES服务 1 root@elk01:~# dpkg -i elasticsearch-7.17.22-amd64.deb 3. 修改配置文件 1 root@elk01:~# vim /etc/elasticsearch/elasticsearch.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 指定集群的名称，每个ES集群的名称要唯一 cluster.name: dawn # 数据的存储路径 path.data: /var/lib/elasticsearch # 日志的存储路径 path.logs: /var/log/elasticsearch # 指定监听的地址 network.host: 0.0.0.0 # 指定ES集群监听的端口号 http.port: 9200 # 数据传输端口 transport.port: 9300 # 部署集群为单点类型 discovery.type: \u0026#34;single-node\u0026#34; 4. 启动ES服务 1 root@elk01:~# systemctl enable --now elasticsearch.service 5. 验证ES服务是否启动成功 1 2 3 root@elk01:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* 6. 访问ES的WebUI 7. 查看集群的节点信息 1 2 3 root@elk01:~# curl 11.0.1.111:9200/_cat/nodes?v ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 11.0.1.111 58 65 0 2.94 1.36 0.53 cdfhilmrstw * elk01 ElasticSearch单点卸载 1. 停止es服务 1 root@elk01:~# systemctl stop elasticsearch.service 2. 卸载软件 1 2 3 4 5 root@elk01:~# dpkg -r elasticsearch (Reading database ... 76508 files and directories currently installed.) Removing elasticsearch (7.17.22) ... Stopping elasticsearch service... OK Deleting log directory... OK 此步骤只会清理日志文件，卸载脚本，删除ES软件等操作，并不会删除ES的数据。\n3. 清理数据(生产环境不建议做，谨慎，数据需要备份或者迁移) 1 root@elk01:~# rm -rf /var/lib/elasticsearch/* /tmp/* Elasticsearch集群搭建 1. 下载ES软件包 将下载好的软件包拷贝到其他两个节点\n1 2 root@elk01:~# scp elasticsearch-7.17.22-amd64.deb 11.0.1.112:~ root@elk01:~# scp elasticsearch-7.17.22-amd64.deb 11.0.1.113:~ 2. 修改各节点的时区 1 2 3 4 5 6 root@elk01:~# ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \u0026#39;/etc/localtime\u0026#39; -\u0026gt; \u0026#39;/usr/share/zoneinfo/Asia/Shanghai\u0026#39; root@elk02:~# ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \u0026#39;/etc/localtime\u0026#39; -\u0026gt; \u0026#39;/usr/share/zoneinfo/Asia/Shanghai\u0026#39; root@elk03:~# ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \u0026#39;/etc/localtime\u0026#39; -\u0026gt; \u0026#39;/usr/share/zoneinfo/Asia/Shanghai\u0026#39; 3. 安装ES服务 1 2 3 root@elk01:~# dpkg -i elasticsearch-7.17.22-amd64.deb root@elk02:~# dpkg -i elasticsearch-7.17.22-amd64.deb root@elk03:~# dpkg -i elasticsearch-7.17.22-amd64.deb 4. 修改ES集群的配置文件 1 2 3 4 5 6 7 8 9 10 11 [root@elk91 ~]# vim /etc/elasticsearch/elasticsearch.yml cluster.name: dawn path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch network.host: 0.0.0.0 http.port: 9200 transport.port: 9300 # 配置ES集群的服务发现列表主机 discovery.seed_hosts: [\u0026#34;11.0.1.111\u0026#34;,\u0026#34;11.0.1.112\u0026#34;,\u0026#34;11.0.1.113\u0026#34;] # 配置ES集群启动时参与master选举的节点 cluster.initial_master_nodes: [\u0026#34;11.0.1.111\u0026#34;,\u0026#34;11.0.1.112\u0026#34;,\u0026#34;11.0.1.113\u0026#34;] 5. 将配置文件拷贝到其他节点 1 2 3 4 5 6 root@elk01:~# scp /etc/elasticsearch/elasticsearch.yml 11.0.1.112:/etc/elasticsearch/elasticsearch.yml root@11.0.1.112\u0026#39;s password: elasticsearch.yml 100% 3641 4.1MB/s 00:00 root@elk01:~# scp /etc/elasticsearch/elasticsearch.yml 11.0.1.113:/etc/elasticsearch/elasticsearch.yml root@11.0.1.113\u0026#39;s password: elasticsearch.yml 100% 3641 5.0MB/s 00:00 6. 启动ES集群 1 2 3 root@elk01:~# systemctl enable --now elasticsearch.service root@elk02:~# systemctl enable --now elasticsearch.service root@elk03:~# systemctl enable --now elasticsearch.service 7. 检查ES服务端口是否存在 1 2 3 4 5 6 7 8 9 root@elk01:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* root@elk02:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* root@elk03:~# ss -ntl | grep \u0026#34;9[2|3]00\u0026#34; LISTEN 0 4096 *:9200 *:* LISTEN 0 4096 *:9300 *:* 8. 查看集群的节点信息 1 2 3 4 5 6 7 8 9 10 11 root@elk01:~# curl -s 11.0.1.111:9200 | grep cluster_uuid \u0026#34;cluster_uuid\u0026#34; : \u0026#34;JwoG2sV0RuOGHqWYzSWVvw\u0026#34;, root@elk01:~# curl -s 11.0.1.112:9200 | grep cluster_uuid \u0026#34;cluster_uuid\u0026#34; : \u0026#34;JwoG2sV0RuOGHqWYzSWVvw\u0026#34;, root@elk01:~# curl -s 11.0.1.113:9200 | grep cluster_uuid \u0026#34;cluster_uuid\u0026#34; : \u0026#34;JwoG2sV0RuOGHqWYzSWVvw\u0026#34;, root@elk01:~# curl -s 11.0.1.111:9200/_cat/nodes 11.0.1.112 25 96 3 0.00 0.10 0.07 cdfhilmrstw * elk02 11.0.1.113 34 96 2 0.00 0.08 0.06 cdfhilmrstw - elk03 11.0.1.111 36 97 3 0.01 0.04 0.17 cdfhilmrstw - elk01 ","permalink":"https://LMDAWN1127.github.io/posts/elastic-stack/","summary":"从 ElasticStack 组件（Elasticsearch / Logstash / Kibana / Beats）入手，重点记录 Elasticsearch 单点部署与集群搭建：软件包下载、服务安装、elasticsearch.yml 关键配置（集群名、数据与日志路径、监听地址端口、单点/集群模式）、节点时区统一、配置分发与集群启动、端口与节点状态验证。","title":"ElasticStack 架构设计与 Elasticsearch 集群部署"},{"content":"一.数据库服务用户管理 1. 创建数据库用户 用户信息由两部分组成：'用户名'@'主机域/白名单'\n用户名：用户字符串标识； 主机域（白名单）：限制允许从哪些客户端 IP 或主机连接该用户。连接主机的客户端 IP 必须符合白名单，否则拒绝握手。 主机域白名单的编写形式：\n类型 示例 覆盖范围与说明 本地限定 root@localhost 仅允许本机通过本地 Socket 连接登录 单机限定 root@'11.0.1.101' 仅允许指定的某台 IP 主机访问 网段（掩码形式） root@'11.0.1.0/255.255.255.0' 允许 11.0.1.1 ~ 11.0.1.254 网段访问。注意：VLSM 子网掩码需长格式指明 网段（通配符形式） root@'11.0.1.%' 匹配该 C 段所有地址 大网段 root@'11.0.%' 匹配 11.0.0.1 ~ 11.0.255.254 整个 B 段 部分 IP 通配 root@'11.0.1.5%' 匹配 11.0.1.50 ~ 11.0.1.59 任意主机 root@'%' 允许所有外部网络主机访问 1 2 mysql\u0026gt; create user dawn@\u0026#39;11.0.1.%\u0026#39; identified by \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.03 sec) 2. 查询数据库用户信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -- 查询所有用户、主机域及加密认证串 mysql\u0026gt; SELECT user, host, authentication_string FROM mysql.user; +------------------+-----------+------------------------------------------------------------------------+ | user | host | authentication_string | +------------------+-----------+------------------------------------------------------------------------+ | dawn | 11.0.1.% | $A$005$Oe@d?4 6UQC h%/XEVaaydjHRpm98F7Zj0Yxrpgf0oz09INWeSa2l.AN7 | | mysql.infoschema | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.session | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.sys | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | root | localhost | $A$005$iqraslXlVV;\\_ z_@dB52NA2pwaK37yFR1GffitWH9mHsG.mAT36BqSDhzQL0wA | +------------------+-----------+------------------------------------------------------------------------+ 5 rows in set (0.00 sec) -- 查看当前登录会话的用户信息 mysql\u0026gt; SELECT user(); +----------------+ | user() | +----------------+ | root@localhost | +----------------+ 1 row in set (0.00 sec) 3. 修改数据库用户密码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 mysql\u0026gt; ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED BY \u0026#39;000000\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; SELECT user, host, authentication_string FROM mysql.user; +------------------+-----------+------------------------------------------------------------------------+ | user | host | authentication_string | +------------------+-----------+------------------------------------------------------------------------+ | dawn | 11.0.1.% | $A$005$ -4w8;QNe]R\u0026#34;D`S/SUP8EwHTypF11Yu0sZCn.3tVzPbHrnEdFFm7WlZOjWEQ8 | | mysql.infoschema | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.session | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.sys | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | root | localhost | $A$005$iqraslXlVV;\\_ z_@dB52NA2pwaK37yFR1GffitWH9mHsG.mAT36BqSDhzQL0wA | +------------------+-----------+------------------------------------------------------------------------+ 5 rows in set (0.00 sec) 4. 删除数据库用户信息 生产规范：在生产环境中严禁随意直接物理删除用户，建议对不再使用的用户进行账号锁定（Lock）。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 mysql\u0026gt; SELECT user, host, authentication_string FROM mysql.user; +------------------+-----------+------------------------------------------------------------------------+ | user | host | authentication_string | +------------------+-----------+------------------------------------------------------------------------+ | dawn | 11.0.1.% | $A$005$ -4w8;QNe]R\u0026#34;D`S/SUP8EwHTypF11Yu0sZCn.3tVzPbHrnEdFFm7WlZOjWEQ8 | | mysql.infoschema | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.session | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.sys | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | root | localhost | $A$005$iqraslXlVV;\\_ z_@dB52NA2pwaK37yFR1GffitWH9mHsG.mAT36BqSDhzQL0wA | +------------------+-----------+------------------------------------------------------------------------+ 5 rows in set (0.00 sec) mysql\u0026gt; DROP USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; SELECT user, host, authentication_string FROM mysql.user; +------------------+-----------+------------------------------------------------------------------------+ | user | host | authentication_string | +------------------+-----------+------------------------------------------------------------------------+ | mysql.infoschema | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.session | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | mysql.sys | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | | root | localhost | $A$005$iqraslXlVV;\\_ z_@dB52NA2pwaK37yFR1GffitWH9mHsG.mAT36BqSDhzQL0wA | +------------------+-----------+------------------------------------------------------------------------+ 4 rows in set (0.00 sec) 5. 锁定与解锁数据库用户 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 -- 1. 锁定用户 mysql\u0026gt; ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; ACCOUNT LOCK; Query OK, 0 rows affected (0.00 sec) -- 2. 检查锁定状态（account_locked 列：Y 表示锁定，N 表示正常） mysql\u0026gt; SELECT user, host, account_locked FROM mysql.user; +------------------+-----------+----------------+ | user | host | account_locked | +------------------+-----------+----------------+ | dawn | 11.0.1.% | Y | | mysql.infoschema | localhost | Y | | mysql.session | localhost | Y | | mysql.sys | localhost | Y | | root | localhost | N | +------------------+-----------+----------------+ 5 rows in set (0.00 sec) -- 被锁定后登录测试，将报错拒绝访问： [root@master ~]#mysql -h 11.0.1.101 -P 3306 -u dawn -p Enter password: ERROR 3118 (HY000): Access denied for user \u0026#39;dawn\u0026#39;@\u0026#39;master\u0026#39;. Account is locked. -- 3. 解锁用户 [root@master ~]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 11 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; ACCOUNT UNLOCK; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; exit Bye [root@master ~]#mysql -h 11.0.1.101 -P 3306 -u dawn -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 12 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. 6. MySQL 5.7 与 8.0 用户管理核心特性区别 区别一：创建与授权语法解耦 MySQL 5.7 及早期版本：一条 GRANT 命令可同时隐式完成用户创建、密码设置与权限授予：\n1 GRANT ALL ON *.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; MySQL 8.0：系统字典全部改为 InnoDB 引擎管理，权限更严格，必须先 CREATE USER 创建用户，再执行 GRANT 授权；GRANT 命令废弃了 IDENTIFIED BY 子句：\n1 2 CREATE USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; GRANT ALL ON *.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; 区别二：默认密码加密插件变更 早期版本（5.7）：默认使用 mysql_native_password，兼容性好； 新版本（8.0+）：默认采用 caching_sha2_password，安全性更高，但会导致旧版客户端软件（如旧版 Navicat、旧驱动）报错无法登录。 应对方案：\n方案一：将用户或全局加密插件调整回 mysql_native_password 进行向下兼容：\n1 2 3 4 5 -- 方法 1：新建用户时指定兼容插件 CREATE USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED WITH mysql_native_password BY \u0026#39;123456\u0026#39;; -- 方法 2：修改已有用户的加密插件 ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED WITH mysql_native_password BY \u0026#39;123456\u0026#39;; 1 2 3 # 方法 3：修改 /etc/my.cnf 全局默认配置 [mysqld] default_authentication_plugin=mysql_native_password 方案二：升级应用系统的数据库连接驱动版本，全面支持 caching_sha2_password。\n二. 数据库服务权限管理 1. 核心权限与系统视图 查看数据库支持的所有权限类型：\n1 SHOW PRIVILEGES; 序号 输出列名 解释说明 01 Privilege 授权权限名称（如 SELECT, INSERT 等） 02 Context 权限作用的资源范围级别（Databases, Tables, Columns 等） 03 Comment 对该权限实际功能的描述说明 核心关键权限详解：\n序号 权限名称 授权资源 解释说明 01 Select Tables 查询数据表中的数据 02 Insert Tables 插入/写入新数据 03 Update Tables 修改已存在的数据 04 Delete Tables 删除数据记录 05 Alter Tables 修改表结构（添加/修改字段、索引属性等） 06 Index Tables 创建或删除索引 07 Create Databases, Tables 创建新数据库或数据表 08 Drop Databases, Tables 删除数据库或数据表（高危操作） 注：ALL PRIVILEGES 拥有除 GRANT OPTION（给其他用户赋权能力）之外的所有权限；普通管理员切忌赋予 WITH GRANT OPTION。\n权限在元数据表中的存储层级：\n序号 授权表名称 存储范围与级别 01 mysql.user 用户全局级别权限（*.*），最高作用域 02 mysql.db 数据库级别权限（db.*） 03 mysql.tables_priv 数据表级别权限（db.table） 04 mysql.columns_priv 字段列级别权限（细粒度） 2. 用户授权与权限回收实战 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -- 授权基本语法： -- GRANT 权限列表 ON 作用范围 TO \u0026#39;用户\u0026#39;@\u0026#39;主机域\u0026#39;; -- 1. 全局授权（*.*） GRANT ALL ON *.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; -- 2. 库级授权（dawn.*） GRANT SELECT, INSERT, UPDATE, DELETE ON dawn.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; -- 3. 表级授权（dawn.t1） GRANT SELECT ON dawn.t1 TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; -- 4. 查看指定用户的权限 SHOW GRANTS FOR \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; -- 5. 回收权限 REVOKE DELETE ON *.* FROM \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; 用户常规授权实战（最小权限原则）：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -- 业务场景对应权限： -- 浏览页面 -\u0026gt; SELECT -- 发表内容 -\u0026gt; INSERT -- 修改内容 -\u0026gt; UPDATE -- 删除内容 -\u0026gt; DELETE -- 创建独立业务库并按最小权限赋权 mysql\u0026gt; CREATE DATABASE dawn; Query OK, 1 row affected (0.00 sec) mysql\u0026gt; USE dawn; Database changed mysql\u0026gt; CREATE TABLE t1 ( -\u0026gt; id INT PRIMARY KEY AUTO_INCREMENT, -\u0026gt; name VARCHAR(50) -\u0026gt; ); Query OK, 0 rows affected (0.03 sec) mysql\u0026gt; create user dawn@\u0026#39;11.0.1.%\u0026#39; identified by \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; GRANT SELECT, INSERT, UPDATE, DELETE ON dawn.* TO \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39;; Query OK, 0 rows affected (0.00 sec) 3. MySQL 8.0 新特性：Role（角色）管理 8.0 引入了角色（Role）机制，允许将一组常用权限打包为一个逻辑角色实体，批量赋予多个用户，极大简化了用户权限维护成本。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 -- 1. 创建角色（可创建读写角色与只读角色） mysql\u0026gt; CREATE ROLE \u0026#39;dawn_rw\u0026#39;, \u0026#39;dawn_r\u0026#39;; Query OK, 0 rows affected (0.00 sec) -- 2. 为角色分配权限 mysql\u0026gt; GRANT SELECT ON dawn.* TO \u0026#39;dawn_r\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; GRANT SELECT, UPDATE, INSERT, DELETE ON dawn.* TO \u0026#39;dawn_rw\u0026#39;; Query OK, 0 rows affected (0.00 sec) -- 3. 创建测试用户并将角色赋予用户 mysql\u0026gt; CREATE USER \u0026#39;user01\u0026#39;@\u0026#39;%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; CREATE USER \u0026#39;user02\u0026#39;@\u0026#39;%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; GRANT \u0026#39;dawn_r\u0026#39; TO \u0026#39;user01\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; GRANT \u0026#39;dawn_rw\u0026#39; TO \u0026#39;user02\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) -- 4. 激活角色 -- 方式一：管理员指定默认生效角色 mysql\u0026gt; SET DEFAULT ROLE ALL TO \u0026#39;user01\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; SET DEFAULT ROLE ALL TO \u0026#39;user02\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) -- 方式二：全局配置自动激活角色（登录时生效） SET GLOBAL activate_all_roles_on_login = ON; -- 5. 验证角色与权限拦截 -- 以 user01 登录后查看激活角色： mysql\u0026gt; SELECT CURRENT_USER(), CURRENT_ROLE(); +----------------+----------------+ | CURRENT_USER() | CURRENT_ROLE() | +----------------+----------------+ | user01@% | `dawn_r`@`%` | +----------------+----------------+ 1 row in set (0.00 sec) -- 此时 user01 执行 INSERT 操作将被严格拦截： mysql\u0026gt; USE dawn; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql\u0026gt; SHOW TABLES; +----------------+ | Tables_in_dawn | +----------------+ | t1 | +----------------+ 1 row in set (0.00 sec) mysql\u0026gt; SELECT * FROM t1; Empty set (0.00 sec) mysql\u0026gt; INSERT INTO t1 VALUES (1); ERROR 1142 (42000): INSERT command denied to user \u0026#39;user01\u0026#39;@\u0026#39;master\u0026#39; for table \u0026#39;t1\u0026#39; 三. 数据库服务连接管理 方式一：内置客户端命令行连接 本地 Socket 方式连接：\n仅适用于本机访问，通过本地 Unix Domain Socket 文件（如 /tmp/mysql.sock）直接通信，速度极快；\n命令：mysql -uroot -p123456 -S /tmp/mysql.sock\nTCP/IP 网络方式连接：\n适用于远程或跨主机访问，需显式指明 -h IP 与 -P 端口；\n命令：mysql -uroot -p123456 -h 11.0.1.101 -P 3306\n方式二：远程图形化工具管理（DBeaver） 利用DBeaver等远程管理客户端，可视化操作与维护数据库对象。\nDBeaver基础管理流程：\n新建 MySQL 连接与连接测试： 输入主机 IP、端口、用户名与密码，点击测试连接确保网络互通。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 这个 Public Key Retrieval is not allowed 错误是 DBeaver 连接 MySQL 8.0+ 时很常见的问题，通常是因为 MySQL 8.0 默认使用了 caching_sha2_password 认证插件，而 JDBC 驱动出于安全考虑，默认禁止从服务器获取公钥。 解决方案：在 DBeaver 中修改驱动属性 最直接的方法是修改 DBeaver 的驱动属性，在连接配置中允许公钥检索。 打开连接设置：在 DBeaver 左侧的“数据库导航器”中，右键点击你的 dawn 连接，选择 “编辑连接”。 进入驱动属性：在弹出的设置窗口中，切换到 “驱动属性” 选项卡。 添加属性：在属性列表中，右键选择 “添加新属性”，或直接在下方空白处双击添加。 设置参数：添加以下两个属性： 属性名：allowPublicKeyRetrieval，值：true。 属性名：useSSL，值：false（如果你的网络环境可信，这是为了快速连接；如果生产环境需要加密，请保持 true 并配置好 SSL 证书）。 保存并测试：点击 “确定” 保存设置，然后再次点击 “测试连接”，应该就能成功了。 备选方案：修改 MySQL 用户认证方式 如果不想在客户端修改参数，也可以从服务端入手，将用户的认证插件改为 mysql_native_password。 ALTER USER \u0026#39;dawn\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED WITH mysql_native_password BY \u0026#39;123456\u0026#39;; FLUSH PRIVILEGES; 执行后，DBeaver 端可以不用修改 allowPublicKeyRetrieval 属性，直接连接即可。 # 安全提示 开启 allowPublicKeyRetrieval=true 会存在中间人攻击（MITM）的风险，因为客户端会通过不安全的连接向服务器索取公钥。因此，该设置仅建议在受信任的本地或内网开发环境中使用。在生产环境中，更安全的做法是配置 SSL/TLS 加密连接，或者使用 mysql_native_password 认证插件。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 mysql\u0026gt; CREATE USER \u0026#39;root\u0026#39;@\u0026#39;11.0.1.%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; GRANT ALL PRIVILEGES ON *.* TO \u0026#39;root\u0026#39;@\u0026#39;11.0.1.%\u0026#39; WITH GRANT OPTION; Query OK, 0 rows affected (0.02 sec) mysql\u0026gt; FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; exit Bye [root@master ~]#mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 15 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; SELECT user, host FROM mysql.user WHERE user=\u0026#39;root\u0026#39;; +------+-----------+ | user | host | +------+-----------+ | root | 11.0.1.% | | root | localhost | +------+-----------+ 2 rows in set (0.00 sec) 图形化界面与创建数据库： 选择连接，右键新建数据库，指定数据库名称、字符集（推荐 utf8mb4）与排序规则（utf8mb4_general_ci 或 utf8mb4_bin）。 创建数据表与表结构设计： 设置字段名、数据类型、长度、是否为主键、自增属性等。 插入与维护数据项： 方式三：程序代码连接数据库 应用程序通过特定编程语言的数据库驱动接口与 MySQL 服务端通信。\n序号 开发语言 常见连接数据库驱动程序 01 Python pymysql、mysql-connector-python 02 Go (Golang) go-sql-driver/mysql 03 Java mysql-connector-java (JAR 包 / JDBC) 04 PHP mysqli、pdo_mysql 驱动官方下载地址：https://dev.mysql.com/downloads/\n四. 数据库服务初始配置 初始化配置方式一：源码编译时指定参数 在源码编译阶段通过 CMake 参数对数据库功能进行深度固化定制：\n1 2 3 4 5 6 7 8 9 10 11 # 源码依赖准备 yum -y install wget cmake gcc gcc-c++ ncurses ncurses-devel libaio-devel openssl openssl-devel # 下载与解压带 boost 的源码包 tar zxf mysql-boost-8.0.12.tar.gz -C /usr/local/ # CMake 编译参数配置 cd /usr/local/src/mysql-8.0.12 cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql8.0 -DMYSQL_DATADIR=/usr/local/mysql/data -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/usr/local/mysql-8.0.12/ -DSYSCONFDIR=/etc -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_EMBEDDED_SERVER=1 -DENABLE_DTRACE=0 -DENABLED_LOCAL_INFILE=1 -DDEFAULT_CHARSET=utf8mb4 make -j$(nproc) \u0026amp;\u0026amp; make install 初始化配置方式二：编写与修改 /etc/my.cnf 配置文件 配置文件读取加载顺序：\n通过 mysqld --help --verbose | grep my.cnf 可查看配置文件的搜索顺序：\n1 /etc/my.cnf -\u0026gt; /etc/mysql/my.cnf -\u0026gt; /usr/local/mysql/etc/my.cnf -\u0026gt; ~/.my.cnf 注：若同一参数在多个配置文件中出现，后加载的配置会覆盖先加载的配置。若启动时使用 --defaults-file=/path/to/my.cnf，则只加载该指定文件。\n配置文件结构规范：\n带 [] 部分：配置标签区（Group），区分服务端与客户端生效作用域： 服务端标签：[mysqld]、[mysqld_safe]、[server] 客户端标签：[mysql]、[mysqldump]、[client]（只影响本地客户端连接） 不带 [] 部分：具体键值对参数（key=value） 核心常用配置参数详解：\n标签 参数配置项 解释说明 [mysqld] user=mysql 守护进程的系统运行用户 [mysqld] basedir=/usr/local/mysql MySQL 软件程序根目录 [mysqld] datadir=/data/3306/data 数据文件持久化存放路径 [mysqld] server_id=6 数据库实例全局唯一标识节点编号（主从复制集群必须唯一） [mysqld] port=3306 服务端监听网络端口 [mysqld] socket=/tmp/mysql.sock 本地通信 Unix Socket 文件路径 [mysqld] default_authentication_plugin=mysql_native_password 指定默认认证插件（用于向下兼容旧客户端） [mysql] socket=/tmp/mysql.sock 本地客户端连接使用的 Socket 文件路径 初始化配置方式三：启动命令行传参 启动命令所附带的参数（如 mysqld_safe --skip-grant-tables --skip-networking \u0026amp;）具备最高优先级，会直接覆盖配置文件中的预设值。\n","permalink":"https://LMDAWN1127.github.io/posts/mysql-database-basic-management/","summary":"围绕 MySQL 数据库服务的基础管理展开：用户的创建/查询/改密/删除/锁定，权限的授予与回收及 Role 角色，内置客户端/DBeaver/程序三种连接方式，以及源码编译、my.cnf、启动传参三种初始化配置，并对比 MySQL 5.7 与 8.0 的用户管理差异。","title":"MySQL 数据库基础管理：用户·权限·连接与初始化配置"},{"content":"1.分布式构建介绍 如果项目需要定期集成，同时每次集成都需要较长时间。如果都运行在master服务器上，会消耗过多资源，导致其他项目搁置无法集成，这时就需要在建立多台设备，并配置作为slave机器来为master提供负载服务。\n2.部署分布式服务器 找一台或者目前已有的服务器配置为slave端（当前使用nexus服务器作为slave测试）\n在nexus上安装java jdk git和nexus客户端（如已安装则略过）\n1 [root@nexus ~]# yum -y install java git 复制jenkins上已配置好的客户端到此服务器sonar和maven\njenkins服务器操作\n1 2 3 4 5 6 7 8 9 10 11 # 优化java.sh脚本 # 原来 code_dir=\u0026#39;/var/lib/jenkins/workspace/java_job/target/*.war\u0026#39; # 改成 code_dir=\u0026#34;$WORKSPACE/target/*.war\u0026#34; [root@jenkins ~]#scp -r /usr/local/sonar 11.0.1.203:/usr/local/ [root@jenkins ~]#scp -r /usr/local/maven/ 11.0.1.203:/usr/local/ [root@nexus ~]#mkdir /server/scripts -p [root@jenkins ~]#scp /server/scripts/* 11.0.1.203:/server/scripts/ [root@jenkins ~]#ssh-copy-id -i .ssh/id_rsa.pub 11.0.1.203 把从节点的公钥上传至gitlab 拥有下载代码的权限\n1 2 3 [root@nexus ~]#ssh-keygen [root@nexus ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzCEs3V2MqRK8BO/ewSoUcjXTkdEj/c1vBz8UOLYgkKK+Jh/MvV71nsCeohy5slQ2phTE6DXFAwfm54FBDL2Wy4THL3tGmZH0sLHa0xg3d8MLjknXWVqizyX8V0SxALcJ9hTj1v0BlB8F1U60DCKqoScezJ7ZDKtbSS2Q4niARjSGxlcAYxLgb3mUr0BX+HHqtiBw+lGW2Q/wRMcCue0WFmYLN9jxiOxNIJ4xPQ6G6gHzpHsGJYyhIM/SKctbGMpqgTvideHqOzThYQGaiLo9DFu7p9QSc3wAHRxE8kyZlmNKEzknebFx+pbQwMzZ7Za0I5c1PTHPmPqhuDOGaXUBZ root@nexus 点击系统管理 -\u0026gt; 节点管理 -\u0026gt; 新建节点\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [root@jenkins ~]#cat .ssh/id_rsa -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAvpGo/zRMBymiBKzBApVWLhtyblq/Qg+16bjuWGeiX7TJjXQD zK70tJlqj+Eg8k+v5XtI9URpAOPBrIqxEnzKlyr80kneQLoSD5lAA7RjzSulUnoj CsqFoIjQRS5+kSDgHFyNDLma2EDCyc/pDmK+pBfXpv+jHxUKy5LfQ8yJZ0fW+wjC 8g5zzmfBRuuNA6ql9LGfBvotr+NVV/3O0RiFgIBISWPpIjEuXA8VpU39pPIaGZod FSGvxvQMJ725JclIhOAAnfRrRCRucVyc3nZ+6tP2IH69uk9vbfikk90pcq1yGhGx AuVsCDMpghCQwjzwhs6i47FmvuHWyYaBDNWn1QIDAQABAoIBABpNFirqt9eBOng0 s4q8u6EvbpKmeaEPdAbm3BZdbz6QQAUCzwdv2hwW1Gs5IgfvUZJ1enNHVsqlpHRA zAGGMZu9dhk70q3SQG3gs8IPInsNnAqPCskRfHHUkBIMQbGb/PJNH7Zy41kXTwr1 Y+/1VuRk2gGVZJkEmXHROnksAIa9WYhr9BD8p+hDjjmiaK8uD2E/mIIWhnWWuxUd fsrlVnu0Vtdwei5wvvCTwguaJMeMsgZvVlu4rt6HMdmS1KCVJYKNBCtQ6BCcmWHt 3G3Lz23S5LibvoJFRMzIrwQop6nFkm7BErYTJf/UBWwOOVPzXCIFNsQFpm1l/DwQ /G1skF0CgYEA32q6BSwSIOquH8gxN+HApznPv6U1U05V0ZatZA/hGv0Bf658NsEm URO4VcEI2xKOhr8BoNFWvt0eTCiM3kKB4ZoBrggVulfxH+9R8m1RHRNOXGm8nfe7 UOTwtSrQwQBdUlI82sHnsS+Dh3+dGFpDCF6DXVElU6RDMbFmI7vy/psCgYEA2lyO WjvRj6FBuc1HYqUFPCN9milx93HoEKbHJoFcgq7jf4++o9VtF9UKkZsvwWqjYees 7QziRjZkf8/2WOF5U2WhtKVK96MTPVdpGj+Nchkecvs1vPuAiBqNDK4Wr979VRDW sRaDLRPlLMbTqUCH3zswyUmghmFkxpp7xN1hok8CgYEAqJ0OuVqSb/9N3LSk2Kzr TgnmjZes0Ct9v0k9oRgO5uRtniRcoKJTPbtpUmb2PQgbQvyiyPFUy4VsGUQ9VHzT MyWdAPAfHdzDITvoF9OC9TOd30b50CqoVxwBwmkG1bJlu/xO6zaOmv3mAXxBXqC4 mHdkIyhbyG1fPQDKuktdz+MCgYEApBJll+FYjd5xv0VCPRmXfF+CccJwcqfw4goe m40Z0MvN/rwoV3v3M7AM9+kuEdupldf5NEi5AF7AVtUQ2inY+tPjlBOa58VtoGq8 e0+DXVcIbQbakvw60Il6sCtiupUHrtIvLvKmb8mCKone4k6tiQs1lhkvd6lYim3r T7m0MbsCgYALDbkebVxvLgEdRc4F4IIkM5wYhEurR//kZqJXDHQCrzeUKKXMnXm5 rSQVgN9RrOF7W6Fbcwa0WljA77moNLfqh9o8AGPybCGLigiNJyjH+ZQa8Kydsyn+ d+B6pUXKtJO6lPaz9/t+N3YNqQgL9zs3Bd5Nris7YsgPBJhiszy3GQ== -----END RSA PRIVATE KEY----- 这里由于agent端Java版本太老所以导致代理启动失败\n1 2 3 4 5 6 7 8 # 在11.0.1.203上面装Java 11 [root@nexus ~]#yum install -y java-11-openjdk [root@nexus ~]#alternatives --install /usr/bin/java java /usr/lib/jvm/jre-11/bin/java 200000 [root@nexus ~]#alternatives --set java /usr/lib/jvm/jre-11/bin/java [root@nexus ~]#java -version openjdk version \u0026#34;11.0.23\u0026#34; 2024-04-16 LTS OpenJDK Runtime Environment (Red_Hat-11.0.23.0.9-2.el7_9) (build 11.0.23+9-LTS) OpenJDK 64-Bit Server VM (Red_Hat-11.0.23.0.9-2.el7_9) (build 11.0.23+9-LTS, mixed mode, sharing) 在JenkinsUI给节点指定用Java 11启动： 节点列表 → node1-11.0.1.203 → 配置 → 展开 高级 → JavaPath\n启动成功\n从节点的公钥复制给web01和web02，实现免密\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@nexus ~]#ssh-copy-id -i .ssh/id_rsa.pub 11.0.1.8 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: \u0026#34;.ssh/id_rsa.pub\u0026#34; The authenticity of host \u0026#39;11.0.1.8 (11.0.1.8)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? 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@11.0.1.8\u0026#39;s password: Number of key(s) added: 1 Now try logging into the machine, with: \u0026#34;ssh \u0026#39;11.0.1.8\u0026#39;\u0026#34; and check to make sure that only the key(s) you wanted were added. [root@nexus ~]#ssh-copy-id -i .ssh/id_rsa.pub 11.0.1.7 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: \u0026#34;.ssh/id_rsa.pub\u0026#34; The authenticity of host \u0026#39;11.0.1.7 (11.0.1.7)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? 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@11.0.1.7\u0026#39;s password: Number of key(s) added: 1 Now try logging into the machine, with: \u0026#34;ssh \u0026#39;11.0.1.7\u0026#39;\u0026#34; and check to make sure that only the key(s) you wanted were added. 手动做一次git克隆yes验证\n1 [root@nexus ~]#git clone git@11.0.1.200:dawn/java.git 将master节点修改成以下用法，这样构建的时候就不会调度到master节点上了，然后测试一下slave可用性\n测试构建成功\n将master节点配置修改回来，同时都修改并发为2\n构建执行状态显示master和node1同时执行\n","permalink":"https://LMDAWN1127.github.io/posts/jenkins-distributed-build/","summary":"记录将 nexus 服务器配置为 Jenkins Slave 节点的完整过程：拷贝 sonar/maven 工具链、建立 SSH 信任、处理 agent 端 Java 版本过旧导致启动失败的坑，以及在 Jenkins UI 指定 JavaPath、调整 master 并发调度实现 master/slave 并行构建。","title":"Jenkins 分布式构建与 Slave 节点配置"},{"content":"1.Maven介绍 Maven是一个项目管理和综合工具。Maven提供给开发人员构建一个完整的生命周期框架。 开发团队可以自动完成该项目的基础设施建设，Maven使用标准的目录结构和默认构建生命周期。 Apache的开源项目主要服务于JAVA平台的构建、依赖管理、项目管理。 Project Object Model，项目对象模型。通过xml格式保存的pom.xml文件。该文件用于管理：源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的url、项目的依赖关系等等。该文件是由开发维护，我们运维人员可以不用去关心。\n2.Maven安装 下载Maven安装包\n官网：http://maven.apache.org/download.cgi\n清华镜像：https://mirrors.tuna.tsinghua.edu.cn/apache/maven/\n安装Maven\n1 2 3 4 5 6 7 8 9 10 [root@jenkins ~]#cd /usr/local/ [root@jenkins local]#tar xf apache-maven-3.3.9-bin.tar.gz [root@jenkins local]#ln -s /usr/local/apache-maven-3.3.9/ /usr/local/maven [root@jenkins local]#/usr/local/maven/bin/mvn -v Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00) Maven home: /usr/local/maven Java version: 17.0.11, vendor: Oracle Corporation Java home: /usr/lib/jvm/jdk-17-oracle-x64 Default locale: en_US, platform encoding: UTF-8 OS name: \u0026#34;linux\u0026#34;, version: \u0026#34;3.10.0-1160.el7.x86_64\u0026#34;, arch: \u0026#34;amd64\u0026#34;, family: \u0026#34;unix\u0026#34; 3.编译部署java项目 1 2 3 4 5 6 7 8 9 [root@jenkins local]#vim /etc/profile export PATH=\u0026#34;/usr/local/apache-maven-3.3.9/bin/:$PATH\u0026#34; [root@jenkins local]#mvn -v Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00) Maven home: /usr/local/apache-maven-3.3.9 Java version: 17.0.11, vendor: Oracle Corporation Java home: /usr/lib/jvm/jdk-17-oracle-x64 Default locale: en_US, platform encoding: UTF-8 OS name: \u0026#34;linux\u0026#34;, version: \u0026#34;3.10.0-1160.el7.x86_64\u0026#34;, arch: \u0026#34;amd64\u0026#34;, family: \u0026#34;unix\u0026#34; 上传一个简单的java项目包 hello-world.tar.gz 进行解压\n1 [root@jenkins ~]#tar -xf hello-world.tar.gz 进入目录执行打包命令\n1 2 3 4 5 [root@jenkins ~]#cd hello-world-war/ [root@jenkins hello-world-war]#mvn package [root@jenkins hello-world-war]#cd target/ [root@jenkins target]#ls hello-world-war-1.0.0 hello-world-war-1.0.0.war maven-archiver 启动web02,和tomcat服务，将默认页删除\n1 2 3 4 5 6 7 8 9 10 [root@web02 ~]#/usr/local/tomcat/bin/startup.sh Using CATALINA_BASE: /usr/local/tomcat Using CATALINA_HOME: /usr/local/tomcat Using CATALINA_TMPDIR: /usr/local/tomcat/temp Using JRE_HOME: /usr Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar Using CATALINA_OPTS: Tomcat started. [root@web02 ~]#cd /usr/local/tomcat/webapps/ROOT [root@web02 ROOT]#rm -fr * 将编译的hello-world-war-1.0.0.war放到/usr/local/tomcat/webapps/ROOT目录下并解压\n1 2 3 [root@jenkins target]#scp hello-world-war-1.0.0.war 11.0.1.8:/usr/local/tomcat/webapps/ROOT/ [root@web02 ROOT]#yum install -y unzip [root@web02 ROOT]#unzip hello-world-war-1.0.0.war 测试没问题\nMaven 生命周期与常用命令说明\nvalidate（验证）：验证项目正确，并且所有必要信息可用。 compile（编译）：编译项目源码。 test（测试）：使用合适的单元测试框架测试编译后的源码。 package（打包）：源码编译之后，使用合适的格式（例如JAR格式）对编译后的源码进行打包。 integration-test（集成测试）：如果有需要，把包处理并部署到可以运行集成测试的环境中去。 verify（验证）：进行各种测试来验证包是否有效并且符合质量标准。 install（安装）：把包安装到本地仓库，使该包可以作为其他本地项目的依赖。 deploy（部署）：在集成或发布环境中完成，将最终软件包复制到远程存储库，以与其他开发人员和项目共享。 常用操作：\n1 2 3 4 5 6 7 8 9 10 11 mvn clean # 清除上次编译的结果 mvn package -Dmaven.test.skip=true # 跳过测试用例 mvn package # 会去maven的中央仓库去下载需要的依赖包和插件到.m2目录下 # 国外仓库慢，可以修改为国内的仓库，比如阿里云 [root@jenkins ~]#vim /usr/local/maven/conf/settings.xml 159 \u0026lt;mirror\u0026gt; 160 \u0026lt;id\u0026gt;nexus-aliyun\u0026lt;/id\u0026gt; 161 \u0026lt;mirrorOf\u0026gt;*\u0026lt;/mirrorOf\u0026gt; 162 \u0026lt;name\u0026gt;Nexus aliyun\u0026lt;/name\u0026gt; 163 \u0026lt;url\u0026gt;http://maven.aliyun.com/nexus/content/groups/public\u0026lt;/url\u0026gt; 164 \u0026lt;/mirror\u0026gt; 4.推送项目至gitlab仓库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 # 清除上次编译的结果 [root@jenkins hello-world-war]#mvn clean [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Hello World Web Application Repository 1.0.0 [INFO] ------------------------------------------------------------------------ Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom (4 KB at 2.4 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at 28.0 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.jar (25 KB at 40.6 KB/sec) [INFO] [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world-war --- Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.pom Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.pom (4 KB at 8.5 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/sonatype/spice/spice-parent/16/spice-parent-16.pom Downloaded: https://repo.maven.apache.org/maven2/org/sonatype/spice/spice-parent/16/spice-parent-16.pom (9 KB at 14.0 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.jar Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.jar (221 KB at 212.1 KB/sec) [INFO] Deleting /root/hello-world-war/target [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5.242 s [INFO] Finished at: 2026-10-12T12:45:21+08:00 [INFO] Final Memory: 9M/29M [INFO] ------------------------------------------------------------------------ # 删除本地仓库的默认远程仓库配置 [root@jenkins hello-world-war]#git remote -v origin git@10.0.0.200:test/hello-world.git (fetch) origin git@10.0.0.200:test/hello-world.git (push) [root@jenkins hello-world-war]#git remote remove origin [root@jenkins hello-world-war]#git remote -v # 提交代码到本地仓库，保证工作区干净 [root@jenkins hello-world-war]#git status # On branch master # Changes not staged for commit: # (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to update what will be committed) # (use \u0026#34;git checkout -- \u0026lt;file\u0026gt;...\u0026#34; to discard changes in working directory) # # modified: pom.xml # modified: src/main/webapp/index.jsp # no changes added to commit (use \u0026#34;git add\u0026#34; and/or \u0026#34;git commit -a\u0026#34;) [root@jenkins hello-world-war]#git add . [root@jenkins hello-world-war]#git config --global user.email \u0026#34;jenkins@example.com\u0026#34; [root@jenkins hello-world-war]#git config --global user.name \u0026#34;jenkins\u0026#34; [root@jenkins hello-world-war]#git commit -m \u0026#34;1\u0026#34; [master fc309ae] 1 2 files changed, 3 insertions(+), 3 deletions(-) [root@jenkins hello-world-war]#git status # On branch master nothing to commit, working directory clean 创建新的项目\n配置远程仓库并推送\n1 2 3 4 5 6 7 8 9 10 11 12 [root@jenkins hello-world-war]#git remote add origin git@11.0.1.200:dawn/java.git [root@jenkins hello-world-war]#git remote -v origin git@11.0.1.200:dawn/java.git (fetch) origin git@11.0.1.200:dawn/java.git (push) [root@jenkins hello-world-war]#git push -u origin master Counting objects: 44, done. Compressing objects: 100% (29/29), done. Writing objects: 100% (44/44), 4.90 KiB | 0 bytes/s, done. Total 44 (delta 11), reused 0 (delta 0) To git@11.0.1.200:dawn/java.git * [new branch] master -\u0026gt; master Branch master set up to track remote branch master from origin. 5.jenkins集成Maven 构建测试，编译成功\n编写配置shell脚本\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 [root@jenkins ~]#vim /server/scripts/java.sh #!/bin/bash code_dir=\u0026#39;/var/lib/jenkins/workspace/java_job/target/*.war\u0026#39; IP=11.0.1.8 DIR=/usr/local/tomcat/webapps SCP(){ scp $code_dir $IP:$DIR } # 清理上一次残留的 war 源包与历史 java_* 版本目录 CLEAN(){ ssh $IP \u0026#34;rm -f $DIR/*.war; rm -rf $DIR/java_*\u0026#34; } MK(){ ssh $IP \u0026#34;mkdir -p $DIR/java_$BUILD_ID\u0026#34; } XF(){ ssh $IP \u0026#34;unzip -o $DIR/*.war -d $DIR/java_$BUILD_ID\u0026#34; } # 解压完成后删除 webapps 下的 war 源包，避免 Tomcat 自动部署出多余上下文 DELWAR(){ ssh $IP \u0026#34;rm -f $DIR/*.war\u0026#34; } RM(){ ssh $IP \u0026#34;rm -rf $DIR/ROOT\u0026#34; } LN(){ ssh $IP \u0026#34;ln -sfn $DIR/java_$BUILD_ID $DIR/ROOT\u0026#34; } # 重启 Tomcat 使新 ROOT 生效 RESTART(){ ssh $IP \u0026#34;/usr/local/tomcat/bin/shutdown.sh; sleep 3; /usr/local/tomcat/bin/startup.sh\u0026#34; } main(){ CLEAN SCP MK XF DELWAR RM LN RESTART } main jenkins对web02做免密\n1 [root@jenkins ~]#ssh-copy-id root@11.0.1.8 将web02的/usr/local/tomcat/webapps/ROOT/清空，方便查看后面的构建测试效果\n1 2 3 4 [root@web02 ~]#cd /usr/local/tomcat/webapps/ROOT/ [root@web02 ROOT]#ls hello-world-war-1.0.0.war index.jsp META-INF WEB-INF [root@web02 ROOT]#rm -fr * 构建测试成功\n6. 配置web-hook 开启触发器\n生成token并保存\n复制Token配置到Gitlab\n配置完成\n测试: 修改代码推送到gitlab仓库的java项目中,会自动触发Jenkins拉取代码并将拉取到的修改后的代码推送到web02服务器\n修改前：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@jenkins hello-world-war]#vim src/main/webapp/index.jsp \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Hello World!\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello World! v1.0\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt; It is now \u0026lt;%= new java.util.Date() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; You are coming from \u0026lt;%= request.getRemoteAddr() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; 修改后：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@jenkins hello-world-war]#vim src/main/webapp/index.jsp \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Hello World!\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello World! v1.1\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt; It is now \u0026lt;%= new java.util.Date() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; You are coming from \u0026lt;%= request.getRemoteAddr() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; 推送代码，自动触发构建\n1 2 3 4 5 6 7 8 9 10 11 [root@jenkins hello-world-war]#git commit -am \u0026#34;v1.1\u0026#34; [master d15a2eb] v1.1 1 file changed, 1 insertion(+), 1 deletion(-) [root@jenkins hello-world-war]#git push -u origin master Counting objects: 11, done. Compressing objects: 100% (4/4), done. Writing objects: 100% (6/6), 442 bytes | 0 bytes/s, done. Total 6 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/java.git fc309ae..d15a2eb master -\u0026gt; master Branch master set up to track remote branch master from origin. 7.Jenkins集成SonarQube jenkins上配置SonarQube服务端\n将Build Steps和执行shell调换位置，Build Steps在上面，执行shell在下面\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@jenkins hello-world-war]#vim src/main/webapp/index.jsp \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Hello World!\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello World! v2.2\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt; It is now \u0026lt;%= new java.util.Date() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; You are coming from \u0026lt;%= request.getRemoteAddr() %\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; [root@jenkins hello-world-war]#git commit -am \u0026#34;v2.2\u0026#34; [master d0ebc06] v2.2 1 file changed, 1 insertion(+), 1 deletion(-) [root@jenkins hello-world-war]#git push -u origin master Counting objects: 17, done. Compressing objects: 100% (8/8), done. Writing objects: 100% (12/12), 849 bytes | 0 bytes/s, done. Total 12 (delta 4), reused 0 (delta 0) To git@11.0.1.200:dawn/java.git 6b4a15b..d0ebc06 master -\u0026gt; master Branch master set up to track remote branch master from origin. 自动构建推送成功\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 # 构建过程 Started by GitLab push by Administrator Running as SYSTEM Building in workspace /var/lib/jenkins/workspace/java_job The recommended git tool is: NONE No credentials specified \u0026gt; git rev-parse --resolve-git-dir /var/lib/jenkins/workspace/java_job/.git # timeout=10 Fetching changes from the remote Git repository \u0026gt; git config remote.origin.url git@11.0.1.200:dawn/java.git # timeout=10 Fetching upstream changes from git@11.0.1.200:dawn/java.git \u0026gt; git --version # timeout=10 \u0026gt; git --version # \u0026#39;git version 1.8.3.1\u0026#39; \u0026gt; git fetch --tags --progress git@11.0.1.200:dawn/java.git +refs/heads/*:refs/remotes/origin/* # timeout=10 \u0026gt; git rev-parse remotes/origin/master^{commit} # timeout=10 \u0026gt; git branch -a -v --no-abbrev --contains d0ebc065fd6d63fb107c9ddc7c0e5305f840099e # timeout=10 Checking out Revision d0ebc065fd6d63fb107c9ddc7c0e5305f840099e (origin/master) \u0026gt; git config core.sparsecheckout # timeout=10 \u0026gt; git checkout -f d0ebc065fd6d63fb107c9ddc7c0e5305f840099e # timeout=10 Commit message: \u0026#34;v2.2\u0026#34; \u0026gt; git rev-list --no-walk 6b4a15b008c56778e80256b0131a6f89f4a5d433 # timeout=10 Parsing POMs Established TCP socket on 33973 [java_job] $ java -cp /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven33-agent-1.14.jar:/usr/local/maven/boot/plexus-classworlds-2.5.2.jar:/usr/local/maven/conf/logging jenkins.maven3.agent.Maven33Main /usr/local/maven/ /var/lib/jenkins/%C/jenkins/war/WEB-INF/lib/remoting-3107.v665000b_51092.jar /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven33-interceptor-1.14.jar /var/lib/jenkins/plugins/maven-plugin/WEB-INF/lib/maven3-interceptor-commons-1.14.jar 33973 \u0026lt;===[JENKINS REMOTING CAPACITY]===\u0026gt;channel started Executing Maven: -B -f /var/lib/jenkins/workspace/java_job/pom.xml clean package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Hello World Web Application Repository 1.0.0 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world-war --- [INFO] Deleting /var/lib/jenkins/workspace/java_job/target [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world-war --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /var/lib/jenkins/workspace/java_job/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world-war --- [INFO] No sources to compile [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world-war --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /var/lib/jenkins/workspace/java_job/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world-war --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world-war --- [INFO] No tests to run. [JENKINS] Recording test results [INFO] [INFO] --- maven-war-plugin:3.3.1:war (default-war) @ hello-world-war --- [INFO] Packaging webapp [INFO] Assembling webapp [hello-world-war] in [/var/lib/jenkins/workspace/java_job/target/hello-world-war-1.0.0] [INFO] Processing war project [INFO] Copying webapp resources [/var/lib/jenkins/workspace/java_job/src/main/webapp] [INFO] Building war: /var/lib/jenkins/workspace/java_job/target/hello-world-war-1.0.0.war [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.040 s [INFO] Finished at: 2026-10-12T15:53:20+08:00 [INFO] Final Memory: 16M/85M [INFO] ------------------------------------------------------------------------ Waiting for Jenkins to finish collecting data [JENKINS] Archiving /var/lib/jenkins/workspace/java_job/pom.xml to com.efsavage/hello-world-war/1.0.0/hello-world-war-1.0.0.pom [JENKINS] Archiving /var/lib/jenkins/workspace/java_job/target/hello-world-war-1.0.0.war to com.efsavage/hello-world-war/1.0.0/hello-world-war-1.0.0.war channel stopped [java_job] $ /usr/local/sonar/bin/sonar-scanner -Dsonar.host.url=http://11.0.1.202:9000/ ******** -Dsonar.projectName=java_job -Dsonar.projectKey=java -Dsonar.sources=. -Dsonar.projectBaseDir=/var/lib/jenkins/workspace/java_job INFO: Scanner configuration file: /usr/local/sonar-scanner-4.2.0.1873-linux/conf/sonar-scanner.properties INFO: Project root configuration file: NONE INFO: SonarQube Scanner 4.2.0.1873 INFO: Java 11.0.3 AdoptOpenJDK (64-bit) INFO: Linux 3.10.0-1160.el7.x86_64 amd64 INFO: User cache: /root/.sonar/cache INFO: SonarQube server 7.0.0 INFO: Default locale: \u0026#34;en_US\u0026#34;, source code encoding: \u0026#34;UTF-8\u0026#34; INFO: Publish mode INFO: Load global settings INFO: Load global settings (done) | time=61ms INFO: Server id: AaD4EZTNcTUVzuJgme-j INFO: User cache: /root/.sonar/cache INFO: Load plugins index INFO: Load plugins index (done) | time=24ms INFO: Load/download plugins INFO: Plugin [l10nzh] defines \u0026#39;l10nen\u0026#39; as base plugin. This metadata can be removed from manifest of l10n plugins since version 5.2. INFO: Load/download plugins (done) | time=19ms INFO: Process project properties INFO: Load project repositories INFO: Load project repositories (done) | time=30ms INFO: Load quality profiles INFO: Load quality profiles (done) | time=36ms INFO: Load active rules INFO: Load active rules (done) | time=670ms INFO: Load metrics repository INFO: Load metrics repository (done) | time=19ms INFO: Project key: java INFO: ------------- Scan java_job INFO: Load server rules INFO: Load server rules (done) | time=46ms INFO: Base dir: /var/lib/jenkins/workspace/java_job INFO: Working dir: /var/lib/jenkins/workspace/java_job/.scannerwork INFO: Source paths: . INFO: Source encoding: UTF-8, default locale: en_US INFO: Index files INFO: 9 files indexed INFO: Quality profile for xml: Sonar way WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by net.sf.cglib.core.ReflectUtils$1 (file:/root/.sonar/cache/cc8ad346e85db9d3735898b533d37d34/sonar-javascript-plugin-4.0.0.5862.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) WARNING: Please consider reporting this to the maintainers of net.sf.cglib.core.ReflectUtils$1 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release INFO: Sensor SonarJavaXmlFileSensor [java] INFO: 3 source files to be analyzed INFO: Sensor SonarJavaXmlFileSensor [java] (done) | time=413ms INFO: Sensor HTML [web] INFO: 3/3 source files have been analyzed INFO: Sensor HTML [web] (done) | time=95ms INFO: Sensor XML Sensor [xml] INFO: Sensor XML Sensor [xml] (done) | time=117ms INFO: Sensor Zero Coverage Sensor INFO: Sensor Zero Coverage Sensor (done) | time=5ms INFO: Sensor CPD Block Indexer INFO: Sensor CPD Block Indexer (done) | time=0ms INFO: SCM provider for this project is: git INFO: 5 files to be analyzed INFO: 3/5 files analyzed WARN: Missing blame information for the following files: WARN: * target/hello-world-war-1.0.0/WEB-INF/web.xml WARN: * target/hello-world-war-1.0.0/index.jsp WARN: This may lead to missing/broken features in SonarQube INFO: Calculating CPD for 2 files INFO: CPD calculation finished INFO: Analysis report generated in 96ms, dir size=31 KB INFO: Analysis reports compressed in 22ms, zip size=14 KB INFO: Analysis report uploaded in 340ms INFO: ANALYSIS SUCCESSFUL, you can browse http://11.0.1.202:9000/dashboard/index/java INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report INFO: More about the report processing at http://11.0.1.202:9000/api/ce/task?id=AaEvGigpXfNgiDsaIH6i INFO: Task total time: 3.816 s INFO: ------------------------------------------------------------------------ INFO: EXECUTION SUCCESS INFO: ------------------------------------------------------------------------ INFO: Total time: 4.908s INFO: Final Memory: 13M/116M INFO: ------------------------------------------------------------------------ [java_job] $ /bin/sh -xe /tmp/jenkins9664183222777241323.sh + sh /server/scripts/java.sh Archive: /usr/local/tomcat/webapps/hello-world-war-1.0.0.war inflating: /usr/local/tomcat/webapps/java_16/META-INF/MANIFEST.MF creating: /usr/local/tomcat/webapps/java_16/WEB-INF/ creating: /usr/local/tomcat/webapps/java_16/WEB-INF/classes/ inflating: /usr/local/tomcat/webapps/java_16/WEB-INF/web.xml inflating: /usr/local/tomcat/webapps/java_16/index.jsp inflating: /usr/local/tomcat/webapps/java_16/META-INF/maven/com.efsavage/hello-world-war/pom.xml inflating: /usr/local/tomcat/webapps/java_16/META-INF/maven/com.efsavage/hello-world-war/pom.properties Tomcat started. Finished: SUCCESS 8.创建Maven私服nexus 部署私服 nexus 下载：https://www.sonatype.com/download-oss-sonatype\n配置仓库两个选项：\n项目下的 pom.xml 配置、只生效当前的项目。 在 maven 配置全局所有项目生效。 上传JDK和nexus安装包：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@nexus ~]#rpm -ivh jdk-8u181-linux-x64.rpm warning: jdk-8u181-linux-x64.rpm: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk1.8-2000:1.8.0_181-fcs ################################# [100%] Unpacking JAR files... tools.jar... plugin.jar... javaws.jar... deploy.jar... rt.jar... jsse.jar... charsets.jar... localedata.jar... [root@nexus ~]#tar xf nexus-3.13.0-01-unix.tar.gz -C /usr/local/ [root@nexus ~]#ln -s /usr/local/nexus-3.13.0-01/ /usr/local/nexus [root@nexus bin]#/usr/local/nexus/bin/nexus start WARNING: ************************************************************ WARNING: Detected execution as \u0026#34;root\u0026#34; user. This is NOT recommended! WARNING: ************************************************************ Starting nexus [root@nexus nexus]#netstat -ntplu Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:35916 0.0.0.0:* LISTEN 2848/java tcp 0 0 0.0.0.0:8081 0.0.0.0:* LISTEN 2848/java tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1012/sshd tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1225/master tcp6 0 0 :::22 :::* LISTEN 1012/sshd tcp6 0 0 ::1:25 :::* LISTEN 1225/master 访问地址：11.0.1.203:8081\n默认账号密码：admin / admin123\n更改代理仓库为阿里云\n修改/usr/local/maven/conf/settings.xml，配置默认仓库为私服nexus\n配置Maven全局配置文件：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 [root@jenkins ~]#cat /usr/local/maven/conf/settings.xml \u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --\u0026gt; \u0026lt;!-- | This is the configuration file for Maven. It can be specified at two levels: | | 1. User Level. This settings.xml file provides configuration for a single user, | and is normally provided in ${user.home}/.m2/settings.xml. | | NOTE: This location can be overridden with the CLI option: | | -s /path/to/user/settings.xml | | 2. Global Level. This settings.xml file provides configuration for all Maven | users on a machine (assuming they\u0026#39;re all using the same Maven | installation). It\u0026#39;s normally provided in | ${maven.home}/conf/settings.xml. | | NOTE: This location can be overridden with the CLI option: | | -gs /path/to/global/settings.xml | | The sections in this sample file are intended to give you a running start at | getting the most out of your Maven installation. Where appropriate, the default | values (values used when the setting is not specified) are provided. | |--\u0026gt; \u0026lt;settings xmlns=\u0026#34;http://maven.apache.org/SETTINGS/1.0.0\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd\u0026#34;\u0026gt; \u0026lt;!-- localRepository | The path to the local repository maven will use to store artifacts. | | Default: ${user.home}/.m2/repository \u0026lt;localRepository\u0026gt;/path/to/local/repo\u0026lt;/localRepository\u0026gt; --\u0026gt; \u0026lt;!-- interactiveMode | This will determine whether maven prompts you when it needs input. If set to false, | maven will use a sensible default value, perhaps based on some other setting, for | the parameter in question. | | Default: true \u0026lt;interactiveMode\u0026gt;true\u0026lt;/interactiveMode\u0026gt; --\u0026gt; \u0026lt;!-- offline | Determines whether maven should attempt to connect to the network when executing a build. | This will have an effect on artifact downloads, artifact deployment, and others. | | Default: false \u0026lt;offline\u0026gt;false\u0026lt;/offline\u0026gt; --\u0026gt; \u0026lt;!-- pluginGroups | This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e. | when invoking a command line like \u0026#34;mvn prefix:goal\u0026#34;. Maven will automatically add the group identifiers | \u0026#34;org.apache.maven.plugins\u0026#34; and \u0026#34;org.codehaus.mojo\u0026#34; if these are not already contained in the list. |--\u0026gt; \u0026lt;pluginGroups\u0026gt; \u0026lt;!-- pluginGroup | Specifies a further group identifier to use for plugin lookup. \u0026lt;pluginGroup\u0026gt;com.your.plugins\u0026lt;/pluginGroup\u0026gt; --\u0026gt; \u0026lt;/pluginGroups\u0026gt; \u0026lt;!-- proxies | This is a list of proxies which can be used on this machine to connect to the network. | Unless otherwise specified (by system property or command-line switch), the first proxy | specification in this list marked as active will be used. |--\u0026gt; \u0026lt;proxies\u0026gt; \u0026lt;!-- proxy | Specification for one proxy, to be used in connecting to the network. | \u0026lt;proxy\u0026gt; \u0026lt;id\u0026gt;optional\u0026lt;/id\u0026gt; \u0026lt;active\u0026gt;true\u0026lt;/active\u0026gt; \u0026lt;protocol\u0026gt;http\u0026lt;/protocol\u0026gt; \u0026lt;username\u0026gt;proxyuser\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;proxypass\u0026lt;/password\u0026gt; \u0026lt;host\u0026gt;proxy.host.net\u0026lt;/host\u0026gt; \u0026lt;port\u0026gt;80\u0026lt;/port\u0026gt; \u0026lt;nonProxyHosts\u0026gt;local.net|some.host.com\u0026lt;/nonProxyHosts\u0026gt; \u0026lt;/proxy\u0026gt; --\u0026gt; \u0026lt;/proxies\u0026gt; \u0026lt;!-- servers | This is a list of authentication profiles, keyed by the server-id used within the system. | Authentication profiles can be used whenever maven must make a connection to a remote server. |--\u0026gt; \u0026lt;servers\u0026gt; \u0026lt;!-- server | Specifies the authentication information to use when connecting to a particular server, identified by | a unique name within the system (referred to by the \u0026#39;id\u0026#39; attribute below). | | NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are | used together. | \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;deploymentRepo\u0026lt;/id\u0026gt; \u0026lt;username\u0026gt;repouser\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;repopwd\u0026lt;/password\u0026gt; \u0026lt;/server\u0026gt; --\u0026gt; \u0026lt;!-- Another sample, using keys to authenticate. \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;siteServer\u0026lt;/id\u0026gt; \u0026lt;privateKey\u0026gt;/path/to/private/key\u0026lt;/privateKey\u0026gt; \u0026lt;passphrase\u0026gt;optional; leave empty if not used.\u0026lt;/passphrase\u0026gt; \u0026lt;/server\u0026gt; --\u0026gt; \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;my-nexus-releases\u0026lt;/id\u0026gt; \u0026lt;username\u0026gt;admin\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;admin123\u0026lt;/password\u0026gt; \u0026lt;/server\u0026gt; \u0026lt;server\u0026gt; \u0026lt;id\u0026gt;my-nexus-snapshot\u0026lt;/id\u0026gt; \u0026lt;username\u0026gt;admin\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;admin123\u0026lt;/password\u0026gt; \u0026lt;/server\u0026gt; \u0026lt;/servers\u0026gt; \u0026lt;!-- mirrors | This is a list of mirrors to be used in downloading artifacts from remote repositories. | | It works like this: a POM may declare a repository to use in resolving certain artifacts. | However, this repository may have problems with heavy traffic at times, so people have mirrored | it to several places. | | That repository definition will have a unique id, so we can create a mirror reference for that | repository, to be used as an alternate download site. The mirror site will be the preferred | server for that repository. |--\u0026gt; \u0026lt;mirrors\u0026gt; \u0026lt;mirror\u0026gt; \u0026lt;id\u0026gt;nexus\u0026lt;/id\u0026gt; \u0026lt;mirrorOf\u0026gt;*\u0026lt;/mirrorOf\u0026gt; \u0026lt;url\u0026gt;http://11.0.1.203:8081/repository/maven-public/\u0026lt;/url\u0026gt; \u0026lt;/mirror\u0026gt; \u0026lt;!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | \u0026lt;mirror\u0026gt; \u0026lt;id\u0026gt;mirrorId\u0026lt;/id\u0026gt; \u0026lt;mirrorOf\u0026gt;repositoryId\u0026lt;/mirrorOf\u0026gt; \u0026lt;name\u0026gt;Human Readable Name for this Mirror.\u0026lt;/name\u0026gt; \u0026lt;url\u0026gt;http://my.repository.com/repo/path\u0026lt;/url\u0026gt; \u0026lt;/mirror\u0026gt; --\u0026gt; \u0026lt;/mirrors\u0026gt; \u0026lt;!-- profiles | This is a list of profiles which can be activated in a variety of ways, and which can modify | the build process. Profiles provided in the settings.xml are intended to provide local machine- | specific paths and repository locations which allow the build to work in the local environment. | | For example, if you have an integration testing plugin - like cactus - that needs to know where | your Tomcat instance is installed, you can provide a variable here such that the variable is | dereferenced during the build process to configure the cactus plugin. | | As noted above, profiles can be activated in a variety of ways. One way - the activeProfiles | section of this document (settings.xml) - will be discussed later. Another way essentially | relies on the detection of a system property, either matching a particular value for the property, | or merely testing its existence. Profiles can also be activated by JDK version prefix, where a | value of \u0026#39;1.4\u0026#39; might activate a profile when the build is executed on a JDK version of \u0026#39;1.4.2_07\u0026#39;. | Finally, the list of active profiles can be specified directly from the command line. | | NOTE: For profiles defined in the settings.xml, you are restricted to specifying only artifact | repositories, plugin repositories, and free-form properties to be used as configuration | variables for plugins in the POM. | |--\u0026gt; \u0026lt;profiles\u0026gt; \u0026lt;!-- profile | Specifies a set of introductions to the build process, to be activated using one or more of the | mechanisms described above. For inheritance purposes, and to activate profiles via \u0026lt;activatedProfiles/\u0026gt; | or the command line, profiles have to have an ID that is unique. | | An encouraged best practice for profile identification is to use a consistent naming convention | for profiles, such as \u0026#39;env-dev\u0026#39;, \u0026#39;env-test\u0026#39;, \u0026#39;env-production\u0026#39;, \u0026#39;user-jdcasey\u0026#39;, \u0026#39;user-brett\u0026#39;, etc. | This will make it more intuitive to understand what the set of introduced profiles is attempting | to accomplish, particularly when you only have a list of profile id\u0026#39;s for debug. | | This profile example uses the JDK version to trigger activation, and provides a JDK-specific repo. \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;jdk-1.4\u0026lt;/id\u0026gt; \u0026lt;activation\u0026gt; \u0026lt;jdk\u0026gt;1.4\u0026lt;/jdk\u0026gt; \u0026lt;/activation\u0026gt; \u0026lt;repositories\u0026gt; \u0026lt;repository\u0026gt; \u0026lt;id\u0026gt;jdk14\u0026lt;/id\u0026gt; \u0026lt;name\u0026gt;Repository for JDK 1.4 builds\u0026lt;/name\u0026gt; \u0026lt;url\u0026gt;http://www.myhost.com/maven/jdk14\u0026lt;/url\u0026gt; \u0026lt;layout\u0026gt;default\u0026lt;/layout\u0026gt; \u0026lt;snapshotPolicy\u0026gt;always\u0026lt;/snapshotPolicy\u0026gt; \u0026lt;/repository\u0026gt; \u0026lt;/repositories\u0026gt; \u0026lt;/profile\u0026gt; --\u0026gt; \u0026lt;!-- | Here is another profile, activated by the system property \u0026#39;target-env\u0026#39; with a value of \u0026#39;dev\u0026#39;, | which provides a specific path to the Tomcat instance. To use this, your plugin configuration | might hypothetically look like: | | ... | \u0026lt;plugin\u0026gt; | \u0026lt;groupId\u0026gt;org.myco.myplugins\u0026lt;/groupId\u0026gt; | \u0026lt;artifactId\u0026gt;myplugin\u0026lt;/artifactId\u0026gt; | | \u0026lt;configuration\u0026gt; | \u0026lt;tomcatLocation\u0026gt;${tomcatPath}\u0026lt;/tomcatLocation\u0026gt; | \u0026lt;/configuration\u0026gt; | \u0026lt;/plugin\u0026gt; | ... | | NOTE: If you just wanted to inject this configuration whenever someone set \u0026#39;target-env\u0026#39; to | anything, you could just leave off the \u0026lt;value/\u0026gt; inside the activation-property. | \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;env-dev\u0026lt;/id\u0026gt; \u0026lt;activation\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;target-env\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;dev\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/activation\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;tomcatPath\u0026gt;/path/to/tomcat/instance\u0026lt;/tomcatPath\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;/profile\u0026gt; --\u0026gt; \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;nexus\u0026lt;/id\u0026gt; \u0026lt;repositories\u0026gt; \u0026lt;repository\u0026gt; \u0026lt;id\u0026gt;central\u0026lt;/id\u0026gt; \u0026lt;url\u0026gt;http://11.0.1.203:8081/repository/maven-public/\u0026lt;/url\u0026gt; \u0026lt;releases\u0026gt;\u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\u0026lt;/releases\u0026gt; \u0026lt;snapshots\u0026gt;\u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\u0026lt;/snapshots\u0026gt; \u0026lt;/repository\u0026gt; \u0026lt;/repositories\u0026gt; \u0026lt;pluginRepositories\u0026gt; \u0026lt;pluginRepository\u0026gt; \u0026lt;id\u0026gt;central\u0026lt;/id\u0026gt; \u0026lt;url\u0026gt;http://11.0.1.203:8081/repository/maven-public/\u0026lt;/url\u0026gt; \u0026lt;releases\u0026gt;\u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\u0026lt;/releases\u0026gt; \u0026lt;snapshots\u0026gt;\u0026lt;enabled\u0026gt;true\u0026lt;/enabled\u0026gt;\u0026lt;/snapshots\u0026gt; \u0026lt;/pluginRepository\u0026gt; \u0026lt;/pluginRepositories\u0026gt; \u0026lt;/profile\u0026gt; \u0026lt;/profiles\u0026gt; \u0026lt;!-- activeProfiles | List of profiles that are active for all builds. | \u0026lt;activeProfiles\u0026gt; \u0026lt;activeProfile\u0026gt;alwaysActiveProfile\u0026lt;/activeProfile\u0026gt; \u0026lt;activeProfile\u0026gt;anotherAlwaysActiveProfile\u0026lt;/activeProfile\u0026gt; \u0026lt;/activeProfiles\u0026gt; --\u0026gt; \u0026lt;activeProfiles\u0026gt; \u0026lt;activeProfile\u0026gt;nexus\u0026lt;/activeProfile\u0026gt; \u0026lt;/activeProfiles\u0026gt; \u0026lt;/settings\u0026gt; 测试nexus私服是否配置成功\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@jenkins hello-world-war]#mvn package ... Downloaded: http://11.0.1.203:8081/repository/maven-public/org/apache/maven/shared/maven-filtering/3.1.1/maven-filtering-3.1.1.jar (0at 0.0 KB/sec) Downloaded: http://11.0.1.203:8081/repository/maven-public/org/codehaus/plexus/plexus-utils/3.3.0/plexus-utils-3.3.0.jar (0 B at 0.0 sec) Downloaded: http://11.0.1.203:8081/repository/maven-public/org/apache/maven/shared/maven-mapping/3.0.0/maven-mapping-3.0.0.jar (0 B a.0 KB/sec) Downloaded: http://11.0.1.203:8081/repository/maven-public/org/sonatype/plexus/plexus-build-api/0.0.7/plexus-build-api-0.0.7.jar (0 B 0.0 KB/sec) [INFO] Packaging webapp [INFO] Assembling webapp [hello-world-war] in [/root/hello-world-war/target/hello-world-war-1.0.0] [INFO] Processing war project [INFO] Copying webapp resources [/root/hello-world-war/src/main/webapp] [INFO] Building war: /root/hello-world-war/target/hello-world-war-1.0.0.war [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 25.059 s [INFO] Finished at: 2026-10-12T17:40:28+08:00 [INFO] Final Memory: 13M/102M [INFO] ------------------------------------------------------------------------ 仓库地址11.0.1.203，配置成功\n","permalink":"https://LMDAWN1127.github.io/posts/java-project-deploy/","summary":"从 Maven 构建、GitLab 托管、Jenkins 拉取与自动部署，到 SonarQube 质量门禁与 Nexus 私服，梳理一条完整的 Java 项目 CI/CD 流水线落地过程。","title":"Java 项目 CI/CD 流水线：Maven+GitLab+Jenkins+SonarQube+Nexus"},{"content":"代码发布和回滚 代码发布前，先将之前测试流程的触发器关闭\n1.将稳定版本的main打标签 tag 1 2 3 4 [root@gitlab game]#vim index.html 修改为v1.0.1 [root@gitlab game]#git commit -am \u0026#34;v1.0.1\u0026#34; #为当前的仓库的代码打tag [root@gitlab game]#git tag -a v1.0.1 -m \u0026#34;v1.0.1\u0026#34; 2.将tag提交到gitlab的game项目中 1 2 推送tag到远程仓库 [root@gitlab game]#git push -u origin v1.0.1 3.jenkins获取gitlab中的tag 1 2 3 jenkins需要通过使用 git parameter插件获取项目中tag版本号信息 注: 接下来安装插件、在可选插件中搜索Git Parameter 直接安装即可，已安装则忽略次步骤 进入到项目把前面的参数化去掉重新选择使用git parameter 4.新建一个任务，只用于代码发布和回滚 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@jenkins scripts]#vim deploy.sh #!/bin/sh CODE_DIR=`pwd` WEB_DIR=\u0026#34;/code/\u0026#34; get_code_tar(){ cd $CODE_DIR \u0026amp;\u0026amp; tar zcf /opt/web-$git_version.tar.gz ./* } scp_code_web(){ scp /opt/web-$git_version.tar.gz 11.0.1.7:$WEB_DIR } code_tarxf(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp;mkdir web-$git_version \u0026amp;\u0026amp; tar xf web-$git_version.tar.gz -C web-$git_version\u0026#34; } ln_html(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp; rm -rf html \u0026amp;\u0026amp; ln -s web-$git_version html\u0026#34; } main(){ get_code_tar; scp_code_web; code_tarxf; ln_html; } main 测试构建，为了演示效果，先删除web服务器/code/目录内容\n1 [root@web01 code]#rm -fr * 1 2 3 4 5 [root@web01 code]#ll total 7740 lrwxrwxrwx 1 root root 10 Oct 10 15:56 html -\u0026gt; web-v1.0.1 drwxr-xr-x 6 root root 126 Oct 10 15:56 web-v1.0.1 -rw-r--r-- 1 root root 7922995 Oct 10 15:56 web-v1.0.1.tar.gz 更新版本（多推送几个版本进行测试）\n使用参数化构建中的选项参数\n优化发布和回滚操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #!/bin/sh CODE_DIR=`pwd` WEB_DIR=\u0026#34;/code/\u0026#34; get_code_tar(){ cd $CODE_DIR \u0026amp;\u0026amp; tar zcf /opt/web-$git_version.tar.gz ./* } scp_code_web(){ scp /opt/web-$git_version.tar.gz 11.0.1.7:$WEB_DIR } code_tarxf(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp;mkdir web-$git_version \u0026amp;\u0026amp; tar xf web-$git_version.tar.gz -C web-$git_version\u0026#34; } ln_html(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp; rm -rf html \u0026amp;\u0026amp; ln -s web-$git_version html\u0026#34; } main(){ get_code_tar; scp_code_web; code_tarxf; ln_html; } if [ $game = \u0026#34;deploy\u0026#34; ];then main elif [ $game = \u0026#34;rollback\u0026#34; ];then ln_html fi 这样发布过的版本就可以直接rollback，不用deploy，可以减少重复构建，不用重新执行整个main函数，只需要执行ln_html就可以了\n5.拉取对应的版本 ","permalink":"https://LMDAWN1127.github.io/posts/jenkins-code-deploy-rollback/","summary":"基于 GitLab 打 tag 标记稳定版本，配合 Jenkins Git Parameter 插件做参数化构建，用一份 deploy/rollback 脚本实现按版本号发布与一键回滚。","title":"Jenkins 代码发布与回滚"},{"content":"Pipeline流水线 一. CI/CD持续集成/持续部署 持续集成(Continuous integration)是一种软件开发实践，即团队开发成员经常集成它们的工作，通过每个成员每天至少集成一次，也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建（包括编译，发布，自动化测试）来验证，从而尽早地发现集成错误。\n持续部署（continuous deployment）是通过自动化的构建、测试和部署循环来快速交付高质量的产品。某种程度上代表了一个开发团队工程化的程度，毕竟快速运转的互联网公司人力成本会高于机器，投资机器优化开发流程化相对也提高了人的效率。\n持续交付 Continuous Delivery:频繁地将软件的新版本，交付给质量团队或者用户，以供评审尽早发现生产环境中存在的问题；如果评审通过，代码就进入生产阶段。\n敏捷思想中提出的这三个观点：通过技术手段自动化这三个工作，加快交付速度。\n二.pipeline介绍 什么是pipeline Jenkins 2.0的精髓是Pipeline as Code，是帮助Jenkins实现CI到CD转变的重要角色。什么是Pipeline，简单来说，就是一套运行于 Jenkins上的工作流框架，将原本独立运行于单个或者多个节点的任务连接起来，实现单个任务难以完成的复杂发布流程。Pipeline的实现方式是一套Groovy DSL，任何发布流程都可以表述为一段Groovy脚本，并且Jenkins支持从代码库直接读取脚本，从而实现了Pipeline as Code的理念。\nPipeline 概念 Pipeline 是一个用户定义的 CD 流水线模式。Pipeline 代码定义了通常包含构建、测试和发布步骤的完整的构建过程。\nNode node 是一个机器，它是 Jenkins 环境的一部分，并且能够执行 Pipeline。同时，node 代码块也是脚本式 Pipeline 语法的关键特性。\nStage Stage 块定义了在整个 Pipeline 中执行的概念上不同的任务子集（例如“构建”，“测试”和“部署”阶段），许多插件使用它来可视化或呈现 Jenkins 管道状态/进度。\nStep 一项任务。从根本上讲，一个步骤告诉 Jenkins 在特定时间点（或过程中的“步骤”）要做什么。例如，使用 sh step：sh \u0026lsquo;make\u0026rsquo; 可以执行 make 这个 shell 命令。\n三.Pipeline 重构发布流程 pipeline示例代码，做一个简单测试\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 pipeline{ agent any stages{ stage(\u0026#34;get code\u0026#34;){ steps{ echo \u0026#34;get code from scm\u0026#34; } } stage(\u0026#34;package\u0026#34;){ steps{ echo \u0026#34;packge code\u0026#34; } } stage(\u0026#34;deploy\u0026#34;){ steps{ echo \u0026#34;deploy packge to node1\u0026#34; } } } } 构建成功\n如果想更直观一点的话，可以打开Blue Ocean看板\n看板效果\n用Pipeline重构前面的发布流程\n1 2 3 4 [root@jenkins ~]#vim /usr/local/sonar/conf/sonar-scanner.properties sonar.host.url=http://11.0.1.202:9000 sonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b sonar.sourceEncoding=UTF-8 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 [root@gitlab game]#vim Jenkinsfile pipeline{ agent any stages{ stage(\u0026#34;get code\u0026#34;){ steps{ echo \u0026#34;get code\u0026#34; } } stage(\u0026#34;unit test\u0026#34;){ steps{ sh \u0026#39;/usr/local/sonar/bin/sonar-scanner -Dsonar.projectKey=html -Dsonar.projectName=${JOB_NAME} -Dsonar.sources=.\u0026#39; } } stage(\u0026#34;package\u0026#34;){ steps{ sh \u0026#39;tar zcf /opt/web-${BUILD_ID}.tar.gz ./* --exclude=./git --exclude=Jenkinsfile\u0026#39; } } stage(\u0026#34;deploy\u0026#34;){ steps{ sh \u0026#39;ssh 11.0.1.7 \u0026#34;cd /root/ \u0026amp;\u0026amp; mkdir web-${BUILD_ID}\u0026#34;\u0026#39; sh \u0026#39;scp /opt/web-${BUILD_ID}.tar.gz 11.0.1.7:/root/web-${BUILD_ID}\u0026#39; sh \u0026#39;ssh 11.0.1.7 \u0026#34;cd /root/web-${BUILD_ID} \u0026amp;\u0026amp; tar xf web-${BUILD_ID}.tar.gz \u0026amp;\u0026amp; rm -rf web-${BUILD_ID}.tar.gz\u0026#34;\u0026#39; sh \u0026#39;ssh 11.0.1.7 \u0026#34;cd /root \u0026amp;\u0026amp; rm -rf game \u0026amp;\u0026amp; ln -s web-${BUILD_ID} game\u0026#34;\u0026#39; } } } } [root@gitlab game]#git commit -am \u0026#34;new Jenkinsfile\u0026#34; [main ca6f632] new Jenkinsfile 1 file changed, 2 insertions(+), 1 deletion(-) [root@gitlab game]#git push -u origin main Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 317 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/game.git ce0d6dc..ca6f632 main -\u0026gt; main Branch main set up to track remote branch main from origin. ","permalink":"https://LMDAWN1127.github.io/posts/jenkins-pipeline/","summary":"从 CI/CD 与 Pipeline as Code 概念讲起，拆解 Node/Stage/Step，并用一份 Jenkinsfile 把「拉取代码 → SonarQube 扫描 → 打包 → 部署」的发布流程落地为可维护的流水线。","title":"Jenkins Pipeline 流水线"},{"content":"01 .Jenkins集成SonarQube 一. jenkins上配置SonarQube服务端 将Build Steps和执行shell调换位置，Build Steps在上面，执行shell在下面\n1.Name定义\n2.URL添加SonarQube的地址 token添加一个secret text 填写之前安装SonarQube的Token\n二. Jekins配置sonar客户端的位置 jenkins配置执行sonar-scanner命令的家目录，让jenkins能找到该命令 系统管理-\u0026gt;全局工具配置\n三. 项目中集成SonarQube 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@gitlab game]#vim index.html .... [\u0026#39;魂斗罗v5\u0026#39;, \u0026#39;roms/Contra1(U)30.nes\u0026#39;], ... [root@gitlab game]#git commit -am \u0026#34;v5\u0026#34; [main c03f21c] v5 1 file changed, 1 insertion(+), 1 deletion(-) [root@gitlab game]#git push -u origin main Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 273 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/game.git d0cef87..c03f21c main -\u0026gt; main Branch main set up to track remote branch main from origin. 自动推送成功\n02. Jenkins集成微信 一. 集成微信介绍 jenkins集成微信，测试环境流程，开发写完代码直接提交到gitlab，自动触发Jenkins执行构建，运行结果可以直接展示在测试平台，每次发版的测试版本信息可以通过Jenkins发送到企业微信通知相关项目人员。\n二. 注册企业微信公众号 网址: https://mp.weixin.qq.com/\n选择完成后把下面信息进行完善-\u0026gt;使用自己微信扫描二维码进行管理后-点注册\n注册成功登陆后点击应用管理-\u0026gt;创建应用\n上传一张log-\u0026gt;填写应用名称-可见范围-\u0026gt;创建应用-保存好agentld和secret写入py脚本\n三. 修改py脚本信息 1 2 3 4 5 6 #1. 上传脚本到/server/scripts [root@jenkins ~]# mkdir -p /server/scripts [root@jenkins ~]# cd /server/scripts [root@jenkins scripts]#ll total 4 -rw-r--r-- 1 root root 2008 Oct 2 18:59 jenkins_notify.py 四. 本地执行脚本测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@jenkins ~]#yum -y install python-pip [root@jenkins ~]#wget https://bootstrap.pypa.io/pip/2.7/get-pip.py [root@jenkins ~]#python get-pip.py [root@jenkins ~]#pip2.7 install requests [root@jenkins scripts]#python jenkins_notify.py 11 /etc/hosts game [变更日志] : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 [变更日志] : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 {\u0026#34;errcode\u0026#34;:0,\u0026#34;errmsg\u0026#34;:\u0026#34;ok\u0026#34;,\u0026#34;msgid\u0026#34;:\u0026#34;cpB2NxqCi8uaKeFHyR1iukyQoHK88hteL3ZQ36qc1J-s6dxwr9nVl5U1rnbiYNHHMEhpIkDaLTfEWfIwu7JVlRs_bPedgINU2lOaYHsoih9vWaXL-BAMnxRfc6wD5f19\u0026#34;} [root@jenkins scripts]#python jenkins_notify.py 11.0.1.201 /etc/hosts game [变更日志] : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 [变更日志] : 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 {\u0026#34;errcode\u0026#34;:0,\u0026#34;errmsg\u0026#34;:\u0026#34;ok\u0026#34;,\u0026#34;msgid\u0026#34;:\u0026#34;cpB2NxqCi8uaKeFHyR1iukyQoHK88hteL3ZQ36qc1J_hJhqCw0rb1QjAxDrJIwd1leNF0jHEWgispRPFbstm-I_VCKgfsRHM266lQO-Mvzg0XD0JGP2Vi-cc9yPN98Fd\u0026#34;} 五. 集成到Jenkins 安装第三方插件:\n1.由于jenkins没有官网的插件来完成此功能，所以我们只能用网络上一些开源的插件\ngithub下载代码 https://github.com/daniel-beck/changelog-environment-plugin 解压到某个目录-\u0026gt; 进入目录执行以下操作 cd 到 changelog-environment-plugin-master 下，执行 mvn verify 时间较长，会在changelog-environment-plugin-master/target/下有个changelog-environment.hpi文件，上传到jenkins即可使用\n2.安装插件\n3.重启Jenkins服务\n1 [root@jenkins scripts]#systemctl restart jenkins 4.集成到jenkins\n进入到项目:\n1 2 Entry Format中添加 %3$s(at %4$s via %1$s)，参数分别为ChangeLog内容，时间，提交人。 Date Format中添加 yyyy-MM-dd HH:mm:ss 就是时间格式。 1 2 3 4 5 复制执行命令 echo \u0026#34;==========Start Notify==============\u0026#34; echo ${SCM_CHANGELOG} \u0026gt; /tmp/${JOB_NAME}_change.log python /server/scripts/jenkins_notify.py ${BUILD_URL} /tmp/${JOB_NAME}_change.log ${JOB_NAME} rm -fv /tmp/${JOB_NAME}_change.log 5.测试效果\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@gitlab game]#git add . [root@gitlab game]#git commit -am \u0026#34;v8\u0026#34; [main 77d13e2] v8 1 file changed, 1 insertion(+), 1 deletion(-) [root@gitlab game]#git push -u origin main Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 270 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/game.git bb52c00..77d13e2 main -\u0026gt; main Branch main set up to track remote branch main from origin. ","permalink":"https://LMDAWN1127.github.io/posts/jenkins-sonarqube-wecom-notification/","summary":"在 Jenkins 中集成 SonarQube 质量门禁做代码扫描，并配合企业微信(WeCom)实现构建结果与变更日志的自动通知，打通质量卡点与团队触达。","title":"Jenkins 集成 SonarQube 质量门禁与企业微信通知"},{"content":"01. Jenkins拉取gitlab代码 一. gitlab创建仓库上传game代码 二. Jenkins创建项目 1.创建项目\n2.配置连接的gitlab项目地址（这里没有报错是因为之前测试的时候做过免密钥了）\n3.Jenkins服务器生成密钥对\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #配置免密钥 [root@jenkins ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Rr4ouinx/cPqVxJsxju2Cjd1mhWmWSuKGOKG532uTvk root@jenkins The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | | | | | o = | | *B o | |o o=oS | |=o . +=O.. | |o=+.Bo+=. | |oo.B.+* | | .=+BOE. | +----[SHA256]-----+ [root@jenkins ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+kaj/NEwHKaIErMEClVYuG3JuWr9CD7XpuO5YZ6JftMmNdAPMrvS0mWqP4SDyT6/le0j1RGkA48GsirESfMqXKvzSSd5AuhIPmUADtGPNK6VSeiMKyoWgiNBFLn6RIOAcXI0MuZrYQMLJz+kOYr6kF9em/6MfFQrLkt9DzIlnR9b7CMLyDnPOZ8FG640DqqX0sZ8G+i2v41VX/c7RGIWAgEhJY+kiMS5cDxWlTf2k8hoZmh0VIa/G9AwnvbklyUiE4ACd9GtEJG5xXJzedn7q0/Ygfr26T29t+KST3SlyrXIaEbEC5WwIMymCEJDCPPCGzqLjsWa+4dbJhoEM1afV root@jenkins 4.将公钥拷贝到gitlab页面的SSH秘钥验证页面\n执行完成后还需要在jenkins服务器中执行一次克隆命令输入一次yes（这里没有提示是因为之前做测试的时候输过了）\n1 2 3 4 5 6 7 8 [root@jenkins ~]#git clone git@11.0.1.200:dawn/game.git Cloning into \u0026#39;game\u0026#39;... remote: Enumerating objects: 98, done. remote: Counting objects: 100% (98/98), done. remote: Compressing objects: 100% (96/96), done. remote: Total 98 (delta 3), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (98/98), 7.35 MiB | 4.49 MiB/s, done. Resolving deltas: 100% (3/3), done. 5.在查看jenkins的项目配置(如果还是看到报错提示,点击保存退出后在重新进入到项目)\n完成后点击立即构建:\n拉取成功\n查看项目目录下是否拉取game代码成功\n1 2 3 4 5 6 7 8 9 [root@jenkins ~]#ll /var/lib/jenkins/workspace/game_job/ total 48 -rw-r--r-- 1 root root 28032 Oct 2 16:11 bgm.mp3 drwxr-xr-x 2 root root 23 Oct 2 16:11 css drwxr-xr-x 2 root root 23 Oct 2 16:11 images -rw-r--r-- 1 root root 8956 Oct 2 16:11 index.html drwxr-xr-x 2 root root 213 Oct 2 16:11 js drwxr-xr-x 2 root root 4096 Oct 2 16:11 roms -rw-r--r-- 1 root root 811 Oct 2 16:11 shuoming.html 02. Jenkins推送代码到web 将拉取到的代码推送到web服务器的Nginx站点目录运行\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1.创建web01服务器 2.安装Nginx服务 3.配置Nginx server [root@web01 conf.d]# cat default.conf server { listen 80 default_server; server_name _; location / { root /code/html; index index.html index.htm; } } 4.jenkins和web服务器做免秘钥 [root@jenkins ~]# ssh-copy-id -i .ssh/id_rsa.pub 11.0.1.7 jenkins项目中使用shell命令做打包推送动作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Shell脚本: DATE=$(date +%Y-%m-%d-%H-%M-%S) CODE_DIR=\u0026#34;./\u0026#34; WEB_DIR=\u0026#34;/code\u0026#34; get_code_tar(){ cd $CODE_DIR \u0026amp;\u0026amp; tar zcf /opt/web-$DATE.tar.gz ./* } scp_code_web(){ scp /opt/web-$DATE.tar.gz 11.0.1.7:$WEB_DIR } code_tarxf(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp;mkdir web-$DATE \u0026amp;\u0026amp; tar xf web-$DATE.tar.gz -C web-$DATE\u0026#34; } ln_html(){ ssh 11.0.1.7 \u0026#34;cd $WEB_DIR \u0026amp;\u0026amp; rm -rf html \u0026amp;\u0026amp; ln -s web-$DATE html\u0026#34; } main(){ get_code_tar; scp_code_web; code_tarxf; ln_html; } main 保存点击立即构建:\n浏览器访问web服务器显示成功\n03. 配置web-hook 1.开启触发器\n生成token并保存\n2.复制Token配置到Gitlab\n配置完成\n测试: 修改代码推送到gitlab仓库的游戏项目中,会自动触发Jenkins拉取代码并将拉取到的修改后的代码推送到web服务器\n修改前：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@gitlab game]#vim index.html .... [\u0026#39;魂斗罗v2\u0026#39;, \u0026#39;roms/Contra1(U)30.nes\u0026#39;], ... [root@gitlab game]#git commit -am \u0026#34;v2\u0026#34; [main ce12479] v2 1 file changed, 2 insertions(+), 2 deletions(-) [root@gitlab game]#git push -u origin main Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 275 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/game.git 0385f69..ce12479 main -\u0026gt; main Branch main set up to track remote branch main from origin 修改后：\n","permalink":"https://LMDAWN1127.github.io/posts/jenkins-gitlab-cicd-auto-deploy/","summary":"手把手实现 Jenkins 拉取 GitLab 代码、经 SSH 推送至 Web 服务器、并配置 GitLab Webhook 触发全自动部署的完整 CI/CD 流程。","title":"Jenkins + GitLab 打通实现 CI/CD 自动部署"},{"content":"数据库版本升级方法 方法一：本地升级 （Inplace）（本实验采用此方式） 单台服务器进行程序升级\n1 11.0.1.101 mysql5.7 --\u0026gt; mysql8.0 (加载数据) 业务影响 方法二：迁移升级 （Mergeing）（后续实验操作） 两台服务器进行程序升级\n1 2 11.0.1.101 mysql5.7 数据信息 迁移 业务影响-最小 11.0.1.102 mysql8.0 数据信息 恢复 测试业务 主从同步 数据库升级规范 数据库服务版本升级时，只支持在GA（General Availability）版本之间进行升级 数据库服务版本升级时，支持从数据库5.6到5.7再到8.0，跨版本升级，但是需要先将5.6升级到最新小版本，在进行跨版本升级 数据库服务版本升级时，需要提前考虑好版本回退的方案，最好升级前做好数据备份（特别是向8.0版本升级） 数据库服务版本升级时，制定的升级方案和升级步骤，需要尽可能降低数据库服务停机的时间 数据库服务官方参考资料：https://dev.mysql.com/doc/refman/8.0/en/upgrade-paths.html\n数据库升级流程 数据库服务数据备份保存（可以采用热备和冷备两种方案，冷备是需要停止业务后备份，热备是无需停止业务备份） 数据库服务最新程序安装（最新版本数据库服务安装过程时，无需停止原有数据库旧版服务） 数据库服务原有程序关闭（网站显示维护页面）2个页面 提前通知 升级过程说明信息 数据库服务最新程序启动（加载原有程序数据实现挂库升级，并采用跳过授权表和跳过网络方式启动） 采用安全模式启动新的数据库服务 \u0026ndash;skip-grant-tables \u0026ndash;skip-networking 加载旧版本数据信息 数据库服务升级数据结构（数据库服务升级程序后，还需要升级数据系统结构信息，因此升级时间和数据量无关） 数据库服务可以正常重启（数据库服务升级完毕后，确认数据库服务是可以正常完成重启操作） 数据库服务功能测试验证（反复核实验证与数据库服务相关的各项功能是否正常） 数据库服务升级工作完毕（取消网站维护页面，恢复正常网站线上运营业务） 数据库升级涉及两个方面 数据库程序升级：安装新的程序（安装新版数据库实例） 新的程序加载升级后的数据目录\n数据库数据升级：数据目录结构升级/将授权表结构信息升级\n数据库版本升级流程 环境准备：安装旧的数据库程序（详情见systemd 服务管理与MySQL 多版本部署） 模拟创建数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 [root@master ~]#mysql -uroot -S /tmp/mysql3356.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 1 Server version: 5.6.48 MySQL Community Server (GPL) Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; create database zlm; Query OK, 1 row affected (0.00 sec) mysql\u0026gt; create database lmy; Query OK, 1 row affected (0.00 sec) mysql\u0026gt; use zlm; Database changed mysql\u0026gt; create table t1 (id int,name char(10),age int); Query OK, 0 rows affected (0.02 sec) mysql\u0026gt; show tables; +---------------+ | Tables_in_zlm | +---------------+ | t1 | +---------------+ 1 row in set (0.00 sec) mysql\u0026gt; insert into t1 values (1,\u0026#39;laoming\u0026#39;,20),(2,\u0026#39;xiaoming\u0026#39;,21),(3,\u0026#39;dawn\u0026#39;,22); Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql\u0026gt; SELECT * FROM t1; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | laoming | 20 | | 2 | xiaoming | 21 | | 3 | dawn | 22 | +------+----------+------+ 3 rows in set (0.00 sec) mysql\u0026gt; 5.6 \u0026ndash; 5.7 步骤一：进行数据备份 （冷备） 1 2 3 4 5 6 7 8 [root@master ~]#systemctl stop mysqld3356 [root@master ~]#netstat -ntpl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 989/sshd tcp6 0 0 :::22 :::* LISTEN 989/sshd [root@master ~]#mkdir /backup/ [root@master ~]#cp -a /data/3356/data /backup/data_3356_bak 步骤二：安全模式启动新版数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master ~]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql57 datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master ~]#mkdir -p /run/mysqld3356 [root@master ~]#chown mysql:mysql /run/mysqld3356 [root@master ~]#/usr/local/mysql57/bin/mysqld_safe --defaults-file=/etc/my3356.cnf --skip-grant-tables --skip-networking \u0026amp; 数据信息没有升级前，授权表结构 5.6 mysql.user 信息\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 mysql\u0026gt; select * from mysql.user\\G *************************** 1. row *************************** Host: localhost User: root Password: Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N *************************** 2. row *************************** Host: master User: root Password: Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N *************************** 3. row *************************** Host: 127.0.0.1 User: root Password: Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N *************************** 4. row *************************** Host: ::1 User: root Password: Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N *************************** 5. row *************************** Host: localhost User: Password: Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: NULL password_expired: N *************************** 6. row *************************** Host: master User: Password: Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: NULL password_expired: N 6 rows in set (0.00 sec) 步骤三：进行数据目录和授权表结构信息升级 1 2 #修改授权表结构 [root@master ~]#/usr/local/mysql57/bin/mysql_upgrade -S /tmp/mysql3356.sock --force 数据信息已经升级后，授权表结构 5.7 mysql.user 信息\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 mysql\u0026gt; select * from mysql.user\\G *************************** 1. row *************************** Host: localhost User: root Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 2. row *************************** Host: master User: root Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 3. row *************************** Host: 127.0.0.1 User: root Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 4. row *************************** Host: ::1 User: root Select_priv: Y Insert_priv: Y Update_priv: Y Delete_priv: Y Create_priv: Y Drop_priv: Y Reload_priv: Y Shutdown_priv: Y Process_priv: Y File_priv: Y Grant_priv: Y References_priv: Y Index_priv: Y Alter_priv: Y Show_db_priv: Y Super_priv: Y Create_tmp_table_priv: Y Lock_tables_priv: Y Execute_priv: Y Repl_slave_priv: Y Repl_client_priv: Y Create_view_priv: Y Show_view_priv: Y Create_routine_priv: Y Alter_routine_priv: Y Create_user_priv: Y Event_priv: Y Trigger_priv: Y Create_tablespace_priv: Y ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 5. row *************************** Host: localhost User: Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: NULL password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 6. row *************************** Host: master User: Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: NULL password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: N *************************** 7. row *************************** Host: localhost User: mysql.session Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: Y Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: Y *************************** 8. row *************************** Host: localhost User: mysql.sys Select_priv: N Insert_priv: N Update_priv: N Delete_priv: N Create_priv: N Drop_priv: N Reload_priv: N Shutdown_priv: N Process_priv: N File_priv: N Grant_priv: N References_priv: N Index_priv: N Alter_priv: N Show_db_priv: N Super_priv: N Create_tmp_table_priv: N Lock_tables_priv: N Execute_priv: N Repl_slave_priv: N Repl_client_priv: N Create_view_priv: N Show_view_priv: N Create_routine_priv: N Alter_routine_priv: N Create_user_priv: N Event_priv: N Trigger_priv: N Create_tablespace_priv: N ssl_type: ssl_cipher: NULL x509_issuer: NULL x509_subject: NULL max_questions: 0 max_updates: 0 max_connections: 0 max_user_connections: 0 plugin: mysql_native_password authentication_string: *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE password_expired: N password_last_changed: 2026-10-08 18:06:03 password_lifetime: NULL account_locked: Y 8 rows in set (0.00 sec) 步骤四：正常重新启动数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master ~]#pkill mysql [root@master ~]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target [root@master ~]#systemctl daemon-reload [root@master ~]#systemctl start mysqld3356 5.7 \u0026ndash; 8.0 步骤一：进行数据备份 （冷备） 1 2 [root@master ~]#systemctl stop mysqld3356 [root@master ~]#cp -a /data/3356/data /backup/data_3357_bak 步骤二：安全模式启动新版数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@master ~]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master ~]#mkdir -p /run/mysqld3356 \u0026amp;\u0026amp; chown mysql:mysql /run/mysqld3356 [root@master ~]#mysqld_safe --defaults-file=/etc/my.cnf --skip-grant-tables --skip-networking \u0026amp; 步骤三：正常重新启动数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@master ~]#pkill mysql [root@master ~]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target [root@master ~]#systemctl daemon-reload [root@master ~]#systemctl start mysqld3356.service 5.7程序升级8.0 mysql-shell（函数功能可以检测数据库程序是否可以升级到8.0版本） 步骤一：下载安装mysql-shell软件工具 https://downloads.mysql.com/archives/shell/\n1 2 3 4 5 6 7 [root@master ~]#cd /usr/local/ [root@master local]#tar xf mysql-shell-8.0.26-linux-glibc2.12-x86-64bit.tar.gz [root@master local]#ln -s mysql-shell-8.0.26-linux-glibc2.12-x86-64bit mysqlsh [root@master local]#vim /etc/profile export PATH=$PATH:/usr/local/mysqlsh/bin [root@master local]#. /etc/profile 步骤二：进行检测低版本数据库程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 # 准备5.7数据库服务环境 [root@master ~]#vim /usr/lib/systemd/system/mysqld3357.service [Unit] Description=MySQL 5.7 Server (3357) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3357.cnf RuntimeDirectory=mysqld3357 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target [root@master ~]#vim /etc/my3357.cnf [mysql] socket=/tmp/mysql3357.sock [mysqld] user=mysql basedir=/usr/local/mysql57 datadir=/data/3357/data socket=/tmp/mysql3357.sock port=3357 pid-file=/run/mysqld3357/mysqld.pid [root@master ~]#systemctl start mysqld3357.service [root@master ~]#mysql -uroot -S /tmp/mysql3357.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 2 Server version: 5.7.40 MySQL Community Server (GPL) Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; create user root@\u0026#39;11.0.1.%\u0026#39; identified by \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.02 sec) mysql\u0026gt; grant all on *.* to root@\u0026#39;11.0.1.%\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; quit Bye [root@master ~]#mysqlsh root:123456@11.0.1.101:3357 -e \u0026#34;util.checkForServerUpgrade()\u0026#34; 执行以上命令，若没有error提示，表示可以实现版本升级 5.7.40 \u0026ndash;\u0026gt; 8.0.26\n数据库程序升级失败，进行程序回退 步骤一：关闭新版数据库服务程序 1 [root@master ~]#systemctl stop mysqld3356 步骤二：还原备份数据信息 5.6 - 5.7 备份56数据重新恢复到56程序的数据目录\n1 2 [root@master ~]#\\rm -rf /data/3356/data/* [root@master ~]#cp -a /backup/data_3356_bak/* /data/3356/data/ 步骤三：重新运行启动旧版本数据库服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@master ~]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql56 datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master ~]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql56/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target [root@master ~]#systemctl daemon-reload [root@master ~]#systemctl start mysqld3356 步骤四：检查旧版数据库服务状态 1 2 3 [root@master ~]#ps -ef|grep mysql /mysql56/bin/mysqld --defaults-file=/etc/my3356.cnf root 4117 1384 0 21:40 pts/0 00:00:00 grep --color=auto mysql ","permalink":"https://LMDAWN1127.github.io/posts/mysql-version-upgrade-rollback/","summary":"从升级方法（本地 Inplace / 迁移 Mergeing）、升级规范与流程讲起，实战 5.6→5.7→8.0 就地升级：冷备、安全模式挂库启动、mysql_upgrade 升级授权表、正常重启验证；并用 mysql-shell 的 util.checkForServerUpgrade() 做 8.0 升级前检测；最后给出升级失败时的程序与数据回退步骤。","title":"MySQL 版本升级实战与回退：5.6→5.7→8.0 就地升级、mysql-shell 检测与回退方案"},{"content":"步骤一：架构环境规划 master 11.0.1.101 搭建3个数据库服务实例\n1 2 3 mysql01 mysql-8.0.26 /data/3307/data /data/3307/my.cnf 3307 /tmp/mysql3307.sock mysql02 mysql-8.0.26 /data/3308/data /data/3308/my.cnf 3308 /tmp/mysql3308.sock mysql03 mysql-8.0.26 /data/3309/data /data/3309/my.cnf 3309 /tmp/mysql3309.sock 步骤二：创建多实例环境 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@master ~]#mkdir -p /data/33{07..09}/data [root@master ~]#chown -R mysql.mysql /data [root@master ~]#cat \u0026gt;/data/3307/my.cnf \u0026lt;\u0026lt;EOF \u0026gt; [mysqld] \u0026gt; mysqlx=0 \u0026gt; port=3307 \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3307/data \u0026gt; socket=/tmp/mysql3307.sock \u0026gt; EOF [root@master ~]#cat \u0026gt;/data/3308/my.cnf \u0026lt;\u0026lt;EOF \u0026gt; [mysqld] \u0026gt; mysqlx=0 \u0026gt; port=3308 \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3308/data \u0026gt; socket=/tmp/mysql3308.sock \u0026gt; EOF [root@master ~]#cat \u0026gt;/data/3309/my.cnf \u0026lt;\u0026lt;EOF \u0026gt; [mysqld] \u0026gt; mysqlx=0 \u0026gt; port=3309 \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3309/data \u0026gt; socket=/tmp/mysql3309.sock \u0026gt; EOF 步骤三：多实例初始化 1 2 3 [root@master ~]#mysqld --initialize-insecure --user=mysql --datadir=/data/3307/data --basedir=/usr/local/mysql [root@master ~]#mysqld --initialize-insecure --user=mysql --datadir=/data/3308/data --basedir=/usr/local/mysql [root@master ~]#mysqld --initialize-insecure --user=mysql --datadir=/data/3309/data --basedir=/usr/local/mysql 步骤四：运行启动多实例 命令启动：\n1 2 3 4 5 6 [root@master ~]#mysqld --defaults-file=/data/3307/my.cnf \u0026amp; [1] 1821 [root@master ~]#mysqld --defaults-file=/data/3308/my.cnf \u0026amp; [2] 1859 [root@master ~]#mysqld --defaults-file=/data/3309/my.cnf \u0026amp; [3] 1897 利用service文件启动：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 [root@master ~]#cat \u0026gt;/usr/lib/systemd/system/mysqld3307.service\u0026lt;\u0026lt;EOF \u0026gt; [Unit] \u0026gt; Description=MySQL Server \u0026gt; Documentation=mysqld.service \u0026gt; After=network.target \u0026gt; After=syslog.target \u0026gt; \u0026gt; [Install] \u0026gt; WantedBy=multi-user.target \u0026gt; \u0026gt; [Service] \u0026gt; User=mysql \u0026gt; Group=mysql \u0026gt; ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/data/3307/my.cnf \u0026gt; LimitNOFILE = 5000 \u0026gt; EOF [root@master ~]#cat \u0026gt;/usr/lib/systemd/system/mysqld3308.service\u0026lt;\u0026lt;EOF \u0026gt; [Unit] \u0026gt; Description=MySQL Server \u0026gt; Documentation=mysqld.service \u0026gt; After=network.target \u0026gt; After=syslog.target \u0026gt; \u0026gt; [Install] \u0026gt; WantedBy=multi-user.target \u0026gt; \u0026gt; [Service] \u0026gt; User=mysql \u0026gt; Group=mysql \u0026gt; ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/data/3308/my.cnf \u0026gt; LimitNOFILE = 5000 \u0026gt; EOF [root@master ~]#cat \u0026gt;/usr/lib/systemd/system/mysqld3309.service\u0026lt;\u0026lt;EOF \u0026gt; [Unit] \u0026gt; Description=MySQL Server \u0026gt; Documentation=mysqld.service \u0026gt; After=network.target \u0026gt; After=syslog.target \u0026gt; \u0026gt; [Install] \u0026gt; WantedBy=multi-user.target \u0026gt; \u0026gt; [Service] \u0026gt; User=mysql \u0026gt; Group=mysql \u0026gt; ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/data/3309/my.cnf \u0026gt; LimitNOFILE = 5000 \u0026gt; EOF [root@master ~]#systemctl daemon-reload [root@master ~]#systemctl start mysqld3307 [root@master ~]#systemctl start mysqld3308 [root@master ~]#systemctl start mysqld3309 步骤五：多实例环境验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 [root@master ~]#ps -ef|grep mysql mysql 1821 1434 0 13:48 pts/0 00:00:02 mysqld --defaults-file=/data/3307/my.cnf mysql 1859 1434 0 13:49 pts/0 00:00:02 mysqld --defaults-file=/data/3308/my.cnf mysql 1897 1434 0 13:49 pts/0 00:00:02 mysqld --defaults-file=/data/3309/my.cnf [root@master ~]#netstat -lntup|grep mysql tcp6 0 0 :::3307 :::* LISTEN 1821/mysqld tcp6 0 0 :::3308 :::* LISTEN 1859/mysqld tcp6 0 0 :::3309 :::* LISTEN 1897/mysqld [root@master ~]#mysql -uroot -S /tmp/mysql3307.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; select @@port; +--------+ | @@port | +--------+ | 3307 | +--------+ 1 row in set (0.00 sec) mysql\u0026gt; quit Bye [root@master ~]#mysql -uroot -S /tmp/mysql3308.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; select @@port; +--------+ | @@port | +--------+ | 3308 | +--------+ 1 row in set (0.00 sec) mysql\u0026gt; quit Bye [root@master ~]#mysql -uroot -S /tmp/mysql3309.sock Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; select @@port; +--------+ | @@port | +--------+ | 3309 | +--------+ 1 row in set (0.00 sec) mysql\u0026gt; quit Bye ","permalink":"https://LMDAWN1127.github.io/posts/mysql-multi-instance-deployment/","summary":"在同一台 master 主机上规划并部署 3 个 MySQL 8.0.26 实例（3307/3308/3309）：独立 my.cnf 与数据目录、initialize-insecure 初始化，命令启动与 systemd 服务托管两种方式，并用 ps / netstat / select @@port 完成验证。","title":"MySQL 多实例部署实战：单主机三实例规划、初始化与 systemd 托管"},{"content":"一、flat网络设计 1 2 [root@Controller ~]# source keystonerc_admin [root@Controller ~(keystone_admin)]# neutron agent-list 每台云主机每块网卡都有一个qbr，因为安全组是关联到网卡的，每块网卡关联不同的安全组\n1 2 3 4 [root@Compute01 ~]# brctl show bridge name\tbridge id\tSTP enabled\tinterfaces qbr24c7a169-96\t8000.ceb36944e179\tno\tqvb24c7a169-96 tap24c7a169-96 QEMU虚拟化套件 + KVM虚拟化内核\n1 integration_bridge=br-int 1 [root@Compute01 ~]# ovs-vsctl show 让VM1（web01）可以访问Internet，或者与其他网段互通\n在网络节点上配置neutron 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@Controller ~(keystone_admin)]# cd /etc/neutron/plugins/ml2/ [root@Controller ml2(keystone_admin)]# vim ml2_conf.ini [ml2] type_drivers=geneve,flat,vlan,vxlan tenant_network_types=flat mechanism_drivers=openvswitch path_mtu=0 extension_drivers=port_security,qos [securitygroup] enable_security_group=True [ml2_type_geneve] max_header_size=38 vni_ranges=10:100 [ml2_type_flat] flat_networks=default,extent [root@Controller ml2(keystone_admin)]# vim openvswitch_agent.ini [ovs] bridge_mappings=default:br-eth1,extnet:br-ex integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.10 [root@Controller ~(keystone_admin)]# systemctl restart neutron-* 在openstack web界面上，使用admin身份登录，在管理员菜单上，创建外部网络，选择flat，名称为extnet 使用租户身份登录，创建路由器，关联外部网络和子网 相关问题 问题1： 弹性IP地址是配置在哪里的？在哪里可以找到这个弹性IP地址\n1 2 3 [root@Controller ~(keystone_admin)]# ip netns ls qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 (id: 1) qdhcp-7d04c8e1-3ccf-4efe-85c3-9370bfc6a88a (id: 0) namespace 命名空间\n1 2 3 4 5 [root@Controller ~(keystone_admin)]# ip netns exec qdhcp-7d04c8e1-3ccf-4efe-85c3-9370bfc6a88a ip addr list [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 ip addr list [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 route -n [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 ping www.baidu.com [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 ping 192.168.31.1 问题2： 弹性IP绑定在路由器上，那是如何通过弹性IP访问云主机的\n1 2 [root@Controller ~(keystone_admin)]# ip netns exec qrouter-8e9ae372-905a-4ded-9ee4-e5d6f487e8f2 iptables -t nat -nL #iptables -t nat -A PREROUTING -d 192.168.31.17 -j DNAT --to-dest 192.168.33.254 问题3： 如果云主机绑定了弹性IP地址，会优先通过弹性IP地址出去，如果没有绑定，会通过路由器开启SNAT出去访问\nVLAN 静态路由 NAT TCP三层握手\n弹性IP地址不一定是为访问Internet而准备的，而是为了访问服务，也可以把弹性IP地址绑定给ELB负载均衡\nflat网络拓扑图\n二、VLAN网络设计和实现 配置网络节点和计算节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Controller ~]# cd /etc/neutron/plugins/ml2/ [root@Controller ml2]# vim ml2_conf.ini [ml2] type_drivers=geneve,flat,vlan,vxlan tenant_network_types=vlan mechanism_drivers=openvswitch path_mtu=0 extension_drivers=port_security,qos [ml2_type_flat] flat_networks=default,extent [ml2_type_vlan] network_vlan_ranges=mynetwork:100:1000 [root@Controller ml2]# vim openvswitch_agent.ini [ovs] bridge_mappings=mynetwork:br-eth1,extnet:br-ex integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.10 [root@Controller ~]# systemctl restart neutron-* 计算节点\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@Compute01 ~]# cd /etc/neutron/plugins/ml2/ [root@Compute01 ml2]# vim openvswitch_agent.ini [ovs] bridge_mappings=mynetwork:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.11 [root@Compute01 ~]# systemctl restart neutron-openvswitch-agent.service [root@Compute02 ~]# cd /etc/neutron/plugins/ml2/ [root@Compute02 ml2]# vim openvswitch_agent.ini [ovs] bridge_mappings=mynetwork:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.12 [root@Compute02 ~]# systemctl restart neutron-openvswitch-agent.service 在openstack web界面上，使用admin身份创建网络 使用zlm身份创建两个云主机 查看\nopenstack neutron流表\nVLAN网络的流表分析\n1 [root@Compute01 ~]# ovs-ofctl dump-flows br-eth1 priority=4 数字越大，优先级越高\n1 2 3 cookie=0x28f07cbfb122feff, duration=1736.064s, table=0, n_packets=267, n_bytes=39412, priority=4,in_port=\u0026#34;phy-br-eth1\u0026#34;,dl_vlan=1 actions=mod_vlan_vid:100,NORMAL [root@Compute01 ~]# ovs-ofctl dump-flows br-int cookie=0xffd369a083b424eb, duration=2050.913s, table=0, n_packets=1241, n_bytes=236428, priority=3,in_port=\u0026#34;int-br-eth1\u0026#34;,dl_vlan=100 actions=mod_vlan_vid:1,resubmit(,60) flat网络流表分析\n1 2 [root@Compute01 ~]# ovs-ofctl dump-flows br-eth1 cookie=0x632acfea08b01b9d, duration=311.151s, table=0, n_packets=81, n_bytes=4210, priority=4,in_port=\u0026#34;phy-br-eth1\u0026#34;,dl_vlan=1 actions=strip_vlan,NORMAL cookie=0x632acfea08b01b9d, duration=326.012s, table=0, n_packets=4, n_bytes=352, priority=2,in_port=\u0026#34;phy-br-eth1\u0026#34; actions=drop cookie=0x632acfea08b01b9d, duration=326.023s, table=0, n_packets=13485, n_bytes=6729360, priority=0 actions=NORMAL 三、VXLAN的网络设计和实现 解决：大二层问题，跨数据中心云主机热迁移问题，热迁移只能在二层网络中\nVXLAN实现将二层网络封装在三层中进行传输，到了对方解封装，实现二层通讯\n修改网络节点配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@Controller ~]# vim /etc/neutron/plugins/ml2/ml2_conf.ini [ml2] type_drivers=geneve,flat,vlan,vxlan tenant_network_types=vxlan mechanism_drivers=openvswitch path_mtu=0 extension_drivers=port_security,qos [ml2_type_vxlan] vxlan_group=224.0.0.1 vni_ranges=10:100 [root@Controller ~]# vim /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings=mynetwork:br-eth1,extnet:br-ex integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.10 [root@Controller ~]# systemctl restart neutron-server.service 相关问题 Compute01： web111云主机 192.168.99.140\nCompute02： web222云主机 192.168.99.14\n问题1： web111可以ping通web222吗？\n可以\n问题2： 192.168.88.126可以ping通192.168.99.0/24主机？\n可以\n**问题3：**云主机是如何获得metadata信息的？\nmetadata元数据\nnova-metadata\nneutron-metadata\n当云主机开机时，会运行cloud-init程序，cloud-init会通知云主机去访问http://169.254.169.254 因为在云主机中有一条默认路由，route -n 169.254.169.254 192.168.99.254 255.255.255.255 UGH 100 0 0 ens3\n从而将请求转发给网关，即VRouter，这个请求将进入网络节点\n网络节点收到这个请求后，将该请求转发给neutron-ns-proxy进程，这个进程通过内部接口转发给neutron-metadata-agent 1 2 3 4 5 6 [root@Controller ~]# ip netns ls [root@Controller ~]# ip netns exec qrouter-d88a6264-ff05-498c-ab98-f56a261da0e0 iptables -t nat -nL REDIRECT tcp -- 0.0.0.0/0 169.254.169.254 tcp dpt:80 redir ports 9697 [root@Controller ~]# ip netns exec qrouter-d88a6264-ff05-498c-ab98-f56a261da0e0 netstat -tulnp [root@Controller ~]# ps aux |grep proxy |grep neutron neutron 52920 0.0 0.0 156032 2700 ? Ss 14:37 0:00 haproxy -f /var/lib/neutron/ns-metadata-proxy/d88a6264-ff05-498c-ab98-f56a261da0e0.conf 因为neutron网络节点与控制节点管理网络互通，当用户申请云主机时，会填写云主机名称，root密码，秘钥等信息，当用户输入这些信息时，将这些请求发送给controller节点nova-api，nova-api会转发给nova-metadata-api，因为网络节点和控制节点管理网络互通，所以neutron-metadata服务向nova-metadata-api服务请求用户自定义信息，从而让云主机得到这些信息。 云主机先得到IP地址，IP地址是从DHCP中获取的，跟metadata无关，当云主机得到IP地址时，才触发了以上流程\n当用户申请云主机时，会输入云主机metadata数据（计算机名，root密码，ssh秘钥等），这个请求会发送nova-api，nova-api会将这些信息转发给nova-api-metadata进行存储\n云主机创建完成后，开机，通过DHCP获取到IP地址，运行cloud-init，cloud-init根据配置文件，去请求http://169.254.169.254\n当用户没有创建路由器时，无法得到用户的metadata信息\n","permalink":"https://LMDAWN1127.github.io/posts/openstack-neutron-flat-vlan-vxlan/","summary":"从 flat 到 VLAN 再到 VXLAN，逐步拆解 Neutron 三种网络类型的 ML2 与 OVS 配置、外部网络与路由器打通方式、OVS 流表转发分析，以及云主机通过 169.254.169.254 获取 metadata 的完整链路。","title":"OpenStack Neutron 网络类型设计与实现：flat、VLAN 与 VXLAN"},{"content":"一、Neutron网络服务 SDN： Software Define Network 软件定义网络\n虚拟交换机\nBridge： 桥接器\nOVS：Open Vswitch\nOVN： Open Virtual Network\n云租户隔离机制\nnamespace机制 命名空间\n1 2 3 4 #用于管理Linux以太网网桥 [root@Controller ~]# yum -y install bridge-utils [root@Compute01 ~]# yum -y install bridge-utils [root@Compute02 ~]# yum -y install bridge-utils tap设备： 虚拟机虚拟网卡名称，在虚拟机操作系统中网卡eth0，在openstack看来，就是一个tap设备\nqbr：Qemu Bridge 提供安全组规则，因为目前openswitch不支持iptables防火墙规则，引入qbr来实现iptables规则\nqvb : Bridge qbr\nqvo: OVS qvo ovs\nbr-int： 内部虚拟交换机\n如果VM1和VM2在同一个子网中，并且在同一台计算节点上，则VM1访问VM2，直接通过br-int这个内部虚拟交换机来实现即可，提升访问性能\n二、跨主机数据流 由于之前电脑资源限制，只创建了一台计算节点，现在为了满足实验需要，现在增加一台计算节点Compute02\n1.克隆Compute,将原来的Compute节点主机名修改成Compute01，将克隆的节点主机名修改成Compute02\n2.将克隆后的主机所有网卡重新生成MAC地址，以防冲突\n3.将克隆后的主机IP地址修改，以防止冲突\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Compute02 ~]# ip a 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens160: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:50:56:29:d6:05 brd ff:ff:ff:ff:ff:ff inet 192.168.100.12/24 brd 192.168.100.255 scope global noprefixroute ens160 valid_lft forever preferred_lft forever inet6 fe80::250:56ff:fe29:d605/64 scope link valid_lft forever preferred_lft forever 3: ens192: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:50:56:26:dc:b9 brd ff:ff:ff:ff:ff:ff inet 192.168.200.11/24 brd 192.168.200.255 scope global noprefixroute ens192 valid_lft forever preferred_lft forever inet6 fe80::250:56ff:fe26:dcb9/64 scope link valid_lft forever preferred_lft forever 4.将Controller、Compute01、Compute02和storage四台的/etc/hosts配置文件同步\n1 2 3 4 5 vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute01 192.168.100.12 Compute02 192.168.200.20 storage 6.将应答文件answers.txt修改，然后进行自动化部署将Compute02节点加入\n1 2 3 [root@Controller ~]# vim /etc/hosts CONFIG_COMPUTE_HOSTS=192.168.100.11,192.168.100.12 [root@Controller ~]# packstack --answer-file=/root/answers.txt 三、跨主机，跨网络数据流 四、访问Internet数据流 OpenStack Neutron SDN 软件定义网络 交换机OVS Router VLAN VXLAN隧道网络 Flat网络\n计算节点运行L2 Agent\nopenstack-openvswitch.service\n网络节点运行 L3 Agent\nneutron-l3-agent.service\nopenstack-openvswitch.service\ndhcp agent\nmetadata agent cloud-init，如果没有该服务，即便安装了cloud-init也无法实现自定义个性化信息，主机名 IP地址，root密码等\n查看各个节点运行的网络组件\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@Controller ~(keystone_admin)]# neutron agent-list neutron CLI is deprecated and will be removed in the future. Use openstack CLI instead. +--------------------------------------+--------------------+------------+-------------------+-------+----------------+---------------------------+ | id | agent_type | host | availability_zone | alive | admin_state_up | binary | +--------------------------------------+--------------------+------------+-------------------+-------+----------------+---------------------------+ | 1eea40a9-8ead-435a-a8b4-e08f39ed44ee | Metadata agent | Controller | | :-) | True | neutron-metadata-agent | | 2b5e9b53-f364-4ff2-8923-2b9124db90c6 | Open vSwitch agent | Compute02 | | :-) | True | neutron-openvswitch-agent | | 4b77d79e-4ce8-4f37-a35c-2c663199f8b3 | Open vSwitch agent | Compute | | :-) | True | neutron-openvswitch-agent | | 7c5771f1-ab63-41ab-b52a-d137b5d09dfd | Metering agent | Controller | | :-) | True | neutron-metering-agent | | 8751ee3e-a7e4-4d70-8233-44b4fb4b8bec | Open vSwitch agent | Controller | | :-) | True | neutron-openvswitch-agent | | 922ff7f3-8a7c-48e2-831c-57621542222c | L3 agent | Controller | nova | :-) | True | neutron-l3-agent | | ac7660d8-58d0-40da-81cd-3e8a2eec89ae | DHCP agent | Controller | nova | :-) | True | neutron-dhcp-agent | +--------------------------------------+--------------------+------------+-------------------+-------+----------------+---------------------------+ Agent： 代理，具体处理业务的存在 L2 Agent 处理真正二层交换数据\n**L3 Agent：**处理路由的数据\n在一个网络中，L2 Agent可以由Bridge或者OVS来实现，但同时只能使用其中之一\n所有的节点必须要么是Bridge，要么是OVS，如果想要我们的环境支持多种L2 Agent，可以引入ML2， 支持多种L2 Agent\n改造现有的VXLAN网络为flat网络（无VLAN隔离的网络类型）\n**LOCAL：**如果Neutron租户采用该网络，只有同一个计算节点的虚拟机才能通讯，仅主机模式，一般不采用\n**Flat：**扁平网络 不采用VLAN隔离的网络\n云主机之间不走192.168.100.0/24，减少网络压力\n单独使用一个网络来走云主机通讯网络\nVLAN： 带vlan tag的网络\nVXLAN,GRE等： 隧道网络\n五、云主机跨主机实现互访 给Controller、Compute01和Compute02分别再添加一张仅主机网卡\n控制节点：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [root@Controller network-scripts]# ip a 14: ens161: \u0026lt;BROADCAST,MULTICAST\u0026gt; mtu 1500 qdisc noop master ovs-system state DOWN group default qlen 1000 link/ether 00:0c:29:0b:63:c0 brd ff:ff:ff:ff:ff:ff [root@Controller network-scripts]# vim ifcfg-ens161 TYPE=Ethernet NAME=ens161 DEVICE=ens161 ONBOOT=yes HWADDR=00:0c:29:0b:63:c0 [root@Controller ~]# systemctl restart network [root@Controller ~]# ovs-vsctl add-br br-eth1 [root@Controller ~]# ovs-vsctl add-port br-eth1 ens161 [root@Controller ~]# ovs-vsctl show ... Bridge br-eth1 Port br-eth1 Interface br-eth1 type: internal Port ens161 Interface ens161 ... 分别在两个计算节点上\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 # Compute01 [root@Compute01 network-scripts]# ip a 3: ens161: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:0c:29:fa:35:77 brd ff:ff:ff:ff:ff:ff inet6 fe80::20c:29ff:fefa:3577/64 scope link valid_lft forever preferred_lft forever [root@Compute01 network-scripts]# vim ifcfg-ens161 TYPE=Ethernet NAME=ens161 DEVICE=ens161 ONBOOT=yes HWADDR=00:0c:29:fa:35:77 [root@Compute01 ~]# nmcli con reload [root@Compute01 ~]# nmcli con down ens161;nmcli con up ens161 Connection \u0026#39;ens161\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/2) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4) [root@Compute01 ~]# ovs-vsctl add-br br-eth1 [root@Compute01 ~]# ovs-vsctl add-port br-eth1 ens161 [root@Compute01 network-scripts]# vim ifcfg-ens161 ... Bridge br-eth1 Port br-eth1 Interface br-eth1 type: internal Port ens161 Interface ens161 ... # Compute02 [root@Compute02 network-scripts]# ip a 3: ens161: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:0c:29:f4:89:20 brd ff:ff:ff:ff:ff:ff inet6 fe80::20c:29ff:fef4:8920/64 scope link valid_lft forever preferred_lft forever [root@Compute02 network-scripts]# vim ifcfg-ens161 TYPE=Ethernet NAME=ens161 DEVICE=ens161 ONBOOT=yes HWADDR=00:0c:29:f4:89:20 [root@Compute02 network-scripts]# nmcli con reload [root@Compute02 network-scripts]# nmcli con down ens161;nmcli con up ens161 Connection \u0026#39;ens161\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/2) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4) [root@Compute02 network-scripts]# ovs-vsctl add-br br-eth1 [root@Compute02 network-scripts]# ovs-vsctl add-port br-eth1 ens161 [root@Compute02 network-scripts]# ovs-vsctl show ... Bridge br-eth1 Port br-eth1 Interface br-eth1 type: internal Port ens161 Interface ens161 ... 在控制节点上：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Controller plugins]# cd /etc/neutron/plugins/ml2/ [root@Controller ml2]# ls ml2_conf.ini openvswitch_agent.ini sriov_agent.ini [root@Controller ml2]# vim ml2_conf.ini [ml2] type_drivers=geneve,flat,vlan,vxlan tenant_network_types=flat mechanism_drivers=openvswitch path_mtu=0 extension_drivers=port_security,qos [ml2_type_flat] flat_networks=extnet,default [root@Controller ml2]# vim openvswitch_agent.ini [ovs] bridge_mappings =extnet:br-ex,default:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.10 [root@Controller ~]# systemctl restart neutron-server.service neutron-openvswitch-agent.service 计算节点：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@Compute01 ~]# vim /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings=default:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.11 [root@Compute01 ~]# systemctl restart neutron-openvswitch-agent.service [root@Compute02 ~]# vim /etc/neutron/plugins/ml2/openvswitch_agent.ini [ovs] bridge_mappings=default:br-eth1 integration_bridge=br-int tunnel_bridge=br-tun local_ip=192.168.100.12 [root@Compute02 ~]# systemctl restart neutron-openvswitch-agent.service 使用admin身份创建网络\n使用zlm用户身份创建两台云主机，测试是否可以互访\n","permalink":"https://LMDAWN1127.github.io/posts/openstack-neutron-network-service/","summary":"梳理 Neutron 的网络虚拟化机制：OVS/OVN 虚拟交换机、namespace 租户隔离，以及 tap/qbr/qvb/qvo/br-int 各设备的作用，并逐层拆解同主机、跨主机、跨网络与访问 Internet 四类数据流。","title":"OpenStack Neutron 网络服务详解：虚拟交换组件与跨主机数据流分析"},{"content":"一、OpenStack 核心组件及作用 1. HORIZON —— 统一图形界面服务 核心功能：提供基于 Web 的 Dashboard 图形管理控制台，供云管理员和普通租户进行云资源的可视化调配与监控。 2. NOVA —— 计算资源管理服务 核心功能：负责管理和配置云计算环境中的虚拟化算力资源（nova-compute）。 角色分工： Controller 控制节点：运行 nova-api、nova-scheduler、nova-conductor 等调度服务。当用户发起创建云主机请求时，nova-api 接收请求并解析，nova-scheduler 根据规格策略选择最佳计算节点进行负载调度； Compute 计算节点：实际承载云主机运行的工作节点（支持对接 KVM、VMware ESXi、Xen 等异构虚拟化主机）。 控制节点的高可用与性能瓶颈： 控制节点承担所有 API 网关、数据库和消息队列，存在高可用与性能压力； 3. GLANCE —— 镜像服务 核心功能：负责操作系统镜像的发现、注册、检索与存储管理。 常见磁盘镜像格式： KVM 常用格式：qcow2（支持瘦分配与写时复制 COW）； VMware 常用格式：vmdk。 部署建议：通常复用并集成部署在控制节点上以实现高可用。 4. SWIFT —— 对象存储服务 核心功能：提供高可用、分布式、最终一致性的对象存储服务（Object Storage），专门用于存储静态无结构数据、云主机系统镜像、数据全量备份等。 5. NEUTRON —— 软件定义网络（SDN） 核心功能：提供云计算环境中的虚拟网络即服务，管理虚拟路由、交换机、安全组、浮动 IP 及跨节点流量打通。 网络技术：全面支持 VLAN、VXLAN、Geneve 及分布式虚拟路由（DVR）； 硬件 SDN 模式：生产环境中通常配备 2 台专用网络节点搭建双活或主备集群。 6. CINDER —— 块存储服务 核心功能：为云主机提供持久化的块存储（云硬盘），可动态挂载、卸载、快照与扩容。 底层对接能力： 对接传统存储阵列：华为 OceanStor、IBM、EMC 等高端硬件存储； 对接开源分布式存储：Ceph RBD、GlusterFS 等。 底层机制：当用户申请一块云硬盘时，Cinder 会向下透传指令到后端存储系统，本质上相当于在物理/分布式存储中划分并映射出一个 LUN。 对比说明：对象存储（如百度网盘、Swift、S3）无法作为块设备直接格式化挂载为云主机文件系统，而 Cinder 块设备可以直接格式化并进行挂载使用。 7. HEAT —— 基础设施编排服务 核心功能：基于声明式模板（HOT 模板或 AWS CloudFormation 格式），通过图形化编排或编写脚本，一键自动化批量部署多台虚拟机、数据库集群、网络拓扑与监控策略。 8. CEILOMETER —— 计量监控服务 核心功能：采集物理资源与虚拟资源的性能指标、运行时间与使用量。 公有云场景：记录 API 调用量、网络流出量、算力时长，对接计费计费系统； 私有云场景：监控租户配额水位，识别资源超用或空闲情况，为容量规划提供支撑。 9. KEYSTONE —— 统一身份认证与授权服务 核心功能：OpenStack 整体安全基石，负责全平台的租户（Project）、用户（User）、角色（Role）、服务目录与 Token 鉴权认证。 企业级身份联邦对接： 可与企业现有的 Windows Active Directory (AD) 或 Linux OpenLDAP 无缝整合，实现企业级统一账号单点登录（SSO）； 流程审批与权限隔离（以华为云为例）： 支持云主机多级申请与审批工作流（支持高达 5 级审批）； 贯彻“三权分立”安全准则（安全员、系统管理员、审计员分权管理）； 支持对接客户企业自研 OA/BPM 审批流系统。 核心组件功能速查表： 组件名称 代码代号 核心服务功能 Horizon Dashboard 统一 Web 图形化管理控制台 Nova Compute 计算资源生命周期与实例调度管理 Glance Image 虚拟机镜像存储与版本注册 Swift Object 大规模非结构化对象存储服务 Neutron Network 软件定义网络（SDN）、路由与安全组管理 Cinder Block 云主机持久化块存储（云硬盘）管理 Heat Orchestration 基础设施即代码（IaC）多资源栈编排 Ceilometer Metering 资源使用量统计、监控计量与配额分析 Keystone Identity 统一身份认证、鉴权令牌与服务目录管理 二、OpenStack 部署规划（以 Victoria 版为例） 1. 部署方式比较 开源 OpenStack 手动部署：按组件逐个编译配置，过程繁琐复杂，常用于底层机制深度学习； 工具化自动化部署： Ansible 部署（如 OpenStack-Ansible、Kolla-Ansible）； Packstack 部署：RedHat 开源的基于 Puppet 的自动化快速交付工具（本实战采用）； 2. 集群节点规格与网络规划 （1）Controller 控制节点（1 台） 承载组件： API 服务：nova-api、cinder-api、glance-api、neutron-server 等； 底层基础服务：MariaDB / MySQL、RabbitMQ 消息队列、Memcached、Keystone； 网络与管理服务：网络节点外网路由转发、YUM 本地缓存、Chrony 时间同步服务端。 系统配置要求： CPU：4 vCPU 起 内存：4GB 内存起 硬盘：100GB 可用磁盘空间 网卡：3 块网卡（网卡 1 配置为桥接模式访问互联网；网卡 2 与网卡 3 配置为仅主机 Host-Only 模式走内网业务） IP 地址规划： 内部管理 IP：192.168.100.10 外部访问 IP：192.168.10.10 （2）Compute 计算节点（1 台） 承载组件：nova-compute、网络 L2 Agent（Open vSwitch Agent） 系统配置要求： CPU：4 vCPU 起 内存：8GB 内存起（最低 4GB） 硬盘：100GB 磁盘空间 网卡：3 块网卡（全配置为仅主机 Host-Only 模式） IP 地址规划： Compute：192.168.100.11 （3）YUM 软件源服务器 + Chrony 时间服务器（1 台） 系统配置要求： CPU：2 vCPU 内存：2GB 网卡：2 块（一块桥接连接 Internet 同步公网时间与上游软件源，另一块仅主机模式为内网节点提供服务） IP 地址规划： 外网 IP：192.168.10.11 内网 IP：192.168.100.20 环境降配说明：若个人电脑硬件资源紧张，可最低压缩为 2 台虚拟机（控制节点复用为 YUM 源与时间服务器，另配 1 台计算节点）。注意：各节点系统时间必须保持高度一致，时钟漂移不得超过 5 分钟，否则 Keystone 握手与消息队列通信将全面失效。\n三、OpenStack 自动化部署实战全流程 阶段一：YUM 源服务器与 Chrony 时间服务器搭建 1. 配置网络接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 # 1. 配置内部通信网卡 ens160 [root@Cloud ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens160 TYPE=Ethernet BOOTPROTO=none NAME=ens160 DEVICE=ens160 ONBOOT=yes IPADDR=192.168.100.20 PREFIX=24 # 2. 配置可连接外网的网卡 ens192（用于同步公网时钟） [root@Cloud ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens192 TYPE=Ethernet BOOTPROTO=none NAME=ens192 DEVICE=ens192 ONBOOT=yes PROXY_METHOD=none BROWSER_ONLY=no IPADDR=192.168.10.11 PREFIX=24 GATEWAY=192.168.10.1 DNS1=192.168.10.1 DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=no UUID=03da7500-2101-c722-2438-d0d006c28c73 # 重启网卡使配置生效 [root@Cloud ~]# nmcli connection down ens160 ; nmcli connection up ens160 Connection \u0026#39;ens160\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/1) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4) [root@Cloud ~]# nmcli connection down ens192 ; nmcli connection up ens192 Connection \u0026#39;ens192\u0026#39; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/3) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/5) 2. 配置本地光盘 YUM 源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@Cloud ~]# cd /etc/yum.repos.d/ [root@Cloud yum.repos.d]# mkdir bak [root@Cloud yum.repos.d]# mv CentOS-Linux-* bak/ # 挂载操作系统 ISO 光盘 mount /dev/cdrom /media/ # 编写光盘本地源配置文件 [root@Cloud ~]# cat \u0026gt; /etc/yum.repos.d/dvd.repo \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; \u0026gt; [BaseOS] \u0026gt; name=BaseOS \u0026gt; baseurl=file:///media/BaseOS \u0026gt; gpgcheck=0 \u0026gt; \u0026gt; [AppStream] \u0026gt; name=AppStream \u0026gt; baseurl=file:///media/AppStream \u0026gt; gpgcheck=0 \u0026gt; EOF 3. 配置 Chrony 时间同步服务端 由于 OpenStack 对节点间时钟同步要求极其严苛，先配置时间源：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # 编辑 /etc/chrony.conf [root@Cloud ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool ntp.tencent.com iburst # Allow NTP client access from local network. allow 192.168.100.0/24 # Serve time even if not synchronized to a time source. local stratum 10 # 重启并验证时间同步服务 [root@Cloud ~]# systemctl restart chronyd.service [root@Cloud ~]# chronyc sources 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* 106.55.184.199 2 6 17 2 -224us[ -144us] +/- 54ms # 关闭防火墙与 SELinux [root@Cloud ~]# systemctl disable firewalld.service --now [root@Cloud ~]# setenforce 0 [root@Cloud ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config 4. 搭建 httpd本地web yum仓库 将 CentOS 8 及 OpenStack Victoria 软件仓库发布为 HTTP 镜像源，供所有集群节点拉取。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 # 1. 安装并启动 Apache Web 服务 [root@Cloud ~]# yum -y install httpd [root@Cloud ~]# systemctl enable httpd --now # 2. 创建目录并挂载镜像文件 [root@Cloud ~]# mkdir /isos [root@Cloud ~]# mkdir -p /var/www/html/centos8 [root@Cloud ~]# mkdir -p /var/www/html/openstack # 挂载基础系统光盘与 OpenStack Victoria 安装镜像 [root@Cloud ~]# mount /dev/cdrom /var/www/html/centos8/ [root@Cloud ~]# mount /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack/ # 3. 持久化挂载写入 /etc/fstab [root@Cloud ~]# vim /etc/fstab /dev/cdrom /var/www/html/centos8 iso9660 defaults 0 0 /isos/26-CentOS8-4-OSP-Victoria.iso /var/www/html/openstack iso9660 defaults 0 0 # 4. 配置 HTTP YUM 仓库文件 [root@Cloud ~]# vim /etc/yum.repos.d/dvd.repo [BaseOS] name=BaseOS baseurl=http://192.168.100.20/centos8/BaseOS gpgcheck=0 [AppStream] name=AppStream baseurl=http://192.168.100.20/centos8/AppStream gpgcheck=0 [root@Cloud ~]# vim /etc/yum.repos.d/openstack.repo [centos-advanced-virtualization] name=centos-advanced-virtualization baseurl=http://192.168.100.20/openstack/centos-advanced-virtualization gpgcheck=0 [centos-nfv-openvswitch] name=centos-nfv-openvswitch baseurl=http://192.168.100.20/openstack/centos-nfv-openvswitch gpgcheck=0 [centos-rabbitmq-38] name=centos-rabbitmq-38 baseurl=http://192.168.100.20/openstack/centos-rabbitmq-38 gpgcheck=0 [PowerTools] name=PowerTools baseurl=http://192.168.100.20/openstack/PowerTools gpgcheck=0 [centos-ceph-nautilus] name=centos-ceph-nautilus baseurl=http://192.168.100.20/openstack/centos-ceph-nautilus gpgcheck=0 [centos-openstack-victoria] name=centos-openstack-victoria baseurl=http://192.168.100.20/openstack/centos-openstack-victoria gpgcheck=0 [extras] name=extras baseurl=http://192.168.100.20/openstack/extras gpgcheck=0 # 5. 将写好的 repo 文件批量分发至各控制节点与计算节点 [root@Cloud ~]# scp /etc/yum.repos.d/dvd.repo /etc/yum.repos.d/openstack.repo root@192.168.100.10:/etc/yum.repos.d/ [root@Cloud ~]# scp /etc/yum.repos.d/dvd.repo /etc/yum.repos.d/openstack.repo root@192.168.100.11:/etc/yum.repos.d/ 阶段二：集群各节点系统环境初始化 1. Controller控制节点初始化（192.168.100.10） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 # 1. 配置管理网络 ens160 [root@Controller ~]# nmcli connection modify ens160 ipv4.addresses 192.168.100.10/24 ipv4.method manual autoconnect yes [root@Controller ~]# nmcli connection down ens160 ; nmcli connection up ens160 # 2. 配置外部网络网卡 ens224 [root@Controller ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens224 TYPE=Ethernet BOOTPROTO=none NAME=ens224 DEVICE=ens224 ONBOOT=yes [root@Controller ~]# nmcli connection reload [root@Controller ~]# nmcli connection modify ens224 ipv4.addresses 192.168.10.10/24 ipv4.gateway 192.168.10.1 ipv4.dns 192.168.10.1 ipv4.method manual autoconnect yes [root@Controller ~]# nmcli connection down ens224 ; nmcli connection up ens224 # 3. 配置 Chrony 时间同步客户端 [root@Controller ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool 192.168.100.20 iburst [root@Controller ~]# systemctl restart chronyd.service [root@Controller ~]# chronyc sources 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^* 192.168.100.20 3 6 377 24 -22us[ -46us] +/- 43ms # 4. 配置主机名 [root@Controller ~]# hostnamectl set-hostname Controller # 5. 关闭防火墙与 SELinux [root@Controller ~]# systemctl disable firewalld.service --now [root@Controller ~]# setenforce 0 [root@Controller ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config # 6. 配置集群 /etc/hosts 解析 [root@Controller ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 7. 清理默认系统 repo 缓存 [root@Controller ~]# cd /etc/yum.repos.d [root@Controller yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ 2. Compute计算节点初始化（192.168.100.11） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 1. 配置管理网络 [root@Compute ~]# nmcli connection modify ens160 ipv4.addresses 192.168.100.11/24 ipv4.method manual autoconnect yes [root@Compute ~]# nmcli connection down ens160 ; nmcli connection up ens160 # 2. 配置 Chrony 时间同步客户端 [root@Compute ~]# vim /etc/chrony.conf # Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool 192.168.100.20 iburst [root@Compute ~]# systemctl enable chronyd.service --now # 3. 设置主机名 [root@Compute ~]# hostnamectl set-hostname Compute # 4. 关闭防火墙与 SELinux [root@Compute ~]# systemctl disable firewalld.service --now [root@Compute ~]# setenforce 0 [root@Compute ~]# sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config # 5. 配置集群 hosts 解析 [root@Compute ~]# vim /etc/hosts 192.168.100.10 Controller 192.168.100.11 Compute # 6. 清理默认系统 repo [root@Compute ~]# cd /etc/yum.repos.d [root@Compute yum.repos.d]# mkdir -p bak \u0026amp;\u0026amp; mv CentOS-Linux-* bak/ ⚠️ 关键检查点：此时建议将所有虚拟机关机，**统一拍摄快照，以便出现异常时快速回退。\n阶段三：Packstack 自动化应答部署 1. 安装 Packstack 部署工具 在控制节点（Controller）执行：\n1 [root@Controller ~]# yum -y install openstack-packstack 2. 生成默认部署应答文件 1 [root@Controller ~]# packstack --gen-answer-file=/root/answers.txt 网络方案说明：OpenStack Victoria 支持 OVN（Open Virtual Network）与传统 OVS（Open vSwitch）。\n3. 精准定制修改应答文件参数 1 2 # 将自动探测的外网 IP 统一替换为内网管理 IP 192.168.100.10 [root@Controller ~]# sed -i \u0026#39;s/192.168.10.10/192.168.100.10/g\u0026#39; /root/answers.txt 编辑 /root/answers.txt，核对并修改以下核心配置项：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # 1. 指定计算节点集群 IP 列表（逗号分隔） CONFIG_COMPUTE_HOSTS=192.168.100.11 # 2. 设置 Keystone 管理员 root 初始密码 CONFIG_KEYSTONE_ADMIN_PW=000000 # 3. 禁用 Demo 演示租户网络（生产化规范） CONFIG_PROVISION_DEMO=n # 4. 开启 Heat 编排组件支持 CONFIG_HEAT_INSTALL=y # 5. Neutron 核心驱动与网络类型配置（ML2 + OVS） CONFIG_NEUTRON_ML2_TYPE_DRIVERS=geneve,flat,vxlan,vlan CONFIG_NEUTRON_ML2_MECHANISM_DRIVERS=openvswitch CONFIG_NEUTRON_ML2_TENANT_NETWORK_TYPES=vxlan CONFIG_NEUTRON_L2_AGENT=openvswitch # 6. OVS 外部网络网桥绑定配置（关联物理外部网卡 ens224） CONFIG_NEUTRON_OVS_BRIDGE_MAPPINGS=extnet:br-ex CONFIG_NEUTRON_OVS_BRIDGE_IFACES=br-ex:ens224 # 7. 预设存储配额容量 CONFIG_SWIFT_STORAGE_SIZE=20G CONFIG_CINDER_VOLUMES_SIZE=50G ⚠️ 关键检查点：修改完应答文件后，建议再次将所有虚拟机关机拍摄快照。\n4. 执行一键自动化部署 开机后在控制节点运行部署命令：\n1 [root@Controller ~]# packstack --answer-file=/root/answers.txt 部署脚本将基于 Puppet 自动在所有节点完成软件源配置、数据库建表、消息队列集群建立、证书生成以及各组件协同联调。完成后将在终端打印登录 Dashboard 的 URL 及 keystonerc_admin 环境变量文件路径。\n四、Linux 系统日志管理与 OpenStack 故障排查 1. Linux Rsyslog 日志分级与配置架构 在 Linux 系统中，系统底层与大部分服务的日志均交由 rsyslog 守护进程集中管理。\n查看配置文件： 1 2 rpm -qc rsyslog # 核心主配置文件: /etc/rsyslog.conf 日志规则语法解析： 1 服务类别(Facility).日志等级(Priority) 日志存储路径 常见配置示例：\n1 2 3 4 5 6 7 8 9 # 1. 系统通用常规消息： # 记录所有服务 \u0026gt;=info 级别的日志，但排除邮件、认证安全和计划任务日志 *.info;mail.none;authpriv.none;cron.none /var/log/messages # 2. 调试日志： *.debug /var/log/debug # 3. 错误级别日志： *.err /var/log/error.log 语法规则要点： mail：服务类别（Facility）； info：日志优先级等级； .：表示匹配**大于等于（\u0026gt;=）**该优先级的日志消息； *.info：表示所有服务的 \u0026gt;= info 级别日志均汇聚写入目标文件。 实时动态监控日志命令： 1 2 3 4 5 # 跟踪系统日志末尾输出 tail /var/log/messages # 持续挂起实时监听日志流 tail -f /var/log/messages 2. OpenStack 自定义服务日志排查准则 排错原则：各组件通常具备独立的自定义日志文件体系（位于 /var/log/\u0026lt;组件名\u0026gt;/ 目录下）。若组件运行异常，优先排查该组件专属日志，无自定义日志时再退回排查系统全局日志 /var/log/messages。\n实战案例：以 Neutron 网络服务故障定位为例 在部署或运行过程中，若虚拟网络创建失败或 OVS 端口未上线，可通过正则匹配直接捕获错误与警告信息：\n1 2 3 4 5 # 1. 跨文件正则过滤所有 Neutron 日志中的错误与告警 tail -f /var/log/neutron/*.log | grep -iE \u0026#39;(err|warn)\u0026#39; # 2. 深度排查 neutron-server 核心 API 服务日志，并输出匹配项前后 3 行上下文： tail -f /var/log/neutron/server.log | grep -iE -A3 -B3 \u0026#39;(err|warn)\u0026#39; 参数说明：\n-i：忽略大小写； -E：开启扩展正则表达式匹配 (err|warn)； -A 3 (After)：打印匹配行后 3 行； -B 3 (Before)：打印匹配行前 3 行。 ","permalink":"https://LMDAWN1127.github.io/posts/openstack-core-architecture-deployment/","summary":"从核心组件（Nova/Glance/Neutron/Cinder 等）到基于 Packstack 的 Victoria 版一键自动化部署，并附 Rsyslog 日志排查方法。","title":"OpenStack 云操作系统核心架构与自动化部署"},{"content":"cinder创建卷和挂载卷架构 创建卷流程是由cinder节点提供（控制节点），挂载卷流程是由nova提供的\n本地 LVM + iSCSI 卷存储配置 在answers.txt应答文件中指定要创建cinder卷，考虑到实验环境无法提供专门的存储设备，所以系统采用本地LVM作为cinder存储 系统默认会创建一个cinder-volumes卷组，大小为50G（用户手动指定）\n该卷组创建在控制节点上\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [root@Controller ~]# vgdisplay --- Volume group --- VG Name cl System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 3 Open LV 3 Max PV 0 Cur PV 1 Act PV 1 VG Size \u0026lt;99.00 GiB PE Size 4.00 MiB Total PE 25343 Alloc PE / Size 25343 / \u0026lt;99.00 GiB Free PE / Size 0 / 0 VG UUID K2c9TX-MlXI-k9Ga-CPPm-3zuu-WNoK-mRuUEZ --- Volume group --- VG Name cinder-volumes System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 1 Open LV 0 Max PV 0 Cur PV 1 Act PV 1 VG Size \u0026lt;51.50 GiB PE Size 4.00 MiB Total PE 13183 Alloc PE / Size 12551 / \u0026lt;49.03 GiB Free PE / Size 632 / \u0026lt;2.47 GiB VG UUID nXhaeU-1PwG-is18-Ef2N-cK5R-9LRk-3ksn1k 在/etc/cinder/cinder.conf调用LVM驱动来识别创建的卷组 将该控制节点当做ISCSI存储（target），将用户创建的卷使用ISCSI共享给compute节点 当用户创建2G的云硬盘，系统会自动调用LVM驱动在卷组中创建2G LV\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 [root@Controller ~]# lvdisplay --- Logical volume --- LV Path /dev/cl/swap LV Name swap VG Name cl LV UUID f9Aqve-GgxT-gQNg-v0W0-ogyz-1cJT-WIEOMw LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 2 LV Size \u0026lt;7.88 GiB Current LE 2016 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:1 --- Logical volume --- LV Path /dev/cl/home LV Name home VG Name cl LV UUID 5f8ntZ-4pxS-yYFl-3BHC-qKaW-pFsi-2ZGkHg LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 1 LV Size 29.89 GiB Current LE 7653 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:2 --- Logical volume --- LV Path /dev/cl/root LV Name root VG Name cl LV UUID 32JpBQ-Ld4z-0bLU-jHuG-iMRC-WLoO-WtcrnT LV Write Access read/write LV Creation host, time localhost.localdomain, 2026-06-27 05:28:10 +0800 LV Status available # open 1 LV Size \u0026lt;61.23 GiB Current LE 15674 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:0 --- Logical volume --- LV Name cinder-volumes-pool VG Name cinder-volumes LV UUID ughXDw-Fl7B-Mbo9-vsHS-OvMy-Pi60-E2GPaa LV Write Access read/write (activated read only) LV Creation host, time Controller, 2026-07-08 16:03:29 +0800 LV Pool metadata cinder-volumes-pool_tmeta LV Pool data cinder-volumes-pool_tdata LV Status available # open 0 LV Size \u0026lt;48.93 GiB Allocated pool data 0.00% Allocated metadata 10.44% Current LE 12525 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:6 --- Logical volume --- LV Path /dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e LV Name volume-3561d822-2929-4b47-87c5-25f769f8f44e VG Name cinder-volumes LV UUID WoSBug-zWc4-wVYn-39xk-MWVw-z3Pj-UOdvok LV Write Access read/write LV Creation host, time Controller, 2026-10-05 15:56:30 +0800 LV Pool name cinder-volumes-pool LV Status available # open 0 LV Size 2.00 GiB Mapped size 0.00% Current LE 512 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 8192 Block device 253:7 当用户要将该卷附加给云主机web01，cinder发送指令给存储端，存储端就会把该LV共享给compute主机，从而让compute主机识别到该硬盘，产生了一个新的2G硬盘，并通过libvirt调用xml文件，给云主机添加\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 [root@Controller ~]# systemctl status target ● target.service - Restore LIO kernel target configuration Loaded: loaded (/usr/lib/systemd/system/target.service; enabled; vendor preset: disabled) Active: active (exited) since Mon 2026-10-05 14:18:51 CST; 1h 30min ago Process: 1873 ExecStart=/usr/bin/targetctl restore (code=exited, status=0/SUCCESS) Main PID: 1873 (code=exited, status=0/SUCCESS) Tasks: 0 (limit: 36406) Memory: 0B CGroup: /system.slice/target.service Oct 05 14:18:50 Controller systemd[1]: Starting Restore LIO kernel target configuration... Oct 05 14:18:51 Controller target[1873]: No saved config file at /etc/target/saveconfig.json, ok, exiting Oct 05 14:18:51 Controller systemd[1]: Started Restore LIO kernel target configuration. [root@Controller ~]# targetcli targetcli shell version 2.1.53 Copyright 2011-2013 by Datera, Inc and others. For help on commands, type \u0026#39;help\u0026#39;. /\u0026gt; ls o- / ......................................................................................................................... [...] o- backstores .............................................................................................................. [...] | o- block .................................................................................................. [Storage Objects: 1] | | o- iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e [/dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e (2.0GiB) write-thru activated] | | o- alua ................................................................................................... [ALUA Groups: 1] | | o- default_tg_pt_gp ....................................................................... [ALUA state: Active/optimized] | o- fileio ................................................................................................. [Storage Objects: 0] | o- pscsi .................................................................................................. [Storage Objects: 0] | o- ramdisk ................................................................................................ [Storage Objects: 0] o- iscsi ............................................................................................................ [Targets: 1] | o- iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e ............................................. [TPGs: 1] | o- tpg1 .......................................................................................... [no-gen-acls, auth per-acl] | o- acls .......................................................................................................... [ACLs: 1] | | o- iqn.1994-05.com.redhat:1220933cde3c ...................................................... [1-way auth, Mapped LUNs: 1] | | o- mapped_lun0 ................. [lun0 block/iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (rw)] | o- luns .......................................................................................................... [LUNs: 1] | | o- lun0 [block/iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (/dev/cinder-volumes/volume-3561d822-2929-4b47-87c5-25f769f8f44e) (default_tg_pt_gp)] | o- portals .................................................................................................... [Portals: 1] | o- 192.168.100.10:3260 .............................................................................................. [OK] o- loopback ......................................................................................................... [Targets: 0] /\u0026gt; [root@Compute ~]# cat /etc/iscsi/initiatorname.iscsi InitiatorName=iqn.1994-05.com.redhat:1220933cde3c [root@Compute ~]# iscsiadm -m session tcp: [1] 192.168.100.10:3260,1 iqn.2010-10.org.openstack:volume-3561d822-2929-4b47-87c5-25f769f8f44e (non-flash) [root@Compute ~]# vim /etc/libvirt/qemu/instance-00000002.xml \u0026lt;disk type=\u0026#39;block\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;raw\u0026#39; cache=\u0026#39;none\u0026#39; io=\u0026#39;native\u0026#39;/\u0026gt; \u0026lt;source dev=\u0026#39;/dev/sda\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vdb\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;serial\u0026gt;3561d822-2929-4b47-87c5-25f769f8f44e\u0026lt;/serial\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x00\u0026#39; slot=\u0026#39;0x07\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; 存储端配置\n安装targetcli软件包，并启动服务 1 2 3 4 5 6 7 8 9 #yum -y install targetcli #systemctl enable target --now #targetcli /\u0026gt; /backstores/block create dev=/dev/sdb1 name=data01 /\u0026gt; /iscsi create iqn.2024.com.storage:disk /\u0026gt; /iscsi/ iqn.2024.com.storage:disk/tpg1/acls create iqn.1994-05.com.redhat:b4ff583759f4 /\u0026gt;/iscsi/ iqn.2024.com.storage:disk /tpg1/luns create data01 /\u0026gt; /iscsi/ iqn.2024.com.storage:disk/tpg1/portals create 192.168.100.10:3260 /\u0026gt; saveconfig 目标器 业务服务器 存储客户端 1 2 3 [root@Compute ~]# iscsiadm -m discovery -t st -p 192.168.100.10 [root@Compute ~]# iscsiadm -m node -T iqn.2024.com.storage:disk -p 192.168.100.10:3260 -l #fdisk -l Cinder对接NFS存储 根据上图规划和配置网络\n1. 配置NFS存储 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #配置网络 [root@storage ~]# nmcli connection modify ens160 ipv4.addresses 192.168.200.20/24 ipv4.method manual autoconnect yes [root@storage ~]# nmcli connection down ens160 ; nmcli connection up ens160 [root@storage ~]# cd /etc/sysconfig/network-scripts/ [root@storage network-scripts]# cp ifcfg-ens160 ifcfg-ens192 [root@storage network-scripts]# cat ifcfg-ens192 # Generated by parse-kickstart TYPE=Ethernet DEVICE=ens192 ONBOOT=yes BOOTPROTO=none NAME=ens192 [root@storage ~]# nmcli connection reload [root@storage ~]# nmcli connection modify ens192 ipv4.addresses 192.168.8.20/24 ipv4.method manual autoconnect yes [root@storage ~]# nmcli connection down ens192 ; nmcli connection up ens192 #安装配置安装NFS服务 [root@storage ~]# yum install -y nfs-utils rpcbind [root@storage ~]# mkdir /storage [root@storage ~]# cat /etc/exports /storage 192.168.200.0/24(rw,sync,no_root_squash) /storage 192.168.8.0/24(rw,sync,no_root_squash) [root@storage ~]# systemctl enable nfs-server --now [root@storage ~]# systemctl enable rpcbind --now 2. 配置cinder节点网络（控制节点） 1 2 3 4 5 6 7 8 [root@Controller ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens192 TYPE=Ethernet NAME=ens192 DEVICE=ens192 ONBOOT=yes IPADDR=192.168.8.10 NETMASK=255.255.255.0 [root@Controller ~]# systemctl restart network 3. 配置计算节点网络 1 2 [root@Compute ~]# nmcli connection modify ens192 ipv4.addresses 192.168.200.10/24 autoconnect yes ipv4.method manual [root@Compute ~]# nmcli connection down ens192 ; nmcli connection up ens192 4.配置cinder 1 2 3 4 5 6 7 8 9 10 [root@Controller ~]# vim /etc/cinder/cinder.conf 410 default_volume_type=nfs 436 enabled_backends=lvm,nfs666 [nfs666] volume_backend_name=nfsdata volume_driver=cinder.volume.drivers.nfs.NfsDriver nfs_shares_config = /etc/cinder/nfs_shares [root@Controller ~]# cat /etc/cinder/nfs_shares storage:/storage [root@Controller ~]# systemctl restart openstack-cinder-* Cinder节点\n1 2 [root@Controller ~]# cat /etc/hosts ####额外增加一条记录 192.168.8.20 storage 计算节点\n1 2 3 4 5 6 [root@Compute ~]# cat /etc/hosts 192.168.200.20 storage [root@Controller ~]# source keystonerc_admin [root@Controller ~(keystone_admin)]# cinder type-create nfs [root@Controller ~(keystone_admin)]# cinder type-key nfs set volume_backend_name=nfsdata [root@Controller ~(keystone_admin)]# systemctl restart openstack-cinder-* 在openstack web界面上，创建卷，选择nfs，将该卷关联给云主机\n在openstack web界面上，创建卷，选择nfs, 源选择镜像，创建云主机时，选择源为刚创建的卷\n创建完云主机后会产生一个卷\n将原来云主机删除，再次创建云主机这个卷就可以使用了\n","permalink":"https://LMDAWN1127.github.io/posts/openstack-cinder-block-storage/","summary":"梳理 OpenStack Cinder 块存储：卷创建与挂载的架构分工（Cinder 建卷、Nova 挂载）、本地 LVM + iSCSI 把控制节点变成存储端的完整链路（VG/LV/targetcli ISCSI 共享/libvirt 挂载），以及对接 NFS 存储的后端配置与 Web 界面实操。","title":"OpenStack Cinder 块存储详解：LVM + iSCSI 本地卷与 NFS 后端对接"},{"content":"keystone服务 OpenStack多域支持\n1 2 [root@Controller ~]# vim /etc/openstack-dashboard/local_settings OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True 列出系统中所有服务\n1 [root@Controller ~(keystone_admin)]# openstack service list keystone地址 API接口\n1 OS_AUTH_URL=http://192.168.100.10:5000/v3 所有的服务都知道keystone地址，因为需要向keystone去检验用户token是否有效 keystone知道所有的服务地址是多少，所有的服务在创建时都向keystone进行注册 endpoint地址\n1 [root@Controller ~(keystone_admin)]# openstack endpoint list 每个服务都会有三个endpoint地址，这三个URL地址可以相同，也可以不相同\npublic url 外网用户访问URL地址\nadmin url ： 管理员访问URL地址\ninternel url：内部组件间访问URL地址\n创建云主机流程\n用户向keystone发送请求，输入用户名和密码，keystone认证成功后，给用户发放Token，用户使用Token向nova-api发送创建云主机请求 nova-api向keystone寻求帮助，帮我校验该Token是否有效，返回结果，如果成功 nova-api将请求发送给nova-compute主机 nova-compute主机根据用户请求信息，创建云主机，并返回给nova-api nova-api将结果返回给用户，云主机创建完成，该主机的信息记录在数据库中 我向nova-api发送创建主机的请求，我是不知道nova-api地址的，但是我知道keystone地址，我向keystone去查询nova-api地址，keystone存储了所有服务的endpoint地址，所以，我只需要知道keystone的地址即可，以下是如何查询keystone地址和通过keystone去查其他地址的方法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 [root@Controller ~(keystone_zlm)]# grep -E \u0026#34;^connection\\s*=.*keystone\u0026#34; /etc/keystone/*.conf /etc/keystone/keystone.conf:connection=mysql+pymysql://keystone_admin:c3fa63522e1f4586@192.168.100.10/keystone # 用户名：keystone_admin # 密码：c3fa63522e1f4586 [root@Controller ~(keystone_zlm)]# mysql -u keystone_admin -p -D keystone Enter password: Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 15494 Server version: 10.3.27-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [keystone]\u0026gt; SELECT s.type AS service_type, -\u0026gt; e.interface, -\u0026gt; e.url -\u0026gt; FROM endpoint e -\u0026gt; JOIN service s ON e.service_id = s.id -\u0026gt; ORDER BY s.type, e.interface; +----------------+-----------+--------------------------------------------------+ | service_type | interface | url | +----------------+-----------+--------------------------------------------------+ | alarming | admin | http://192.168.100.10:8042 | | alarming | internal | http://192.168.100.10:8042 | | alarming | public | http://192.168.100.10:8042 | | cloudformation | admin | http://192.168.100.10:8000/v1 | | cloudformation | internal | http://192.168.100.10:8000/v1 | | cloudformation | public | http://192.168.100.10:8000/v1 | | compute | admin | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | compute | internal | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | compute | public | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | identity | admin | http://192.168.100.10:5000 | | identity | internal | http://192.168.100.10:5000 | | identity | public | http://192.168.100.10:5000 | | image | admin | http://192.168.100.10:9292 | | image | internal | http://192.168.100.10:9292 | | image | public | http://192.168.100.10:9292 | | metering | admin | http://192.168.100.10:8777 | | metering | internal | http://192.168.100.10:8777 | | metering | public | http://192.168.100.10:8777 | | metric | admin | http://192.168.100.10:8041 | | metric | internal | http://192.168.100.10:8041 | | metric | public | http://192.168.100.10:8041 | | network | admin | http://192.168.100.10:9696 | | network | internal | http://192.168.100.10:9696 | | network | public | http://192.168.100.10:9696 | | object-store | admin | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | object-store | internal | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | object-store | public | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | orchestration | admin | http://192.168.100.10:8004/v1/%(tenant_id)s | | orchestration | internal | http://192.168.100.10:8004/v1/%(tenant_id)s | | orchestration | public | http://192.168.100.10:8004/v1/%(tenant_id)s | | placement | admin | http://192.168.100.10:8778/placement | | placement | internal | http://192.168.100.10:8778/placement | | placement | public | http://192.168.100.10:8778/placement | | volumev2 | admin | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev2 | internal | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev2 | public | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev3 | admin | http://192.168.100.10:8776/v3/%(tenant_id)s | | volumev3 | internal | http://192.168.100.10:8776/v3/%(tenant_id)s | | volumev3 | public | http://192.168.100.10:8776/v3/%(tenant_id)s | +----------------+-----------+--------------------------------------------------+ 39 rows in set (0.001 sec) MariaDB [keystone]\u0026gt; SELECT s.type AS service_type, -\u0026gt; e.interface, -\u0026gt; e.url -\u0026gt; FROM endpoint e -\u0026gt; JOIN service s ON e.service_id = s.id -\u0026gt; WHERE e.interface = \u0026#39;public\u0026#39; -\u0026gt; ORDER BY s.type; +----------------+-----------+--------------------------------------------------+ | service_type | interface | url | +----------------+-----------+--------------------------------------------------+ | alarming | public | http://192.168.100.10:8042 | | cloudformation | public | http://192.168.100.10:8000/v1 | | compute | public | http://192.168.100.10:8774/v2.1/%(tenant_id)s | | identity | public | http://192.168.100.10:5000 | | image | public | http://192.168.100.10:9292 | | metering | public | http://192.168.100.10:8777 | | metric | public | http://192.168.100.10:8041 | | network | public | http://192.168.100.10:9696 | | object-store | public | http://192.168.100.10:8080/v1/AUTH_%(tenant_id)s | | orchestration | public | http://192.168.100.10:8004/v1/%(tenant_id)s | | placement | public | http://192.168.100.10:8778/placement | | volumev2 | public | http://192.168.100.10:8776/v2/%(tenant_id)s | | volumev3 | public | http://192.168.100.10:8776/v3/%(tenant_id)s | +----------------+-----------+--------------------------------------------------+ 13 rows in set (0.000 sec) MariaDB [keystone]\u0026gt; 部署openstack时，创建service\n进入数据库，创建database，比如glance 创建数据库用户glance，设置glance用户对glance数据库中的所有的表有ALL（增删改查）权限 创建glance service，并将glance-api地址注册到数据库中 endpoint地址 当用户需要访问glance-api时，会去向keystone发送请求，通过keystone 数据库查询到glance api endpoint地址\n1 2 #示例 如果将zlm用户加入admin角色，那他就拥有了admin角色权限 [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME admin 用户向nova-api发送请求，nova-api向keystone校验用户身份，Token是否在有效期，并不会判断用户是否有权限创建云主机，用户是否有权限跟每个服务的策略文件有关\n1 2 3 4 5 6 7 8 9 [root@Controller nova]# cd /etc/openstack-dashboard/ [root@Controller openstack-dashboard]# ls cinder_policy.json keystone_policy.json neutron_policy.json nova_policy.json glance_policy.json local_settings nova_policy.d [root@Controller nova(keystone_user)]# cp /etc/openstack-dashboard/glance_policy.json /etc/glance/policy.json [root@Controller nova(keystone_user)]# openstack image create --container-format bare --disk-format qcow2 --file /root/cirros-0.3.5-x86_64-disk.img --public test2 [root@Controller nova(keystone_user)]# chgrp glance /etc/glance/policy.json [root@Controller nova(keystone_user)]# systemctl restart openstack-glance-api.service nova服务 Controller节点\nnova-api 模块\nnova-scheduler 模块\nnova-conductor 模块\nnova-novncproxy模块\n计算节点\nnova-compute模块\nnova-api\n对外提供rest接口的处理\n对传入的参数进行合法性校验和约束限制\n对请求的资源进行配额（quota）的校验和预留\n资源的创建，更新，删除查询等\n虚拟机生命周期的入口\n可水平扩展部署\nnova-conductor\n解耦 (nova-compute)对数据库直接访问，当用户申请创建云主机，创建成功后，将由nova-conductor模块将云主机信息写入到数据库中\ndatabase： 存放跟该组件相关的信息，比如云主机信息，比如镜像信息，比如网络信息\nnova-scheduler\n当openstack环境中有多台Compute计算节点时，用户创建云主机，nova-scheduler模块会根据每台compute主机的权重来决定该云主机运行在哪台计算节点上\nHAProxy提供负载均衡，当用户要访问nova-api时，会将请求丢给HAProxy，该负载均衡软件根据各个nova-api的负载情况，决定将请求发送给哪个API节点\nMQ： Message Queue 消息队列\n当一个组件有多个模块时，模块与模块之间的访问，需要经过消息队列来进行拥塞控制等\n如果有1000个用户将请求发送给nova-api\nnova-api会将请求发送给nova-compute\nnova-api 会将这1000个请求发送给消息队列，消息队列会根据每个compute主机的消息消费情况，处理进度，有序的将这些消息发送给nova-compute节点来处理\nnova-compute\n真正用来运行虚拟机的模块\nnova-novncproxy\n工作原理： nova-compute主机会为每个运行的虚拟机开启一个VNC Server端口\nnova-compute主机不能对外暴露该端口，否则会导致Internet主机都可以访问该端口和虚拟机\n为了解决该问题，在控制节点上开启一个nova-novncproxy服务对外提供，当用户访问该虚拟机控制台，会先将请求发送给nova-novncproxy，根据用户提供的信息（虚拟机UUID，Token等）查询到该虚拟机运行在哪台nova-compute上，将请求发送给运行了该虚拟机的Compute主机\n通过VNC打开虚拟机工作原理\nvncserver: 5900端口\nvncviewer: vncviewer\n客户端不需要安装vncviewer，而是将该功能集成在浏览器html中，novnc协议\nglance和swift服务 一、部署云主机：\n当用户发送一个部署云主机的指令后，验证通过，nova-compute主机会从glance镜像服务器下载镜像到本地（云主机部署在本地），/var/lib/nova/instances/_base/\n云主机开机后，会产生一个差分盘，当新写入数据时，会将新数据写入到差分盘中\n实验： 将glance镜像存放到swift对象存储中，而不是存放在glance本地硬盘\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@Controller ~]# cd /etc/glance/ [root@Controller glance]# cp glance-api.conf glance-api.conf.bak [root@Controller ~]# vim /etc/glance/glance-api.conf 3057 stores=file,http,swift 3111 default_store=swift 3982 swift_store_region = RegionOne 4032 swift_store_endpoint_type = publicURL 4090 swift_store_container = public 4118 swift_store_large_object_size = 5120 4142 swift_store_large_object_chunk_size = 100 4160 swift_store_create_container_on_put = True 4182 swift_store_multi_tenant = True 4230 swift_store_admin_tenants = services 4382 swift_store_auth_version = 2 4391 swift_store_auth_address = http://192.168.100.10:5000/v3 4399 swift_store_user = zlm 4408 swift_store_key = 000000 [root@Controller ~]# systemctl restart openstack-glance-api.service 备注： zlm用户添加swiftOperation角色才允许对swift有操作权限\n","permalink":"https://LMDAWN1127.github.io/posts/openstack-keystone-glance-nova/","summary":"梳理 OpenStack 三大核心服务：Keystone 的多域支持、service/endpoint 注册与策略文件权限控制；Nova 各模块（api/scheduler/conductor/compute/novncproxy）、消息队列与差分盘机制；Glance 镜像服务及改用 Swift 对象存储作为后端。","title":"OpenStack 核心服务详解：Keystone 认证、Nova 计算与 Glance/Swift 镜像存储"},{"content":"一、图形化界面发放云主机 admin用户是云超级管理员\nadmin管理身份\n一、创建租户 tenant project 项\n二、创建用户，关联项目\n三、上传镜像 管理员上传镜像是可以设置为公共镜像，所有租户可以访问，用户自行上传镜像是私有镜像，只能在租户内共享\n四、创建规格\n五、创建外部网络\nL3 Agent部署在哪里哪个节点就是网络节点\n租户身份登录\n一、使用租户身份登录\n二、创建私有网络和子网 VPC\n三、创建路由器，关联外部网络和私有网络\n四、创建安全组和策略\n五、创建秘钥对\n六、发放云主机并且绑定浮动IP\n七、测试（登录，主机名，网络）\ncloud-init 初始化自定义信息 主机名 密码等\n假如我创建了一个镜像，这个镜像内容特别多，大概占用了20G硬盘，需要4G内存才能更流畅的运行，但租户不知道啊，他发放云主机时，选择了10G硬盘，2G内存的规格，导致发放失败，但租户不知道什么原因，所以此时我在上传镜像时，指定硬盘最少20G，内存至少4G，将来选择规格时，如果规格小于这个，则不能选择，只能选择满足该条件的规则才行。\n以下是OpenStack Nova 中用于发现并将新加入的计算节点映射到其所属 Cell 数据库的关键命令，在 Cells v2 架构下，每个计算节点必须经过此步骤完成注册，否则调度器无法在其上部署实例，API 也不会列出该主机\n1 [root@Controller ~(keystone_admin)]# nova-manage cell_v2 discover_hosts --verbose 发放云主机可以通过路由器访问Internet，那配置弹性IP的意义是可以对外提供服务，web服务器，可以通过SNAT出去访问，但远程用户不能进来访问该服务，以下是查看OpenStack底层使用iptables的策略\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 [root@Controller ~(keystone_admin)]# ip netns qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 (id: 1) qdhcp-a82a6d9e-26ec-497f-8f71-68d8395386ad (id: 0) [root@Controller ~(keystone_admin)]# ip netns exec qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 ip addr list 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 33: qg-d9305000-ef: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fa:16:3e:18:37:69 brd ff:ff:ff:ff:ff:ff inet 192.168.10.106/24 brd 192.168.10.255 scope global qg-d9305000-ef valid_lft forever preferred_lft forever inet 192.168.10.182/32 brd 192.168.10.182 scope global qg-d9305000-ef valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe18:3769/64 scope link valid_lft forever preferred_lft forever 34: qr-eb02e78e-a3: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fa:16:3e:18:b8:5f brd ff:ff:ff:ff:ff:ff inet 192.168.20.254/24 brd 192.168.20.255 scope global qr-eb02e78e-a3 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe18:b85f/64 scope link valid_lft forever preferred_lft forever [root@Controller ~(keystone_admin)]# ip netns exec qrouter-aa6470e8-9c0e-4a50-9be6-71566d0c3903 iptables -t nat -nL Chain PREROUTING (policy ACCEPT) target prot opt source destination neutron-l3-agent-PREROUTING all -- 0.0.0.0/0 0.0.0.0/0 Chain INPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination neutron-l3-agent-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 neutron-postrouting-bottom all -- 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination neutron-l3-agent-OUTPUT all -- 0.0.0.0/0 0.0.0.0/0 Chain neutron-l3-agent-OUTPUT (1 references) target prot opt source destination DNAT all -- 0.0.0.0/0 192.168.10.182 to:192.168.20.197 Chain neutron-l3-agent-POSTROUTING (1 references) target prot opt source destination ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ! ctstate DNAT Chain neutron-l3-agent-PREROUTING (1 references) target prot opt source destination REDIRECT tcp -- 0.0.0.0/0 169.254.169.254 tcp dpt:80 redir ports 9697 DNAT all -- 0.0.0.0/0 192.168.10.182 to:192.168.20.197 Chain neutron-l3-agent-float-snat (1 references) target prot opt source destination SNAT all -- 192.168.20.197 0.0.0.0/0 to:192.168.10.182 random-fully Chain neutron-l3-agent-snat (1 references) target prot opt source destination neutron-l3-agent-float-snat all -- 0.0.0.0/0 0.0.0.0/0 SNAT all -- 0.0.0.0/0 0.0.0.0/0 to:192.168.10.106 random-fully SNAT all -- 0.0.0.0/0 0.0.0.0/0 mark match ! 0x2/0xffff ctstate DNAT to:192.168.10.106 random-fully Chain neutron-postrouting-bottom (1 references) target prot opt source destination neutron-l3-agent-snat all -- 0.0.0.0/0 0.0.0.0/0 /* Perform source NAT on outgoing traffic. */ 实验需求：创建一台云主机，可以访问Internet，yum源指向阿里云，并且安装httpd软件包，启动服务，从外部主机访问该web页面http://192.168.10.182\nCompute主机本质上是一台KVM宿主机，在openstack中发放云主机，会通过调度程序将云主机分发到某一台Compute主机上\n二、命令行界面发放云主机 1 [root@Controller ~(keystone_admin)]# cp keystonerc_admin keystonerc_zlm 需求一：使用命令行删除所有的资源\n1 2 3 4 5 6 7 8 9 10 11 12 [root@Controller ~(keystone_zlm)]# cat keystonerc_zlm unset OS_SERVICE_TOKEN export OS_USERNAME=zlm export OS_PASSWORD=\u0026#39;000000\u0026#39; export OS_REGION_NAME=RegionOne export OS_AUTH_URL=http://192.168.100.10:5000/v3 export PS1=\u0026#39;[\\u@\\h \\W(keystone_zlm)]\\$ \u0026#39; export OS_PROJECT_NAME=LMandMY_HOME export OS_USER_DOMAIN_NAME=Default export OS_PROJECT_DOMAIN_NAME=Default export OS_IDENTITY_API_VERSION=3 [root@Controller ~(keystone_zlm)]# source keystonerc_zlm 删除云主机 1 2 3 4 5 6 7 [root@Controller ~(keystone_zlm)]# openstack server list +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ | ID | Name | Status | Networks | Image | Flavor | +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ | 44f0fa66-94ed-44ac-a9ae-0a3a13c1faba | web | ACTIVE | VPC01=192.168.20.197, 192.168.10.182 | CentOS 8.4 64bit | m3.web | +--------------------------------------+------+--------+--------------------------------------+------------------+--------+ [root@Controller ~(keystone_zlm)]# openstack server delete 44f0fa66-94ed-44ac-a9ae-0a3a13c1faba 删除卷 1 2 [root@Controller ~(keystone_zlm)]# openstack volume list [root@Controller ~(keystone_zlm)]# openstack volume delete data01 删除路由器 1 2 3 4 5 [root@Controller ~(keystone_zlm)]# openstack router list [root@Controller ~(keystone_zlm)]# openstack router show router01 [root@Controller ~(keystone_zlm)]# openstack subnet list [root@Controller ~(keystone_zlm)]# openstack router remove subnet router01 subnet01 [root@Controller ~(keystone_zlm)]# openstack router delete router01 删除安全组 1 2 [root@Controller ~(keystone_zlm)]# openstack security group list [root@Controller ~(keystone_zlm)]# openstack security group delete SG01 删除网络 1 2 [root@Controller ~(keystone_zlm)]# openstack network list [root@Controller ~(keystone_zlm)]# openstack network delete VPC01 进入admin身份删除public网络，镜像，规格，租户，用户等\n1 [root@Controller ~(keystone_zlm)]# source keystonerc_admin 删除public网络 1 2 [root@Controller ~(keystone_admin)]# openstack network list [root@Controller ~(keystone_admin)]# openstack network delete public 删除镜像 1 2 3 4 [root@Controller ~(keystone_admin)]# openstack image list [root@Controller ~(keystone_admin)]# openstack image delete e3336d90-e673-4b1b-82ec-69b70fd6402c [root@Controller ~(keystone_admin)]# openstack image delete cec813dd-8141-4a99-8883-805ac6e3ec17 [root@Controller ~(keystone_admin)]# openstack image delete c6ffe7db-ccb0-4df4-8715-e8ce0fd5dc7d 删除规格 1 2 [root@Controller ~(keystone_admin)]# openstack flavor list [root@Controller ~(keystone_admin)]# openstack flavor delete m3.web 删除租户和用户 1 2 3 4 [root@Controller ~(keystone_admin)]# openstack project list [root@Controller ~(keystone_admin)]# openstack project delete LMandMY_HOME [root@Controller ~(keystone_admin)]# openstack user list [root@Controller ~(keystone_admin)]# openstack user delete zlm 需求二：使用命令行创建所有的资源 发放云主机，添加弹性IP，访问云主机，创建网络\n1 [root@Controller ~(keystone_admin)]# source keystonerc_admin 创建项目，用户，将用户与项目关联 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@Controller ~(keystone_admin)]# openstack project create LMandMY_HOME [root@Controller ~(keystone_admin)]# openstack user create --project LMandMY_HOME --project-domain Default --password-prompt zlm [root@Controller ~(keystone_admin)]# openstack role list +----------------------------------+------------------+ | ID | Name | +----------------------------------+------------------+ | 0adc17230a5a434195d5995863280b3d | heat_stack_owner | | 2e5abdde67bb4f23bbf15454616af53e | ResellerAdmin | | 46d49fecd6f4411bbd72d0b9f575bcae | admin | | 5f5bf78c40b143bbb2df74fc855ea204 | reader | | 84ebb81407f644b18e1a529e0c317f16 | member | | b83f4c27c83445489554e988fb9af5a1 | heat_stack_user | | b8f8cd45891240bfa0f80e93f0e37ab9 | _member_ | | ebc22a15458c4712bf0dd4f4de09e0bf | SwiftOperator | +----------------------------------+------------------+ [root@Controller ~(keystone_admin)]# openstack role add --user zlm --project LMandMY_HOME b8f8cd45891240bfa0f80e93f0e37ab9 上传镜像 1 [root@Controller ~(keystone_admin)]# openstack image create --file /data/cirros-0.3.5-x86_64-disk.img --disk-format qcow2 --container-format bare --public cirros 创建规格 1 [root@Controller ~(keystone_admin)]# openstack flavor create --ram 1024 --disk 10 --public m3.web 创建public网络和子网 1 [root@Controller ~(keystone_admin)]# openstack network create --external --share --provider-network-type flat --provider-physical-network extnet --project LMandMY_HOME public 创建子网\n1 [root@Controller ~(keystone_admin)]# openstack subnet create --no-dhcp --subnet-range 192.168.10.0/24 --allocation-pool start=192.168.10.100,end=192.168.10.200 --gateway 192.168.10.1 --network public publicsub01 进入租户身份\n1 [root@Controller ~(keystone_admin)]# source keystonerc_zlm 创建网络和子网 1 [root@Controller ~(keystone_zlm)]# openstack network create VPC01 创建子网\n1 [root@Controller ~(keystone_zlm)]# openstack subnet create --network VPC01 --subnet-range 192.168.20.0/24 --dhcp --gateway 192.168.20.254 --dns-nameserver 114.114.114.114 subnet01 创建路由器 1 2 3 [root@Controller ~(keystone_zlm)]# openstack router create router01 [root@Controller ~(keystone_zlm)]# openstack router add subnet router01 subnet01 [root@Controller ~(keystone_zlm)]# openstack router set --external-gateway public router01 创建安全组 1 [root@Controller ~(keystone_zlm)]# openstack security group create SG01 放行ICMP协议\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol icmp --remote-ip 0.0.0.0/0 SG01 放行ssh服务\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 22 --remote-ip 0.0.0.0/0 SG01 放行80端口\n1 [root@Controller ~(keystone_zlm)]# openstack security group rule create --ingress --protocol tcp --dst-port 80 --remote-ip 0.0.0.0/0 SG01 创建浮动IP 1 [root@Controller ~(keystone_zlm)]# openstack floating ip create public 创建秘钥对 1 [root@Controller ~(keystone_zlm)]# openstack key create key01 \u0026gt; key01.pem 创建云主机 1 2 3 4 5 6 7 8 [root@Controller ~(keystone_zlm)]# openstack network list +--------------------------------------+--------+--------------------------------------+ | ID | Name | Subnets | +--------------------------------------+--------+--------------------------------------+ | a82a6d9e-26ec-497f-8f71-68d8395386ad | VPC01 | 40bd75e0-3d22-4e7b-8659-8a6c0a44e3aa | | d99ba601-05e3-4f1f-88b8-f0587c6a5f6e | public | 5d39db23-8359-4e33-bde7-0f9ac6a0f7c7 | +--------------------------------------+--------+--------------------------------------+ [root@Controller ~(keystone_zlm)]# openstack server create --image cirros --security-group SG01 --key-name key01 --flavor m3.web --nic net-id=a82a6d9e-26ec-497f-8f71-68d8395386ad web 将弹性IP关联给该云主机\n1 2 [root@Controller ~(keystone_zlm)]# openstack port list [root@Controller ~(keystone_zlm)]# openstack floating ip set --port=46ba21d5-6f14-4693-a215-5a6fc8781adf 192.168.10.182 访问云主机\n1 2 [root@Controller ~(keystone_zlm)]# chmod 400 key01.pem [root@Controller ~(keystone_zlm)]# ssh -i key01.pem cirros@192.168.10.182 如果外网网卡不能通讯\n1 [root@Controller ~(keystone_zlm)]# ifup br-ex ; ifup ens224 ","permalink":"https://LMDAWN1127.github.io/posts/openstack-provision-cloud-server/","summary":"从图形化界面到命令行，完整演示 OpenStack 发放云主机的全流程：租户/用户/镜像/规格/网络/路由器/安全组/密钥对/浮动IP 的创建与删除，并附 iptables SNAT/DNAT 与 Nova cell_v2 discover_hosts 排查要点。","title":"OpenStack发放云主机"},{"content":"1.整理 systemctl 数据库 Service 文件 方案一：标准生产守护进程配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [Unit] Description=MySQL Server After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/var/run/mysqld/mysqld.pid PrivateTmp=true RuntimeDirectory=mysqld [Install] WantedBy=multi-user.target 方案二：带配置文件与文件句柄限制配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=MySQL Server After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf RuntimeDirectory=mysqld LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target 修改完成后\n1 2 3 [root@master local]#systemctl daemon-reload [root@master local]#systemctl restart mysqld.service [root@master local]#systemctl enable mysqld.service 2.多版本 MySQL（5.6 / 5.7）安装与升级准备 步骤一：下载软件归档包 1 2 3 4 5 # MySQL 5.7.40 二进制包 [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz # MySQL 5.6.48 二进制包 [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz 步骤二：解压安装与创建软链接 1 2 3 4 5 6 7 8 9 # 安装 5.7 版本 [root@master local]#tar xf mysql-5.7.40-linux-glibc2.12-x86_64.tar.gz [root@master local]#ln -s mysql-5.7.40-linux-glibc2.12-x86_64 mysql57 # 注意：多版本共存调试期间，暂不配置全局环境变量，避免命令路径冲突 # 安装 5.6 版本 [root@master local]#tar xf mysql-5.6.48-linux-glibc2.12-x86_64.tar.gz [root@master local]#ln -s mysql-5.6.48-linux-glibc2.12-x86_64 mysql56 # 注意：暂不配置全局环境变量 步骤三：编写多实例专属配置文件 MySQL 5.7（3357 实例）配置：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master local]#vim /etc/my3357.cnf [mysql] socket=/tmp/mysql3357.sock [mysqld] user=mysql basedir=/usr/local/mysql57 datadir=/data/3357/data socket=/tmp/mysql3357.sock port=3357 pid-file=/run/mysqld3357/mysqld.pid [root@master local]#vim /usr/lib/systemd/system/mysqld3357.service [Unit] Description=MySQL 5.7 Server (3357) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3357.cnf RuntimeDirectory=mysqld3357 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target MySQL 5.6（3356 实例）配置：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master local]#vim /etc/my3356.cnf [mysql] socket=/tmp/mysql3356.sock [mysqld] user=mysql basedir=/usr/local/mysql56 datadir=/data/3356/data socket=/tmp/mysql3356.sock port=3356 pid-file=/run/mysqld3356/mysqld.pid [root@master local]#vim /usr/lib/systemd/system/mysqld3356.service [Unit] Description=MySQL 5.6 Server (3356) After=network.target [Service] User=mysql Group=mysql ExecStart=/usr/local/mysql56/bin/mysqld --defaults-file=/etc/my3356.cnf RuntimeDirectory=mysqld3356 LimitNOFILE=65536 Restart=on-failure [Install] WantedBy=multi-user.target 步骤四：初始化数据库软件 MySQL 5.6 初始化（数据目录 3356）：\n1 2 3 4 5 6 7 8 9 10 11 [root@master local]#mkdir -p /data/3356/data [root@master local]#chown -R mysql.mysql /data/3356/data [root@master ~]#/usr/local/mysql56/scripts/mysql_install_db --defaults-file=/etc/my3356.cnf --user=mysql --basedir=/usr/local/mysql56 --datadir=/data/3356/data FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql56/scripts/mysql_install_db: Data::Dumper # 缺少Perl模块 [root@master local]#yum install -y perl-Data-Dumper [root@master local]#/usr/local/mysql56/scripts/mysql_install_db --user=mysql --datadir=/data/3356/data --basedir=/usr/local/mysql56 # 观察输出信息中是否包含两个 OK，并确认退出状态码： [root@master local]#echo $? 0 MySQL 5.7 初始化（数据目录 3357）：\n1 2 3 4 5 [root@master local]#mkdir -p /data/3357/data [root@master local]#chown -R mysql.mysql /data/3357/data [root@master ~]#/usr/local/mysql57/bin/mysqld --defaults-file=/etc/my3357.cnf --initialize-insecure --user=mysql --basedir=/usr/local/mysql57 --datadir=/data/3357/data [root@master local]#echo $? 0 步骤五：运行启动数据库服务 启动/停止 MySQL 5.7 服务：\n1 2 3 4 5 6 7 8 9 10 11 # 第一种启动方式 [root@master local]#cp /usr/local/mysql57/support-files/mysql.server /etc/init.d/mysqld57 [root@master local]#chmod +x /etc/init.d/mysqld57 [root@master local]#/etc/init.d/mysqld57 start [root@master local]#/etc/init.d/mysqld57 stop # 第二种启动方式 [root@master local]#systemctl daemon-reload [root@master local]#systemctl start mysqld3357.service [root@master local]#systemctl enable mysqld3357.service Created symlink from /etc/systemd/system/multi-user.target.wants/mysqld3357.service to /usr/lib/systemd/system/mysqld3357.service. 启动/停止 MySQL 5.6 服务：\n1 2 3 4 5 6 7 8 9 10 11 # 第一种启动方式 [root@master local]#cp /usr/local/mysql56/support-files/mysql.server /etc/init.d/mysqld56 [root@master local]#chmod +x /etc/init.d/mysqld56 [root@master local]#/etc/init.d/mysqld56 start [root@master local]#/etc/init.d/mysqld56 stop # 第二种启动方式 [root@master local]#systemctl daemon-reload [root@master local]#systemctl start mysqld3356.service [root@master local]#systemctl enable mysqld3356.service Created symlink from /etc/systemd/system/multi-user.target.wants/mysqld3356.service to /usr/lib/systemd/system/mysqld3356.service. 三个服务状态验证（全都running）\n1 2 3 4 5 6 7 8 [root@master local]#for svc in mysqld mysqld3356 mysqld3357; do \u0026gt; a=$(systemctl show -p ActiveState \u0026#34;$svc\u0026#34; | cut -d= -f2) \u0026gt; s=$(systemctl show -p SubState \u0026#34;$svc\u0026#34; | cut -d= -f2) \u0026gt; echo \u0026#34;$svc: $a ($s)\u0026#34; \u0026gt; done mysqld: active (running) mysqld3356: active (running) mysqld3357: active (running) ","permalink":"https://LMDAWN1127.github.io/posts/systemd-mysql-multiversion/","summary":"围绕 systemd Unit 文件管理与 MySQL 5.6/5.7 多版本、多实例部署实战，涵盖两种生产守护进程配置、归档包下载、初始化、专属配置文件编写，以及启动/停止与三服务状态验证。","title":"systemd 服务管理与 MySQL 多版本部署"},{"content":"01-数据库服务安装部署 1.1 数据库服务程序下载 官方下载地址（历史归档二进制安装包）：\n1 2 3 # 存放目录 [root@master ~]#cd /usr/local/ [root@master local]#wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 查看安装包信息\n1 2 [root@master local]#ll mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz -rw-r--r-- 1 root root 914806904 Jul 2 2021 mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 1.2 二进制方式安装数据库服务 步骤一：操作系统环境准备 1. 确认网络地址 1 2 3 4 5 6 7 [root@master local]#ip addr show eth0 2: eth0: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:4b:b7:38 brd ff:ff:ff:ff:ff:ff inet 11.0.1.101/24 brd 11.0.1.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe4b:b738/64 scope link valid_lft forever preferred_lft forever 2. 配置主机名解析 1 2 3 4 [root@master local]#vim /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 11.0.1.101 master 3. 关闭系统防火墙 1 [root@master local]#systemctl disable firewalld --now 4. 关闭 SELinux 1 2 3 4 5 [root@master local]#sed -i \u0026#39;s/^SELINUX=enforcing/SELINUX=disabled/\u0026#39; /etc/selinux/config # 重启生效 [root@master local]#reboot [root@master local]#getenforce Disabled 5. 避免数据库服务安装产生冲突 1 2 3 4 5 6 # 查看系统中是否存在自带的 mariadb 数据库程序 [root@master local]#rpm -qa | grep mariadb mariadb-libs-5.5.68-1.el7.x86_64 # 将系统自带的数据库程序包卸载 [root@master local]#yum remove -y mariadb-libs 6. 安装数据库程序依赖软件（缺少会导致初始化报错） 1 [root@master local]#yum install -y libaio-devel 步骤二：安装数据库软件程序 1. 解压安装包并创建软链接（便于后续版本管理） 1 2 [root@master local]#tar xf mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz [root@master local]#ln -s mysql-8.0.26-linux-glibc2.12-x86_64 mysql 2. 配置系统全局环境变量 1 2 3 4 5 [root@master local]#vim /etc/profile # 在文件末尾添加 export PATH=$PATH:/usr/local/mysql/bin # 生效配置 [root@master local]#source /etc/profile 3. 验证安装版本 1 2 [root@master local]#mysql -V mysql Ver 8.0.26 for Linux on x86_64 (MySQL Community Server - GPL) 步骤三：进行软件程序初始化操作 MySQL两个核心目录：\n程序目录：/usr/local/mysql 数据目录：/data/3306/data（存储实际数据信息） 1. 创建专用运行用户（无需系统登录权限） 1 [root@master local]#useradd mysql -M -s /sbin/nologin 2. 创建数据存储目录并授权 1 2 [root@master local]#mkdir /data/3306/data -p [root@master local]#chown mysql.mysql /data/3306/data 3. 执行数据目录初始化 1 2 3 4 5 # 清理可能存在的干扰配置文件 [root@master local]#rm -f /etc/my.cnf # 执行无密码初始化（初始化后管理员无密码） [root@master local]#mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 步骤四：编写数据库服务配置文件 创建并编辑 /etc/my.cnf：\n1 2 3 4 5 6 7 8 9 10 [root@master local]#cat \u0026gt; /etc/my.cnf \u0026lt;\u0026lt; \u0026#39;eof\u0026#39; \u0026gt; [mysql] \u0026gt; socket=/tmp/mysql.sock \u0026gt; \u0026gt; [mysqld] \u0026gt; user=mysql \u0026gt; basedir=/usr/local/mysql \u0026gt; datadir=/data/3306/data \u0026gt; socket=/tmp/mysql.sock \u0026gt; eof 注：配置文件中 basedir 与 datadir 路径需结合自己实际环境进行调整。\n步骤五：启动数据库服务程序 1. 利用官方服务管理脚本启动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@master local]#cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld [root@master local]#/etc/init.d/mysqld start Starting MySQL.Logging to \u0026#39;/data/3306/data/master.err\u0026#39;. . SUCCESS! # 能够正常进入数据库说明启动成功 [root@master local]#mysql -uroot Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; 2. 利用命令行直接启动 通过 mysqld 命令启动： 1 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data/3306/data --user=mysql --socket=/tmp/mysql.sock \u0026amp; 通过 mysqld_safe 安全守护进程启动： 1 /usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data \u0026amp; 3. 利用 systemctl / service 启动 方式一：编写专属的 Systemd Unit 文件（/etc/systemd/system/mysqld.service） 方式二：直接激活并调用 /etc/init.d/ 目录下的服务脚本： 1 2 3 4 systemctl enable mysqld service mysqld start # 或 systemctl start mysqld 1.3 补充知识介绍 1. 初始化操作的两种方式对比 方式一：不安全初始化（--initialize-insecure）\n1 mysqld --initialize-insecure --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 特点：数据库管理员没有密码，可以直接免密登录数据库，便于测试或脚本初始化。 方式二：安全初始化（--initialize）\n1 mysqld --initialize --user=mysql --datadir=/data/3306/data --basedir=/usr/local/mysql 特点：数据库管理员会生成随机临时密码，打印在日志中，用于首次登录数据库。 注意事项：临时密码只能用于首次登录数据库；登录成功后必须重新设置新密码，在修改密码前无法执行其他操作： 1 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 2. 数据库启动失败排错标准流程 步骤一：确认系统基础环境 检查 SELinux 是否已彻底关闭； 检查端口 3306 是否被占用，是否存在其他冲突的数据库服务进程。 步骤二：排查与分析错误日志 典型报错：Starting MySQL. ERROR! The server quit without updating PID file (/data/3306/data/master.pid). 查看错误日志明细： 1 2 3 cat /data/3306/data/master.err # 日志错误示例: # 2024-06-14T09:15:36.120048Z 1 [ERROR] [MY-012271] [InnoDB] The innodb_system data file \u0026#39;ibdata1\u0026#39; must be writable 步骤三：没有错误日志时的排查方法 检查配置文件 /etc/my.cnf： 确认配置文件所在路径与文件命名是否正确； 确认配置文件内部参数语法及路径配置是否正确。 02-数据库服务启动关闭 有多种方式可以启动与关闭数据库服务，常见方式汇总如下：\n序号 启动方式 对应关闭方式 说明与建议 01 mysqld \u0026amp; kill / pkill / killall 不建议；严禁使用 kill -9 02 mysqld_safe \u0026amp; kill / pkill / killall 不建议直接 kill 03 mysql.server start mysql.server stop 官方原生控制脚本 04 service mysqld start service mysqld stop 建议使用（经典服务管理） 05 systemctl start mysqld systemctl stop mysqld 建议使用（现代 Linux 标准） 06 客户端/管理命令 mysqladmin -uroot -p密码 shutdown 管理员远程或本地命令行优雅关闭 07 SQL 会话内关闭 进入终端执行 shutdown; 需管理员权限 03-数据库服务密码管理 1. 没有密码时设置密码 方式一：利用命令行操作\n1 2 3 [root@master local]#mysqladmin -uroot password \u0026#39;123456\u0026#39; mysqladmin: [Warning] Using a password on the command line interface can be insecure. Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety. 方式二：利用 SQL 语句操作\n1 2 mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;000000\u0026#39;; Query OK, 0 rows affected (0.02 sec) 2. 已有密码时修改密码 方式一：利用命令行操作 1 2 3 [root@master local]#mysqladmin -uroot -p000000 password \u0026#39;123456\u0026#39; mysqladmin: [Warning] Using a password on the command line interface can be insecure. Warning: Since password will be sent to server in plain text, use ssl connection to ensure password safety. 方式二：利用 SQL 语句操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master local]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 14 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; 3. 已有密码但是忘记密码（密码重置） 重置密码实操流程 步骤一：关闭数据库服务\n1 mysql\u0026gt; shutdown; 或在系统终端执行：\n1 2 3 4 [root@master local]#service mysqld stop Shutting down MySQL.. SUCCESS! # 或 pkill mysqld 步骤二：采用安全模式启动数据库（跳过授权表加载，实现免密登录）\n1 2 [root@master local]#/usr/local/mysql/bin/mysqld_safe --datadir=/data/3306/data --skip-grant-tables --skip-networking \u0026amp; [1] 2658 说明：--skip-networking 用于禁用网络监听，防止在免密期间其他网络客户端介入。\n步骤三：免密登录并重置密码\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master local]#mysql Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 7 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; FLUSH PRIVILEGES; Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; 在 MySQL 终端中依次执行：\n1 2 3 4 5 6 7 -- 1. 刷新权限表（同步内存与磁盘中的授权表数据） -- 作用一：将内存中授权表信息同步写入磁盘 -- 作用二：将磁盘中授权表信息重新加载到内存 FLUSH PRIVILEGES; -- 2. 重置密码 ALTER USER root@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; 注意：若未执行 FLUSH PRIVILEGES; 直接执行 ALTER USER，系统会抛出报错：\n1 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement 步骤四：重新正常启动数据库服务\n1 2 3 4 5 6 7 # 终止临时安全模式进程 [root@master local]#pkill mysqld # 正常启动数据库服务 [root@master local]#/etc/init.d/mysqld start Starting MySQL.. SUCCESS! # 或 service mysqld start 步骤五：使用新密码进行登录访问验证\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master local]#mysql -uroot -p123456 mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 8 Server version: 8.0.26 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; ","permalink":"https://LMDAWN1127.github.io/posts/mysql-binary-deploy/","summary":"从官方二进制包安装 MySQL 8.0，涵盖系统环境准备、初始化、配置文件编写、多种启动/关闭方式，以及密码设置、修改与忘记密码重置的完整运维实战。","title":"MySQL 实战：二进制安装部署、启动关闭与密码管理"},{"content":"01. sonarQube简介 1 2 3 1. sonarQube基于java开发，需安装open JDK8版本 2. sonarQube需要依赖MySQL数据库，至少5.6版本以上 3. sonarQube的小型实例至少4G内存，大型实例需要16G内存 02. 部署服务 一. 安装运行环境JDK 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@sonarqube ~]#ll total 166040 -rw-r--r-- 1 root root 170023183 Oct 1 22:32 jdk-8u181-linux-x64.rpm [root@sonarqube ~]#rpm -ivh jdk-8u181-linux-x64.rpm warning: jdk-8u181-linux-x64.rpm: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk1.8-2000:1.8.0_181-fcs ################################# [100%] Unpacking JAR files... tools.jar... plugin.jar... javaws.jar... deploy.jar... rt.jar... jsse.jar... charsets.jar... localedata.jar... 二. 安装数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 # 1. 下载MySQL官方的Yum仓库配置文件 [root@sonarqube ~]#wget dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm # 2. 安装MySQL源 [root@sonarqube ~]#rpm -ivh mysql-community-release-el6-5.noarch.rpm Preparing... ################################# [100%] Updating / installing... 1:mysql-community-release-el6-5 ################################# [100%] # 3. 安装MySQL服务器 [root@sonarqube ~]# yum install mysql-community-server -y [root@sonarqube ~]#mysql --version mysql Ver 14.14 Distrib 5.6.51, for Linux (x86_64) using EditLine wrapper # 4. 启动MySQL服务 [root@sonarqube ~]#service mysqld start Starting mysqld (via systemctl): [ OK ] [root@sonarqube ~]#netstat -ntpl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1227/master tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1008/sshd tcp6 0 0 ::1:25 :::* LISTEN 1227/master tcp6 0 0 :::3306 :::* LISTEN 3113/mysqld tcp6 0 0 :::22 :::* LISTEN 1008/sshd # 5. 设置密码 [root@sonarqube ~]# mysqladmin -uroot password 000000 # 6. 创建sonarqube库 [root@sonarqube ~]# mysql -uroot -p000000 -e \u0026#34;CREATE DATABASE sonarqube DEFAULT CHARACTER SET utf8;\u0026#34; [root@sonarqube ~]#mysql -u root -p000000 -e \u0026#34;show databases\u0026#34; Warning: Using a password on the command line interface can be insecure. +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sonarqube | +--------------------+ 三. 安装sonarqubeqube 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 # 1. 上传soanrqube [root@sonarqube ~]# cd /usr/local/ [root@sonarqube local]#ll sonarqube-7.0.zip -rw-r--r-- 1 root root 155709573 Oct 1 22:50 sonarqube-7.0.zip # 2. 解压到当前目录 [root@sonarqube local]#yum install -y unzip [root@sonarqube local]# unzip sonarqube-7.0.zip [root@sonarqube local]# ln -s sonarqube-7.0 sonarqube # 3. 安装插件: 删除默认的插件: [root@sonarqube ~]# rm -rf /usr/local/sonarqube/extensions/plugins/* 上传插件: [root@sonarqube ~]#ll sonar_plugins.tar.gz -rw-r--r-- 1 root root 45106788 Oct 1 22:56 sonar_plugins.tar.gz [root@sonarqube ~]#tar xf sonar_plugins.tar.gz -C /usr/local/sonarqube/extensions/plugins/ [root@sonarqube ~]#ll /usr/local/sonarqube/extensions/plugins/ total 4 drwxr-xr-x 2 1000 1000 4096 Oct 24 2019 plugins [root@sonarqube ~]# cd /usr/local/sonarqube/extensions/plugins/ [root@sonarqube plugins]# mv plugins/* . [root@sonarqube plugins]#rm -fr plugins/ # 4. 创建普通用户 [root@sonarqube ~]# useradd sonarqube # 5. 授权代码属主属组为sonarqube [root@sonarqube ~]# chown -R sonarqube.sonarqube /usr/local/sonarqube/ [root@sonarqube ~]# ll /usr/local/sonarqube/ total 12 drwxr-xr-x 8 sonarqube sonarqube 136 Feb 2 2018 bin drwxr-xr-x 2 sonarqube sonarqube 50 May 15 15:09 conf -rw-r--r-- 1 sonarqube sonarqube 7651 Feb 2 2018 COPYING drwxr-xr-x 2 sonarqube sonarqube 24 Feb 2 2018 data drwxr-xr-x 7 sonarqube sonarqube 150 Feb 2 2018 elasticsearch drwxr-xr-x 4 sonarqube sonarqube 40 Feb 2 2018 extensions drwxr-xr-x 9 sonarqube sonarqube 138 Feb 2 2018 lib drwxr-xr-x 2 sonarqube sonarqube 6 Feb 2 2018 logs drwxr-xr-x 2 sonarqube sonarqube 24 Feb 2 2018 temp drwxr-xr-x 9 sonarqube sonarqube 4096 Feb 2 2018 web # 6. 配置连接数据库 [root@sonarqube ~]#vim /usr/local/sonarqube/conf/sonar.properties ... sonar.jdbc.username=root # 数据库用户名 sonar.jdbc.password=000000 # 数据库密码 #----- Embedded Database (default) # H2 embedded database server listening port, defaults to 9092 #sonarqube.embeddedDatabase.port=9092 #----- MySQL 5.6 or greater # Only InnoDB storage engine is supported (not myISAM). # Only the bundled driver is supported. It can not be changed. sonar.jdbc.url=jdbc:mysql://localhost:3306/sonarqube?useUnicode=true\u0026amp;characterE ncoding=utf8\u0026amp;rewriteBatchedStatements=true\u0026amp;useConfigs=maxPerformance\u0026amp;useSSL =false # 开启本地连接 ... 四. 运行sonarqubeQube 1 2 sonarqubeQube服务启动不能使用root,必须使用普通用户运行 [root@sonarqube ~]# su - sonarqube -c \u0026#34;/usr/local/sonarqube/bin/linux-x86-64/sonar.sh start\u0026#34; 五. 配置远程访问token 启动后的端口为9000使用浏览器直接访问http://11.0.1.202:9000/about 用户名admin 密码admin 生成一个tokin,只显示一次记录好。\n1 2 3 4 5 [root@jenkins ~]# sonar-scanner \\ -Dsonar.projectKey=html \\ -Dsonar.sources=. \\ -Dsonar.host.url=http://11.0.1.202:9000 \\ -Dsonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b 六. 客户端部署 客户端IP:11.0.1.201\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 1. 上传客户端 [root@jenkins ~]# cd /usr/local/ [root@jenkins ~]#ll sonar-scanner-cli-4.2.0.1873-linux.zip -rw-r--r-- 1 root root 42397119 Oct 2 00:09 sonar-scanner-cli-4.2.0.1873-linux.zip # 2. 解压做软链接 [root@jenkins local]#yum install -y unzip [root@jenkins local]#unzip /root/sonar-scanner-cli-4.2.0.1873-linux.zip [root@jenkins local]# ln -s sonar-scanner-4.2.0.1873-linux sonar # 3. 设置PATH全局变量 [root@jenkins ~]# export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 4. 永久变量写入/etc/profile [root@jenkins ~]# vim /etc/profile .. export PATH=\u0026#34;$PATH:/usr/local/sonar/bin/\u0026#34; # 5. 客户端测试将代码扫描到服务器进行代码检测 将/var/lib/jenkins/workspace/test_job/目录下的blog代码推送到SonrQube测试 [root@jenkins ~]#cd /var/lib/jenkins/workspace/test_job/ [root@jenkins test_job]#ll total 8 -rw-r--r-- 1 root root 7 Oct 1 20:32 a.txt -rw-r--r-- 1 root root 3634 Oct 1 20:32 blog_state.txt -rw-r--r-- 1 root root 0 Oct 1 21:12 c.txt drwxr-xr-x 2 root root 6 Oct 1 20:32 myblog drwxr-xr-x 2 root root 6 Oct 1 20:32 myblog-deploy [root@jenkins test_job]#sonar-scanner \\ -Dsonar.projectKey=html \\ -Dsonar.sources=. \\ -Dsonar.host.url=http://11.0.1.202:9000 \\ -Dsonar.login=a3bd8e829343edabbbc58fefe0d3e97fdb12268b 执行成功:\n查看服务器: 上传成功\n","permalink":"https://LMDAWN1127.github.io/posts/sonarqube-practice/","summary":"从 JDK8 + MySQL5.6 安装、SonarQube 解压与插件部署、普通用户启动，到 Web Token 配置与 sonar-scanner 客户端代码扫描的完整私有化实战。","title":"SonarQube 实战：私有化部署、Token 配置与代码质量扫描"},{"content":"01. Jenkins简介 官网 jenkins.io\nJenkins是一个开源软件项目，是基于Java开发的一种持续集成工具，用于监控持续重复的工作，旨在提供一个开放易用的软件平台，使软件的持续集成变成可能。\n02. Jenkins部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 一.环境准备 1、CentOS7.9 2、内存：实验（2G）生产（至少6G） 3、禁用防火墙，关闭selinux 4、JDK17 二.安装JDK 上传rpm包 [root@jenkins ~]#ll total 178212 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm [root@jenkins ~]#rpm -ivh jdk-17_linux-x64_bin.rpm warning: jdk-17_linux-x64_bin.rpm: Header V3 RSA/SHA256 Signature, key ID ad986da3: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jdk-17-2000:17.0.11-7 ################################# [100%] 查看是否安装成功: [root@jenkins ~]# rpm -qa|grep jdk jdk-17-17.0.11-7.x86_64 二.安装jenkins 上传jenkins包 [root@jenkins ~]#ll total 269432 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm -rw-r--r-- 1 root root 93405530 Oct 1 19:06 jenkins-2.405-1.1.noarch.rpm [root@jenkins ~]#rpm -ivh jenkins-2.405-1.1.noarch.rpm warning: jenkins-2.405-1.1.noarch.rpm: Header V4 RSA/SHA512 Signature, key ID ef5975ca: NOKEY Preparing... ################################# [100%] Updating / installing... 1:jenkins-2.405-1.1 ################################# [100%] 三.修改Jenkins启动用户为root 1)修改配置文件的启动用户 [root@jenkins ~]# vim /etc/sysconfig/jenkins ... JENKINS_USER=\u0026#34;root\u0026#34; ... 2)修改启动配置的用户 [root@jenkins ~]# vim /usr/lib/systemd/system/jenkins.service ... User=root Group=root ... [root@jenkins ~]# systemctl daemon-reload 3)安装字体配置 [root@jenkins ~]# yum -y install fontconfig 四.启动Jenkins并加入开机自动启动 [root@jenkins ~]# systemctl start jenkins [root@jenkins ~]# systemctl enable jenkins 03. 登陆Jenkins 04. 修改密码 修改完密码后会重新登陆,用户名为admin\n05. 插件安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1.上传插件到jenkins家目录 [root@jenkins ~]# cd /var/lib/jenkins/ [root@jenkins ~]#ll total 576228 -rw-r--r-- 1 root root 182486159 Oct 1 19:04 jdk-17_linux-x64_bin.rpm -rw-r--r-- 1 root root 93405530 Oct 1 19:06 jenkins-2.405-1.1.noarch.rpm -rw-r--r-- 1 root root 314156543 Oct 1 19:30 jenkins_plu.tar.gz 2.将插件解压到plugins目录中 [root@jenkins jenkins]#tar xf /root/jenkins_plu.tar.gz -C plugins/ 3.重启Jenkins生效 [root@jenkins ~]# systemctl restart jenkins 06. 创建项目 创建一个自由风格项目,执行pwd命令,查看使用jenkins运行默认的路径所在位置\n说明: 使用jenkins创建完项目后,系统会自动在jenkins的家目录/var/lib/jenkins/下创建一个workspace项目目录，并且在此目录下创建以项目名称命名的目录。\n后续jenkins拉取gitlab的代码就是存储在此项目目录下。\n1 2 [root@jenkins ~]#ls /var/lib/jenkins/workspace/ test_job 将gitlab与jenkins打通\n没打通前提示无法连接：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #配置免密钥 [root@jenkins ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Rr4ouinx/cPqVxJsxju2Cjd1mhWmWSuKGOKG532uTvk root@jenkins The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | | | | | o = | | *B o | |o o=oS | |=o . +=O.. | |o=+.Bo+=. | |oo.B.+* | | .=+BOE. | +----[SHA256]-----+ [root@jenkins ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+kaj/NEwHKaIErMEClVYuG3JuWr9CD7XpuO5YZ6JftMmNdAPMrvS0mWqP4SDyT6/le0j1RGkA48GsirESfMqXKvzSSd5AuhIPmUADtGPNK6VSeiMKyoWgiNBFLn6RIOAcXI0MuZrYQMLJz+kOYr6kF9em/6MfFQrLkt9DzIlnR9b7CMLyDnPOZ8FG640DqqX0sZ8G+i2v41VX/c7RGIWAgEhJY+kiMS5cDxWlTf2k8hoZmh0VIa/G9AwnvbklyUiE4ACd9GtEJG5xXJzedn7q0/Ygfr26T29t+KST3SlyrXIaEbEC5WwIMymCEJDCPPCGzqLjsWa+4dbJhoEM1afV root@jenkins #jenkins安装git [root@jenkins ~]#yum install -y git 在gitlab中添加jenkins生成的密钥：\n需要在jenkins上git clone一下，完成一次yes输入\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@jenkins ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. remote: Enumerating objects: 20, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (8/8), done. remote: Total 20 (delta 0), reused 0 (delta 0), pack-reused 11 Receiving objects: 100% (20/20), done. Resolving deltas: 100% (2/2), done. 然后就可以连接了\n再次构建一次测试拉取\n拉取成功\n配置webhook\n通过配置webhook触发jenkins自动拉取gitlab的代码,自动构建完成测试\n两边保存后测试\n项目修改后重新推送测试\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@gitlab blog]#git branch * master [root@gitlab blog]#touch c.txt [root@gitlab blog]#git add . [root@gitlab blog]#git commit -m \u0026#34;test_v2\u0026#34; [master 7b1f29e] test_v2 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 c.txt [root@gitlab blog]#git pull origin master remote: Enumerating objects: 17, done. remote: Counting objects: 100% (9/9), done. remote: Compressing objects: 100% (8/8), done. remote: Total 17 (delta 0), reused 0 (delta 0), pack-reused 8 Unpacking objects: 100% (17/17), done. From 11.0.1.200:dawn/blog * branch master -\u0026gt; FETCH_HEAD Merge made by the \u0026#39;recursive\u0026#39; strategy. a.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 a.txt [root@gitlab blog]#git push -u origin master Counting objects: 7, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (5/5), 469 bytes | 0 bytes/s, done. Total 5 (delta 2), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git 47f606c..91e80a5 master -\u0026gt; master Branch master set up to track remote branch master from origin. 自动构建成功\n","permalink":"https://LMDAWN1127.github.io/posts/jenkins-practice/","summary":"从 RPM 安装 JDK 与 Jenkins、改为 root 启动、插件离线安装，到打通 GitLab SSH 免密与 webhook 自动构建的完整 Jenkins 私有化实战。","title":"Jenkins 实战：私有化部署、插件安装与 GitLab 联动 CI"},{"content":"01. gitlab相关网站 官网：https://about.gitlab.com/\n国内镜像：https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/\n02.gitlab部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 一.环境准备 1、系统：CentOS7.9 2、内存：实验（4G）生产（至少8G） 3、禁用防火墙，关闭selinux 二.下载gitlab的rpm包 [root@gitlab ~]# wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm 三.安装gitlab # 安装过程中提示需要安装依赖 [root@gitlab ~]# rpm -ivh gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm warning: gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY error: Failed dependencies: policycoreutils-python is needed by gitlab-ce-16.9.8-ce.0.el7.x86_64 # 安装依赖 [root@gitlab ~]# yum -y install policycoreutils-python # 安装完依赖继续安装gitlab [root@gitlab ~]# rpm -ivh gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm warning: gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm: Header V4 RSA/SHA1 Signature, key ID f27eab47: NOKEY Preparing... ################################# [100%] Updating / installing... 1:gitlab-ce-16.9.8-ce.0.el7 ################################# [100%] 四.启动前配置访问域名或IP [root@gitlab ~]# vim /etc/gitlab/gitlab.rb ... external_url \u0026#39;http://11.0.1.200\u0026#39; ... 配置完成后执行命令: [root@gitlab ~]# gitlab-ctl reconfigure 03. gitlab组件 1 2 3 4 5 6 7 nginx： 静态web服务器 gitlab-workhorse: 轻量级的反向代理服务器 logrotate： 日志文件管理工具 postgresql： 数据库 redis： 缓存数据库 sidekiq： 用于在后台执行队列任务（异步执行）。（Ruby） unicorn：An HTTP server for Rack applications，gitlab Rails应用是托管在这个服务器上面的。（Ruby Web Server,主要使用Ruby编写） 04. gitlab常用命令 1 2 3 4 5 gitlab-ctl start # 启动gitlab 在执行gitlab-ctl reconfigure 服务默认已经启动 gitlab-ctl status # 查看目前gitlab所有服务运维状态 gitlab-ctl stop # 停止gitlab服务 gitlab-ctl stop nginx # 单独停止某个服务 gitlab-ctl tail # 查看所有服务的日志 05. gitlab页面配置 登陆gitlab: 11.0.1.200\n一. 登陆密码 执行gitlab-ctl reconfigure完成后会提示密码存放的位置:\n二. 配置页面显示为中文 三. 修改默认密码 四. 开启钩子本地网络请求 06. gitlab实战 一. 创建组 二. 基于组创建项目 至此第一个代码仓库就创建完成了,不过里面是空的。接下来上传代码\n三. 将代码提交到项目中存储 项目创建完成后,需要将blog代码上传到此项目\n服务器和代码仓库连接方式有两种\n第一种: 基于用户名和密码的方式连接\n第二种: 基于SSH免秘钥方式\n我们使用基于免秘钥方式: 方便+安全性高\n1.生成密钥对(在这是直接使用的gitlab服务器)\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@gitlab ~]#ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory \u0026#39;/root/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:j3FozajJdufxM0ZRtgPa2ZNSwWKvo8bvxbMJl8F5V3M root@gitlab The key\u0026#39;s randomart image is: +---[RSA 2048]----+ | ... | | + = | | + X +E| | =. =.B.+| | S + ++oo| | . + = +. +.| | = o.+o..* | | . . o+o++ + | | ..++oo | +----[SHA256]-----+ 查看秘钥对: [root@gitlab ~]# ll .ssh/ total 8 -rw------- 1 root root 1679 May 14 15:02 id_rsa # 私钥 -rw-r--r-- 1 root root 393 May 14 15:02 id_rsa.pub # 公钥 [root@gitlab ~]#cat .ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJGpCKL64eidZ3I/nAVNCNJwD8yeDqelZP0GUTwlcp6IdKaZ7wUOIRDCtGn5r+YnOCKNwMhUNzb3VCOSVLDVIE0x53dPrCMK4OKLmZ6OtTYFBveGOZWWwdYyGQITx2eP7zOSBxeW3AnEWcya/UZdiZrYXA/d29DWjMjCt5QAxOit0EWSir1Z4i2MkTPuljOmjk6A55XAoem0WJRdg/d8KKIiCdcGrj4adYq2kNJ8oYH/9QcnpASUW/ANhzrALnLIX2WaH/p0T3gisLvk2KDUe3rVKD3XaKDlyLgWIViQFm3Me6L2e4taRFiu3VdsbfFfzjDevfDtLaqKLOqPtaczlF root@gitlab 2.将生成的公钥粘贴到gitlab页面的SSH秘钥管理页面\n至此就完成了服务器和gitlab页面之间的SSH免秘钥,接下来将代码推送到blog项目中\n推送代码的方式有两种:\n1.将空的仓库克隆到本地,然后将代码拷贝到仓库中,然后提交到远程仓库gitlab\n进入到项目中找到项目的链接地址\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 1.复制链接执行 [root@gitlab ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. warning: remote HEAD refers to nonexistent ref, unable to checkout. 2.查看将blog仓库下载到了本地 [root@gitlab ~]#ll total 1176964 drwxr-xr-x 3 root root 18 Oct 1 14:43 blog -rw-r--r-- 1 root root 84579612 Oct 1 14:35 blog_backup_20261001-1435.tar.gz drwxr-xr-x 3 root root 18 Sep 30 19:14 dawn -rw-r--r-- 1 root root 1120627456 Sep 30 23:12 gitlab-ce-16.9.8-ce.0.el7.x86_64.rpm 3.进入blog目录 [root@gitlab ~]#cd blog/ 4.解压代码: [root@gitlab blog]#tar xf /root/blog_backup_20261001-1435.tar.gz -C . 5.将代码提交到本地仓库 [root@gitlab blog]#git add . [root@gitlab blog]#git commit -m \u0026#34;DAWN’s Blog\u0026#34; [master (root-commit) 5af4246] DAWN’s Blog 3 files changed, 112 insertions(+) create mode 100644 blog_state.txt create mode 160000 myblog create mode 160000 myblog-deploy 6.将代码提交到远程仓库gitlab的blog仓库中 因为我们是直接克隆的gitlab的仓库下载到本地的所以目录中默认已经有了远程仓库blog的项目地址 查看远程仓库: [root@gitlab blog]#git remote -v origin git@11.0.1.200:dawn/blog.git (fetch) origin git@11.0.1.200:dawn/blog.git (push) 将本地的master推送到远程仓库origin [root@gitlab blog]#git push -u origin master Counting objects: 3, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 1.23 KiB | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git * [new branch] master -\u0026gt; master Branch master set up to track remote branch master from origin. 查看blog项目,代码上传成功\n2.将本地git仓库推送到远程仓库gitlab\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 1.配置一个本地仓库 [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn 2.初始化仓库 [root@gitlab dawn]#git init 3.上传代码 [root@gitlab dawn]#touch a.txt [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;test_v1\u0026#34; [master (root-commit) 815c600] test_v1 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt 4.设置gitlab的blog仓库为本地的远程仓库 [root@gitlab dawn]#git remote add origin git@11.0.1.200:dawn/blog.git 5.将本地仓库的内容提交到远程仓库（由于远程仓库 origin 里已经有内容了，直接推送报错，在这里需要保留远程内容，所以先拉取合并再推送） [root@gitlab dawn]#git pull origin master warning: no common commits remote: Enumerating objects: 3, done. remote: Counting objects: 100% (3/3), done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From 11.0.1.200:dawn/blog * branch master -\u0026gt; FETCH_HEAD Merge made by the \u0026#39;recursive\u0026#39; strategy. blog_state.txt | 110 ++++++++++++++++++++++++++++++++++++ myblog | 1 + myblog-deploy | 1 + 3 files changed, 112 insertions(+) create mode 100644 blog_state.txt create mode 160000 myblog create mode 160000 myblog-deploy [root@gitlab dawn]#git push -u origin master Counting objects: 6, done. Delta compression using up to 2 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (5/5), 462 bytes | 0 bytes/s, done. Total 5 (delta 1), reused 0 (delta 0) To git@11.0.1.200:dawn/blog.git 5af4246..0242e0a master -\u0026gt; master Branch master set up to track remote branch master from origin. 3.创建普通账号dev\n创建完成后修改dev密码-别退出，点击用户界面的编辑\n完成后使用dev登录gitlab(第一次登录需要修改密码)\n4.将dev用户添加到dawn组\n5.dev的服务器端生成秘钥对\n1 [root@lb01 ~]#ssh-keygen 6.将生成的公钥复制到dev账号下的ssh-key页面中\n7.克隆代码到11.0.1.5 dev\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@lb01 ~]#git clone git@11.0.1.200:dawn/blog.git Cloning into \u0026#39;blog\u0026#39;... The authenticity of host \u0026#39;11.0.1.200 (11.0.1.200)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Ldn1vBzBEFxynXs0qBNr0XzJ2lTx/0caMuK5yJtb+Zo. ECDSA key fingerprint is MD5:3d:1d:28:3f:33:ec:ee:c1:69:83:af:14:a3:7f:79:ac. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;11.0.1.200\u0026#39; (ECDSA) to the list of known hosts. remote: Enumerating objects: 8, done. remote: Counting objects: 100% (8/8), done. remote: Compressing objects: 100% (6/6), done. remote: Total 8 (delta 1), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (8/8), done. Resolving deltas: 100% (1/1), done. 8.修改并上传代码到远程仓库\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 设置11.0.1.5的使用者 [root@lb01 blog]#git config --global user.email \u0026#34;dev@mail.com\u0026#34; [root@lb01 blog]#git config --global user.name \u0026#34;dev\u0026#34; [root@lb01 blog]#vim a.txt 123 [root@lb01 blog]#git commit -am \u0026#34;123\u0026#34; 提交到远程仓库 [root@lb01 blog]#git push -u origin master Counting objects: 5, done. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 257 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: GitLab: You are not allowed to push code to protected branches on this project.To git@11.0.1.200:dawn/blog.git ! [remote rejected] master -\u0026gt; master (pre-receive hook declined) error: failed to push some refs to \u0026#39;git@11.0.1.200:dawn/blog.git\u0026#39; # 不允许推送到master分支 推送到其他分支: 1.创建dev分支 [root@lb01 blog]#git branch dev [root@lb01 blog]#git branch dev * master 2.推送dev分支到远程仓库 [root@lb01 blog]#git push -u origin dev 合并请求\n1.在dev账号发起合并请求\n2.在root账号中同意合并并删除源dev分支\n3.在web服务器下载代码\n1 2 [root@web01 ~]#git clone http://11.0.1.200/dawn/blog.git 输入用户名和密码 ","permalink":"https://LMDAWN1127.github.io/posts/gitlab-practice/","summary":"从 RPM 安装、组件构成、常用命令到 Web 配置、SSH 免密与多账号协作的完整 GitLab 私有化部署实战。","title":"GitLab 实战：私有化部署、SSH 免密与代码仓库管理"},{"content":"01.安装git并初始化仓库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@gitlab ~]#yum install -y git [root@gitlab ~]#git --version git version 1.8.3.1 [root@gitlab ~]#git config --global user.name \u0026#34;dawn\u0026#34; [root@gitlab ~]#git config --global user.email \u0026#34;dawn@mail.com\u0026#34; [root@gitlab ~]#git config --global color.ui true [root@gitlab ~]#cat .gitconfig [user] name = dawn email = dawn@mail.com [color] ui = true [root@gitlab ~]#mkdir dawn [root@gitlab ~]#cd dawn [root@gitlab dawn]#ll total 0 [root@gitlab dawn]#git init Initialized empty Git repository in /root/dawn/.git/ [root@gitlab dawn]#ll total 0 [root@gitlab dawn]#ll -a total 0 drwxr-xr-x 3 root root 18 Jun 11 10:57 . dr-xr-x---. 4 root root 174 Jun 11 10:57 .. drwxr-xr-x 7 root root 119 Jun 11 10:57 .git 02.Git命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 1.查看仓库的状态(时刻保证仓库的干净) [root@gitlab dawn]#git status # On branch master # # Initial commit # nothing to commit (create/copy files and use \u0026#34;git add\u0026#34; to track) 2.创建普通文件 [root@gitlab dawn]#touch a.txt 3.将工作区的内容提交到暂存区 [root@gitlab dawn]#git add a.txt #git add .表示所有工作区的内容提交到暂存区 [root@gitlab dawn]#git add . 4.删除暂存区的内容 [root@gitlab dawn]#git rm --cached a.txt rm \u0026#39;a.txt\u0026#39; 5.提交到暂存区和本地仓库 [root@gitlab dawn]#git add a.txt [root@gitlab dawn]#git status # On branch master # # Initial commit # # Changes to be committed: # (use \u0026#34;git rm --cached \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # new file: a.txt # #使用git commit -m描述 提交到本地仓库 [root@gitlab dawn]#git commit -m \u0026#34;create a.txt\u0026#34; [master (root-commit) b65d348] create a.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a.txt 查看状态: [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 创建文件 touch a.txt 提交到暂存区 git add a.txt 提交到本地仓库 git commit -m \u0026#34;xxx\u0026#34; 2.git删除 [root@gitlab dawn]#git rm a.txt rm \u0026#39;a.txt\u0026#39; [root@gitlab dawn]#git status # On branch master # Changes to be committed: # (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # deleted: a.txt # [root@gitlab dawn]#git commit -m \u0026#34;del a.txt\u0026#34; [master 41fc71b] del a.txt 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 a.txt [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 注意: 如果工作区的文件提交到了暂存区域反悔了,删除暂存区的内容 [root@gitlab dawn]#touch c.txt [root@gitlab dawn]#git add c.txt [root@gitlab dawn]#git rm --cached c.txt rm \u0026#39;c.txt\u0026#39; 3.恢复工作区内容 [root@gitlab dawn]#git reset HEAD b.txt [root@gitlab dawn]#git checkout -- b.txt 4.git比对 git diff 比对的是工作区域和暂存区域的不同 [root@gitlab dawn]#git diff diff --git a/b.txt b/b.txt index e69de29..ccc3e7b 100644 --- a/b.txt +++ b/b.txt @@ -0,0 +1 @@ +aaaaa git diff --cached 比对的是暂存区和本地仓库的不同 [root@gitlab dawn]#git diff --cached diff --git a/b.txt b/b.txt index e69de29..ccc3e7b 100644 --- a/b.txt +++ b/b.txt @@ -0,0 +1 @@ +aaaaa 5.改名 [root@gitlab dawn]#git mv b.txt b.bak [root@gitlab dawn]#git status # On branch master # Changes to be committed: # (use \u0026#34;git reset HEAD \u0026lt;file\u0026gt;...\u0026#34; to unstage) # # renamed: b.txt -\u0026gt; b.bak # [root@gitlab dawn]#git commit -m \u0026#34;newname b.txt--\u0026gt;b.bak\u0026#34; [master 55335a7] newname b.txt--\u0026gt;b.bak 1 file changed, 0 insertions(+), 0 deletions(-) rename b.txt =\u0026gt; b.bak (100%) [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean 6.指定回滚到哪个快照 显示所有的快照: [root@gitlab dawn]#git log --oneline ae6585c xx 55335a7 newname b.txt--\u0026gt;b.bak 09ffe53 modifile b.txt 043a297 newfile b.txt 41fc71b del a.txt b65d348 create a.txt [root@gitlab dawn]#ll total 8 -rw-r--r-- 1 root root 6 Jun 11 12:10 b.bak -rw-r--r-- 1 root root 6 Jun 11 12:07 b.txt 使用git reset --hard 回滚 [root@gitlab dawn]#git reset --hard b65d348 HEAD is now at b65d348 create a.txt [root@gitlab dawn]#ll total 0 -rw-r--r-- 1 root root 0 Jun 11 12:12 a.txt 7.查看历史所有的提交动作: [root@gitlab dawn]#git reflog b65d348 HEAD@{0}: reset: moving to b65d348 ae6585c HEAD@{1}: commit: xx 55335a7 HEAD@{2}: commit: newname b.txt--\u0026gt;b.bak 09ffe53 HEAD@{3}: commit: modifile b.txt 043a297 HEAD@{4}: commit: newfile b.txt 41fc71b HEAD@{5}: commit: del a.txt b65d348 HEAD@{6}: commit (initial): create a.txt [root@gitlab dawn]#git reset --hard ae6585c HEAD is now at ae6585c xx [root@gitlab dawn]#ll total 8 -rw-r--r-- 1 root root 6 Jun 11 12:16 b.bak -rw-r--r-- 1 root root 6 Jun 11 12:16 b.txt Git分支 1.查看git分支 *表示当前所在的分支 [root@gitlab dawn]#git branch * master 2.创建分支 [root@gitlab dawn]#git branch dev [root@gitlab dawn]#git branch dev * master 3.切换到dev分支 [root@gitlab dawn]#git checkout dev Switched to branch \u0026#39;dev\u0026#39; [root@gitlab dawn]#git branch * dev master 4.分支创建文件并且提交到本地仓库 [root@gitlab dawn]#touch aa.txt [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;newfile aa.txt\u0026#34; [dev 2694c7f] newfile aa.txt 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aa.txt 5.合并分支 先切换回master分支 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master 合并dev分支到master分支 [root@gitlab dawn]#git merge dev Updating ae6585c..2694c7f Fast-forward aa.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aa.txt 6.分支写完功能后立刻删除(因为代码会落后于远程分支) [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 2694c7f). [root@gitlab dawn]#git branch * master 7.冲突合并 ---------------------------------- 修改文件的内容不影响分支的合并 修改主干分支的文件增加bbbbb [root@gitlab dawn]#vim b.txt aaaaa bbbbb 使用一条命令可以直接创建并切换到dev分支 [root@gitlab dawn]#git checkout -b dev Switched to a new branch \u0026#39;dev\u0026#39; [root@gitlab dawn]# [root@gitlab dawn]# [root@gitlab dawn]#git branch * dev master 修改b.txt的内容 [root@gitlab dawn]#vim b.txt aaaaa bbccc 提交到本地仓库 [root@gitlab dawn]#git add . [root@gitlab dawn]#git commit -m \u0026#34;modifle bbbccc\u0026#34; 返回到master分支将dev分支合并 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master [root@gitlab dawn]#cat b.txt aaaaa bbbbb [root@gitlab dawn]#git merge dev Updating f56faba..dc17f6d Fast-forward b.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) [root@gitlab dawn]#cat b.txt aaaaa bbccc ------------------------------------------------------ 影响冲突合并的案例 创建dev分支 [root@gitlab dawn]#git branch dev 在master分支修改b.txt第3行 [root@gitlab dawn]#cat b.txt aaaaa iiiii bbbbb [root@gitlab dawn]#git commit -am \u0026#34;modifile b.txt bbbbb\u0026#34; [master ae4ab87] modifile b.txt bbbbb 1 file changed, 1 insertion(+) 在切换到dev分支修改b.txt第3行 [root@gitlab dawn]#cat b.txt aaaaa iiiii ccccc 提交到本地仓库 [root@gitlab dawn]#git commit -am \u0026#34;modifile b.txt ccccc\u0026#34; [dev 925c06c] modifile b.txt ccccc 1 file changed, 1 insertion(+) 在切换到master分支进行分支合并 [root@gitlab dawn]#git checkout master Switched to branch \u0026#39;master\u0026#39; [root@gitlab dawn]#git branch dev * master [root@gitlab dawn]#git merge dev Auto-merging b.txt CONFLICT (content): Merge conflict in b.txt Automatic merge failed; fix conflicts and then commit the result. 查看文件显示代码冲突 [root@gitlab dawn]#cat b.txt aaaaa iiiii \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt; HEAD bbbbb ======= ccccc \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; dev 手动解决代码冲突 留下一行或者全部留下 [root@gitlab dawn]#vim b.txt aaaaa iiiii bbbbb ccccc [root@gitlab dawn]#git commit -am \u0026#34;merge dev\u0026#34; [master 325eef2] merge dev [root@gitlab dawn]# [root@gitlab dawn]# [root@gitlab dawn]#git status # On branch master nothing to commit, working directory clean [root@gitlab dawn]#cat b.txt aaaaa iiiii bbbbb ccccc 合并后删除dev分支 [root@gitlab dawn]#git branch -d dev Deleted branch dev (was 925c06c). ","permalink":"https://LMDAWN1127.github.io/posts/git-practice-notes/","summary":"从安装配置到常用命令，再到分支管理与冲突合并，一份完整的 Git 实操记录。","title":"Git 实战笔记：安装配置、常用命令与分支管理"},{"content":"1. 获取系统的登录用户数 1 2 3 4 5 6 7 [root@web02 ~]#w 17:44:18 up 1:17, 2 users, load average: 0.01, 0.02, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 11.0.1.1 16:31 11:46 0.07s 0.07s -bash root pts/1 11.0.1.52 17:07 2.00s 0.04s 0.00s w [root@web02 ~]#w|awk \u0026#39;NR==1{print $4}\u0026#39; 2 2. 定义为 zabbix 的监控项 1 2 3 4 5 6 7 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; # 查看监控项 zabbix_agentd -p 有语法检测功能 [root@web02 ~]#zabbix_agentd -p|grep login_number login_number [t|2] # 重启客户端 [root@web02 ~]# systemctl restart zabbix-agent.service 3. 使用 zabbix 服务器端进行采集监控 服务端通过命令行测试是否通过监控项来获取用户自定义的值\n1 2 3 4 # 安装 zabbix-get 命令 [root@zabbix ~]# yum -y install zabbix-get [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k login_number 2 在 Web 页面添加监控项\n查看监控项\n4. 监控 TCP 22 端口是否存活 键值在选择中选取需要监控的信息\n检查 TCP 端口是否处于侦听状态。返回 0 - 未侦听；1 - 正在侦听\n停止 web02 sshd 服务\n1 [root@web02 ~]# systemctl stop sshd 5. 自定义配置展示值映射 调用值映射\n6. 自定义监控服务器内存百分比 第一步：自定义监控项，获取可用百分比\n1 2 3 4 5 6 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; UserParameter=mem_free,free|awk \u0026#39;NR==2{print $NF/$2*100}\u0026#39; [root@web02 ~]# systemctl restart zabbix-agent.service [root@web02 ~]#zabbix_agentd -p|grep mem_free mem_free [t|79.8753] 第二步：服务端测试获取 key 值并添加监控\n1 2 [root@zabbix ~]#zabbix_get -s 172.16.1.8 -k mem_free 79.9775 7. 为可用内存设置触发器 当内存可用百分比小于 20% 则触发报警规则\n测试：关闭 swap 分区\n1 [root@web02 ~]# swapoff -a 使用 dd 压测消耗内存\n1 [root@web02 ~]# dd if=/dev/zero of=/dev/null bs=1200M count=2048 8. 配置多条件触发器 同时满足两个条件触发告警\n添加 swap 可用百分比\n1 2 3 4 [root@web02 ~]# cat /etc/zabbix/zabbix_agentd.d/system.conf UserParameter=login_number,w|awk \u0026#39;NR==1{print $4}\u0026#39; UserParameter=mem_free,free|awk \u0026#39;NR==2{print $NF/$2*100}\u0026#39; UserParameter=swap_free,free -m|awk \u0026#39;NR==3{print $3/$2*100}\u0026#39; 修改触发器配置\n完成后在使用 dd 压测查看结果\n","permalink":"https://LMDAWN1127.github.io/posts/zabbix-custom-monitor/","summary":"Zabbix 自定义监控实战：用 UserParameter 采集登录用户数/内存可用百分比等自定义指标，通过 zabbix_get 验证，在 Web 页面添加监控项、值映射、触发器（含多条件触发器）实现自定义告警。","title":"Zabbix 自定义监控"},{"content":"企业 zabbix 高并发场景使用\n1. 准备一台数据库服务器 11.0.1.52 安装 MariaDB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@db02 ~]#yum -y install mariadb-server [root@db02 ~]#systemctl enable mariadb --now [root@db02 ~]#mysql Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 2 Server version: 5.5.68-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; create database zabbix character set utf8 collate utf8_bin; Query OK, 1 row affected (0.02 sec) MariaDB [(none)]\u0026gt; create user \u0026#39;zabbix\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#39;zabbix\u0026#39;; Query OK, 0 rows affected (0.10 sec) MariaDB [(none)]\u0026gt; grant all privileges on zabbix.* to \u0026#39;zabbix\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.00 sec) 2. 导出 Zabbix 服务器 zabbix 数据库、导入到新的数据库 1 2 [root@zabbix ~]# mysqldump -uroot -B zabbix \u0026gt; zabbix.sql [root@zabbix ~]# cat zabbix.sql |mysql -h 172.16.1.52 -uzabbix -pzabbix zabbix 3. 配置 Web 连接数据库信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@zabbix ~]# systemctl stop mariadb [root@zabbix ~]# cat /etc/zabbix/web/zabbix.conf.php \u0026lt;?php // Zabbix GUI configuration file. global $DB; $DB[\u0026#39;TYPE\u0026#39;] = \u0026#39;MYSQL\u0026#39;; $DB[\u0026#39;SERVER\u0026#39;] = \u0026#39;172.16.1.52\u0026#39;; $DB[\u0026#39;PORT\u0026#39;] = \u0026#39;0\u0026#39;; $DB[\u0026#39;DATABASE\u0026#39;] = \u0026#39;zabbix\u0026#39;; $DB[\u0026#39;USER\u0026#39;] = \u0026#39;zabbix\u0026#39;; $DB[\u0026#39;PASSWORD\u0026#39;] = \u0026#39;zabbix\u0026#39;; [root@zabbix ~]# grep ^DB /etc/zabbix/zabbix_server.conf DBHost=172.16.1.52 DBName=zabbix DBUser=zabbix DBPassword=zabbix 4. 重启服务 1 [root@zabbix ~]# systemctl restart zabbix-server httpd ","permalink":"https://LMDAWN1127.github.io/posts/zabbix-split-database/","summary":"面向 Zabbix 高并发场景，将数据库从 Zabbix 服务器拆分到独立 MariaDB 服务器（11.0.1.52）的操作步骤：建库授权、导出导入数据、修改 Web 与 Server 的数据库连接配置、重启服务。","title":"拆分数据库"},{"content":"1.安装zabbix-agent客户端 准备一台服务器11.0.1.8 1 2 3 4 5 6 7 8 配置仓库: [root@web02 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 将服务端和客户端的yum仓库同步: [root@zabbix ~]#scp /etc/yum.repos.d/CentOS-SCLo-scl.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl.repo [root@zabbix ~]#scp /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl-rh.repo [root@zabbix ~]#scp /etc/yum.repos.d/zabbix.repo root@172.16.1.8:/etc/yum.repos.d/zabbix.repo 如果安装提示软件冲突: [root@web02 ~]# yum -y install centos-release-scl 2.配置zabbix客户端服务器信息 启动客户端 1 2 3 4 5 6 [root@web02 ~]# yum -y install zabbix-agent [root@web02 ~]# vim /etc/zabbix/zabbix_agentd.conf Server=172.16.1.71 ServerActive=172.16.1.71 Hostname=web02 [root@web02 ~]# systemctl enable zabbix-agent --now 3.zabbix监控web02 4.web02服务器完成监控操作 ","permalink":"https://LMDAWN1127.github.io/posts/zabbix-monitor-server/","summary":"\u003ch2 id=\"1安装zabbix-agent客户端-准备一台服务器11018\"\u003e1.安装zabbix-agent客户端 准备一台服务器11.0.1.8\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e5\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e6\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e7\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e8\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e配置仓库:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@web02 ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#  rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e将服务端和客户端的yum仓库同步:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#scp /etc/yum.repos.d/CentOS-SCLo-scl.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl.repo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#scp /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo root@172.16.1.8:/etc/yum.repos.d/CentOS-SCLo-scl-rh.repo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#scp /etc/yum.repos.d/zabbix.repo  root@172.16.1.8:/etc/yum.repos.d/zabbix.repo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e如果安装提示软件冲突:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@web02 ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# yum -y install centos-release-scl\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"2配置zabbix客户端服务器信息-启动客户端\"\u003e2.配置zabbix客户端服务器信息 启动客户端\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e5\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e6\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@web02 ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# yum -y install zabbix-agent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@web02 ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# vim /etc/zabbix/zabbix_agentd.conf \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eServer\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e172.16.1.71\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eServerActive\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e172.16.1.71\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eHostname\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eweb02\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@web02 ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl enable zabbix-agent --now\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"3zabbix监控web02\"\u003e3.zabbix监控web02\u003c/h2\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/p9_img63.png\"\u003e\u003c/p\u003e","title":"Zabbix 监控服务器信息"},{"content":"1.配置仓库 1 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 2.安装zabbix-server zabbix-agent 1 yum -y install zabbix-server-mysql zabbix-agent 3.安装scl源（解决版本冲突问题） 1 [root@zabbix ~]# yum -y install centos-release-scl 4.开启前端仓库 1 2 3 4 5 [root@zabbix ~]# vim /etc/yum.repos.d/zabbix.repo [zabbix-frontend] ... enabled=1 ... 5.将CentOS-SCLo-scl.repo和CentOS-SCLo-scl-rh.repo失效的mirrorlist替换为阿里云的baseurl 1 2 3 4 5 6 7 8 9 10 [root@zabbix yum.repos.d]#vim CentOS-SCLo-scl-rh.repo [centos-sclo-rh] ... baseurl=https://mirrors.aliyun.com/centos/7/sclo/x86_64/rh ... [root@zabbix yum.repos.d]#vim CentOS-SCLo-scl.repo [centos-sclo-sclo] ... baseurl=https://mirrors.aliyun.com/centos/7/sclo/x86_64/sclo ... 6.安装 php apache 前端 1 [root@zabbix ~]# yum -y install zabbix-web-mysql-scl zabbix-apache-conf-scl 7.安装mysql数据库 1 2 3 [root@zabbix ~]# yum -y install mariadb-server 启动数据库 [root@zabbix ~]#systemctl enable mariadb --now 8.创建zabbix库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@zabbix ~]#mysql -uroot Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 7 Server version: 5.5.68-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; create database zabbix character set utf8 collate utf8_bin; Query OK, 1 row affected (0.00 sec) MariaDB [(none)]\u0026gt; create user zabbix@localhost identified by \u0026#39;zabbix\u0026#39;; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; grant all privileges on zabbix.* to zabbix@localhost; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 1; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]\u0026gt; quit Bye 9.初始化数据(业务运行前需要依赖数据库中的一些数据表格 需要提前导入) 1 2 3 4 5 [root@zabbix ~]# zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix #需要输入zabbix密码 [root@zabbix ~]# mysql -uroot MariaDB [(none)]\u0026gt; set global log_bin_trust_function_creators = 0; quit 10.修改zabbix连接数据的信息 1 2 3 4 [root@zabbix ~]# egrep ^DB /etc/zabbix/zabbix_server.conf DBName=zabbix DBUser=zabbix DBPassword=zabbix # 将注释打开配置密码 11.修改时区 1 2 [root@zabbix ~]# tail -1 /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf php_value[date.timezone] = Asia/Shanghai 12.启动服务 server agent httpd php 加入开机自启 1 [root@zabbix ~]# systemctl enable zabbix-server zabbix-agent httpd rh-php72-php-fpm --now 13.访问zabbix页面 11.0.1.71/zabbix 14.zabbix安装完成 15.调整为中文重新配置字体 查找windows字体复制到zabbix的字体目录下\nwindows字体位置\n上传其中一个字体到并改名\n1 2 3 4 5 6 7 8 9 10 [root@zabbix ~]#cd /usr/share/zabbix/assets/fonts/ [root@zabbix fonts]#ls graphfont.ttf simhei.ttf#上传的字体 [root@zabbix fonts]#rm -f graphfont.ttf #删除原来的字体 [root@zabbix fonts]#ln -s /usr/share/zabbix/assets/fonts/simhei.ttf graphfont.ttf #链接到上传的字体 [root@zabbix fonts]#chmod 644 simhei.ttf [root@zabbix fonts]#ll total 9520 lrwxrwxrwx 1 root root 41 Sep 23 01:19 graphfont.ttf -\u0026gt; /usr/share/zabbix/assets/fonts/simhei.ttf -rw-r--r-- 1 root root 9745792 Sep 23 01:18 simhei.ttf ","permalink":"https://LMDAWN1127.github.io/posts/zabbix-install/","summary":"\u003ch2 id=\"1配置仓库\"\u003e1.配置仓库\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"2安装zabbix-server-zabbix-agent\"\u003e2.安装zabbix-server zabbix-agent\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eyum -y install zabbix-server-mysql zabbix-agent\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"3安装scl源解决版本冲突问题\"\u003e3.安装scl源（解决版本冲突问题）\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# yum -y install centos-release-scl\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"4开启前端仓库\"\u003e4.开启前端仓库\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e5\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# vim /etc/yum.repos.d/zabbix.repo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003ezabbix-frontend\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eenabled\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"5将centos-sclo-sclrepo和centos-sclo-scl-rhrepo失效的mirrorlist替换为阿里云的baseurl\"\u003e5.将CentOS-SCLo-scl.repo和CentOS-SCLo-scl-rh.repo失效的mirrorlist替换为阿里云的baseurl\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 5\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 6\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 7\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 8\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 9\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e10\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix yum.repos.d\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#vim CentOS-SCLo-scl-rh.repo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003ecentos-sclo-rh\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebaseurl\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ehttps://mirrors.aliyun.com/centos/7/sclo/x86_64/rh\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix yum.repos.d\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#vim CentOS-SCLo-scl.repo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003ecentos-sclo-sclo\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebaseurl\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ehttps://mirrors.aliyun.com/centos/7/sclo/x86_64/sclo\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"6安装-php-apache-前端\"\u003e6.安装 php apache 前端\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# yum -y install zabbix-web-mysql-scl zabbix-apache-conf-scl\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"7安装mysql数据库\"\u003e7.安装mysql数据库\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# yum -y install mariadb-server\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e启动数据库\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#systemctl enable mariadb --now\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"8创建zabbix库\"\u003e8.创建zabbix库\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 5\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 6\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 7\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 8\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 9\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e10\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e11\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e12\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e13\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e14\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e15\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e16\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e17\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e18\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e19\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e20\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e21\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e22\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e23\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#mysql -uroot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWelcome to the MariaDB monitor.  Commands end with ; or \u003cspan style=\"color:#ae81ff\"\u003e\\g\u003c/span\u003e.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eYour MariaDB connection id is \u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eServer version: 5.5.68-MariaDB MariaDB Server\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCopyright \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e 2000, 2018, Oracle, MariaDB Corporation Ab and others.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eType \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;help;\u0026#39;\u003c/span\u003e or \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\h\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e help. Type \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\c\u0026#39;\u003c/span\u003e to clear the current input statement.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMariaDB \u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003enone\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\u0026gt; create database zabbix character set utf8 collate utf8_bin;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eQuery OK, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e row affected \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e0.00 sec\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMariaDB \u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003enone\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\u0026gt; create user zabbix@localhost identified by \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;zabbix\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eQuery OK, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e rows affected \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e0.00 sec\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMariaDB \u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003enone\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\u0026gt; grant all privileges on zabbix.* to zabbix@localhost;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eQuery OK, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e rows affected \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e0.00 sec\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMariaDB \u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003enone\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\u0026gt; set global log_bin_trust_function_creators \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e 1;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eQuery OK, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e rows affected \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e0.00 sec\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMariaDB \u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003enone\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\u0026gt; quit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBye\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"9初始化数据业务运行前需要依赖数据库中的一些数据表格-需要提前导入\"\u003e9.初始化数据(业务运行前需要依赖数据库中的一些数据表格 需要提前导入)\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e5\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#需要输入zabbix密码\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# mysql -uroot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMariaDB \u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003enone\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\u0026gt; set global log_bin_trust_function_creators \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e 0;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003equit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"10修改zabbix连接数据的信息\"\u003e10.修改zabbix连接数据的信息\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# egrep ^DB /etc/zabbix/zabbix_server.conf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDBName\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ezabbix\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDBUser\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ezabbix\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDBPassword\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ezabbix   \u003cspan style=\"color:#75715e\"\u003e# 将注释打开配置密码\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"11修改时区\"\u003e11.修改时区\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# tail -1 /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ephp_value\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003edate.timezone\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Asia/Shanghai\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"12启动服务-server-agent-httpd-php-加入开机自启\"\u003e12.启动服务 server agent httpd php 加入开机自启\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@zabbix ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl enable zabbix-server zabbix-agent httpd rh-php72-php-fpm --now\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"13访问zabbix页面-110171zabbix\"\u003e13.访问zabbix页面 11.0.1.71/zabbix\u003c/h2\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/p4_img29.png\"\u003e\u003c/p\u003e","title":"Zabbix 安装部署"},{"content":"规划如下：\nmaster节点： 计算机名： master IP地址：192.168.8.30\nworker01节点： 计算机名： node1 IP地址： 192.168.8.31\nworker02节点： 计算机名： node2 IP地址： 192.168.8.32\n在三个节点上均完成以下步骤，并且保证三个节点时间均一致。\n1. 关闭防火墙和SELinux 1 2 3 4 [root@master ~]# systemctl disable firewalld.service --now [root@master ~]# vim /etc/selinux/config SELINUX=disabled [root@master ~]# setenforce 0 2. 关闭swap 1 2 [root@master ~]#sed -i \u0026#39;/swap/s/^/#/\u0026#39; /etc/fstab [root@master ~]# swapoff -a 3. 设置系统参数，加载内核模块 1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# cat \u0026gt; /etc/modules-load.d/k8s.conf \u0026lt;\u0026lt; EOF overlay br_netfilter EOF [root@master ~]#modprobe overlay [root@master ~]#modprobe br_netfilter [root@master ~]# cat \u0026gt; /etc/sysctl.d/k8s.conf \u0026lt;\u0026lt; EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF [root@master ~]# sysctl --system 4. 安装容器运行时（Containerd） Kubernetes v1.30 推荐使用 Containerd 作为容器运行时：\n1 [root@master ~]# yum -y remove runc 删除系统自带的yum仓库文件\n1 [root@master ~]# rm -rf /etc/yum.repos.d/Cent*.repo 配置本地yum仓库\n1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# mount /dev/cdrom /media/ [root@master ~]# cat /etc/fstab |grep iso9660 /dev/cdrom\t/media\tiso9660 defaults 0 0 [root@master ~]# cat /etc/yum.repos.d/dvd.repo [BaseOS] name=BaseOS baseurl=file:///media/BaseOS gpgcheck=0 [AppStream] name=AppStream baseurl=file:///media/AppStream gpgcheck=0 配置Containerd\n1 2 [root@master ~]#yum install -y yum-utils [root@master ~]#yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 安装containerd.io\n1 2 3 4 5 6 7 8 9 10 [root@master ~]#yum install -y containerd.io # 生成并修改配置文件 [root@master ~]#mkdir -p /etc/containerd [root@master ~]#containerd config default \u0026gt; /etc/containerd/config.toml # 修改配置使用systemd cgroup驱动 #sed -i \u0026#39;s/SystemdCgroup = false/SystemdCgroup = true/g\u0026#39; /etc/containerd/config.toml # 配置镜像加速（可选） #sed -i \u0026#39;s|registry.k8s.io|registry.aliyuncs.com/google_containers|g\u0026#39; /etc/containerd/config.toml # 启动并设置开机自启 #systemctl enable --now containerd 5. 安装和配置kubernetes 1 2 3 4 5 6 7 [root@master yum.repos.d]# cat kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/ enabled=1 gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/repodata/repomd.xml.key 安装指定版本的kubernetes组件\n1 2 3 [root@master ~]#yum install -y kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0 --disableexcludes=kubernetes # 启动kubelet [root@master ~]#systemctl enable --now kubelet 6. 初始化kubernetes 注意：当所有的节点完成以上配置后，仅需在master节点上执行以下步骤\n1 2 3 4 5 [root@master ~]# kubeadm init --pod-network-cidr=10.244.0.0/16 --service-cidr=172.16.0.0/16 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.30.0 --control-plane-endpoint=\u0026#34;192.168.8.30:6443\u0026#34; --upload-certs # 配置kubectl [root@master ~]#mkdir -p $HOME/.kube [root@master ~]#cp -i /etc/kubernetes/admin.conf $HOME/.kube/config [root@master ~]#chown $(id -u):$(id -g) $HOME/.kube/config 7. 将worker节点加入kubernetes集群 按照以下命令在worker节点上执行，注意：不同的节点token和ca证书可能都会不同，请根据自己系统的提示执行，证书也可能会过期，如果已经过期，可以执行kubeadm token create \u0026ndash;print-join-command重新生成\n1 2 [root@node1 ~]# kubeadm join 192.168.8.30:6443 --token yfjrsi.60lmqgzobp0b8al3 --discovery-token-ca-cert-hash sha256:f4e3c924918545abfd8148e71a377e086d591c6a59856a496b4b33e0987d9c18 [root@node2 ~]# kubeadm join 192.168.8.30:6443 --token yfjrsi.60lmqgzobp0b8al3 --discovery-token-ca-cert-hash sha256:f4e3c924918545abfd8148e71a377e086d591c6a59856a496b4b33e0987d9c18 8. 在master节点上安装calico网络插件 1 2 # 安装Calico网络插件 [root@master ~]#kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml 备注：由于众所周知的原因，可能会出现无法下载calico镜像的问题\n我重新修改了该文件，请下载到master节点上，执行该文件即可\n文件已上传至百度网盘，下载链接：https://pan.baidu.com/s/1BPCFxv0kE0KWb04Qsgy71g （提取码：s44k）\n1 [root@master ~]#kubectl apply -f /root/calico-ucloud.yaml 安装完成后，执行\n1 2 3 4 5 [root@master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready control-plane 65m v1.30.13 node1 Ready \u0026lt;none\u0026gt; 62m v1.30.0 node2 Ready \u0026lt;none\u0026gt; 62m v1.30.0 如果看到以上结果，表示kubernetes已经安装完成。\n9. 扩展内容 在各个节点上执行以下命令\n1 2 3 4 5 kubectl completion bash \u0026gt; /etc/bash_completion.d/kubectl source /etc/bash_completion.d/kubectl vim /root/.bashrc alias docker=\u0026#39;crictl\u0026#39; source /root/.bashrc 说明：\nkubectl 命令补全：上面前两行把 kubectl 的补全脚本写入 /etc/bash_completion.d/。但前提是系统已安装 bash-completion 软件包（如 yum install -y bash-completion 或 apt install -y bash-completion），否则该目录不存在、补全无法生效；此外 kubectl 本身还需执行这两条命令才能真正开启补全（即 k8s 需额外配置才能补全）。 docker 别名：Kubernetes 改用 containerd 容器运行时后，容器管理命令由 docker 变为 crictl。这里把 docker 别名成 crictl，是为了照顾习惯使用 docker 命令的用户，使其沿用原有的操作习惯。 当如果执行#docker images，报错情况：\nk8s出现以下提示，如何解决\nWARN[0000] image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.\n这个警告提示表明你使用的容器运行时客户端（如 crictl 或 kubelet）正在使用默认的端点配置，而这些默认配置已被弃用，需要显式指定端点。以下是解决此问题的详细步骤：\n1 2 3 4 5 6 7 cat \u0026gt; /etc/crictl.yaml \u0026lt;\u0026lt; EOF runtime-endpoint: unix:///run/containerd/containerd.sock # 根据你的容器运行时选择 image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false pull-image-on-create: false EOF 说明：\n若使用 Containerd，端点为 unix:///run/containerd/containerd.sock。\n若使用 CRI-O，端点为 unix:///run/crio/crio.sock。\n备注： 可选\n如果手动安装kubernetes，默认使用的是iptables转发数据，如果大规模的场景，则建议修改为ipvs模式，如果需要在安装完成后，修改为ipvs模式\n1. 在所有的节点上安装ipvsadm软件包 2. 在所有节点上加载模块 1 2 3 4 5 6 7 cat \u0026gt; /etc/modules-load.d/ipvs.conf \u0026lt;\u0026lt; EOF ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack EOF 3. 编辑kube-proxy ConfigMap 1 kubectl edit cm kube-proxy -n kube-system 在打开的编辑器中，找到并修改以下部分：\n1 2 3 4 5 6 7 apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: \u0026#34;ipvs\u0026#34; # 将原来的\u0026#34;iptables\u0026#34;改为\u0026#34;ipvs\u0026#34; ipvs: scheduler: \u0026#34;rr\u0026#34; # 负载均衡算法，可选 rr|wrr|lc 4. 强制重启 kube-proxy Pods 删除所有kube-proxy Pod，触发重建\n1 #kubectl delete pods -n kube-system -l k8s-app=kube-proxy 5. 验证新 Pod 是否正常运行： 1 2 #kubectl get pods -n kube-system -l k8s-app=kube-proxy # 所有Pod状态应为Running 6. 验证 IPVS 配置生效 1 2 3 4 5 [root@master ~]# ipvsadm -L -n TCP 192.168.8.30:30080 rr -\u0026gt; 10.244.104.3:80 Masq 1 0 0 -\u0026gt; 10.244.104.4:80 Masq 1 0 0 -\u0026gt; 10.244.166.132:80 Masq 1 0 0 ","permalink":"https://LMDAWN1127.github.io/posts/k8s-v1.30-manual-deploy/","summary":"\u003cp\u003e规划如下：\u003c/p\u003e\n\u003cp\u003emaster节点：   计算机名： master   IP地址：192.168.8.30\u003c/p\u003e\n\u003cp\u003eworker01节点： 计算机名： node1   IP地址： 192.168.8.31\u003c/p\u003e\n\u003cp\u003eworker02节点： 计算机名： node2   IP地址： 192.168.8.32\u003c/p\u003e\n\u003cp\u003e在三个节点上均完成以下步骤，并且保证三个节点时间均一致。\u003c/p\u003e\n\u003ch2 id=\"1-关闭防火墙和selinux\"\u003e1. 关闭防火墙和SELinux\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl disable firewalld.service --now\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# vim /etc/selinux/config\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSELINUX\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003edisabled\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# setenforce 0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"2-关闭swap\"\u003e2. 关闭swap\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#sed -i \u0026#39;/swap/s/^/#/\u0026#39; /etc/fstab\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# swapoff -a\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"3-设置系统参数加载内核模块\"\u003e3. 设置系统参数，加载内核模块\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 5\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 6\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 7\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 8\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e 9\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e10\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e11\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e12\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# cat \u0026gt; /etc/modules-load.d/k8s.conf \u0026lt;\u0026lt; EOF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eoverlay\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebr_netfilter\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEOF\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#modprobe overlay\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e#modprobe br_netfilter\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# cat \u0026gt; /etc/sysctl.d/k8s.conf \u0026lt;\u0026lt; EOF\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enet.bridge.bridge-nf-call-iptables  \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enet.bridge.bridge-nf-call-ip6tables \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enet.ipv4.ip_forward                 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eEOF\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@master ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# sysctl --system\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"4-安装容器运行时containerd\"\u003e4. 安装容器运行时（Containerd）\u003c/h2\u003e\n\u003cp\u003eKubernetes v1.30 推荐使用 Containerd 作为容器运行时：\u003c/p\u003e","title":"手动部署 Kubernetes v1.30 集群（kubeadm 方式）"},{"content":"KVM 虚拟化入门到实战：部署、管理与网络配置 本文系统介绍 KVM 虚拟化技术，涵盖发展历史、部署方法、虚拟机管理、网络模式配置，以及使用命令行自动化安装虚拟机。所有命令均在 CentOS/RHEL 环境下验证。\n一、KVM 发展历史 KVM（Kernel-based Virtual Machine，基于内核的虚拟机）是当前云计算领域主流的虚拟化技术。了解它的发展脉络，有助于理解为什么业界最终选择了 KVM。\n虚拟化技术大致可分为两类：闭源虚拟化（如 VMware、Microsoft Hyper-V）和开源虚拟化（如 Xen、KVM）。\n早期的开源虚拟化代表是 Xen。AWS 和阿里云早期都采用 Xen 方案；Citrix 的 XenServer（思杰）以及桌面云产品 FusionAccess（对应 Citrix XenDesktop）也基于 Xen。华为的 FusionCompute 在 6.3 版本之前同样使用 Xen。\n关键的时间节点如下：\n2008 年：RedHat 收购 KVM，并在 RHEL 5.4 版本中正式发布 KVM（RHEL 5.0 发布的是 Xen）。 2010 年：OpenStack 项目诞生，红帽联合 IBM、Dell、VMware、Cisco、HP 等厂商共同拥抱 KVM 开源社区，在开源软件基金会下共建生态。同期 Citrix 主推 CloudStack 社区。 2014 年：RHEL 5.11 发布（RHEL 5.x 系列的生命周期终点，提供 7 年有效技术支持，可免费升级到 KVM）。 2017 年前后：AWS 推出基于 KVM 的 Nitro 平台实例，阿里云及全球众多厂商全面拥抱 KVM。 如今，主流云厂商的虚拟化底层基本都是 KVM。华为 FusionCompute 6.3 版本也从 Xen 切换到了 KVM。2019 年 IBM 以约 340 亿美金收购红帽公司，ansible、glusterfs、ceph 等红帽生态项目随之纳入麾下，进一步印证了这条技术路线的价值。\n二、什么是 KVM KVM 全称 Kernel-based Virtual Machine，即基于内核的虚拟机。它的核心特点是：\n无需升级内核：直接复用系统本身的内核，每个虚拟机被当做一个进程来运行。 轻量级虚拟化平台：相比传统方案更轻巧高效。 统一管理：XEN/KVM 虚拟化平台都提供统一的管理平台和命令行工具。 三、部署 KVM 1. 安装 KVM 所需软件包 前提条件：在物理机 BIOS 中开启虚拟化支持 VT-x（大部分电脑默认开启）。如果是在 VMware 中嵌套安装，还需要在 VMware 里开启 VT-x 支持。\n建议资源配置：CPU 4 核，内存 4G。\n1 2 [root@cloud ~]# yum -y groupinstall \u0026#34;Virtualization*\u0026#34; [root@cloud ~]# systemctl restart libvirtd.service 2. 图形化安装虚拟机 本实验使用图形化工具 virt-manager 来创建并安装虚拟机。\n说明：图形化向导的操作过程本文不再截图展示，全文以命令行操作为主，图形界面按提示点击即可完成。另外，宿主机指运行 KVM 虚拟机的那台机器，本实验在 VMware 中进行嵌套虚拟化，因此这里的宿主机是一台 VMware 虚拟机，而非物理机。\n首先准备系统安装镜像：创建 /isos 目录，并将 CentOS 8.4 的 ISO 文件复制进去。\n1 [root@cloud ~]# mkdir /isos 然后启动图形化管理界面：\n1 [root@cloud ~]# virt-manager 在界面中点击新建虚拟机，选择刚才放入 /isos 的 ISO 作为安装源，再按向导依次配置 CPU、内存和磁盘，即可完成安装。安装好的虚拟机磁盘文件默认保存在 /var/lib/libvirt/images/ 目录下，形如 server1.qcow2：\n1 2 3 [root@cloud ~]# cd /var/lib/libvirt/images/ [root@cloud images]# ls server1.qcow2 3. 创建磁盘 上一步图形化安装时 virt-manager 会自动创建磁盘；这里介绍手动创建的方式，后面制作云镜像、给虚拟机添加硬盘都会用到。使用 qemu-img 创建 qcow2 格式磁盘：\n1 [root@cloud ~]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server1.qcow2 10g 提示：如果要用这种方式创建云镜像，容量越小速度越快，可以研究如何把镜像做得最小。通常系统盘性能要求不高，真正的性能瓶颈在数据磁盘。\n4. 创建桥接模式（带上行链路的虚拟交换机） 进入网络配置目录，基于现有网卡复制一份桥接配置：\n1 2 [root@cloud ~]# cd /etc/sysconfig/network-scripts/ [root@cloud network-scripts]# cp ifcfg-ens33 ifcfg-br0 编辑桥接网卡 ifcfg-br0：\n1 [root@cloud network-scripts]# cat ifcfg-br0 1 2 3 4 5 6 7 8 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 GATEWAY=192.168.8.254 DNS1=192.168.8.254 编辑物理网卡 ifcfg-ens33，将其桥接到 br0：\n1 [root@cloud network-scripts]# cat ifcfg-ens33 1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 重新加载网络配置并重启连接：\n1 2 3 [root@cloud ~]# nmcli connection reload [root@cloud ~]# nmcli connection down ens33 ; nmcli connection up ens33 [root@cloud ~]# nmcli connection down br0 ; nmcli connection up br0 5. 制作云主机镜像 需求：创建一个自定义云主机镜像。\n在 KVM 虚拟机部署完成后，进入虚拟机安装一些必要软件：\n配置本地 yum 源 安装必要的软件包： 1 # yum -y install net-tools vim-enhanced bash-completion cloud-init 编辑 /etc/cloud/cloud.cfg 配置文件，按实际需求调整其中的参数 删除 /etc/ssh/ssh_host* 执行 init 0 关机 最后压缩镜像：\n1 [root@cloud images]# qemu-img convert -c -O qcow2 /var/lib/libvirt/images/server1.qcow2 /isos/server1.qcow2 四、管理 KVM 进入 virsh 交互界面：\n1 [root@cloud ~]# virsh 常用的虚拟机管理命令如下。\n列出所有虚拟机：\n1 2 3 4 [root@cloud ~]# virsh list --all Id Name State ------------------------- - server1 shut off 开启 / 关闭 / 强制关闭 / 重启虚拟机：\n1 2 3 4 [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh shutdown server1 [root@cloud ~]# virsh destroy server1 [root@cloud ~]# virsh reboot server1 6. 进入虚拟机 console 接口 进入 KVM 虚拟机，给虚拟机添加虚拟 console，从而实现从宿主机以 console 方式访问虚拟机。\n在虚拟机中编辑 grub 配置：\n1 [root@VM1 ~]# vim /etc/default/grub 在 GRUB_CMDLINE_LINUX 末尾追加 console=ttyS0：\n1 GRUB_CMDLINE_LINUX=\u0026#34;crashkernel=auto resume=UUID=94e88e69-fad6-4d1f-99cf-0ba83cc793b5 rhgb quiet console=ttyS0\u0026#34; 重新生成 grub 配置并重启：\n1 2 [root@VM1 ~]# grub2-mkconfig -o /boot/grub2/grub.cfg [root@VM1 ~]# reboot 在宿主机上连接 console：\n1 [root@cloud ~]# virsh console server1 退出 console 的快捷键是 Ctrl + ]。\n7. 通过配置给虚拟机添加硬盘 （1）创建磁盘文件：\n1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/disk2.qcow2 5g （2）通过配置文件添加硬盘：\n1 [root@cloud ~]# vim /etc/libvirt/qemu/server1.xml 在配置中加入如下 disk 段：\n1 2 3 4 5 6 \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39;/\u0026gt; \u0026lt;source file=\u0026#39;/var/lib/libvirt/images/disk2.qcow2\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vdb\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x07\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; 重启服务并启动虚拟机：\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server1 [root@cloud ~]# virsh console server1 在 KVM 虚拟机中验证磁盘：\n1 2 3 [root@localhost ~]# fdisk -l /dev/vda /dev/vdb 对比：Xen 虚拟化下磁盘设备名为 /dev/xvda、/dev/xvdb。\n8. 快速创建 KVM 虚拟机 通过复制现有虚拟机的 XML 配置来快速创建：\n1 2 3 4 [root@cloud qemu]# cp server1.xml server2.xml [root@cloud qemu]# uuidgen f3ccabc3-ca4c-41f2-8d6f-e99077c24eeb [root@cloud qemu]# vim server2.xml 修改 name、uuid、磁盘路径和 MAC 地址等关键参数：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \u0026lt;domain type=\u0026#39;kvm\u0026#39;\u0026gt; \u0026lt;name\u0026gt;server2\u0026lt;/name\u0026gt; \u0026lt;uuid\u0026gt;f3ccabc3-ca4c-41f2-8d6f-e99077c24eeb\u0026lt;/uuid\u0026gt; \u0026lt;metadata\u0026gt; \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39;/\u0026gt; \u0026lt;source file=\u0026#39;/isos/server1.qcow2\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;vda\u0026#39; bus=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x04\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; \u0026lt;interface type=\u0026#39;bridge\u0026#39;\u0026gt; \u0026lt;mac address=\u0026#39;52:54:00:96:f6:a2\u0026#39;/\u0026gt; \u0026lt;source bridge=\u0026#39;br0\u0026#39;/\u0026gt; \u0026lt;model type=\u0026#39;virtio\u0026#39;/\u0026gt; \u0026lt;address type=\u0026#39;pci\u0026#39; domain=\u0026#39;0x0000\u0026#39; bus=\u0026#39;0x01\u0026#39; slot=\u0026#39;0x00\u0026#39; function=\u0026#39;0x0\u0026#39;/\u0026gt; \u0026lt;/interface\u0026gt; 重启服务并启动新虚拟机：\n1 2 3 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh start server2 [root@cloud ~]# virsh list --all 五、KVM 网络配置 KVM 常见有三种网络模式：NAT 模式、仅主机模式（Host-Only）和桥接模式。\n1. NAT 模式 通过 iptables 地址伪装实现 NAT：\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE 或者使用 SNAT 指定源地址：\n1 # iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j SNAT --to-source 192.168.8.99 查看 NAT 规则：\n1 [root@cloud ~]# iptables -t nat -nL 默认网络配置文件位于 /etc/libvirt/qemu/networks：\n1 2 3 [root@cloud networks]# pwd /etc/libvirt/qemu/networks [root@cloud networks]# vim default.xml 2. 仅主机模式 仅主机模式下，虚拟机只能与宿主机通信，无法访问外部网络。\n3. 让 KVM 中的虚拟机访问 Internet （1）给 VMware 宿主机添加一块可以访问 Internet 的网卡。\n（2）进入宿主机操作系统，配置双桥接。\nifcfg-br0（内网桥接）：\n1 2 3 4 5 6 TYPE=Bridge NAME=br0 DEVICE=br0 ONBOOT=yes IPADDR=192.168.8.99 PREFIX=24 ifcfg-br1（外网桥接，DHCP 获取地址）：\n1 2 3 4 5 TYPE=Bridge NAME=br1 DEVICE=br1 ONBOOT=yes BOOTPROTO=dhcp ifcfg-ens33 桥接到 br0：\n1 2 3 4 5 TYPE=Ethernet NAME=ens33 DEVICE=ens33 ONBOOT=yes BRIDGE=br0 ifcfg-ens37 桥接到 br1：\n1 2 3 4 5 TYPE=Ethernet NAME=ens37 DEVICE=ens37 ONBOOT=yes BRIDGE=br1 重启服务并关闭虚拟机：\n1 2 3 4 5 [root@cloud ~]# systemctl restart libvirtd.service [root@cloud ~]# virsh shutdown server1 Domain server1 is being shutdown [root@cloud ~]# virsh shutdown server2 Domain server2 is being shutdown 将 KVM 中 server1 虚拟机网卡修改为桥接到 br1，进入 VM1 把 IP 获取方式改为 DHCP：\n1 2 3 4 5 6 7 8 9 10 11 [root@VM1 ~]# nmcli connection modify enp1s0 ipv4.method auto autoconnect yes [root@VM1 ~]# nmcli connection down enp1s0 ; nmcli connection up enp1s0 [root@VM1 ~]# ifconfig enp1s0: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1500 inet 192.168.31.158 netmask 255.255.255.0 broadcast 192.168.31.255 inet6 fe80::5054:ff:fe96:f6a1 prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether 52:54:00:96:f6:a1 txqueuelen 1000 (Ethernet) RX packets 1253 bytes 279527 (272.9 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 119 bytes 17932 (17.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 测试外网连通性：\n1 2 3 4 5 [root@VM1 ~]# ping www.baidu.com PING www.a.shifen.com (183.2.172.17) 56(84) bytes of data. 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=1 ttl=52 time=23.6 ms 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=2 ttl=52 time=24.3 ms 64 bytes from 183.2.172.17 (183.2.172.17): icmp_seq=3 ttl=52 time=23.5 ms 六、使用命令行安装 KVM 虚拟机 借助 kickstart 自动化安装脚本，可以无人值守地批量安装虚拟机。\n1. 创建磁盘 1 [root@cloud images]# qemu-img create -f qcow2 -o preallocation=metadata /var/lib/libvirt/images/server3.qcow2 10g 2. 自动化安装 使用 virt-install 指定安装源和 kickstart 文件：\n1 2 3 4 5 [root@cloud ~]# virt-install -n server3 --memory 2048 --vcpu 2 \\ --disk /var/lib/libvirt/images/server3.qcow2 \\ --network bridge=br1 \\ -l http://192.168.31.96/pub \\ -x inst.ks=http://192.168.31.96/ks/ks01.cfg --vnc kickstart 配置文件 ks01.cfg 示例：\n1 # vim /var/www/html/ks/ks01.cfg 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #version=RHEL8 # Use text install for fully unattended text repo --name=\u0026#34;AppStream\u0026#34; --baseurl=http://192.168.0.107/pub/AppStream repo --name=\u0026#34;BaseOS\u0026#34; --baseurl=http://192.168.0.107/pub/BaseOS reboot %packages @^minimal-environment kexec-tools %end # Keyboard layouts keyboard --xlayouts=\u0026#39;us\u0026#39; # System language lang en_US.UTF-8 # Network information network --bootproto=dhcp --activate --hostname=server3 --onboot=yes # Use network installation media url --url=http://192.168.0.107/pub/BaseOS # Run the Setup Agent on first boot firstboot --disable ignoredisk --only-use=vda clearpart --all --initlabel autopart # System timezone timezone Asia/Shanghai --utc # Root password rootpw --iscrypted $6$c31XqEglO8ECIli0$enF8T.LW3OknT.178Vb9m2HHYD6dqRdKF2QZia5.G5Tv/zMD//04S2h61oBOKihJZwPIUmWxLc7oXuWtkHQy10 user --name=admin --password=$6$Ey847S5bcqYg8ktk$sd4BJeFF01IFsGOaFwRoqXoYuZ5H1RV3bLC6n6gQGa1tNAjplFDhTnm3Cjw2pgwltIE.xr27X7E/ro4gF2JSG1 --iscrypted --gecos=\u0026#34;admin\u0026#34; %addon com_redhat_kdump --enable --reserve-mb=\u0026#39;auto\u0026#39; %end %anaconda pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty %end 说明：上面的安装源地址（192.168.0.107）、主机名（server3）以及 rootpw、user 后的密码哈希均为示例，实际使用时请替换为自己环境的地址和生成的密码密文。\n","permalink":"https://LMDAWN1127.github.io/posts/kvm-virtualization-guide/","summary":"\u003ch1 id=\"kvm-虚拟化入门到实战部署管理与网络配置\"\u003eKVM 虚拟化入门到实战：部署、管理与网络配置\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e本文系统介绍 KVM 虚拟化技术，涵盖发展历史、部署方法、虚拟机管理、网络模式配置，以及使用命令行自动化安装虚拟机。所有命令均在 CentOS/RHEL 环境下验证。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"一kvm-发展历史\"\u003e一、KVM 发展历史\u003c/h2\u003e\n\u003cp\u003eKVM（Kernel-based Virtual Machine，基于内核的虚拟机）是当前云计算领域主流的虚拟化技术。了解它的发展脉络，有助于理解为什么业界最终选择了 KVM。\u003c/p\u003e\n\u003cp\u003e虚拟化技术大致可分为两类：闭源虚拟化（如 VMware、Microsoft Hyper-V）和开源虚拟化（如 Xen、KVM）。\u003c/p\u003e\n\u003cp\u003e早期的开源虚拟化代表是 Xen。AWS 和阿里云早期都采用 Xen 方案；Citrix 的 XenServer（思杰）以及桌面云产品 FusionAccess（对应 Citrix XenDesktop）也基于 Xen。华为的 FusionCompute 在 6.3 版本之前同样使用 Xen。\u003c/p\u003e\n\u003cp\u003e关键的时间节点如下：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e2008 年\u003c/strong\u003e：RedHat 收购 KVM，并在 RHEL 5.4 版本中正式发布 KVM（RHEL 5.0 发布的是 Xen）。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e2010 年\u003c/strong\u003e：OpenStack 项目诞生，红帽联合 IBM、Dell、VMware、Cisco、HP 等厂商共同拥抱 KVM 开源社区，在开源软件基金会下共建生态。同期 Citrix 主推 CloudStack 社区。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e2014 年\u003c/strong\u003e：RHEL 5.11 发布（RHEL 5.x 系列的生命周期终点，提供 7 年有效技术支持，可免费升级到 KVM）。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e2017 年前后\u003c/strong\u003e：AWS 推出基于 KVM 的 Nitro 平台实例，阿里云及全球众多厂商全面拥抱 KVM。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e如今，主流云厂商的虚拟化底层基本都是 KVM。华为 FusionCompute 6.3 版本也从 Xen 切换到了 KVM。2019 年 IBM 以约 340 亿美金收购红帽公司，ansible、glusterfs、ceph 等红帽生态项目随之纳入麾下，进一步印证了这条技术路线的价值。\u003c/p\u003e","title":"KVM 虚拟化入门到实战：部署、管理与网络配置"},{"content":"1. 选择合适的基础镜像 1 2 3 4 5 6 7 8 9 10 # ❌ 太大 FROM ubuntu:22.04 # ~77MB FROM python:3.11 # ~1GB # ✅ 推荐 FROM python:3.11-alpine # ~50MB FROM python:3.11-slim # ~130MB # ✅ 最小 FROM gcr.io/distroless/python3 # ~20MB 基础镜像 大小 ubuntu:22.04 ~77MB python:3.11 ~1GB python:3.11-alpine ~50MB distroless ~20MB 2. 使用多阶段构建 最有效的优化手段：编译阶段用大镜像，运行阶段只复制产物。\n1 2 3 4 5 6 7 8 9 10 11 # 阶段 1：编译 FROM golang:1.21 AS builder WORKDIR /app COPY . . RUN go build -o myapp # 阶段 2：运行 FROM alpine:3.18 WORKDIR /app COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] Go 应用从 ~1GB 缩小到 ~15MB。\n3. 合并 RUN 指令 1 2 3 4 5 6 7 8 9 10 11 12 # ❌ 多个层 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN rm -rf /var/lib/apt/lists/* # ✅ 合并为一个层 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 4. 清理构建缓存 1 2 3 4 5 6 7 8 9 # Python - 禁用 pip 缓存 RUN pip install --no-cache-dir -r requirements.txt # Node.js - 清理 npm 缓存 RUN npm ci --only=production \u0026amp;\u0026amp; \\ npm cache clean --force # Go - 去除调试信息 RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 5. 使用 .dockerignore 1 2 3 4 5 6 7 8 9 10 11 # .dockerignore .git node_modules *.md .env .vscode __pycache__ *.pyc dist build coverage 1 2 # 检查上下文大小 docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; 6. 只安装必要的包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ❌ 运行镜像里装了编译工具 FROM ubuntu:22.04 RUN apt-get install -y build-essential gcc make cmake COPY . . RUN make \u0026amp;\u0026amp; make install CMD [\u0026#34;./myapp\u0026#34;] # ✅ 编译和运行分离 FROM ubuntu:22.04 AS builder RUN apt-get install -y build-essential gcc make COPY . . RUN make \u0026amp;\u0026amp; make install FROM ubuntu:22.04 COPY --from=builder /app/myapp . CMD [\u0026#34;./myapp\u0026#34;] 7. 使用 \u0026ndash;no-install-recommends 1 2 3 4 5 # ❌ 安装了所有推荐包 RUN apt-get install -y curl # ✅ 只装必须的包（减少约 50% 依赖） RUN apt-get install -y --no-install-recommends curl 8. 压缩静态资源 1 2 3 4 5 6 7 8 9 10 11 # 压缩 JavaScript RUN uglifyjs app.js -o app.min.js # 压缩 CSS RUN cssnano app.css app.min.css # 压缩图片 RUN find . -name \u0026#34;*.png\u0026#34; -exec optipng {} \\; # Go 二进制去除调试信息 RUN go build -ldflags=\u0026#34;-s -w\u0026#34; -o myapp 9. 使用 Squash 合并层 1 2 # 合并所有层为一层，去除中间层的重复文件 docker build --squash -t myapp:slim . 需要在 /etc/docker/daemon.json 中启用 experimental 功能。\n10. 分析镜像层 1 2 3 4 5 6 7 # 使用 dive 工具分析每层大小和内容 docker run --rm -it \\ -v /var/run/docker.sock:/var/run/docker.sock \\ wagoodman/dive myapp:latest # 或使用 docker history docker history myapp:latest 实战案例：Python 应用 1 2 3 4 5 6 # ❌ 优化前（1.2GB） FROM python:3.11 WORKDIR /app COPY . . RUN pip install -r requirements.txt CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 优化后（150MB，缩小 87%） FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /root/.local /root/.local COPY . . ENV PATH=/root/.local/bin:$PATH CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 实战案例：Node.js 应用 1 2 3 4 5 6 # ❌ 优化前（1.1GB） FROM node:18 WORKDIR /app COPY . . RUN npm install CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 优化后（180MB，缩小 84%） FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM node:18-alpine WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY . . USER node CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 推荐工具 工具 用途 dive 分析镜像每层内容和大小 docker-slim 自动精简镜像 hadolint Dockerfile 静态检查 trivy 镜像安全扫描 ","permalink":"https://LMDAWN1127.github.io/posts/dockerfile-smaller-image/","summary":"Docker 镜像太大导致部署慢、存储成本高？本文分享 10 个经过验证的优化技巧，帮你把镜像缩小 90% 以上。","title":"Dockerfile 构建镜像太大？10 个技巧让镜像瘦身 90%"},{"content":"1. 合理安排指令顺序（利用缓存） 把变化频率低的指令放前面，变化频率高的放后面。一旦某层缓存失效，后续所有层都会重新构建。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 错误：每次改代码都要重新装依赖 FROM node:18-alpine WORKDIR /app COPY . . # 代码经常变 RUN npm ci --production # 依赖很少变 CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # ✅ 正确：依赖变化时才重新安装 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # 依赖定义（很少变） RUN npm ci --production # 安装依赖（很少变） COPY . . # 代码（经常变） CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # 检查缓存命中情况 docker build -t myapp . 2\u0026gt;\u0026amp;1 | grep -E \u0026#34;(CACHED|Using cache)\u0026#34; 2. 使用 .dockerignore 减少上下文 docker build 会把整个目录发送到 Docker daemon，包括 .git、node_modules 等不需要的文件。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # .dockerignore .git .github .vscode .idea node_modules dist build *.md *.log .env .env.local .DS_Store coverage .nyc_output 1 2 3 4 # 检查上下文大小 docker build --no-cache -t test . 2\u0026gt;\u0026amp;1 | grep \u0026#34;Sending build context\u0026#34; # 优化前：Sending build context to Docker daemon 1.2GB # 优化后：Sending build context to Docker daemon 50MB 3. 使用轻量级基础镜像 基础镜像 大小 说明 ubuntu:22.04 ~77MB 完整 Ubuntu python:3.11 ~1GB 包含完整 Python python:3.11-slim ~130MB 精简版 python:3.11-alpine ~50MB Alpine 版 1 2 3 4 5 6 7 8 # ❌ 太大 FROM python:3.11 # 1GB # ✅ 推荐 FROM python:3.11-slim # 130MB # ✅ 最小（注意 musl 兼容性问题） FROM python:3.11-alpine # 50MB Alpine 使用 musl 而非 glibc，某些软件可能不兼容。遇到问题选 slim 版本。\n4. 合并 RUN 指令减少层数 每个 RUN 创建一个新层，层越多构建越慢、镜像越大。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ❌ 5 个层 RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* # ✅ 1 个层 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends \\ curl \\ wget \u0026amp;\u0026amp; \\ apt-get clean \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* 1 2 # 检查镜像层数 docker history myapp:latest 5. 使用多阶段构建 在一个阶段构建，在另一个阶段运行，只复制需要的文件。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 阶段 1：构建 FROM golang:1.21-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o app . # 阶段 2：运行（只有 ~15MB） FROM alpine:3.18 RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/app . EXPOSE 8080 CMD [\u0026#34;./app\u0026#34;] 方案 镜像大小 构建时间 单阶段 ~1.2GB 5 分钟 多阶段 ~15MB 2 分钟 6. 使用 BuildKit 加速构建 BuildKit 支持并行构建和高级缓存。\n1 2 3 4 5 6 7 # 启用 BuildKit export DOCKER_BUILDKIT=1 # 或在 /etc/docker/daemon.json 中永久配置 # { # \u0026#34;features\u0026#34;: { \u0026#34;buildkit\u0026#34;: true } # } 1 2 3 4 5 6 7 8 9 10 # 使用缓存挂载（BuildKit 特性） # syntax=docker/dockerfile:1 FROM node:18-alpine WORKDIR /app COPY package.json package-lock.json ./ # 缓存挂载避免每次重新下载依赖 RUN --mount=type=cache,target=/root/.npm \\ npm ci --production COPY . . CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] 1 2 # 检查 BuildKit 是否启用 docker build --progress=plain -t myapp . 2\u0026gt;\u0026amp;1 | head -5 7. 使用缓存导入导出 在 CI/CD 中，缓存会在每次构建后丢失。使用缓存导入导出可以跨构建保留缓存。\n1 2 3 4 5 6 7 8 9 10 11 # 导出到本地 docker build \\ --cache-from type=local,src=/tmp/cache \\ --cache-to type=local,dest=/tmp/cache \\ -t myapp . # 使用注册表作为缓存 docker build \\ --cache-from type=registry,ref=myregistry/myapp:cache \\ --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \\ -t myapp . 1 2 3 4 5 6 7 8 9 # GitHub Actions 示例 - name: Build and push uses: docker/build-push-action@v5 with: context: . push: true tags: myapp:latest cache-from: type=gha cache-to: type=gha,mode=max 8. 并行安装依赖 1 2 3 4 5 6 7 8 9 10 11 12 # Node.js - npm ci 比 npm install 快 RUN npm ci --production # Python - 禁用缓存 RUN pip install --no-cache-dir -r requirements.txt # Go - 并行下载模块 RUN go mod download # Rust - 使用 cargo-chef COPY --from=chef /app/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json 9. 清理构建缓存和临时文件 在同一个 RUN 指令中清理，避免文件保留在层中。\n1 2 3 4 5 6 7 8 9 10 11 12 # ✅ 正确：同一层中清理 RUN apt-get update \u0026amp;\u0026amp; \\ apt-get install -y --no-install-recommends build-essential \u0026amp;\u0026amp; \\ make \u0026amp;\u0026amp; make install \u0026amp;\u0026amp; \\ apt-get purge -y --auto-remove build-essential \u0026amp;\u0026amp; \\ apt-get clean \u0026amp;\u0026amp; \\ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # ❌ 错误：清理在另一层，文件仍然存在 RUN apt-get update \u0026amp;\u0026amp; apt-get install -y build-essential RUN make \u0026amp;\u0026amp; make install RUN apt-get clean # 太晚了！前面的层已经包含了文件 各语言清理命令：\n1 2 3 4 5 6 7 8 # Node.js RUN npm ci --production \u0026amp;\u0026amp; npm cache clean --force # Python RUN pip install --no-cache-dir -r requirements.txt # Go RUN go build -o app . \u0026amp;\u0026amp; go clean -cache 10. 使用特定版本标签 1 2 3 4 5 6 7 8 # ❌ latest 可能变化，缓存不可预测 FROM node:latest # ✅ 特定版本 FROM node:18.17.0-alpine3.18 # ✅ 更安全：使用 SHA256 摘要 FROM node:18.17.0-alpine3.18@sha256:abc123... 镜像层级详解 1 2 # 查看镜像层历史 docker history \u0026lt;image-name\u0026gt; 创建新层的指令：FROM、RUN、COPY、ADD\n不创建新层的指令（只是元数据）：CMD、ENTRYPOINT、ENV、EXPOSE、WORKDIR、USER、LABEL、ARG、VOLUME\n1 2 3 4 5 6 7 8 9 10 11 # 这会创建 4 个层 FROM node:18-alpine # 层 1 RUN apk add --no-cache curl # 层 2 COPY package.json . # 层 3 RUN npm install # 层 4 # 这些指令不增加层 WORKDIR /app # 无层 ENV NODE_ENV=production # 无层 EXPOSE 3000 # 无层 CMD [\u0026#34;node\u0026#34;, \u0026#34;server.js\u0026#34;] # 无层 优化效果对比 1 2 3 4 5 6 7 8 9 # ❌ 优化前（8 分钟，1.2GB） FROM ubuntu:22.04 RUN apt-get update RUN apt-get install -y python3 python3-pip RUN apt-get install -y build-essential WORKDIR /app COPY . . RUN pip3 install -r requirements.txt CMD [\u0026#34;python3\u0026#34;, \u0026#34;app.py\u0026#34;] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ✅ 优化后（1.5 分钟，150MB） # syntax=docker/dockerfile:1 FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN --mount=type=cache,target=/root/.cache/pip \\ pip install --no-cache-dir -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY . . EXPOSE 8000 CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] 指标 优化前 优化后 提升 构建时间 8 分钟 1.5 分钟 81% ⬇️ 镜像大小 1.2GB 150MB 87% ⬇️ 上下文大小 500MB 50MB 90% ⬇️ 缓存命中率 10% 90% 80% ⬆️ 监控和调试 1 2 3 4 5 6 7 8 9 10 11 # 显示详细构建时间 time docker build -t myapp . # BuildKit 详细进度 DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp . # 分析镜像层 docker history myapp:latest # 使用 dive 工具深入分析 dive myapp:latest ","permalink":"https://LMDAWN1127.github.io/posts/dockerfile-faster-build/","summary":"Docker 构建太慢？本文分享 10 个经过验证的优化技巧，从缓存策略到多阶段构建，帮你大幅缩短构建时间。","title":"Dockerfile 构建镜像如何更快？10 个实用优化技巧"},{"content":"1. 基本语法 两者语法几乎一样，但行为差异很大：\n1 2 3 4 5 # COPY - 纯粹的文件复制，不做任何额外处理 COPY [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; # ADD - 支持自动解压和远程 URL，但行为不可预测 ADD [--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;] \u0026lt;src\u0026gt;... \u0026lt;dest\u0026gt; 2. 自动解压压缩文件 ADD 会自动解压本地压缩包，COPY 不会：\n1 2 3 4 5 6 7 # ADD 自动解压 .tar.gz ADD app.tar.gz /opt/app/ # 结果：/opt/app/ 下是解压后的内容 # COPY 只复制文件本身 COPY app.tar.gz /opt/app/ # 结果：/opt/app/app.tar.gz 是原始压缩包 支持的压缩格式：.tar、.tar.gz/.tgz、.tar.bz2、.tar.xz\n如果你想复制压缩包本身而不解压，必须用 COPY。\n3. 远程 URL 下载 ADD 可以直接从 URL 下载文件，COPY 不支持：\n1 2 3 4 5 # ADD 可以下载远程文件（但不推荐） ADD https://example.com/app.tar.gz /opt/ # COPY 不支持 URL COPY https://example.com/app.tar.gz /opt/ # 报错！ 推荐用 RUN + curl 替代 ADD 下载远程文件，因为 ADD 下载无法利用构建缓存、无法做错误处理、无法验证完整性。\n1 2 3 4 # 推荐：RUN + curl RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \\ \u0026amp;\u0026amp; tar -xzf /tmp/app.tar.gz -C /opt/ \\ \u0026amp;\u0026amp; rm /tmp/app.tar.gz 4. 自动创建目录 两者都会自动创建不存在的中间目录：\n1 2 3 4 5 6 # 如果 /etc/myapp/config/ 不存在，会自动创建 COPY app.conf /etc/myapp/config/ ADD app.conf /etc/myapp/config/ # 不需要手动 mkdir # RUN mkdir -p /etc/myapp/config ← 多余 5. 构建缓存行为 1 2 3 4 5 # COPY - 严格匹配文件内容，内容变了才缓存失效 COPY config/app.conf /etc/app/ # ADD - 压缩文件内容相同时缓存仍然有效 ADD app.tar.gz /opt/app/ 场景 COPY ADD 文件内容变化 缓存失效 缓存失效 文件元数据变化 缓存失效 缓存失效 压缩文件内容相同 N/A 缓存有效 6. 功能对比 特性 COPY ADD 复制本地文件 ✅ ✅ 复制目录 ✅ ✅ 支持通配符 ✅ ✅ 自动创建目录 ✅ ✅ 自动解压压缩文件 ❌ ✅ 支持远程 URL ❌ ✅ 语义清晰度 ✅ 高 ❌ 低 推荐使用 ✅ 首选 ⚠️ 特定场景 7. 使用场景 COPY 适用于：\n1 2 3 4 5 6 7 8 9 # 复制配置文件 COPY nginx.conf /etc/nginx/nginx.conf # 复制应用代码 COPY src/ /app/src/ COPY package.json /app/ # 多阶段构建中复制二进制 COPY --from=builder /app/build/app /usr/local/bin/ ADD 仅适用于需要自动解压的场景：\n1 2 3 4 5 # 合理使用 ADD ADD node-v18.17.0-linux-x64.tar.gz /usr/local/ # 普通文件不要用 ADD ADD app.conf /etc/app/ # ← 用 COPY 代替 8. 最佳实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 规则 1：默认用 COPY COPY requirements.txt /app/ # ✅ ADD requirements.txt /app/ # ❌ 没必要 # 规则 2：只在需要解压时用 ADD ADD node-v18.tar.gz /usr/local/ # ✅ 需要解压 # 规则 3：远程文件用 RUN + curl RUN curl -fsSL -o /tmp/app.tar.gz https://example.com/app.tar.gz \\ \u0026amp;\u0026amp; tar -xzf /tmp/app.tar.gz -C /opt/ \\ \u0026amp;\u0026amp; rm /tmp/app.tar.gz # ✅ ADD https://example.com/app.tar.gz /opt/ # ❌ # 规则 4：用 .dockerignore 减少上下文 # .dockerignore # .git # node_modules # *.md # .env # 规则 5：合理安排 COPY 顺序利用缓存 COPY package.json package-lock.json ./ # 先复制依赖（变化少） RUN npm ci --production COPY . . # 再复制代码（变化多） 9. 常见陷阱 1 2 3 4 5 6 7 8 9 10 # 陷阱 1：意外的自动解压 ADD app.tar.gz /opt/app/ # 你以为复制了文件，实际解压成了目录 # 陷阱 2：远程文件无法缓存 ADD https://github.com/user/repo/releases/download/v1.0/app.tar.gz /tmp/ # 每次构建都重新下载 # 陷阱 3：权限问题 COPY --chown=app:app src/ /app/src/ # 指定所有者 10. 选择决策 1 2 3 4 5 6 7 需要复制文件？ ├── 本地压缩文件且需要解压？ │ └── 用 ADD ├── 本地普通文件？ │ └── 用 COPY └── 远程文件？ └── 用 RUN + curl ","permalink":"https://LMDAWN1127.github.io/posts/dockerfile-copy-vs-add/","summary":"COPY 和 ADD 都可以复制文件，但它们有本质区别。本文详解两者差异，助你写出更好的 Dockerfile。","title":"Dockerfile 中 COPY 与 ADD 的区别详解"},{"content":"1. 环境要求 操作系统: CentOS 7/8/Stream 9 Node.js: v18 或更高 内存: 建议 2GB 以上 网络: 需要访问 Anthropic API 2. 安装 Node.js 1 2 3 4 5 6 7 8 9 # 安装 NodeSource 仓库 curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - # 安装 Node.js sudo yum install -y nodejs # 检查 node -v npm -v 如果 Node.js 版本过低，使用 nvm 管理版本：\n1 2 3 4 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash source ~/.bashrc nvm install 20 nvm use 20 3. 安装 Claude Code 1 2 3 4 5 # 全局安装 npm install -g @anthropic-ai/claude-code # 检查 claude --version 遇到权限问题有两种解决方案：\n1 2 3 4 5 6 7 8 # 方案一：sudo sudo npm install -g @anthropic-ai/claude-code # 方案二：修改 npm 全局目录（推荐） mkdir -p ~/.npm-global npm config set prefix \u0026#34;~/.npm-global\u0026#34; echo \u0026#39;export PATH=~/.npm-global/bin:$PATH\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 4. 配置 API Key 1 2 3 4 5 6 # 方式一：环境变量（推荐） echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;your-api-key-here\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc # 检查 echo $ANTHROPIC_API_KEY 方式二：直接运行 claude，首次启动时会提示输入 API Key。\n5. 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 启动交互式会话 cd /path/to/your/project claude # 直接提问（非交互模式） claude -p \u0026#34;写一个 Python 脚本，统计当前目录下所有文件的数量\u0026#34; # 分析代码 cat main.py | claude -p \u0026#34;分析这段代码的潜在问题\u0026#34; # 查看/修改配置 claude config # 更新到最新版本 claude update 命令 说明 claude 启动交互式会话 claude -p \u0026quot;问题\u0026quot; 直接提问，非交互模式 claude config 查看/修改配置 claude update 更新到最新版本 6. 网络问题排查 如果连接 Anthropic API 超时：\n1 2 3 4 5 6 # 配置代理 export https_proxy=\u0026#34;http://your-proxy:port\u0026#34; export http_proxy=\u0026#34;http://your-proxy:port\u0026#34; # 测试连通性 curl -s https://api.anthropic.com/ | head -5 7. 安全建议 1 2 3 4 5 6 7 8 9 # 不要硬编码 API Key，始终用环境变量 echo \u0026#39;export ANTHROPIC_API_KEY=\u0026#34;sk-ant-xxx\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.bashrc # ✅ # ANTHROPIC_API_KEY=sk-ant-xxx 写在代码里 # ❌ # 限制文件权限 chmod 600 ~/.bashrc # 检查是否泄露 grep -r \u0026#34;ANTHROPIC_API_KEY\u0026#34; --include=\u0026#34;*.py\u0026#34; --include=\u0026#34;*.js\u0026#34; . ","permalink":"https://LMDAWN1127.github.io/posts/centos-deploy-claude-code/","summary":"Claude Code 是 Anthropic 推出的 CLI 工具，可在终端直接与 Claude AI 交互完成编码任务。本文介绍 CentOS 部署全流程。","title":"CentOS 部署 Claude Code 全流程指南"},{"content":"flannel 实战：从零搭建 Docker 跨主机容器网络 一、网络环境规划 etcd 主机 IP：192.168.8.88 主机名：docker-network Host1 IP：192.168.8.188 主机名：web01 Host2 IP：192.168.8.99 主机名：web02 二、etcd 主机配置 步骤 1：下载并解压 etcd 从github.com 下载etcd-v3.6.0-linux-amd64.tar.gz 软件包 将软件包复制到 etcd 主机/root 目录并解压： 1 [root@docker-network ~]# tar -zxvf etcd-v3.6.0-linux-amd64.tar.gz 步骤 2：安装 etcd 可执行文件 1 2 [root@docker-network ~]# cd etcd-v3.6.0-linux-amd64 [root@docker-network etcd-v3.6.0-linux-amd64]# cp etcd* /usr/local/bin/ 步骤 3：创建数据目录 1 [root@docker-network ~]# mkdir /var/lib/etcd 步骤 4：配置并启动 etcd 服务 创建etcd.service 文件： 1 [root@docker-network ~]# vim /usr/lib/systemd/system/etcd.service 1 2 3 4 5 6 [Unit] Description=etcd_service [Service] ExecStart=/usr/local/bin/etcd --name etcd1 --data-dir /var/lib/etcd --listen-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 [Install] WantedBy=multi-user.target 重载系统服务配置并启动 etcd： 1 2 3 [root@docker-network ~]# systemctl daemon-reload [root@docker-network ~]# systemctl enable etcd.service [root@docker-network ~]# systemctl restart etcd.service 验证 etcd 监听状态： 1 2 3 [root@docker-network ~]# netstat -tulnp |grep :2379 tcp 0 0 192.168.8.88:2379 0.0.0.0:* LISTEN 1912/etcd tcp 0 0 127.0.0.1:2379 0.0.0.0:* LISTEN 1912/etcd 步骤 5：向 etcd 添加 flannel 网络配置 1 etcdctl --endpoints http://127.0.0.1:2379 put /coreos.com/network/config \u0026#39;{\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}}\u0026#39; 1 etcdctl --endpoints http://192.168.8.88:2379 get /coreos.com/network/config 配置说明：\nNetwork：Flannel 地址池 SubnetLen：分配给单个宿主机的 docker0 子网掩码长度（24 位即255.255.255.0） SubnetMin/SubnetMax：可分配的子网范围（示例为172.16.1.0/24 到172.16.10.0/24） Backend：数据转发模式（vxlan 支持跨主机通信） 步骤 6：关闭防火墙 1 2 [root@docker-network ~]# systemctl disable firewalld [root@docker-network ~]# systemctl stop firewalld 三、web01 主机配置（Host1: 192.168.8.188） 步骤 1：下载并解压 flannel 下载flannel-v0.26.7-linux-amd64.tar.gz 到主机 解压文件： 1 [root@web01 ~]# tar -zxvf flannel-v0.26.7-linux-amd64.tar.gz 步骤 2：安装 flannel 可执行文件 1 [root@web01 ~]# cp -p flanneld mk-docker-opts.sh /usr/local/bin/ 步骤 3：配置并启动 flannel 服务 创建flanneld.service 文件： 1 [root@web01 ~]# vim /usr/lib/systemd/system/flanneld.service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=Flanneld Documentation=https://github.com/coreos/flannel After=network.target Before=docker.service [Service] User=root ExecStartPost=/usr/local/bin/mk-docker-opts.sh ExecStart=/usr/local/bin/flanneld --etcd-endpoints=http://192.168.8.88:2379 --iface=192.168.8.188 --ip-masq=true --etcd-prefix=/coreos.com/network Restart=on-failure Type=notify LimitNOFILE=65536 [Install] WantedBy=multi-user.target 参数说明：\n--iface=192.168.8.188：指定本机连接 etcd 的 IP --etcd-endpoints：etcd 服务器地址 重载服务配置并启动 flannel：\n1 2 3 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl enable flanneld.service [root@web01 ~]# systemctl restart flanneld.service 步骤 4：验证 flannel 网络接口 1 [root@web01 ~]# ifconfig 应显示flannel.1 接口（示例输出）：\n1 2 3 4 5 6 7 8 flannel.1: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1450 inet 172.16.5.0 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::f06c:96ff:fefe:b7ab prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether f2:6c:96:fe:b7:ab txqueuelen 0 (Ethernet) RX packets 315 bytes 26460 (25.8 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 314 bytes 34636 (33.8 KiB) TX errors 0 dropped 29 overruns 0 carrier 0 collisions 0 步骤 5：修改 docker 配置文件 查看 flannel 生成的子网环境变量： 1 [root@web01 ~]# cat /run/flannel/subnet.env 输出示例：\n1 2 3 4 FLANNEL_NETWORK=172.16.0.0/16 FLANNEL_SUBNET=172.16.5.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true 1 [root@web01 ~]# cat /run/docker_opts.env 输出示例：\n1 2 3 4 DOCKER_OPT_BIP=\u0026#34;--bip=172.16.5.1/24\u0026#34; DOCKER_OPT_IPMASQ=\u0026#34;--ip-masq=false\u0026#34; DOCKER_OPT_MTU=\u0026#34;--mtu=1450\u0026#34; DOCKER_OPTS=\u0026#34; --bip=172.16.5.1/24 --ip-masq=false --mtu=1450\u0026#34; 修改 docker 服务配置： 1 [root@web01 ~]# vim /usr/lib/systemd/system/docker.service 在[Service]部分添加：\n1 2 3 ExecStart=/usr/bin/dockerd $DOCKER_OPTS -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID EnvironmentFile=-/run/docker_opts.env 重启 docker 服务： 1 2 [root@web01 ~]# systemctl daemon-reload [root@web01 ~]# systemctl restart docker.service 步骤 6：验证 docker 网络 创建 nginx 容器并进入： 1 2 3 4 5 6 7 8 9 [root@web01 ~]# ifconfig docker0: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1450 inet 172.16.5.1 netmask 255.255.255.0 broadcast 172.16.5.255 inet6 fe80::42:f6ff:fe5d:5423 prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether 02:42:f6:5d:54:23 txqueuelen 0 (Ethernet) RX packets 10073 bytes 407167 (397.6 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 48185 bytes 38083646 (36.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 1 2 [root@web01 ~]# docker run -d -p 8800:80 nginx [root@web01 ~]# docker exec -it \u0026lt;容器ID\u0026gt; /bin/bash 查看容器 IP（示例为172.16.5.3）： 1 2 root@容器ID:/# ifconfig eth0: inet 172.16.5.3 netmask 255.255.255.0 四、web02 主机配置（Host2: 192.168.0.208） 步骤说明 配置步骤与 web01 完全一致，仅需修改以下参数： --iface=192.168.8.99（本机 IP） docker 容器 IP 示例：172.16.5.2（由 flannel 自动分配） 五、实验验证 在 web01 容器中 ping web02 容器 IP（示例：172.16.9.2）：\n在web02 中创建容器，查看容器IP 地址\n结果：容器间通信成功，flannel 网络配置完成。\n附录：etcd 网络配置查询 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/config /coreos.com/network/config {\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}} 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/subnets --prefix --keys-only /coreos.com/network/subnets/172.16.5.0-24 /coreos.com/network/subnets/172.16.9.0-24 1 2 3 [root@docker-network ~]# etcdctl --endpoints=http://127.0.0.1:2379 get /coreos.com/network/subnets/172.16.5.0-24 /coreos.com/network/subnets/172.16.5.0-24 {\u0026#34;PublicIP\u0026#34;:\u0026#34;192.168.8.188\u0026#34;,\u0026#34;PublicIPv6\u0026#34;:null,\u0026#34;BackendType\u0026#34;:\u0026#34;vxlan\u0026#34;,\u0026#34;BackendData\u0026#34;:{\u0026#34;VNI\u0026#34;:1,\u0026#34;VtepMAC\u0026#34;:\u0026#34;5a:67:18:61:6a:5d\u0026#34;}} ","permalink":"https://LMDAWN1127.github.io/posts/flannel-network-lab/","summary":"\u003ch1 id=\"flannel-实战从零搭建-docker-跨主机容器网络\"\u003eflannel 实战：从零搭建 Docker 跨主机容器网络\u003c/h1\u003e\n\u003ch2 id=\"一网络环境规划\"\u003e一、网络环境规划\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eetcd 主机 IP：192.168.8.88 主机名：docker-network\u003c/li\u003e\n\u003cli\u003eHost1 IP：192.168.8.188 主机名：web01\u003c/li\u003e\n\u003cli\u003eHost2 IP：192.168.8.99 主机名：web02\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"二etcd-主机配置\"\u003e二、etcd 主机配置\u003c/h2\u003e\n\u003ch3 id=\"步骤-1下载并解压-etcd\"\u003e步骤 1：下载并解压 etcd\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e从github.com 下载etcd-v3.6.0-linux-amd64.tar.gz 软件包\u003c/li\u003e\n\u003cli\u003e将软件包复制到 etcd 主机/root 目录并解压：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# tar -zxvf etcd-v3.6.0-linux-amd64.tar.gz\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"步骤-2安装-etcd-可执行文件\"\u003e步骤 2：安装 etcd 可执行文件\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# cd etcd-v3.6.0-linux-amd64\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network etcd-v3.6.0-linux-amd64\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# cp etcd* /usr/local/bin/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"步骤-3创建数据目录\"\u003e步骤 3：创建数据目录\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# mkdir /var/lib/etcd\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"步骤-4配置并启动-etcd-服务\"\u003e步骤 4：配置并启动 etcd 服务\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e创建etcd.service 文件：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# vim /usr/lib/systemd/system/etcd.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e5\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e6\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eetcd_service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/local/bin/etcd --name etcd1 --data-dir /var/lib/etcd --listen-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.8.88:2379,http://127.0.0.1:2379\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"2\"\u003e\n\u003cli\u003e重载系统服务配置并启动 etcd：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl daemon-reload\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl enable etcd.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# systemctl restart etcd.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"3\"\u003e\n\u003cli\u003e验证 etcd 监听状态：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eroot@docker-network ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# netstat -tulnp |grep :2379\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etcp        \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e      \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e 192.168.8.88:2379       0.0.0.0:*               LISTEN      1912/etcd\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etcp        \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e      \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e 127.0.0.1:2379          0.0.0.0:*               LISTEN      1912/etcd\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"步骤-5向-etcd-添加-flannel-网络配置\"\u003e步骤 5：向 etcd 添加 flannel 网络配置\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eetcdctl --endpoints http://127.0.0.1:2379 put /coreos.com/network/config \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{\u0026#34;Network\u0026#34;: \u0026#34;172.16.0.0/16\u0026#34;, \u0026#34;SubnetLen\u0026#34;: 24, \u0026#34;SubnetMin\u0026#34;: \u0026#34;172.16.1.0\u0026#34;,\u0026#34;SubnetMax\u0026#34;: \u0026#34;172.16.10.0\u0026#34;, \u0026#34;Backend\u0026#34;: {\u0026#34;Type\u0026#34;: \u0026#34;vxlan\u0026#34;}}\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eetcdctl --endpoints http://192.168.8.88:2379 get /coreos.com/network/config\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e配置说明：\u003c/p\u003e","title":"flannel 实战：从零搭建 Docker 跨主机容器网络"},{"content":"关注领域 AI 编程工具 — Claude Code、Cursor 等 AI 辅助开发 Linux 运维 — CentOS/Ubuntu 服务器管理、Docker 容器化 Web 开发 — 前后端开发、Hugo 博客搭建 自动化 — CI/CD、脚本编写、效率工具 技术栈 语言\n1 Python | Go | JavaScript | Shell 工具 \u0026amp; 平台\n1 Linux | Docker | Git | GitHub Actions | Hugo AI 工具\n1 Claude Code | ChatGPT | Copilot 联系我 GitHub: LMDAWN1127 邮箱: LMDAWN1127@users.noreply.github.com 关于本站 本博客使用 Hugo + PaperMod 主题搭建，托管在 GitHub Pages。\n主要记录：\n技术学习笔记 项目实践总结 工具使用教程 日常学习成长 如果我的文章对你有帮助，欢迎 Star 我的 GitHub 仓库！\n","permalink":"https://LMDAWN1127.github.io/about/","summary":"\u003ch3 id=\"关注领域\"\u003e关注领域\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eAI 编程工具\u003c/strong\u003e — Claude Code、Cursor 等 AI 辅助开发\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eLinux 运维\u003c/strong\u003e — CentOS/Ubuntu 服务器管理、Docker 容器化\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWeb 开发\u003c/strong\u003e — 前后端开发、Hugo 博客搭建\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e自动化\u003c/strong\u003e — CI/CD、脚本编写、效率工具\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"技术栈\"\u003e技术栈\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e语言\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePython | Go | JavaScript | Shell\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e工具 \u0026amp; 平台\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLinux | Docker | Git | GitHub Actions | Hugo\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eAI 工具\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eClaude Code | ChatGPT | Copilot\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"联系我\"\u003e联系我\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eGitHub\u003c/strong\u003e: \u003ca href=\"https://github.com/LMDAWN1127\"\u003eLMDAWN1127\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e邮箱\u003c/strong\u003e: \u003ca href=\"mailto:LMDAWN1127@users.noreply.github.com\"\u003eLMDAWN1127@users.noreply.github.com\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"关于本站\"\u003e关于本站\u003c/h3\u003e\n\u003cp\u003e本博客使用 \u003ca href=\"https://gohugo.io/\"\u003eHugo\u003c/a\u003e + \u003ca href=\"https://github.com/adityatelange/hugo-PaperMod\"\u003ePaperMod\u003c/a\u003e 主题搭建，托管在 GitHub Pages。\u003c/p\u003e","title":"关于我"}]