写点什么

Kubernetes Nginx 配置热加载

作者:CTO技术共享
  • 2022 年 8 月 19 日
    广东
  • 本文字数:3721 字

    阅读完需:约 12 分钟

Kubernetes Nginx配置热加载

Nginx 本身是支持热更新的,通过 nginx -s reload 指令,实际通过向进程发送 HUB 信号实现不停服重新加载配置,然而在 Docker 或者 Kubernetes 中,每次都需要进容器执行 nginx -s reload 指令,单 docker 容器还好说,可以在外面通过 exec 指定容器执行该指令进行热加载,Kubernetes 的话,就比较难受了


今天介绍一下 Kubernetes 中 Nginx 热加载配置的处理方法——reloader


reloader 地址:https://github.com/stakater/Reloader


reloader 主要就是用来监测 ConfigMap 或 Secret 的变化,然后对相关 DeploymentConfig 的 Deployment、DaemonSet 执行滚动升级


reloader 需要 kubernetes1.9 以上的版本才支持

使用方法

首先是安装部署 reloader

# 直接通过官方yaml文件部署kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml
复制代码


默认情况下 reloader 是部署在 default 命名空间,但是它是监控所有命名空间的 configmaps 和 secrets


当然,如果不想监控某个 configmap 或 secret,可以通过--resources-to-ignore=configMaps/secrets 来忽略某个资源



部署成功后,就可以直接使用了,我提前部署了 nginx 和 configmap



这是目前的配置,看一下 Nginx 目前的配置


接着,我修改 Nginx 的 Deployment,添加 reloader,监听 nginx-config 这个 ConfigMap,执行

reload

{  "kind": "Deployment",  "apiVersion": "extensions/v1beta1",  "metadata": {    "name": "nginx",    "namespace": "default",    "selfLink": "/apis/extensions/v1beta1/namespaces/default/deployments/nginx",    "uid": "7eee5fa8-7514-11ec-a916-0210d5e9ca3b",    "resourceVersion": "286141",    "generation": 10,    "creationTimestamp": "2022-01-14T08:32:23Z",    "labels": {      "k8s-app": "nginx"    },    "annotations": {      "deployment.kubernetes.io/revision": "9",      "description": "nginx应用"      # 主要是这行      "reloader.stakater.com/reload": "nginx-config"    }  },  "spec": {    "replicas": 1,    "selector": {      "matchLabels": {        "k8s-app": "nginx"      }    }    ……
复制代码


然后 apply 该 Deployment,之后我们去更新 ConfigMap,更新 nginx 配置文件


更新完成,去掉 proxy_redirect,然后去看 nginx 容器是否执行滚动更新



可以看到,nginx 执行了滚动更新,接着看下 nginx 配置文件是否更新


这样很简单的通过 reloader 就可以实现 Nginx 的配置热加载


除了这种方法,常见的方法还有使用 sidecar,通过 sidecar 去做的话,需要自己写监听脚本,比较麻烦,但是有时候也相对灵活,这里也附一个 sidecar 的 python 脚本


#!/usr/bin/env python# -*- encoding: utf8 -*-"""需求:nginx配置文件变化,自动更新配置文件,类似nginx -s reload实现:    1、用pyinotify实时监控nginx配置文件变化    2、如果配置文件变化,给系统发送HUP来reload nginx"""import osimport reimport pyinotifyimport loggingfrom threading import Timer
# ParamLOG_PATH = "/root/python/log"CONF_PATHS = [ "/etc/nginx",]DELAY = 5SUDO = FalseRELOAD_COMMAND = "nginx -s reload"if SUDO: RELOAD_COMMAND = "sudo " + RELOAD_COMMAND
# Loglogger = logging.getLogger(__name__)logger.setLevel(level = logging.INFO)log_handler = logging.FileHandler(LOG_PATH)log_handler.setLevel(logging.INFO)log_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')log_handler.setFormatter(log_formatter)logger.addHandler(log_handler)
# Reloaderdef reload_nginx(): os.system(RELOAD_COMMAND) logger.info("nginx is reloaded")
t = Timer(DELAY, reload_nginx)
def trigger_reload_nginx(pathname, action): logger.info("nginx monitor is triggered because %s is %s" % (pathname, action)) global t if t.is_alive(): t.cancel() t = Timer(DELAY, reload_nginx) t.start() else: t = Timer(DELAY, reload_nginx) t.start()
events = pyinotify.IN_MODIFY | pyinotify.IN_CREATE | pyinotify.IN_DELETE
watcher = pyinotify.WatchManager()watcher.add_watch(CONF_PATHS, events, rec=True, auto_add=True)
class EventHandler(pyinotify.ProcessEvent): def process_default(self, event): if event.name.endswith(".conf"): if event.mask == pyinotify.IN_CREATE: action = "created" if event.mask == pyinotify.IN_MODIFY: action = "modified" if event.mask == pyinotify.IN_DELETE: action = "deleted" trigger_reload_nginx(event.pathname, action)
handler = EventHandler()notifier = pyinotify.Notifier(watcher, handler)
# Startlogger.info("Start Monitoring")notifier.loop()
复制代码

如果喜欢用 go 的,这里也提供 go 脚本


package main
import ( "log" "os" "path/filepath" "syscall"
"github.com/fsnotify/fsnotify" proc "github.com/shirou/gopsutil/process")
const ( nginxProcessName = "nginx" defaultNginxConfPath = "/etc/nginx" watchPathEnvVarName = "WATCH_NGINX_CONF_PATH")
var stderrLogger = log.New(os.Stderr, "error: ", log.Lshortfile)var stdoutLogger = log.New(os.Stdout, "", log.Lshortfile)
func getMasterNginxPid() (int, error) { processes, processesErr := proc.Processes() if processesErr != nil { return 0, processesErr }
nginxProcesses := map[int32]int32{}
for _, process := range processes { processName, processNameErr := process.Name() if processNameErr != nil { return 0, processNameErr }
if processName == nginxProcessName { ppid, ppidErr := process.Ppid()
if ppidErr != nil { return 0, ppidErr }
nginxProcesses[process.Pid] = ppid } }
var masterNginxPid int32
for pid, ppid := range nginxProcesses { if ppid == 0 { masterNginxPid = pid
break } }
stdoutLogger.Println("found master nginx pid:", masterNginxPid)
return int(masterNginxPid), nil}
func signalNginxReload(pid int) error { stdoutLogger.Printf("signaling master nginx process (pid: %d) -> SIGHUP\n", pid) nginxProcess, nginxProcessErr := os.FindProcess(pid)
if nginxProcessErr != nil { return nginxProcessErr }
return nginxProcess.Signal(syscall.SIGHUP)}
func main() { watcher, watcherErr := fsnotify.NewWatcher() if watcherErr != nil { stderrLogger.Fatal(watcherErr) } defer watcher.Close()
done := make(chan bool) go func() { for { select { case event, ok := <-watcher.Events: if !ok { return }
if event.Op&fsnotify.Create == fsnotify.Create { if filepath.Base(event.Name) == "..data" { stdoutLogger.Println("config map updated")
nginxPid, nginxPidErr := getMasterNginxPid() if nginxPidErr != nil { stderrLogger.Printf("getting master nginx pid failed: %s", nginxPidErr.Error())
continue }
if err := signalNginxReload(nginxPid); err != nil { stderrLogger.Printf("signaling master nginx process failed: %s", err) } } } case err, ok := <-watcher.Errors: if !ok { return } stderrLogger.Printf("received watcher.Error: %s", err) } } }()
pathToWatch, ok := os.LookupEnv(watchPathEnvVarName) if !ok { pathToWatch = defaultNginxConfPath }
stdoutLogger.Printf("adding path: `%s` to watch\n", pathToWatch)
if err := watcher.Add(pathToWatch); err != nil { stderrLogger.Fatal(err) } <-done}
复制代码


发布于: 刚刚阅读数: 2
用户头像

学如逆水行舟,不进则退 2022.08.05 加入

大型企业CTO,专注大数据、架构框架、集群、中间件、分布式、数据库、监控、开源、基础架构等技术分享,助力数字化转型。

评论

发布
暂无评论
Kubernetes Nginx配置热加载_开源_CTO技术共享_InfoQ写作社区