Lua OpenResty容器化(考古歷程)

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

背景

公司有幾個「遠古時期」的項目,一直都相對較爲穩定,可是項目天天總會在一些時段,請求每分鐘QPS到達峯值800K左右,致使機器的性能出現了一些瓶頸,每到峯值時期,總會出現一個告警,實在是使人頭疼。更糟糕的是這只是遠古時期項目中的其中一個並且都是部署在物理機器上,全部機器加起來接近100臺。node

出於穩定性(削峯)和成本的角度考慮,咱們最終決定將全部的Lua OpenResty項目上到k8s集羣。
linux

選擇合適的openresty基礎鏡像

經過查看線上在使用的openresty版本信息:nginx

/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.2Lua 5.1.4 :c++

docker pull openresty/openresty:1.13.6.2-2-centos

Q:能不能選擇使用更小的alpine系列的呢?git

A:由於項目依賴許多的so庫,都是glibc編譯的,alpine的話是musl-lib,不兼容。github

Q:爲啥不從新編譯?docker

A:一方面是風險問題,另一方面是有一些so庫不必定能找到。shell

查找項目的動態庫依賴關係

Nginx配置文件

$ tree -L 3 nginx/conf
nginx/conf
├── vhosts/
│    ├── inner.prometheus.nginx.conf
│    └── project.nginx.conf
└── nginx.conf

自編譯的C動態庫文件,如binary_protocol.so

編寫好dockerfile,而後將項目打包進容器,執行:json

/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調用的,如何找到它們呢?

A:是lddpldd又或者使用lsof查看動態庫文件。

經過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_pathlua_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/ngin...

安裝依賴

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的一個項目容器化完成,中途遇到的問題仍是蠻多的,上面也只記錄了幾個主要的步驟和問題。

相關文章
相關標籤/搜索