引言
在上一篇文章中,我写了有关Kubernetes健康检查的文章,作为Python的开发人员和粉丝,我开始着手在Django中实现它。 运行健康检查是协助Kubernetes让你的应用具有高可用性的好方法,例如Django应用。 但是,使用Django并不像听起来那样简单。
健康检查是Kubernetes探查应用是否健康的一种方式,它们认为你的应用可以在未准备就绪的情况下启动,因此不要预设应用的启动顺序,因为这个缘故,我们需要检查数据库或者memcached是否已停止运行同时应用无法访问。
健康检查视图
编写运行健康检查的简单方法是为其编写视图,类似这样:
def liveliness(request):
return HttpResponse("OK")
def readiness(request):
try:
# Connect to database
return HttpResponse("OK")
except Exception, e:
return HttpResponseServerError("db: cannot connect to database.")
但是Django在运行视图之前会运行许多用户代码,例如中间件和装饰器,所以这些故障会使就绪探针产生一些我们不希望看到的响应。
使用中间件解决健康检查
由于许多Django中间件(例如Django的AuthenticationMiddleware)都使用该数据库,因此无法将存活检查和就绪检查作为简单的视图来实现。 当您的应用无法访问数据库时,Django会生成一个异常,并在Django执行视图很久之前返回500错误页面,这将无法为Kubernetes提供最佳的开发体验。
为了部分解决Django应用程序的健康状况检查,我编写了一些中间件来实现健康状况检查,我想在不假设使用任何特定Django模型的情况下执行数据库检查,因此我直接生成了查询。
由于memcached客户端不支持ping方法,因此我还可以通过调用get_stats()来对服务器进行ping操作,从而检查高速缓存服务器是否可用。
import logging
from django.http import HttpResponse, HttpResponseServerError
logger = logging.getLogger("healthz")
class HealthCheckMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
if request.method == "GET":
if request.path == "/readiness":
return self.readiness(request)
elif request.path == "/healthz":
return self.healthz(request)
return self.get_response(request)
def healthz(self, request):
"""
Returns that the server is alive.
"""
return HttpResponse("OK")
def readiness(self, request):
# Connect to each database and do a generic standard SQL query
# that doesn't write any data and doesn't depend on any tables
# being present.
try:
from django.db import connections
for name in connections:
cursor = connections[name].cursor()
cursor.execute("SELECT 1;")
row = cursor.fetchone()
if row is None:
return HttpResponseServerError("db: invalid response")
except Exception, e:
logger.exception(e)
return HttpResponseServerError("db: cannot connect to database.")
# Call get_stats() to connect to each memcached instance and get it's stats.
# This can effectively check if each is online.
try:
from django.core.cache import caches
from django.core.cache.backends.memcached import BaseMemcachedCache
for cache in caches.all():
if isinstance(cache, BaseMemcachedCache):
stats = cache._cache.get_stats()
if len(stats) != len(cache._servers):
return HttpResponseServerError("cache: cannot connect to cache.")
except Exception, e:
logger.exception(e)
return HttpResponseServerError("cache: cannot connect to cache.")
return HttpResponse("OK")
你可以将其添加到MIDDLEWARE_CLASSES的开头,以将健康状况检查添加到你的应用中,将其放在MIDDLEWARE_CLASSES的开头可确保它在其他可能访问数据库的中间件类之前运行。
支持健康检查
我们需要做更多的工作来支持健康检查,默认情况下,Django在每次连接时都连接到数据库,即使使用连接池,当请求到来时它也会惰性地连接到数据库。 在Kubernetes中,如果service没有endpoints(即所有mysql或memcached pod均未通过就绪检查),那么当连接到该服务时,该服务的cluster IP将无法访问,因为它是虚拟IP,所以没有机器可以拒绝连接。
Python的最大问题之一是没有默认的套接字超时。 如果你的数据库没有健康的endpoints,那么你的Django应用可能会永远挂起,以等待与其连接,我们需要做的是确保在settings.py中正确设置了连接超时(提示:无论如何,你都应该这样做),根据使用的数据库后端的不同而不同,请阅读文档)。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myapp',
'USER': 'myapp',
'PASSWORD': os.environ.get('DB_PASS'),
'HOST': 'mysql',
'PORT': '3306',
'OPTIONS': {
'connect_timeout': 3,
}
}
}
然后,你要确保健康检查的timeoutSeconds比这更长。
readinessProbe:
# an http probe
httpGet:
path: /readiness
port: 8080
initialDelaySeconds: 20
timeoutSeconds: 5
弹性Django网站
希望这可以帮助你编写更具弹性的Django网站! Django在Docker,Kubernetes和容器化流行之前就已经写得很好了,而Django应用程序也可以很容易地适应它们。 一些PaaS解决方案(例如Eldarion Cloud))已经让使用Kubernetes部署Python应用程序变得更容易。
如果有兴趣了解有关Kubernetes的更多信息,请查看Kubernetes Slack),最好的Kubernetes开发人员在那里聚集在一起讨论Kubernetes的所有内容,查看#sig-apps频道,以获取有关在Kubernetes上部署应用程序的讨论。