CentOS + Docker + iptables（DOCKER-USER）生产环境防火墙完整方案
CentOS Docker 使用 DOCKER-USER + iptables 限制容器端口访问方案1. 适用场景本文适用于以下场景：CentOS 服务器使用 Docker 部署服务Docker 容器通过 -p 或 ports 暴露端口需要限制容器暴露端口只允许指定服务器或指定 IP 访问不希望关闭 Docker 自动管理 iptables不希望影响 Docker NAT、端口映射、容器访问公网需要服务器重启后规则仍然生效推荐方案：iptables-services + Docker 默认 iptables 管理 + DOCKER-USER 链做访问控制2. 为什么使用 DOCKER-USERDocker 启动容器并映射端口时，会自动写入 iptables 规则，例如：nat 表filter 表DOCKER 链DOCKER-USER 链其中：DOCKER 链：Docker 自动维护，不建议手工修改 DOCKER-USER 链：Docker 预留给用户自定义访问控制Docker 的转发流量大致会经过：FORWARD ↓ DOCKER-USER ↓ DOCKER ↓ 容器因此，在 DOCKER-USER 链中写规则，可以在 Docker 自动放行容器端口之前，先做访问控制。3. 重要原则3.1 不建议关闭 Docker iptables不要在 /etc/docker/daemon.json 中配置：{ "iptables": false }否则可能导致：Docker NAT 失效容器端口映射异常容器访问外网异常Docker bridge 网络异常Docker Compose 网络异常推荐保持 Docker 默认配置：{ "iptables": true }或者不配置该项。3.2 不要直接修改 DOCKER 链不建议直接修改：DOCKER因为该链由 Docker 自动维护，Docker 重启、容器启动或停止时都可能改写。应使用：DOCKER-USER3.3 不要只写 INPUT 链Docker 暴露端口的访问流量很多情况下走的是：FORWARD而不是：INPUT所以只写 INPUT 链经常无法限制 Docker 映射端口。3.4 DOCKER-USER 不只影响入站DOCKER-USER 作用于 FORWARD 链，因此它不仅影响外部访问容器，也会影响容器访问公网。容器访问公网路径大致是：容器 ↓ docker0 / br-xxxx ↓ FORWARD ↓ DOCKER-USER ↓ 外网所以规则里必须先放行 Docker bridge 的出站流量，否则会出现：宿主机可以访问公网 IP 和端口 但容器里面访问不通4. 推荐启动顺序推荐顺序：1. 启动 iptables 服务 2. 启动 Docker 服务 3. Docker 自动创建 DOCKER / DOCKER-USER 链 4. 写入 DOCKER-USER 访问控制规则 5. 保存 iptables 规则原因：DOCKER-USER 链通常由 Docker 创建 Docker 未启动前，该链可能不存在5. 安装和初始化5.1 安装 iptables-servicesCentOS 7：yum install -y iptables-servicesCentOS 8：dnf install -y iptables-services5.2 停止 firewalld如果决定使用 iptables-services 管理规则，建议关闭 firewalld：systemctl stop firewalld systemctl disable firewalld查看状态：systemctl status firewalld5.3 启动 iptablessystemctl enable iptables systemctl start iptables查看状态：systemctl status iptables5.4 启动 Dockersystemctl enable docker systemctl start docker查看状态：systemctl status docker5.5 确认 DOCKER-USER 存在iptables -L -n应能看到类似内容：Chain DOCKER Chain DOCKER-USER也可以单独查看：iptables -L DOCKER-USER -n --line-numbers6. 示例需求假设服务器上有 Docker 容器暴露端口：8080要求：允许 1.1.1.1 访问 8080 允许 2.2.2.2 访问 8080 拒绝其他 IP 访问 8080 允许容器正常访问公网7. 推荐规则模板7.1 清理旧的 DOCKER-USER 自定义规则如果之前 DOCKER-USER 规则较乱，可以先清空该链：iptables -F DOCKER-USER如果提示链不存在，说明 Docker 可能未启动，先执行：systemctl restart docker然后再次检查：iptables -L DOCKER-USER -n --line-numbers7.2 添加规则：放行容器出站默认 Docker bridge：iptables -I DOCKER-USER 1 -i docker0 -j ACCEPTDocker Compose 或自定义 bridge 网络通常是 br-xxxx，建议放行：iptables -I DOCKER-USER 2 -i br+ -j ACCEPT这两条用于保证容器可以正常访问外网，例如：DNSHTTP / HTTPSyum / aptcurlAPI 调用连接外部数据库或服务7.3 添加规则：放行已建立连接iptables -I DOCKER-USER 3 \ -m state --state RELATED,ESTABLISHED \ -j ACCEPT作用：允许已经建立的连接继续返回数据7.4 添加规则：放行白名单 IP允许 1.1.1.1 访问容器暴露的 8080：iptables -I DOCKER-USER 4 \ -s 1.1.1.1 \ -p tcp --dport 8080 \ -j ACCEPT允许 2.2.2.2 访问容器暴露的 8080：iptables -I DOCKER-USER 5 \ -s 2.2.2.2 \ -p tcp --dport 8080 \ -j ACCEPT7.5 添加规则：拒绝其他 IPiptables -A DOCKER-USER \ -p tcp --dport 8080 \ -j DROP7.6 添加默认 RETURN建议在链末尾保留：iptables -A DOCKER-USER -j RETURN说明：未被本链拦截的流量返回给 Docker 后续链处理如果 DOCKER-USER 链中原本已有 RETURN，无需重复添加。8. 完整命令示例以下命令可按需修改白名单 IP 和端口后使用。# 清空 DOCKER-USER 旧规则 iptables -F DOCKER-USER # 放行 Docker 默认 bridge 出站 iptables -A DOCKER-USER -i docker0 -j ACCEPT # 放行 Docker Compose / 自定义 bridge 出站 iptables -A DOCKER-USER -i br+ -j ACCEPT # 放行已建立连接 iptables -A DOCKER-USER \ -m state --state RELATED,ESTABLISHED \ -j ACCEPT # 放行白名单 IP 访问 8080 iptables -A DOCKER-USER \ -s 1.1.1.1 \ -p tcp --dport 8080 \ -j ACCEPT iptables -A DOCKER-USER \ -s 2.2.2.2 \ -p tcp --dport 8080 \ -j ACCEPT # 拒绝其他 IP 访问 8080 iptables -A DOCKER-USER \ -p tcp --dport 8080 \ -j DROP # 其他流量交还 Docker 后续规则处理 iptables -A DOCKER-USER -j RETURN9. 多端口示例如果要限制多个端口，例如：80,443,8080只允许 1.1.1.1 和 2.2.2.2 访问：iptables -F DOCKER-USER iptables -A DOCKER-USER -i docker0 -j ACCEPT iptables -A DOCKER-USER -i br+ -j ACCEPT iptables -A DOCKER-USER \ -m state --state RELATED,ESTABLISHED \ -j ACCEPT iptables -A DOCKER-USER \ -s 1.1.1.1 \ -p tcp \ -m multiport --dports 80,443,8080 \ -j ACCEPT iptables -A DOCKER-USER \ -s 2.2.2.2 \ -p tcp \ -m multiport --dports 80,443,8080 \ -j ACCEPT iptables -A DOCKER-USER \ -p tcp \ -m multiport --dports 80,443,8080 \ -j DROP iptables -A DOCKER-USER -j RETURN10. 查看规则查看规则顺序：iptables -L DOCKER-USER -n --line-numbers查看规则命中计数：iptables -L DOCKER-USER -n -v --line-numbers预期类似：Chain DOCKER-USER num target prot opt source destination 1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 in docker0 2 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 in br+ 3 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 4 ACCEPT tcp -- 1.1.1.1 0.0.0.0/0 tcp dpt:8080 5 ACCEPT tcp -- 2.2.2.2 0.0.0.0/0 tcp dpt:8080 6 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 7 RETURN all -- 0.0.0.0/0 0.0.0.0/011. 保存规则规则验证无误后保存。CentOS 7 常用：service iptables save或者：iptables-save > /etc/sysconfig/iptables确认文件已写入：cat /etc/sysconfig/iptables12. 服务器重启后是否失效只要已经保存到：/etc/sysconfig/iptables并且启用了 iptables 服务：systemctl enable iptables服务器重启后会自动加载规则。推荐重启后的顺序：1. iptables 服务启动 2. 加载 /etc/sysconfig/iptables 3. Docker 服务启动 4. Docker 自动恢复 DOCKER / DOCKER-USER 相关链 5. DOCKER-USER 中的白名单规则继续生效如果重启后发现 DOCKER-USER 链异常，可执行：systemctl restart docker然后检查：iptables -L DOCKER-USER -n --line-numbers13. 清理混乱规则并重新配置13.1 最安全方式：只清理 DOCKER-USER推荐：iptables -F DOCKER-USER然后重新按本文第 8 节添加规则。这种方式不会影响 Docker 自动生成的 NAT 规则。13.2 清理 filter 表如果整个 filter 表规则混乱，可以先备份：iptables-save > /root/iptables-backup-$(date +%F-%H%M%S).rules再清理 filter 表：iptables -F iptables -X然后重启 Docker：systemctl restart docker再重新写入 DOCKER-USER 规则。13.3 不建议清理 nat 表不要轻易执行：iptables -t nat -F iptables -t nat -X因为 Docker 的端口映射和 MASQUERADE 依赖 nat 表。如果已经误清理 nat 表，需要重启 Docker：systemctl restart docker必要时重启相关容器。14. 验证方法14.1 验证白名单访问在白名单服务器执行：curl http://服务器IP:8080应可以访问。14.2 验证非白名单访问在非白名单服务器执行：curl http://服务器IP:8080应无法访问或连接超时。14.3 验证容器访问公网进入容器：docker exec -it 容器名 sh测试 IP 连通性：ping 8.8.8.8测试域名解析和 HTTPS：curl https://www.baidu.com如果宿主机能访问外网，但容器不能访问，优先检查：iptables -L DOCKER-USER -n -v --line-numbers确认是否有以下放行规则：-i docker0 -j ACCEPT -i br+ -j ACCEPT15. 常见问题15.1 宿主机能访问公网，容器不能访问公网原因通常是 DOCKER-USER 拦截了容器出站流量。解决：iptables -I DOCKER-USER 1 -i docker0 -j ACCEPT iptables -I DOCKER-USER 2 -i br+ -j ACCEPT15.2 白名单规则不生效检查规则顺序：iptables -L DOCKER-USER -n --line-numbers白名单 ACCEPT 必须在 DROP 前面。15.3 Docker 端口映射失效检查 nat 表：iptables -t nat -L -n检查 Docker 服务：systemctl status docker如有必要：systemctl restart docker15.4 Docker Compose 网络访问异常Docker Compose 通常使用 br-xxxx 网桥。确认是否有：iptables -A DOCKER-USER -i br+ -j ACCEPT15.5 不知道容器使用哪个网桥查看网络接口：ip addr常见接口：docker0 br-xxxxxxxxxxxx也可以查看 Docker 网络：docker network ls docker network inspect 网络名16. 生产环境建议16.1 最推荐Docker 保持默认 iptables 管理 iptables-services 做持久化 DOCKER-USER 做访问控制 不要手工修改 DOCKER 链 不要关闭 Docker iptables16.2 更安全的架构如果条件允许，不建议容器端口直接暴露公网。更推荐：公网 ↓ Nginx / HAProxy / SLB ↓ Docker 内网服务Docker 端口可以只监听本地：docker run -p 127.0.0.1:8080:80 nginx然后由 Nginx 做：IP 白名单TLS访问日志限流WAF17. 最终可执行模板请将以下内容中的 IP 和端口替换为实际值。# 1. 确认 Docker 已启动 systemctl start docker # 2. 清空 DOCKER-USER 旧规则 iptables -F DOCKER-USER # 3. 放行容器出站 iptables -A DOCKER-USER -i docker0 -j ACCEPT iptables -A DOCKER-USER -i br+ -j ACCEPT # 4. 放行已建立连接 iptables -A DOCKER-USER \ -m state --state RELATED,ESTABLISHED \ -j ACCEPT # 5. 放行白名单访问容器端口 iptables -A DOCKER-USER \ -s 1.1.1.1 \ -p tcp --dport 8080 \ -j ACCEPT iptables -A DOCKER-USER \ -s 2.2.2.2 \ -p tcp --dport 8080 \ -j ACCEPT # 6. 拒绝其他来源访问该端口 iptables -A DOCKER-USER \ -p tcp --dport 8080 \ -j DROP # 7. 其他流量交还 Docker 处理 iptables -A DOCKER-USER -j RETURN # 8. 查看规则 iptables -L DOCKER-USER -n -v --line-numbers # 9. 保存规则 service iptables save18. 回滚方法如果规则配置后出现异常，可恢复备份：iptables-restore < /root/iptables-backup.rules或者临时清空 DOCKER-USER：iptables -F DOCKER-USER iptables -A DOCKER-USER -j RETURN然后重启 Docker：systemctl restart docker19. 注意事项修改前一定要备份 iptables。远程操作时，谨慎执行 DROP 规则，避免误封 SSH。Docker 主机不要随意清理 nat 表。Kubernetes 节点不要直接套用本文清理规则，kube-proxy、Calico、Flannel 等也依赖 iptables。白名单规则必须写在 DROP 规则前面。容器出站放行规则必须写在 DROP 规则前面。
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 x509 -in cert.pem -noout -text打印证书有效期openssl x509 -in server.crt -noout -datesSHELL命令查看证书是否过期openssl s_client -servername 目标域名 -connect 目标域名:443
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备份脚本，有时候一个数据库下面有多个数据库，就写了一个简单的逻辑备份排除系统默认库的全量数据库的脚本。#!/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脚本多进行进行，这里记录一下，后续有其他需要进行多线程的也可以随便改一下就可以用。#!/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-move/logs/error.log && rm -f database3.sql
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两个文档都匹配，但是第一个文档比第二个匹配程度更高。如果没有别的条件，限制这两个包含关键字的文档都将返回。再来一个示例，比如通过博客标签来搜索博客文章。那么倒排索引就是这样的一个结构：博客文章（原始数据）索引列表（倒排索引）博客文章ID标签标签博客文章ID1pythonpython1，2，32pythonlinux3，43linux,python 4linux 如果要搜索含有 python 标签的文章，那相对于查找所有原始数据而言，查找倒排索引后的数据将会快的多。只需要查看标签这一栏，然后获取相关的文章ID即可。完全过滤掉无关的所有数据，提高效率！elasticsearch 的索引和 Lucene 的索引对比：在 elasticsearch中，索引（数据库）这个词被频繁使用，这就是术语的使用。在 elasticsearch 中，索引被分为多个分片，每份分片是一个 Lucene的索引。==所以一个elasticsearch索引是由多个Lucene索引组成的。==如无特指，说起索引都是指 elasticsearch 的索引。IK 分词器插件什么是IK分词器分词：即把一段中文或者别的划分成功一个个的关键字，我们在搜索时会把自己的信息进行分词，会把数据库中或者索引库中的数据进行分词，然后进行一个匹配操作，默认的中文分词是将每个字看成一个词，比如“我是沙比”会被分为“我”，“是”，“沙”，“比”，这显然是不符合要求的，所以我们需要安装中文分词器ik来解决这个问题。如果要使用中文，简易使用ik分词器！IK 提供了两个分词算法：==ik_smart 和 ik_max_word==，其中 ik_smart 为最少切分，ik_max_word 为最细粒度划分！安装1、https://github.com/medcl/elasticsearch-analysis-ik2、下载完毕之后，放入到我们的elasticsearch 插件即可！3、重启观察ES，可以看到ik分词器被加载了！4、elasticsearch-plugin 可以通过这个命令来查看加载进来的插件GET _analyze { "analyzer": "ik_smart", "text": "中国共产党" } 输出： 中国共产党 # 使用 ik_smart 最小切分未做任何切分GET _analyze { "analyzer": "ik_max_word", "text": "中国共产党" } 输出： 中国，国共，共产党 # 使用 ik_max_word 对该段文字做了一定切分ik 分词器增加自己的配置！GET _analyze { "analyzer": "ik_smart", "text": "超级喜欢沙比" } 输出： 超级，喜欢，沙，比 # 由于 沙比 ，不认为这是个连贯的词语，所以单独列出来了对于通常不是词语的处理，IK分词器会将分为每一个字为一个词语；所以，这种我们自己需要的词语，需要自己加到我们的分词器的字典中！plugins/ik/config # es 插件目录下，创建自己需要的词语命名 {name.}dic，写入内容 沙比 plugins/ik/config/IKAnalyzer.cfg.xml # 修改该配置文件,插入 {name}.dic <properties> <comment>IK Analyzer 扩展配置</comment> <entry key="ext_dict">{name}.dic</entry> <entry key="ext_stopwords"></entry> <!--<entry key="remote_ext_dict">words_location</entry>--> <!--<entry key="remote_ext_stopwords">words_location</entry>--> </properties>重启 es！即可GET _analyze { "analyzer": "ik_smart", "text": "超级喜欢沙比" } 输出： 超级，喜欢，沙比 # 因为加入了，自定义的词库，所以把沙比认为是一个词语切分了Rest 风格说明methodurl地址描述PUTlocalhost:9200/索引名称/类型名称/文档id创建文档（指定文档id）POSTlocalhost:9200/索引名称/类型名称创建文档（随机文档id）POSTlocalhost:9200/索引名称/类型名称/文档id/_update修改文档DELETElocalhost:9200/索引名称/类型名称/文档id删除文档GETlocalhost:9200/索引名称/类型名称/文档id查询文档通过文档idPOSTlocalhost:9200/索引名称/类型名称/_search查询所有数据关于索引的基本操作 1、创建一个索引；PUT /索引名/~类型名~/文档id {请求体}PUT /test1/type1/1 { "name": "沙比", "age": 3 }完成了自动增加了索引！数据也成功的添加了，我们未定义 mapping 但是也能插入！但是建议是提前设置好索引的mapping 字段类型，因为自动识别可能不一定正确。字符串类型text 、 keyword数值类型long, integer, short, byte, double, ﬂoat, half_ﬂoat, scaled_ﬂoat日期类型datete 布尔值类型boolean等等......2、指定字段的类型；PUT /test2 { "mappings": { "properties": { "name": { "type": "text" }, "age": { "type": "long" }, "birthday": { "type": "date" } } } } # 创建 mapping 规则获取这个规则！可以直接 getGET /test23、查看默认的信息PUT /test3/_doc/1 { "name": "买买买"， "age": 13, "birth": "1997-01-05" } # 先不指定字段，随便写点东西进一个新的 索引GET test3 # 查看es 匹配的字段类型扩展：通过命令 elasticsearch 索引情况！ 通过 get _cat/ 可以获取 es的当前很多信息！4、修改文档PUT 请求做的是覆盖操作，如果只需要修改已添加信息的文档的某个字段信息，可以通过 POST 请求即可POST /test3/_doc/1/_update { "doc": { "name": "法外狂徒张三" } }但不管是 PUT 还是 POST 只要对该文档做了修改，该文档中的 version 字段都会 +1 ，版本加一。关于文档的基本操作（重点） 基本操作1、添加数据；PUT /jw/_doc/1 { "name": "没法开", "age": 25, "desc": "一顿操作猛如虎，一看工资2500", "tags": ["死宅","直男"] }2、获取数据 GET；PUT /jw/_doc/1 { "name": "风景照", "age": 10, "desc": "diwjad 你好吗", "tags": ["靓女","唱歌","跳舞"] }GET /jw/_doc/13、更新数据 PUT（覆盖更新）；PUT /jw/_doc/1 { "name": "米线", "age": 8, "desc": "How are you", "tags": [唱歌","跳舞"] } version 版本将出现变动，具体为改动的次数 result 状态为 updated4、POST _update，推荐使用这种更新方式（POST 指定更新指定的字段）；POST /jw/_doc/1 { "doc": { "tags": ["唱","跳","rap"] } }搜索！简单的搜索！GET /jw/_doc/1简单的条件查询，可以根据默认的映射规则，产生基本的查询！GET /jw/_doc/_search?q=name:米线复杂操作搜索 select（排序，分页，高亮，模糊查询，精准查询！）GET /jw/_doc/_search { "query": { "match": { "name": "米线" } } }对普通搜索，转换为curl操作curl 'http://127.0.0.1:9200/jw/_search?pretty' -H 'Content-Type: application/json' -d '{ "query": { "match": { "name": "米线" } } }'搜索结果只要特定的几个字段，对结果过滤GET /jw/_doc/_search { "query": { "match": { "name": "米线" } }, "_source": ["name","desc"] }排序！GET /jw/_doc/_search { "query": { "match": { "name": "米线" } }, "sort": [ { "age": { # 指定哪个字段进行排序，正序还是倒序 asc,desc "order": "asc" } } ] }分页查询！{ "query": { "match": { "name": "米线" } }, "sort": [ { "age": { # 指定哪个字段进行排序，正序还是倒序 asc,desc "order": "asc" } } ]， "from": 0, # 从第几个数据开始 "size": 10 # 返回多少条数据（单页的数据条数） }数据下标还是从0开始，跟学的所有数据结构是一样的！/search/{current}/{pagesize}布尔值查询must（and），所有的条件都要符合 where id=1 and name=xxxGET /jw/_doc/_search { "query": { "bool": { "must": [ { "match": { "name": "米线" } }, { "match": { "age": 8 } } ] } } }should（or），所有的条件都要符合 where id = 1 or name = xxxGET /jw/_doc/_search { "query": { "bool": { "should": [ { "match": { "name": "米线" } }, { "match": { "age": 8 } } ] } } }must_not （not）GET /jw/_doc/_search { "query": { "bool": { "must_not": [ { "match": { "age": 3 } } ] } } } # 查询年龄不是3岁的过滤器 filterGET /jw/_doc/_search { "query": { "bool": { "must": [ { "match": { "name": "米线" } } ], "filter": { "range": { "age": { "lt": 10 } } } } } } # 对取出的数据在进行过滤gt 大于gte 大于等于lt 小于lte 小于等于GET /jw/_doc/_search { "query": { "bool": { "must": [ { "match": { "name": "米线" } } ], "filter": { "range": { "age": { "lt": 10, "gte" 1 } } } } } }匹配多个条件GET /jw/_doc/_search { "query": { "match": { "tags": "男 技术" } } } # 多个条件使用空格隔开，只要满足其中一个结果就可以被查出 # 这个时候可以通过分值进行基本的判断。精确查询term 查询是直接通过倒排索引指定的词条进程精确查找！关于分词：term，直接查询精确的 keywordmatch，会使用分词器解析！（先分析文档，然后再通过分析的文档进行查询！）两个类型 text keywordkeyword 类型不会被分词器解析！ 会始终认为是一个整体，必须eq相等才能搜索多个值匹配精确查询GET /jw/_search { "query": { "bool": { "should": [ { "term": { "t1": "22" } }, { "term": { "t1": "33" } } ] } } }高亮查询GET /textdb/_search { "query": { "match": { "name": "沙比" } }, "highlight": { "fields": { "name": {} } } }自定义搜索高亮条件GET /textdb/_search { "query": { "match": { "name": "沙比" } }, "highlight": { "pre_tags": " ", "post_tags": " ", "fields": { "name": {} } } }这些其实 MySQL 也都可以做，只是 MySQL 效率较低！查看一个索引下的所有数据GET /{indexname}/_search?pretty 由于查询每页面的数量限制，所以一般需结合 from,sizeGET /{indexname}/_search?pretty { "from": 0, "size": 100 }或者 match_allGET /unionist_cloud_setting_city_area_gets/_search { "query": { "match_all": {} }, "from": 0, "size": 100 }es 获取某个索引下的数据量GET _cat/indices/MembershipCloudAnalysisMember?v # 获取单个索引数据量GET _cat/indices?v # 获取所有索引数据量kibana 汉化kibana.yml 配置文件插入一行i18n.locale: "zh-CN"ElasticSearch添加密码#Elasticsearch docker run -itd \ --restart=always \ --name elasticsearch \ -e discovery.type=single-node \ -e xpack.security.enabled=true \ -e ELASTIC_PASSWORD="password" \ -p 9200:9200 \ -p 9300:9300 \ -v /data/db_server/elasticsearch/data:/usr/share/elasticsearch/data \ elasticsearch:latest # 非容器模式启动修改，elasticsearch.yml设置 xpack.security.enabled: true # 启动服务后，执行设置密码命令 # 自动设置密码 ./bin/elasticsearch-setup-passwords auto # 手动设置密码 ./bin/elasticsearch-setup-passwords interactive
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 # daemonset $ kubectl get daemonset {daemonsetname} -n {namespaces} -o yaml4、进入容器抓包关键思路；kubectl describe pod 拿到容器 id；通过 docker 或 crictl 查看容器 id 对应的进程号；使用 nsenter –n --target 进入容器进程的 netns；使用宿主机上的抓包工具进行抓包 (tcpdump)# 拿到 pod 中 nginx 的容器 id $ kubectl describe pod tcpbench-6484d4b457-847gl | grep -A10 "^Containers:" | grep -Eo 'docker://.*$' | head -n 1 | sed 's/docker:\/\/\(.*\)$/\1/' 49b4135534dae77ce5151c6c7db4d528f05b69b0c6f8b9dd037ec4e7043c113e # 通过容器 id 拿到 nginx 进程 pid $ docker inspect -f {{.State.Pid}} 49b4135534dae77ce5151c6c7db4d528f05b69b0c6f8b9dd037ec4e7043c113e 3985 # 进入 nginx 进程所在的 netns $ nsenter -n -t 3985 $ tcpdump -i eth0 -nnnn -ttt port 245685、Pod 无法调度；可能原因：节点资源不够；不满足 nodeSelector 与 affinity；Node 存在 Pod 没有容忍的污点；有状态应用漂移找不到符合条件的同可用区节点；scheduler 不正常 (概率低)6、Pod无法启动；可能原因：拉取镜像失败；镜像仓库认证失败 (imagePullSecret 没有配置或配置有误)；镜像仓库协议与证书问题 (http 没有加 insecure-registry / https 自签没有加 CA) ；镜像不存在 (镜像名配置有误)；镜像文件损坏；镜像拉取超时 (镜像太大或在国内拉不到国外镜像)；程序启动配置不当导致进程主动退出 ；limit 没配置单位，内存默认用 byte，导致一直被 kill ；依赖的 ConfigMap、Secret 或者 PVC 等不存在；被管理员配置的 LimitRange, PodSecurityPolicy 限制住了；挂载 Volume 失败；命中 bug #68211；Pod 漂移没有正常解挂之前的磁盘；磁盘爆满；节点内存碎片化 ；controller-manager 异常；CNI 网络错误；程序启动慢被存活检查 kill；7、Pod 发生 Crash；可能原因：cgroup OOM / 系统 OOM ；DNS 故障导致解析失败，业务进程报错退出；高负载导致网络不通，业务进程报错退出；存活检查探测失败，容器被 kill；业务本身 bug；容器进程被木马进程杀死8、Pod 无法删除；可能原因：磁盘爆满；存在 Finalizers；资源被其它进程占用；存在 “i” 文件属性；运行时 bug；9、Namespace 无法删除；可能原因：Namespace 上存在 Finalizers (比如安装过 KubeSphere 或 Rancher，卸载后没清理 Finalizers)；注册过的 apiservice 状态异常 (比如安装过 prometheus-adapter，卸载后没清理 apiservice)10、Service/Ingress 创建失败；可能原因：LB 数量达到最大配额；复用已有 LB 但上面存在规则或正在被其它 Service/Ingress 使用 ；复用已有 LB 但 ID 写错；Secret 不正确；LB 内部错误 (概率低)；11、网络不通；可能原因：节点安全组没有放通容器网段、节点网段或者 NodePort 端口区间 (30000-32768)；外部服务防火墙没有放开容器网段 (如 CDB、自建 DNS)；DNS 异常；高负载；进程没有监听端口；12、节点状态异常；可能原因：节点高负载；磁盘故障；踩内核 bug；运行时 hang 住或挂掉；系统 OOM ；节点 DNS 被修改；案例: Pod 访问另一个集群的 apiserver 有延时真相大白:kubectl 没有使用底层库查询域名，在 alpine 下底层库是 musl libc，但 kubectl 用的却是类似 glibc 的查询逻辑；由于 alpine 没有 /etc/nsswitch.conf 文件，所以 kubectl 先查 dns，再查 hosts；虽然 dns 解析没有用，但始终会先尝试解析 dns，当踩上内核 conntrack bug 时导致部分请求丢包，等待 5s 超时后 kubectl 继续重 试，造成 5s 延时现象；解决方案:换基础镜像，不用 alpine；挂载 nsswitch.conf 文件 (hostPath或configMap)；
由于工作中，经常会出现测试在群里发一个报错的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 self.server in svc_name: # print(s) svc_type = s.spec.type svc_ip = s.spec.cluster_ip svc_port = s.spec.ports[0].port svc_message = "http://x.x.x.x:8080" + s.metadata.self_link if self.namespace == 'default': svc_bundle = {'svc_name': svc_name, 'svc_type': svc_type, 'svc_ip': svc_ip, 'svc_port': svc_port, 'socket_status': "阿里云暂不支持检测", 'svc_message': "暂不支持阿里云"} else: svc_bundle = {'svc_name': svc_name, 'svc_type': svc_type, 'svc_ip': svc_ip, 'svc_port': svc_port, 'socket_status': self.telnet(svc_ip, svc_port), 'svc_message': svc_message} yield svc_bundle except ApiException as e: traceback.print_exc(e) def readSvc(self): try: api_instance = self.initSetting().CoreV1Api() api_response = api_instance.read_namespaced_service(name=self.server, namepace=self.namespace) svc_list = api_response.items # for s in svc_list: # # svc name svc_name = svc_list.metadata.name if self.server in svc_name: # print(s) svc_type = svc_list.spec.type svc_ip = svc_list.spec.cluster_ip svc_port = svc_list.spec.ports[0].port svc_message = "http://x.x.x.x:8080" + s.metadata.self_link if self.namespace == 'default': svc_bundle = {'svc_name': svc_name, 'svc_type': svc_type, 'svc_ip': svc_ip, 'svc_port': svc_port, 'socket_status': "阿里云暂不支持检测", 'svc_message': "暂不支持阿里云"} else: svc_bundle = {'svc_name': svc_name, 'svc_type': svc_type, 'svc_ip': svc_ip, 'svc_port': svc_port, 'socket_status': self.telnet(svc_ip, svc_port), 'svc_message': svc_message} yield svc_bundle except ApiException as e: traceback.print_exc(e) def filterPod(self): try: api_instance = self.initSetting().CoreV1Api() api_response = api_instance.list_namespaced_pod(self.namespace) pod_list = api_response.items for p in pod_list: # pod名字 pod_name = p.metadata.name # print(self.server) if self.server in pod_name or self.server[:-6] in pod_name: # print(i) # 镜像版本 image_version = p.status.container_statuses[0].image.split(':')[1] # pod状态 pod_status = p.status.phase pod_time = p.status.start_time + datetime.timedelta(hours=8) # pod所在宿主机 pod_host = p.status.host_ip pod_ip = p.status.pod_ip log_url = "http://x.x.x.x:8080/api/v1/namespaces/{}/pods/{}/log".format(self.namespace, pod_name) if self.namespace == 'default': pod_bundle = {'pod_name': pod_name, 'status': pod_status, 'pod_ip': pod_ip, 'version': image_version, 'starttime': str(pod_time).split('+')[0], 'host': pod_host, 'host_status': "阿里云暂不支持检测", "log_url": "暂不支持阿里云"} else: pod_bundle = {'pod_name': pod_name, 'status': pod_status, 'pod_ip': pod_ip, 'version': image_version, 'starttime': str(pod_time).split('+')[0], 'host': pod_host, 'host_status': self.hostStatus(pod_host), "log_url": log_url} yield pod_bundle except ApiException as e: traceback.print_exc(e) def filterIng(self): try: api_instance = self.initSetting().ExtensionsV1beta1Api() api_response = api_instance.list_namespaced_ingress(namespace=self.namespace) ing_list = api_response.items for i in ing_list: # pod名字 ing_http = i.spec.rules[0].http.paths for s in ing_http: ing_svc = s.backend.service_name # print(self.server) if self.server in ing_svc: ing_name = i.metadata.name ing_domain = i.spec.rules[0].host ing_port = s.backend.service_port ing_path = "http://" + ing_domain + s.path ing_msg = "http://192.168.1.100:8080" + i.metadata.self_link if self.namespace == 'default': ing_msg = "暂不支持阿里云" else: ing_msg = "http://192.168.1.100:8080" + i.metadata.self_link ing_bundle = {"ing_name": ing_name, "ing_domain": ing_domain, "ing_svc": ing_svc, "ing_port": ing_port, "ing_path": ing_path, "ing_msg": ing_msg} yield ing_bundle except ApiException as e: traceback.print_exc(e) def filterPath(self, ingress_name, ingress_path): try: api_instance = self.initSetting().ExtensionsV1beta1Api() api_response = api_instance.read_namespaced_ingress(name=ingress_name, namespace=self.namespace) ing_http = api_response.spec.rules[0].http.paths for s in ing_http: ing_path = s.path if ingress_path == ing_path: ing_svc = s.backend.service_name ing_name = api_response.metadata.name ing_domain = api_response.spec.rules[0].host ing_port = s.backend.service_port ing_path = "http://" + ing_domain + s.path ing_msg = "http://x.x.x.x:8080" + api_response.metadata.self_link ing_bundle = {"ing_name": ing_name, "ing_domain": ing_domain, "ing_svc": ing_svc, "ing_port": ing_port, "ing_path": ing_path, "ing_msg": ing_msg} self.server = ing_svc yield ing_bundle except ApiException as e: traceback.print_exc(e) class ToolsBundle(): def __init__(self, serverName='', urlPath='', envName='sit'): if envName == 'release': self.apiserver = 'https://x.x.x.x:6443' self.Token = 'token,自行通过命令行拿' self.envName = 'default' else: self.Token = 'token,自行通过命令行拿' self.apiserver = 'https://x.x.x.x:6443' self.envName = envName self.serverName = serverName self.urlPath = urlPath self.tools = FilterBundle(token=self.Token, apiserver=self.apiserver, server=self.serverName, namespace=self.envName) def pod(self): pod_bundle = self.tools.filterPod() return pod_bundle def svc(self): svc_bundle = self.tools.filterSvc() return svc_bundle def ing(self): ing_bundle = self.tools.filterIng() return ing_bundle def ingpath(self): dev_ingdata = {"域名":"ingressname",自行通过命令行用awk快速生成一下} sit_ingdata = {"域名":"ingressname",自行通过命令行用awk快速生成一下} release_ingdata = {"域名":"ingressname",自行通过命令行用awk快速生成一下} if self.envName == 'sit': ing_name = sit_ingdata.get(self.urlPath.split('/')[2]) ing_path = "/" + self.urlPath.split('/')[3] elif self.envName == 'default': ing_name = release_ingdata.get(self.urlPath.split('/')[2]) ing_path = "/" + self.urlPath.split('/')[3] else: ing_name = dev_ingdata.get(self.urlPath.split('/')[2]) ing_path = "/" + self.urlPath.split('/')[3] ingpath_bundle = self.tools.filterPath(ingress_name=ing_name, ingress_path=ing_path) return ingpath_bundle def read(self): return self.tools.readSvc()views.pyfrom django.http import HttpResponse, JsonResponse from django.shortcuts import render import logman.kubetools as tools def index(request): if request.GET.get('svc') and request.GET.get('env'): keyword = request.GET['svc'] if '_' in keyword: keyword = keyword.replace('_', '-') env = request.GET['env'] all = tools.ToolsBundle(serverName=keyword, envName=env) svc_data = all.svc() pod_data = all.pod() ing_data = all.ing() return render(request, "svc.html", {"PodBundle": pod_data, "SvcBundle": svc_data, "IngBundle": ing_data}) elif request.GET.get('urlpath') and request.GET.get('env'): urlpath = request.GET['urlpath'] env = request.GET['env'] all = tools.ToolsBundle(urlPath=urlpath, envName=env) dataing = all.ingpath() ing_data = dataing for s in ing_data: server = s.get('ing_svc') all2 = tools.ToolsBundle(serverName=server, envName=env) svc_data = all2.svc() pod_data = all2.pod() ing_data = all2.ing() return render(request, "svc.html", {"PodBundle": pod_data, "SvcBundle": svc_data, "IngBundle": ing_data}) else: return render(request, 'svc.html') 前端页面 svc.html： Welcome 服务可用性查询 服务名或URL 条件二选一！ 示例：stocking-cloud-grpc-stuff-return-server 或 http://order.chtwebapi.lingcb.net/retail_order/ 服务： URL： 环境： sit dev release Service SvcName SvcType SvcIP SvcPort Socket_Status message {% for line in SvcBundle %} {{ line.svc_name }} {{ line.svc_type }} {{ line.svc_ip }} {{ line.svc_port }} {{ line.socket_status }} {{ line.svc_message }} {% endfor %} Pod PodName PodStatus PodIP PodVersion 启动时间 Host HostStatus LogURL {% for line in PodBundle %} {{ line.pod_name }} {{ line.status }} {{ line.pod_ip }} {{ line.version }} {{ line.starttime }} {{ line.host }} {{ line.host_status }} {{ line.log_url }} {% endfor %} Ingress IngName IngDomain IngSvc IngPort IngPath IngMsg {% for line in IngBundle %} {{ line.ing_name }} {{ line.ing_domain }} {{ line.ing_svc }} {{ line.ing_port }} {{ line.ing_path }} {{ line.ing_msg }} {% endfor %} 项目地址：https://github.com/J-jingwei/kubernetes-log/tree/master/Django
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.tcp_no_metrics_save', value: '1'} # 内核放弃建立连接之前发送SYN 包的数量 - {name: 'net.ipv4.tcp_syn_retries', value: '1'} # 内核放弃建立连接之前发送SYNACK 包的数量 - {name: 'net.ipv4.tcp_synack_retries', value: '1'} #开启重用。允许将TIME-WAIT sockets 重新用于新的TCP 连接 - {name: 'net.ipv4.tcp_tw_reuse', value: '1'} # 启用timewait 快速回收 - {name: 'net.ipv4.tcp_tw_recycle', value: '0'} # 表示如果套接字由本端要求关闭，这个参数决定了它保持在FIN-WAIT-2状态的时间 - {name: 'net.ipv4.tcp_fin_timeout', value: '30'} #当keepalive 起用的时候，TCP 发送keepalive 消息的频度。缺省是2 小时 - {name: 'net.ipv4.tcp_keepalive_time', value: '300'} - {name: 'net.ipv4.tcp_orphan_retries', value: '3'} - {name: 'net.ipv4.tcp_keepalive_probes', value: '3'} # 处理无源路由的包 - {name: 'net.ipv4.conf.default.accept_source_route', value: '0'} # ARP参数，检查一次相邻层记录的有效性的周期。当相邻层记录失效时，将在给它发送数据前，再解析一次。缺省值是60秒。 - {name: 'net.ipv4.neigh.default.gc_stale_time', value: '120'} - {name: 'net.ipv4.conf.all.rp_filter', value: '0'} - {name: 'net.ipv4.conf.default.rp_filter', value: '0'} - {name: 'net.ipv4.conf.default.arp_announce', value: '2'} - {name: 'net.ipv4.conf.lo.arp_announce', value: '2'} - {name: 'net.ipv4.conf.all.arp_announce', value: '2'}limit--- - name: Set Sysctl File Limits pam_limits: dest={{ item.dest }} domain='*' limit_type={{ item.limit_type }} limit_item={{ item.limit_item }} value={{ item.value }} with_items: - { dest: '/etc/security/limits.conf' , limit_type: 'soft' , limit_item: 'nofile' , value: '655350' } - { dest: '/etc/security/limits.conf' , limit_type: 'hard' , limit_item: 'nofile' , value: '655350'} - { dest: '/etc/security/limits.conf' , limit_type: 'soft' , limit_item: 'nproc' , value: '102400' } - { dest: '/etc/security/limits.conf' , limit_type: 'hard' , limit_item: 'nproc' , value: '102400' } - { dest: '/etc/security/limits.conf' , limit_type: 'soft' , limit_item: 'sigpending' , value: '255377' } - { dest: '/etc/security/limits.conf' , limit_type: 'hard' , limit_item: 'sigpending' , value: '255377' } - { dest: '/etc/security/limits.d/90-nproc.conf', limit_type: 'soft', limit_item: 'nproc' , value: '262144' } - { dest: '/etc/security/limits.d/90-nproc.conf', limit_type: 'hard', limit_item: 'nproc' , value: '262144' }Ansible通过该方式修改的内核参数会写入到sysctl.conf配置文件中，从而实现永久修改。