Lua OpenResty容器化(考古历程)
背景
公司有几个“远古时期”的项目,一直都相对较为稳定,但是项目每天总会在一些时段,请求每分钟QPS到达峰值800K
左右,导致机器的性能出现了一些瓶颈,每到峰值时期,总会出现一个告警,实在是令人头疼。更糟糕的是这只是远古时期项目中的其中一个而且都是部署在物理机器上,所有机器加起来接近100台。
出于稳定性(削峰)和成本的角度考虑,我们最终决定将所有的Lua OpenResty项目上到k8s集群。
选择合适的openresty基础镜像
通过查看线上在使用的openresty版本信息:
/usr/local/openresty/nginx/sbin/nginx -V
nginx version: openresty/1.13.6.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
built with OpenSSL 1.1.0h 27 Mar 2018 (running with OpenSSL 1.1.0k 28 May 2019)
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx ...
lua -v
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
得知在使用的是openresty/1.13.6.2
和Lua 5.1.4
:
docker pull openresty/openresty:1.13.6.2-2-centos
Q:能不能选择使用更小的alpine系列的呢?
A:因为项目依赖许多的so库,都是glibc
编译的,alpine的话是musl-lib
,不兼容。
Q:为啥不重新编译?
A:一方面是风险问题,另外一方面是有一些so库不一定能找到。
查找项目的动态库依赖关系
Nginx配置文件
$ tree -L 3 nginx/conf
nginx/conf
├── vhosts/
│ ├── inner.prometheus.nginx.conf
│ └── project.nginx.conf
└── nginx.conf
自编译的C动态库文件,如binary_protocol.so
编写好dockerfile,然后将项目打包进容器,执行:
/usr/local/openresty/nginx/sbin/nginx nginx -t
果不其然,报错:
/usr/local/openresty/nginx/lua/init.lua:1: module 'binary_protocol' not found:
no field package.preload['binary_protocol']
no file '/usr/local/openresty/nginx/lua/binary_protocol.lua'
no file '/usr/local/openresty/nginx/lua_lib/binary_protocol.lua'
no file '/usr/local/openresty/nginx/luarocks/share/lua/5.1/binary_protocol.lua'
no file '/usr/local/openresty/site/lualib/binary_protocol.ljbc'
…… ……
no file '/usr/local/openresty/nginx/luarocks/lib64/lua/5.1/binary_protocol.so'
no file '/usr/local/openresty/site/lualib/binary_protocol.so'
no file '/usr/local/openresty/lualib/binary_protocol.so'
no file '/usr/local/openresty/site/lualib/binary_protocol.so'
no file '/usr/local/openresty/lualib/binary_protocol.so'
no file './binary_protocol.so'
no file '/usr/local/lib/lua/5.1/binary_protocol.so'
no file '/usr/local/openresty/luajit/lib/lua/5.1/binary_protocol.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
no file '/usr/local/openresty/luajit/lib/lua/5.1/binary_protocol.so'
Q:仔细观察,发现so动态库是内部编译出来提供给lua调用的,如何找到它们呢?
通过ldd、pldd命令,可以查看so所相关的依赖
ldd binary_protocol.so
linux-vdso.so.1 => (0x00007fff40bd4000)
libtolua++.so => not found ## 会告诉我们ldd缺少这个依赖
libcrypto.so.6 => not found
liblog4cplus.so.2 => not found
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f458d9ef000)
libm.so.6 => /lib64/libm.so.6 (0x00007f458d6ed000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f458d4d7000)
libc.so.6 => /lib64/libc.so.6 (0x00007f458d10a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f458df1e000)
通过这些方法,一点点跟踪,知道找齐所有依赖库即可。
Luarocks外部包文件
从线上的nginx.conf
找到lua_package_path
和lua_package_cpath
中包括的luarocks
路径,再从这个路径中,找到manifest
文件,此文件有描述安装了哪些luarocks库。
luarocks 外部依赖安装
RUN luarocks --tree=${WORK_DIR}/luarocks install lua-cjson \
&& luarocks --tree=${WORK_DIR}/luarocks install penlight \
&& luarocks --tree=${WORK_DIR}/luarocks install version \
&& luarocks --tree=${WORK_DIR}/luarocks install lua-resty-http \
&& luarocks --tree=${WORK_DIR}/luarocks install luaunit \
&& luarocks --tree=${WORK_DIR}/luarocks install ldoc \
&& luarocks --tree=${WORK_DIR}/luarocks install lua-discount \
&& luarocks --tree=${WORK_DIR}/luarocks install serpent \
&& luarocks --tree=${WORK_DIR}/luarocks install luacov \
&& luarocks --tree=${WORK_DIR}/luarocks install cluacov \
&& luarocks --tree=${WORK_DIR}/luarocks install mmdblua \
&& luarocks --tree=${WORK_DIR}/luarocks install lua-resty-jit-uuid \
&& luarocks --tree=${WORK_DIR}/luarocks install luasocket
RUN luarocks --tree=/usr/local/openresty/nginx/luarocks install nginx-lua-prometheus
遇到的问题及其解决方法
问题1:容器老被OOM Killed
经过分析,的确占用了非常大的内存:
通过ps命令定位到 worker 数量非常多
解决方法:
限定worker数量:worker_processes 4;
Q:为啥会产生这么多worker?
A:在k8s上,nginx 启动的 worker process,并没有遵循我们给 Pod 设置的 limit,而是与 Pod 所在 node 有关。
问题2:nginx worker process exited on signal 9
是由于Deployment设定的内存限额太小所致
解决方法:调大requests
资源限额
resources:
limits:
cpu: "2000m"
memory: "1Gi"
requests:
cpu: "1000m"
memory: "512Mi"
ps:启动4个Worker大约消耗200Mi。
问题3:attempt to index upvalue ‘result_dict’ (a nil value)
原因是线上的nginx.conf有相关的定义
而代码层面上没有,加上即可:
lua_shared_dict monitor_status 150m;
缩减镜像大小的一个小技巧
如何接入Prometheus监控
在OpenResty中接入 Prometheus,https://github.com/knyar/nginx-lua-prometheus
安装依赖
luarocks --tree=/usr/local/openresty/nginx/luarocks install nginx-lua-prometheus
新增配置
为nginx/conf/vhosts/project.nginx.conf
增加:
lua_shared_dict prometheus_metrics 10M;
log_by_lua_block {
metric_requests:inc(1, {ngx.var.server_name, ngx.var.status})
metric_latency:observe(tonumber(ngx.var.request_time), {ngx.var.server_name})
}
新增配置文件
新增nginx/conf/vhosts/inner.prometheus.nginx.conf
server {
listen 8099;
location /metrics {
content_by_lua_block {
metric_connections:set(ngx.var.connections_reading, {"reading"})
metric_connections:set(ngx.var.connections_waiting, {"waiting"})
metric_connections:set(ngx.var.connections_writing, {"writing"})
prometheus:collect()
}
}
}
更新deployment配置
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ${name}
namespace: ${namespace}
labels:
test-app: test-server
spec:
replicas: ${replicas}
template:
metadata:
labels:
test-app: test-server
annotations: # <----------------------- 新增
prometheus.io/scrape: "true"
prometheus.io/path: "/metrics"
prometheus.io/port: "8099"
总结
至此,lua的一个项目容器化完成,中途遇到的问题还是蛮多的,上面也只记录了几个主要的步骤和问题。
Lua OpenResty容器化(考古历程)的更多相关文章
- 用Nginx+Lua(OpenResty)开发高性能Web应用
在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等场景:而把Nginx作为一个Web容器使用的还不是那么广泛.Nginx的高性能是大家公认的,而Nginx开 ...
- Nginx+Lua(OpenResty)开发高性能Web应用
使用Nginx+Lua(OpenResty)开发高性能Web应用 博客分类: 跟我学Nginx+Lua开发 架构 ngx_luaopenresty 在互联网公司,Nginx可以说是标配组件,但是主要场 ...
- 使用Nginx+Lua(OpenResty)开发高性能Web应用
摘自(http://jinnianshilongnian.iteye.com/blog/2280928) 在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等 ...
- 支付宝客户端架构解析:Android 容器化框架初探
摘要: 本文将介绍支付宝 Android 容器化框架设计的基本思路. 1. 前言 由本章节开始,我们将从支付宝客户端的架构设计方案入手,细分拆解客户端在“容器化框架设计”.“网络优化”.“性能启动优化 ...
- 容器化-Docker介绍
导读:本文章对Docker技术进行了介绍,阐述了Docker的技术发展历程.容器与虚拟机的差异.Docker原理.特点.Docker三组件和Docker带来的影响,为我们进一步理解Docker打下基础 ...
- 安装Nginx+Lua+OpenResty开发环境配置全过程实例
安装Nginx+Lua+OpenResty开发环境配置全过程实例 OpenResty由Nginx核心加很多第三方模块组成,默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用. ...
- 【运维技术】JENKINS管道部署容器化初探
目标服务器安装docker参考官方文档 https://docs.docker.com/install/linux/docker-ce/centos/ (可选)在目标服务器上安装docker私服 ht ...
- 案例 | 腾讯广告 AMS 的容器化之路
作者 张煜,15年加入腾讯并从事腾讯广告维护工作.20年开始引导腾讯广告技术团队接入公司的TKEx-teg,从业务的日常痛点并结合腾讯云原生特性来完善腾讯广告自有的容器化解决方案 项目背景 腾讯广告承 ...
- docker4dotnet #2 容器化主机
.NET 猿自从认识了小鲸鱼,感觉功力大增.上篇<docker4dotnet #1 前世今生&世界你好>中给大家介绍了如何在Windows上面配置Docker for Window ...
随机推荐
- IDEA 敏捷开发技巧——后缀完成
前言 "工欲善其事,必先利其器." 所以说今天来看一看如何压榨 IDEA ,让你的 IDEA 使用的更顺手! 今日技巧: 后缀完成 自定义后缀完成模版 示例 上面动图使用了 .so ...
- Scrapy 项目:QuotesBot
QuotesBot This is a Scrapy project to scrape quotes from famous people from http://quotes.toscrape.c ...
- Redis-第十章节-链表
目录 数组和链表 链表 对比 总结 1.数组和链表 数组: 数组会在内存中开辟一块连续的空间存储数据,这种存储方式有利也有弊端.当获取数据的时候,直接通过下标值就可以获取到对应的元素,时间复杂度为O( ...
- s2010 安装mvc3
下载链接如下:MVC 3安装包:http://www.microsoft.com/downloads/zh-cn/details.aspx?familyid=d2928bc1-f48c-4e95-a0 ...
- 数组的常用方法之split
今天我们来聊一下数组的常用方法:split 返回值:一个新数组. 1.该方法可以直接调用不传任何值,则会直接将字符串转化成数组. var str = 'I love Javascript'; cons ...
- 184. 部门工资最高的员工 + join + in
184. 部门工资最高的员工 LeetCode_MySql_184 题目描述 题解分析 1.首先需要使用group by找出工资最高的值 2. 然后考虑到最高工资的可能有多位,所以使用in语句找到所有 ...
- 剑指 Offer 44. 数字序列中某一位的数字 + 找规律 + 数位
剑指 Offer 44. 数字序列中某一位的数字 Offer_44 题目描述 题解分析 java代码 package com.walegarrett.offer; /** * @Author Wale ...
- 去哪找Java练手项目?
经常有读者在微信上问我: 在学编程的过程中,看了不少书.视频课程,但是看完.听完之后感觉还是不会编程,想找一些项目来练手,但是不知道去哪儿找? 类似的问题,有不少读者问,估计是大部分人的困惑. 练手项 ...
- Mysql被黑客攻击了?一定要注意一点,不要关闭mysql服务
因为mysql没有关闭的情况,可以从缓存里面获取到数据,如果关闭了只能从文件里面去获取数据了,会大大加大恢复难度
- C语言II博客作业02
这个作业属于那个课程 https://edu.cnblogs.com/campus/zswxy/SE2020-4 这个作业要求在哪里 https://edu.cnblogs.com/campus/zs ...