原文地址:Lua OpenResty容器化(考古历程)

背景

公司有几个“远古时期”的项目,一直都相对较为稳定,但是项目每天总会在一些时段,请求每分钟QPS到达峰值800K左右,导致机器的性能出现了一些瓶颈,每到峰值时期,总会出现一个告警,实在是令人头疼。更糟糕的是这只是远古时期项目中的其中一个而且都是部署在物理机器上,所有机器加起来接近100台。

出于稳定性(削峰)和成本的角度考虑,我们最终决定将所有的Lua OpenResty项目上到k8s集群。

选择合适的openresty基础镜像

通过查看线上在使用的openresty版本信息:

  1. /usr/local/openresty/nginx/sbin/nginx -V
  2. nginx version: openresty/1.13.6.2
  3. built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
  4. built with OpenSSL 1.1.0h 27 Mar 2018 (running with OpenSSL 1.1.0k 28 May 2019)
  5. TLS SNI support enabled
  6. configure arguments: --prefix=/usr/local/openresty/nginx ...
  1. lua -v
  2. Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio

得知在使用的是openresty/1.13.6.2Lua 5.1.4 :

  1. docker pull openresty/openresty:1.13.6.2-2-centos

Q:能不能选择使用更小的alpine系列的呢?

A:因为项目依赖许多的so库,都是glibc编译的,alpine的话是musl-lib,不兼容。

Q:为啥不重新编译?

A:一方面是风险问题,另外一方面是有一些so库不一定能找到。

查找项目的动态库依赖关系

Nginx配置文件

  1. $ tree -L 3 nginx/conf
  2. nginx/conf
  3. ├── vhosts/
  4. ├── inner.prometheus.nginx.conf
  5. └── project.nginx.conf
  6. └── nginx.conf

自编译的C动态库文件,如binary_protocol.so

编写好dockerfile,然后将项目打包进容器,执行:

  1. /usr/local/openresty/nginx/sbin/nginx nginx -t

果不其然,报错:

  1. /usr/local/openresty/nginx/lua/init.lua:1: module 'binary_protocol' not found:
  2. no field package.preload['binary_protocol']
  3. no file '/usr/local/openresty/nginx/lua/binary_protocol.lua'
  4. no file '/usr/local/openresty/nginx/lua_lib/binary_protocol.lua'
  5. no file '/usr/local/openresty/nginx/luarocks/share/lua/5.1/binary_protocol.lua'
  6. no file '/usr/local/openresty/site/lualib/binary_protocol.ljbc'
  7. …… ……
  8. no file '/usr/local/openresty/nginx/luarocks/lib64/lua/5.1/binary_protocol.so'
  9. no file '/usr/local/openresty/site/lualib/binary_protocol.so'
  10. no file '/usr/local/openresty/lualib/binary_protocol.so'
  11. no file '/usr/local/openresty/site/lualib/binary_protocol.so'
  12. no file '/usr/local/openresty/lualib/binary_protocol.so'
  13. no file './binary_protocol.so'
  14. no file '/usr/local/lib/lua/5.1/binary_protocol.so'
  15. no file '/usr/local/openresty/luajit/lib/lua/5.1/binary_protocol.so'
  16. no file '/usr/local/lib/lua/5.1/loadall.so'
  17. no file '/usr/local/openresty/luajit/lib/lua/5.1/binary_protocol.so'

Q:仔细观察,发现so动态库是内部编译出来提供给lua调用的,如何找到它们呢?

A:是lddpldd又或者使用lsof查看动态库文件。

通过ldd、pldd命令,可以查看so所相关的依赖

  1. ldd binary_protocol.so
  2. linux-vdso.so.1 => (0x00007fff40bd4000)
  3. libtolua++.so => not found ## 会告诉我们ldd缺少这个依赖
  4. libcrypto.so.6 => not found
  5. liblog4cplus.so.2 => not found
  6. libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f458d9ef000)
  7. libm.so.6 => /lib64/libm.so.6 (0x00007f458d6ed000)
  8. libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f458d4d7000)
  9. libc.so.6 => /lib64/libc.so.6 (0x00007f458d10a000)
  10. /lib64/ld-linux-x86-64.so.2 (0x00007f458df1e000)

通过这些方法,一点点跟踪,知道找齐所有依赖库即可。

Luarocks外部包文件

从线上的nginx.conf找到lua_package_pathlua_package_cpath中包括的luarocks路径,再从这个路径中,找到manifest文件,此文件有描述安装了哪些luarocks库。

luarocks 外部依赖安装

  1. RUN luarocks --tree=${WORK_DIR}/luarocks install lua-cjson \
  2. && luarocks --tree=${WORK_DIR}/luarocks install penlight \
  3. && luarocks --tree=${WORK_DIR}/luarocks install version \
  4. && luarocks --tree=${WORK_DIR}/luarocks install lua-resty-http \
  5. && luarocks --tree=${WORK_DIR}/luarocks install luaunit \
  6. && luarocks --tree=${WORK_DIR}/luarocks install ldoc \
  7. && luarocks --tree=${WORK_DIR}/luarocks install lua-discount \
  8. && luarocks --tree=${WORK_DIR}/luarocks install serpent \
  9. && luarocks --tree=${WORK_DIR}/luarocks install luacov \
  10. && luarocks --tree=${WORK_DIR}/luarocks install cluacov \
  11. && luarocks --tree=${WORK_DIR}/luarocks install mmdblua \
  12. && luarocks --tree=${WORK_DIR}/luarocks install lua-resty-jit-uuid \
  13. && luarocks --tree=${WORK_DIR}/luarocks install luasocket
  14. 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资源限额

  1. resources:
  2. limits:
  3. cpu: "2000m"
  4. memory: "1Gi"
  5. requests:
  6. cpu: "1000m"
  7. memory: "512Mi"

ps:启动4个Worker大约消耗200Mi。

问题3:attempt to index upvalue ‘result_dict’ (a nil value)

原因是线上的nginx.conf有相关的定义

而代码层面上没有,加上即可:

  1. lua_shared_dict monitor_status 150m;

缩减镜像大小的一个小技巧

借鸡生蛋

如何接入Prometheus监控

在OpenResty中接入 Prometheus,https://github.com/knyar/nginx-lua-prometheus

安装依赖

  1. luarocks --tree=/usr/local/openresty/nginx/luarocks install nginx-lua-prometheus

新增配置

nginx/conf/vhosts/project.nginx.conf增加:

  1. lua_shared_dict prometheus_metrics 10M;
  2. log_by_lua_block {
  3. metric_requests:inc(1, {ngx.var.server_name, ngx.var.status})
  4. metric_latency:observe(tonumber(ngx.var.request_time), {ngx.var.server_name})
  5. }

新增配置文件

新增nginx/conf/vhosts/inner.prometheus.nginx.conf

  1. server {
  2. listen 8099;
  3. location /metrics {
  4. content_by_lua_block {
  5. metric_connections:set(ngx.var.connections_reading, {"reading"})
  6. metric_connections:set(ngx.var.connections_waiting, {"waiting"})
  7. metric_connections:set(ngx.var.connections_writing, {"writing"})
  8. prometheus:collect()
  9. }
  10. }
  11. }

更新deployment配置

  1. apiVersion: extensions/v1beta1
  2. kind: Deployment
  3. metadata:
  4. name: ${name}
  5. namespace: ${namespace}
  6. labels:
  7. test-app: test-server
  8. spec:
  9. replicas: ${replicas}
  10. template:
  11. metadata:
  12. labels:
  13. test-app: test-server
  14. annotations: # <----------------------- 新增
  15. prometheus.io/scrape: "true"
  16. prometheus.io/path: "/metrics"
  17. prometheus.io/port: "8099"

总结

至此,lua的一个项目容器化完成,中途遇到的问题还是蛮多的,上面也只记录了几个主要的步骤和问题。

Lua OpenResty容器化(考古历程)的更多相关文章

  1. 用Nginx+Lua(OpenResty)开发高性能Web应用

    在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等场景:而把Nginx作为一个Web容器使用的还不是那么广泛.Nginx的高性能是大家公认的,而Nginx开 ...

  2. Nginx+Lua(OpenResty)开发高性能Web应用

    使用Nginx+Lua(OpenResty)开发高性能Web应用 博客分类: 跟我学Nginx+Lua开发 架构 ngx_luaopenresty 在互联网公司,Nginx可以说是标配组件,但是主要场 ...

  3. 使用Nginx+Lua(OpenResty)开发高性能Web应用

    摘自(http://jinnianshilongnian.iteye.com/blog/2280928) 在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等 ...

  4. 支付宝客户端架构解析:Android 容器化框架初探

    摘要: 本文将介绍支付宝 Android 容器化框架设计的基本思路. 1. 前言 由本章节开始,我们将从支付宝客户端的架构设计方案入手,细分拆解客户端在“容器化框架设计”.“网络优化”.“性能启动优化 ...

  5. 容器化-Docker介绍

    导读:本文章对Docker技术进行了介绍,阐述了Docker的技术发展历程.容器与虚拟机的差异.Docker原理.特点.Docker三组件和Docker带来的影响,为我们进一步理解Docker打下基础 ...

  6. 安装Nginx+Lua+OpenResty开发环境配置全过程实例

    安装Nginx+Lua+OpenResty开发环境配置全过程实例 OpenResty由Nginx核心加很多第三方模块组成,默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用. ...

  7. 【运维技术】JENKINS管道部署容器化初探

    目标服务器安装docker参考官方文档 https://docs.docker.com/install/linux/docker-ce/centos/ (可选)在目标服务器上安装docker私服 ht ...

  8. 案例 | 腾讯广告 AMS 的容器化之路

    作者 张煜,15年加入腾讯并从事腾讯广告维护工作.20年开始引导腾讯广告技术团队接入公司的TKEx-teg,从业务的日常痛点并结合腾讯云原生特性来完善腾讯广告自有的容器化解决方案 项目背景 腾讯广告承 ...

  9. docker4dotnet #2 容器化主机

    .NET 猿自从认识了小鲸鱼,感觉功力大增.上篇<docker4dotnet #1 前世今生&世界你好>中给大家介绍了如何在Windows上面配置Docker for Window ...

随机推荐

  1. C# 类中操作主窗体控件

    主窗体程序: using System; using System.Collections.Generic; using System.ComponentModel; using System.Dat ...

  2. 怎么去掉右下角的thinkphp的图标

    关闭thinkphp右下角的trace可以试试以下步骤: 1.在入口文件index.php 加入 define("APP_DEBUG", false); 2.在config.php ...

  3. 使用 Tye 辅助开发 k8s 应用竟如此简单(六)

    续上篇,这篇我们来进一步探索 Tye 更多的使用方法.本篇我们将进一步研究 Tye 与分布式应用程序运行时 Dapr 如何碰撞出更精彩的火花. Newbe.Claptrap 是一个用于轻松应对并发问题 ...

  4. Charles 抓取https 包

    1.  Recording Settings中 include 添加 host , port端口为443 2.  SSL Proxying Settings 选中 Enable SSL Proxyin ...

  5. 原生JS快速实现拖放

    原生 JS 快速实现拖放 拖放是很常见的一种交互效果,很多时候我们都会借助于第三方的控件来实现,其实用原生 js 实现起来也非常的方便.接下来我们就用原生 js 和 css 快速实现拖放效果 html ...

  6. golang操作redis/go-redis库

    目录 Redis介绍 Redis支持的数据结构 Redis应用场景 准备Redis环境 go-redis库 安装 连接 普通连接 V8新版本相关 连接Redis哨兵模式 连接Redis集群 基本使用 ...

  7. Java I/O流 01

    文件IO·异常 和 File类 异常的概述和分类 * A:异常的概述 * 异常就是Java程序在运行过程中出现的错误 * B:异常的分类 * 用过API查看Throwable * Error * 服务 ...

  8. SQL驱动限制,导致插入失败

    insert into TB_IF_ORDERS (DC_CD,JOB_DT,SEQ_NO,ORDER_KEY,ORDER_ID,ORDER_LINE_NUM,COMPANY_CD,CUST_CD,S ...

  9. teprunner测试平台部署到Linux系统Docker

    本文是一篇过渡,在进行用例管理模块开发之前,有必要把入门篇开发完成的代码部署到Linux系统Docker中,把部署流程走一遍,这个过程对后端设计有决定性影响. 本地运行 通过在Vue项目执行npm r ...

  10. 新元科技签订5.8亿元IPFS合同,IPFS国家认可吗?IPFS挖矿是不是合法的?

    就在昨天,也就是 3 月 15 日,FIL 又有一波较大的涨幅,现流通市值排名第 15,总市值预计 6818.16 亿.FIL 见涨,一些公司也按耐不住了.也是在3月15日,A 股上市公司新元科技签订 ...