背景

  1. 最近一直在准备k8s 发布20000 ops吞吐量图片识别服务。服务外部Nginx入口部署在多台腾讯云虚拟机器上,后端计算服务部署在机房k8s实例上,入口虚拟和公司机房通过专线和腾讯云虚拟打通。

K8 Ingress nginx 转发

腾讯云虚拟机器也可以作为k8s节点,但是这个比较绕, 为了安装ingres nginx,虚拟机上部署kubelet, 而且夸网络。开始可以通过比较简单方法,把nginx upsream 指向私有云机房的k8s ingress nginx, 如图:<br />  <br />                   ![Nginx-old.png](https://cdn.nlark.com/yuque/0/2020/png/614455/1607567641822-5cfd4827-da91-457e-bad9-a5eede3ee192.png#align=left&display=inline&height=515&margin=%5Bobject%20Object%5D&name=Nginx-old.png&originHeight=515&originWidth=592&size=22069&status=done&style=none&width=592)

Nginx 动态upstream server

  这架构并发当吞吐量上5000 ops的时候,k8s ingress nginx 不定时出现 connect reset。为了不影响当前其他应用使用ingress nginx 转发。打算打通腾讯云虚拟机器和pod网段。 `打通外部机器和pod网段通信后面在分享` 但是服务发现怎么做呢,想到借助了nginx-upstream-dynamic-server模块。外面nginx可以通过域名解析解析 headless service 所用 pod 地址, 采用了在外部部署nginx来实现负载均衡的方法,由于upstream里的pod ip会动态变化,所以我们不能直接在upstream里写死pod的ip地址,而只能用service的域名来替代,并让nginx自己去解析这个域名,我们知道headless service的域名由于没有内部的service ip。架构简化如下:<br />             ![Nginx-2.png](https://cdn.nlark.com/yuque/0/2020/png/614455/1607568958496-44f7ef01-dfcf-45e7-ae70-615341db34a3.png#align=left&display=inline&height=453&margin=%5Bobject%20Object%5D&name=Nginx-2.png&originHeight=453&originWidth=547&size=20951&status=done&style=none&width=547)

部署和配置

编译

nginx-upstream-dynamic-server是不提供动态加载, 需要重新编译nginx,可以通过线上版本rpm 安装nginx v1.16.0,重新编译. 获取线上版本编译参数

$ nginx -V

configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

下载nginx-upstream-dynamic-server
git clone https://github.com/GUI/nginx-upstream-dynamic-servers.git

下载对应版本nginx然后编译

$ ./configure  --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --add-module=/path/to/nginx-upstream-dynamic-servers/ --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

$ make -j4

安装

编译以后,在objs文件有nginx可执行文件,可以把他覆盖 /usr/sbin/nginx 可以了。

配置

k8s dns cache

k8s headless服务都是通过k8s-coredns解析到所有pod ip地址,可以和nginx-upstream-dynamic-server完美组合。但是测试发送,启动nginx服务以后,nginx直接占用了100% cpu, 发现一个不是bug的bug。打开debug日志发现,upstream-dynamic-server模块发送dns解析动态server,导致cpu占用很多。
模块nginx-upstream-dynamic-server根据dns 返回 ttl 间隔定时发送dns请求更新updstream,我们可用dig工具获取k8s dns返回ttl是多少, 下图
企业微信截图_20201210151657.png
默认是5ttl, 有时候返回小于5,每次访问都比较耗时,所有不能偷懒,再得部署一个dns cache,上游可以解析k8s 域名dns地址,并且覆盖5ttl。coredns 1.4 以上的可以使用proxy插件,同时也有cache minttl支持。配置如下:

 cat /usr/local/coredns/Corefile 
.:53 {
    errors
    whoami
    log
    # 遇到后缀 cluster.local 指向k8s dns
    proxy cluster.local <ip for k8s dns>
    # 腾讯云内部dns
    proxy . 183.60.83.19 183.60.83.98 

    cache 30 {
      # 如果成功缓存多少个, ttl, minttl
      success 5000 30 30
    }     
}

启动core-dns以后在虚拟机器解析k8s 服务域名,ttl覆盖为30, 如图
企业微信截图_16075856356225.png

Nginx 配置修改

nginx 如果在 upstream 配置域名,nginx启动解析以后就不去更新,以后k8s pod 滚动更新以后,nginx不能转发pod上。需要按照动态upstream插件。需要配置下面文件:
/etc/nginx/nginx.conf

...
# dns cache ip 可以多个空格分隔
resolver <dns-cache-ip>  valid=30s;
...

/etc/nginx/conf.d/your-service.conf

upstream cg-lobby {
    server <your-k8s-service-name>.<your-service-namespace>.svc.cluster.local:51020 max_fails=2 resolve;
    keepalive 100; # 如果太大压力测试反而发现连接不能改变,不是长连接了
}
server {
    listen 80;
    server_name ;
    location / {
        client_body_buffer_size 1m; # max requst body size 1m
        proxy_set_header  Host $http_host;
        proxy_set_header  X-Real-IP Sremote_addr;
        proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass  http://cg-lobby;
    }
}

资源消耗

腾讯云 Nginx 可以根据带宽进行添加,一般可以下面配置。这个压力其实5台VM就差不多了

CPU: 4 core
内存:8G
网络:2Gb/s 网卡