个人生活技术分享 - Linux运维工作
type: channel
link: https://image.ineedyou.xyz/category/Linux运维工作
items: CertBot自动签发SSL证书
items: OpenSSL 常用命令
items: Rabbitmq通过Nginx转发
items: 一个MySQL简单备份脚本
items: Shell多进程并发执行
items: Elasticsearch-学习总结
items: Kuberntes中常见问题以及故障定位
items: 利用Django编写Kubernetes中服务可用性查询
items: Optimize Kernel
items: MySQL-FEDERATED引擎 跨主机联表查询-（2）实践

CertBot自动签发SSL证书
type: item
date: 2025-12-25T19:49:00+08:00
description: 1.安装CertBotUbuntu系统安装sudo apt install -y certbotsudo apt install -y python3-certbot-nginxCentOS系统通过安装pip安装yum -y install python3 python-devel augeas-devel gccpip install certbot certbot-nginx2.申请证书sudo certbot --nginx -d 你的域名 -d xxx.com -d aaa.com（注意：--nginx会基于直接主机安装的nginx,读取nginx目录/etc/nginx配置文件，生成前端校验文件获取证书，需要80端口打开，通过80校验文件）3.设置自动续期sudo crontab -e0 0 1 sudo certbot renew --quiet

OpenSSL 常用命令
type: item
date: 2023-03-16T13:56:00+08:00
description: 打印出证书的内容：openssl x509 -in cert.pem -noout -text打印证书有效期openssl x509 -in server.crt -noout -datesSHELL命令查看证书是否过期openssl s_client -servername 目标域名 -connect 目标域名:443

Rabbitmq通过Nginx转发
type: item
date: 2023-03-10T15:54:18+08:00
description: location /rabbitmq/ { port_in_redirect on; proxy_redirect off; proxy_pass http://*.*.*.*:15672/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header User-Agent $http_user_agent; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location ~* /rabbitmq/api/ { rewrite ^ $request_uri; rewrite ^/rabbitmq/api/(.*) /api/$1 break; return 400; proxy_pass http://*.*.*.*:15672$uri; proxy_buffering off; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }

一个MySQL简单备份脚本
type: item
date: 2022-11-14T16:06:00+08:00
description: 由于很多项目部署，需要使用MySQL备份脚本，有时候一个数据库下面有多个数据库，就写了一个简单的逻辑备份排除系统默认库的全量数据库的脚本。#!/bin/bash DATE=`date +"%Y%m%d"` host='127.0.0.1' port='3306' user='*****' passWord='******' databaseBundle=`mysql -h $host -P $port -u$user -p$passWord -e 'show databases;'` if [ $? -ne 0 ];then echo '数据库连接异常退出！' exit fi for data in $databaseBundle; do # 几个判断排除系统自带库 if [ $data == 'Database' ];then continue elif [ $data == 'information_schema' ];then continue elif [ $data == 'mysql' ];then continue elif [ $data == 'performance_schema' ];then continue elif [ $data == 'sys' ];then continue else echo $data mysqldump -h $host -P $port -u$user -p$passWord --databases ${data}>/mysql-bak/mysql-${data}-$DATE.sql if [ $? -ne 0 ];then echo '数据库备份异常退出！' # 删除错误备份的空sql rm -f /mysql-bak/mysql-${data}-$DATE.sql continue fi # 保留最新的30份备份 find /mysql-bak/ -type f -mtime +30 -name "mysql-${data}*.sql" | xargs rm -f fi done

Shell多进程并发执行
type: item
date: 2020-11-15T16:28:00+08:00
description: 有一个需求，需要迁移大量数据库，并且数据库都并不大，需要快速的将其全量迁移过去。这里通过Shell脚本多进行进行，这里记录一下，后续有其他需要进行多线程的也可以随便改一下就可以用。#!/bin/bash # MySQL 迁移正式脚本 start_time=`date +%s` #创建有名管道 [ -e /tmp/fd1 ] || mkfifo /tmp/fd1 #创建文件描述符，以可读（<）可写（>）的方式关联管道文件，这时候文件描述符3就有了有名管道文件的所有特性 exec 3<>/tmp/fd1 #关联后的文件描述符拥有管道文件的所有特性,所以这时候管道文件可以删除，我们留下文件描述符来用就可以了 rm -rf /tmp/fd1 # i=5 5个并发 for ((i=1;i<=5;i++)) do #&3代表引用文件描述符3，这条命令代表往管道里面放入了一个"令牌" echo >&3 done # 打开迁移文件，逐行执行 data=`cat /data/mysql-move/0-mysql-move-last.txt` IFS=$'\n' for i in $data do #代表从管道中读取一个令牌 read -u3 { start_time_two=`date +%s` echo $i | sh stop_time_two=`date +%s` if [ $? -ne 0 ];then echo ${i}导入失败 >> /data/mysql-move/logs/move-error.log echo "TIME:`expr $stop_time_two - $start_time_two` s" >> /data/mysql-move/logs/move-error.log else echo ${i}导入成功 >> /data/mysql-move/logs/move-succes.log echo "TIME:`expr $stop_time_two - $start_time_two` s" >> /data/mysql-move/logs/move-succes.log fi #代表我这一次命令执行到最后，把令牌放回管道 echo >&3 }& done wait stop_time=`date +%s` echo "TIME:`expr $stop_time - $start_time`" > /data/mysql-move/logs/time.txt #关闭文件描述符的读 exec 3<&- #关闭文件描述符的写 exec 3>&-上图的脚本中，有用到这样一个文件，这个文件其实就是我把 需迁移的库 与 目标库的命令组成每行的Shell命令。例如mysqldump -h x.x.x.x -P 3306 -uUser -pPasswd database1 > database1.sql && mysql -h y.y.y.y -P 3306 -uUser2 -PPasswd2 < database1 && echo "database1 导入成功" >> /data/mysql-move/logs/succes.log && rm -f database1.sql || echo "database1 导入失败" >> /data/mysql-move/logs/error.log && rm -f database1.sql mysqldump -h x.x.x.x -P 3306 -uUser -pPasswd database2 > database2.sql && mysql -h y.y.y.y -P 3306 -uUser2 -PPasswd2 < database2 && echo "database2 导入成功" >> /data/mysql-move/logs/succes.log && rm -f database2.sql || echo "database2 导入失败" >> /data/mysql-move/logs/error.log && rm -f database2.sql mysqldump -h x.x.x.x -P 3306 -uUser -pPasswd database3 > database1.sql && mysql -h y.y.y.y -P 3306 -uUser3 -PPasswd3 < database3 && echo "database3 导入成功" >> /data/mysql-move/logs/succes.log && rm -f database3.sql || echo "database3 导入失败" >> /data/mysql-m…

Elasticsearch-学习总结
type: item
date: 2020-09-17T12:19:00+08:00
description: Elasticsearch 核心概念1、索引==数据库；2、mapping==字段类型；3、文档==数据。elasticsearch 是面向文档。这里用关系型数据库和elasticsearch 客观的对比！Relational DBelasticsearch数据库（database）索引（indices）表（tables）types 后续会被弃用行（rows）documents（文档）字段（columns）fields（字段）elasticsearch（集群）中可以包含多个索引（数据库），每个索引中可以包含多个类型（表），每个类型下又包含多个文档（行），每个文档中又包含多个字段（列）。物理设计：elasticsearch 在后台把每个索引划分成多个分片，每个分片可以在集群中的不同服务器间迁移。逻辑设计：一个索引类型中，包含多个文档，比如文档1，文档2.但我们索引一篇文档时，可以通过这样的一个顺序找到它：索引》类型》文档ID，通过这个组合我们就能索引到某个具体的文档。注意：ID不必是整数，实际上它是个字符串。文档elasticsearch 是面向文档的，那么就意味着索引和搜索数据的最小单位是文档，elasticsearch 中，文档有几个重要属性：自我包含，一篇文档同时包含字段和对应的值，也就是同时包含 key:value；可以是层次型，一个文档中包含自文档，复杂的逻辑实体就是这么来的；灵活的结构，文档不依赖预先定义的模式，我们知道关系型数据库中，要提前定义字段才能使用，在elasticsearch中，对应字段是非常灵活的，有时候，我们可以忽略该字段，或者动态的添加一个新的字段。尽管我们可以随意的新增或忽略某个字段，但是，每个字段的类型非常重要，比如一个年龄字段类型，可以是字符串也可以是整型。因为elasticsearch 会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型，这也是为什么在elasticsearch中，类型有时候也成为映射类型。类型类型是文档的逻辑容器，就像关系型数据库一样，表格是行的容器。类型中对于字段的定义成为映射，比如 name 映射为字符串类型。我们说文档是无模式的，它们不需要拥有映射中说定义的所有字段，比如新增一个字段，那么elasticsearch 是怎么做的呢？ elasticsearch 会自动的将新字段加入映射，但是这个字段的不确定它是什么类型，elasticsearch 就开始猜，如果这个值是18，那么elasticsearch 会认为它是整型。但是elasticsearch 也可能猜不对，所以最安全的方式就是提前定义好说需要的映射，这点跟关系型数据库殊途同归了，先定义好字段，然后再使用。索引索引是映射类型的容器，elasticsearch 中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上。我们来研究下分片是如何工作的。物理设计：节点和分片 如何工作一个集群至少有一个节点，而一个节点就是一个 elasticsearch 进程，节点可以有多个索引默认的，如果你创建索引，那么索引将会有5个分片（primary shard，又称主分片，默认5个分片）构成的，每一个主分片会有一个副本（replica shard，又称复制分片）。主分片和对应的复制分片不会在同一个节点内，这样有利于某个节点挂掉了，数据也不至于丢失。实际上，==一个分片是一个 Lucene 索引==，一个包含倒排索引的文件目录，倒排索引的结构使得elasticsearch 在不扫描全部文档的情况下就能告诉你哪些文档包含特定的关键字。倒排索引elasticsearch 使用的是一种称为倒排索引的结构，采用Lucene倒排索引作为底层，这种结构适用于快速的全文搜索，一个索引由文档中所有不重复的列表构成，对于每一个词，都有一个包含它的文档列表。例如，现在有两个文档，每个文档包含如下内容：Study every day,good good up to forever # 文档1包含内容 To forever, study every day, good good up # 文档2包含内容为了创建倒排索引，我们首先将每个文档拆分成独立的词（或称为词条或者tokens），然后创建一个包含所有不重复的词条的排序列表，然后列出每个词条出现在哪个文档。termdoc_1doc_2Study√×To×√every√√forever√√day√√study×√good√√every√√to√×up√√现在，我们尝试搜索 to forever，只需要查看包含每个词条的文档。termdoc_1doc_2to√×forever√√total21两个文档都匹配，但是第一个文档比第二个匹配程度更高。如…

Kuberntes中常见问题以及故障定位
type: item
date: 2020-08-15T21:22:00+08:00
description: 1.长连接服务：滚动更新导致负载不均建议: client 侧设置连接过期时间或者达到一定请求数就重连2.高并发服务：ipvs 下，高并发 client 导致 “no route to host”client 高并发导致源端口重用，ipvs 模块匹配到旧连接(weight为0，但没真正剔除，目的是ipvs为了支持graceful termination)旧连接的后端 pod 已经销毁，导致连接异常建议：暂时没有完美解决方案，可通过 Pod 反亲和打散 client 避免流量集中规避大流量的边缘节点源端口耗尽边缘节点通过 NodePort 接收外界流量，发生大量 SNAT，导致源端口耗尽连接队列溢出问题syn 队列保存半连接状态的连接， accpet 队列保存已建立但还没有被应用处理的连接。ss –lnt 查看连接队列情况, Send-Q 表示 accept 队列大小, Recv-Q 表示 accept 队列中连接数量，通常为0，比 Send-Q 大于1表示溢出；通常 somaxconn 决定 accpet 队列大小，默认为 128；$ netstat -s | grep -E 'overflow|drop'流量过大或高负载易导致应用处理不过来(accept过慢)，从而队列溢出丢包；使用 K8S sysctls 特性设置 somaxconn (此特性在 k8s v1.12 beta，默认开启)；使用 initContainers 设置 somaxconn (特权容器)；nginx 在 listen() 时并没有指定 somaxconn 的值为队列大小，而是有自己的默认值：511listen 80 default backlog=1024通过在 nginx.conf 配置 backlog 可调整队列大小慎用alpinealpine 比较精简，很多依赖库没有，或者跟其它镜像不一样，一些依赖动态链接库的程序容易报错，比如 go 的 cgo 调用；底层库是 musl libc，域名解析行为跟 glibc 有差异，在 resolv.conf 配置的很多选项不支持；没有内置 bash，运行 bash 脚本不兼容；使用 lxcfs 提升容器资源可见性对 alpine 不生效(容器内看到的还是宿主机的cpu, memory使用情况)，因为 alpine 内置 busybox 用系统调用获取信息而非读取 /proc；DNS优化search 与 ndots:若域名中的 “.” 数量大于等于 ndots 数量，直接向 dns server 查询此域名；否则就先依次遍历 search 并拼接到域名后缀查询；默认 ndots 为 5，即便把 service 名称拼全(比如 test.default.svc.cluster.local，4个 “.”)，也还是会有 4 查询，前 3 次是多余的；优化:Pod dnsConfig 配置较小的 ndots；集群内部通过 service 访问时拼全后缀(namespaces.svc.cluster.local)DNS cache优化:改造业务，避免每次请求都查dns，比如 java 设置 networkaddress.cache.ttl ;部署 NodeLocal DNS 作为本地 DNS 缓存常见故障定位1、查看状态码与详情；$ kubectl describe pod {xxx} -n {namespaces}使用 kubectl describe pod 查看容器上次退出状态码：分析退出状态码：1-128 表示进程主动退出 (只是约定)，具体状态码含义取决于应用程序逻辑；有时主动退出也会是 255 状态码: 代码里使用类似 exit(-1) 时，-1 被自动转成 255，通常状态码为 1 和 255 是一般性错误，看不出具体含义，需要结合日志分析；129-255 表示进程因外界中断信号退出，最常见的是 137，表示被 SIGKILL 杀死，可能是 Cgroup OOM，系统 OOM，存活检查失败或者被其它进程杀死导致。2、查看日志；使用 kubectl logs 查看容器日志 (-p 可看上次退出日志)：$ kubectl logs {podname} -n {namespaces}3、查看资源配置；使用 -o yaml 查看资源 yaml：# pod $ kubectl get pod {podname} -n {namespaces} -o yaml # deploy $ kubectl get deploy {deplyname} -n {namespaces} -o yaml # svc $ kubectl get svc {svcname} -n {namespaces} -o yaml # daem…

利用Django编写Kubernetes中服务可用性查询
type: item
date: 2020-06-09T16:30:00+08:00
description: 由于工作中，经常会出现测试在群里发一个报错的url，发到群里面造成AOE范围伤害波及到运维，所以自己根据需求，整理了一个可 根据 服务名进行匹配，以及url 进行匹配对应服务的东西，便于排查问题；因为Kubernetes本身各方面还是较为完善，所以在Master上开启一个 kubectl proxy 模式可以通过这个模式，直接通过http请求访问到对应日志、配置文件相关之类文件；nohup kubectl proxy --address='0.0.0.0' --port=8080 --accept-hosts='^*$' &以及后面通过命令行，快速生成字典：kubectl get ing | grep -v NAME |awk 'BEGIN{print "{"}{print "\""$2"\"" ":" "\""$1"\""","}END{print "}"}'注意删除最后一个逗号！界面使用的示例：然后发一下代码：Django部分就是很简单的配置，前端页面也是最简单的html + css调用代码kubetools.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- '''通过输入server，取出所需的所有pod''' import os os.environ['DJANGO_SETTINGS_MODULE'] = 'books.settings' # 配置系统变量 import django import urllib3 import datetime import traceback import socket from kubernetes import client from kubernetes.client.rest import ApiException django.setup() class FilterBundle(): def __init__(self, token, apiserver, namespace, server, scope=0): self.token = token self.apiserver = apiserver self.namespace = namespace self.server = server self.scope = scope def initSetting(self): urllib3.disable_warnings() configuration = client.Configuration() configuration.host = self.apiserver configuration.verify_ssl = False configuration.api_key = {'authorization': 'Bearer ' + self.token} client.Configuration.set_default(configuration) return client def telnet(self, ip, port): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.settimeout(2) s.connect((ip, int(port))) s.shutdown(2) return True except: return False def hostStatus(self, host): try: api_instance = self.initSetting().CoreV1Api() api_response = api_instance.list_node() for h in api_response.items: svc_ip = h.status.addresses[0].address if host in svc_ip: host_status = h.status.conditions[3].status return host_status except ApiException as e: traceback.print_exc(e) def filterSvc(self): try: api_instance = self.initSetting().CoreV1Api() api_response = api_instance.list_namespaced_service(self.namespace) svc_list = api_response.items for s in svc_list: # svc name svc_name = s.metadata.name if sel…

Optimize Kernel
type: item
date: 2020-05-30T11:42:00+08:00
description: Optimize Kernel--- - name: Optimize Kernel sysctl: name={{ item.name }} value={{ item.value }} sysctl_set=yes state=present reload=yes with_items: #core文件名中添加pid作为扩展名 - {name: 'kernel.core_uses_pid', value: '1'} # 关闭sysrq功能 - {name: 'kernel.sysrq', value: '0'} # 修改消息队列长度 - {name: 'kernel.msgmnb', value: '65536'} - {name: 'kernel.msgmax', value: '65536'} # 设置最大内存共享段大小bytes - {name: 'kernel.shmmax', value: '68719476736'} - {name: 'kernel.shmall', value: '4294967296'} - {name: 'vm.min_free_kbytes', value: '65535'} - {name: 'vm.swappiness', value: '0'} - {name: 'vm.overcommit_memory', value: '1'} # 关闭ipv6 - {name: 'net.ipv6.conf.all.disable_ipv6', value: '1'} - {name: 'net.ipv6.conf.default.disable_ipv6', value: '1'} - {name: 'net.ipv6.conf.lo.disable_ipv6', value: '1'} # 避免放大攻击 - {name: 'net.ipv4.icmp_echo_ignore_broadcasts', value: '1'} # 开启恶意icmp错误消息保护 - {name: 'net.ipv4.icmp_ignore_bogus_error_responses', value: '1'} # 开启路由转发 - {name: 'net.ipv4.ip_forward', value: '1'} # 一次性处理TCP队列中的数量 - {name: 'net.core.somaxconn', value: '32768'} # 每个网络接口接收数据包的速率比内核处理这些包的速率快时，允许送到队列的数据包的最大数目 - {name: 'net.core.netdev_max_backlog', value: '32768'} # 未收到客户端确认信息的连接请求的最大值 - {name: 'net.ipv4.tcp_max_syn_backlog', value: '4096'} # 验证时间戳 - {name: 'net.ipv4.tcp_timestamps', value: '1'} # 开启SYN洪水攻击保护 - {name: 'net.ipv4.tcp_syncookies', value: '1'} #限制仅仅是为了防止简单的DoS 攻击 - {name: 'net.ipv4.tcp_max_orphans', value: '3276800'} # timewait的数量，默认180000 - {name: 'net.ipv4.tcp_max_tw_buckets', value: '6000'} # 用来查找特定的遗失的数据包---因此有助于快速恢复状态 - {name: 'net.ipv4.tcp_sack', value: '1'} # 设置tcp/ip会话的滑动窗口大小是否可变 - {name: 'net.ipv4.tcp_window_scaling', value: '1'} # 设置TCP缓存区配置 - {name: 'net.core.wmem_default', value: '8388608'} - {name: 'net.core.rmem_default', value: '8388608'} - {name: 'net.core.rmem_max', value: '16777216'} - {name: 'net.core.wmem_max', value: '16777216'} - {name: 'net.ipv4.tcp_rmem', value: '4096 87380 4194304'} - {name: 'net.ipv4.tcp_wmem', value: '4096 16384 4194304'} - {name: 'net.ipv4.t…

MySQL-FEDERATED引擎 跨主机联表查询-（2）实践
type: item
date: 2020-04-16T19:36:00+08:00
description: MySQL FEDERATED 跨实例联表查询实践环境说明：一台 CentOS 7.7.1908 服务器 IP: 192.168.110.233；使用docker ，模拟 本地、远程两个不同的 MySQL 服务器。问题：不同实例间如何实现联表查询？1、启动Mysql# 第一台 MySQL （作为本地MySQL服务器） $ docker run -itd \ --name test-mysql01 \ --restart=always \ -v /data/mysql01:/var/lib/mysql \ -v /etc/localtime:/etc/localtime \ -e MYSQL_ROOT_PASSWORD=123456 \ -e MYSQL_DATABASE=test_db \ -e MYSQL_USER=test \ -e MYSQL_PASSWORD=123456 \ -p 3316:3306 \ -d mysql:5.7 # 第二台 MySQL （作为远程MySQL服务器） $ docker run -itd \ --name test-mysql02 \ --restart=always \ -v /data/mysql02:/var/lib/mysql \ -v /etc/localtime:/etc/localtime \ -e MYSQL_ROOT_PASSWORD=123456 \ -e MYSQL_DATABASE=test_db \ -e MYSQL_USER=test \ -e MYSQL_PASSWORD=123456 \ -p 3326:3306 \ -d mysql:5.7确保MySQL 顺利启动[root@jiangjw-test ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cadc379c04a3 mysql:5.7 "docker-entrypoint.s…" 8 seconds ago Up 7 seconds 33060/tcp, 0.0.0.0:3326->3306/tcp test-mysql02 91b91237da2e mysql:5.7 "docker-entrypoint.s…" 53 seconds ago Up 52 seconds 33060/tcp, 0.0.0.0:3316->3306/tcp test-mysql012、远程服务器（test-mysql02）建表，插入数据[root@jiangjw-test ~]# docker exec -it cad bash root@cadc379c04a3:/# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3 Server version: 5.7.29 MySQL Community Server (GPL) Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | test_db | +--------------------+ 5 rows in set (0.00 sec) mysql> use test_db; Database changed mysql> CREATE TABLE test_table ( -> id INT(20) NOT NULL AUTO_INCREMENT, -> name VARCHAR(32) NOT NULL DEFAULT '', -> other INT(20) NOT NULL…