如何解决AWS Elastic Beanstalk Django健康检查问题

10

我最近将Django API后端部署到AWS EB上,部署在他们的Linux 2系统上(确切的平台名称是Python 3.7运行在64位Amazon Linux 2上)。

几乎一切都按预期工作,但我的应用健康状态为严重,经过数小时的调试后,我仍然不知道原因。

使用以下端点(django-health-check模块)处理应用程序的健康检查。

url(r'^ht/', include('health_check.urls'))

所有请求的状态码都是200,但我的整体健康状况如下:

|--------------------|----------------|---------------------------------------------------|
|   instance-id      |   status       |   cause                                           |
|--------------------|----------------|---------------------------------------------------|
|   Overall          |   Degraded     |   Impaired services on all instances.             |
|   i-0eb89f...      |   Severe       |   Following services are not running: release.    |
|--------------------|----------------|---------------------------------------------------|
最奇怪的是这个消息“以下服务未运行:release。”只在互联网上出现过(似乎以前没有人遇到过这样的问题)。
另一个奇怪的事情是我的/var/log/healthd/daemon.log文件的内容,它的行类似于
W, [2020-07-21T09:00:01.209091 #3467]  WARN -- : log file "/var/log/nginx/healthd/application.log.2020-07-21-09" does not exist

时间改变的地方。

可能相关的最后一件事是我在.ebextensions目录中的单个文件的内容:

option_settings:
  "aws:elasticbeanstalk:application:environment":
    DJANGO_SETTINGS_MODULE: "app.settings"
    "PYTHONPATH": "/var/app/current:$PYTHONPATH"
  "aws:elasticbeanstalk:container:python":
    WSGIPath: app.wsgi:application
    NumProcesses: 3
    NumThreads: 20
  aws:elasticbeanstalk:environment:proxy:staticfiles:
    /static: static
    /static_files: static_files
container_commands:
  01_migrate:
    command: "source /var/app/venv/staging-LQM1lest/bin/activate && python manage.py migrate --noinput"
    leader_only: true
packages:
  yum:
    git: []
    postgresql-devel: []

有人知道怎么解决吗?最终目标是获得绿色的“OK”状态。


编辑: 最终我切换到使用基础健康系统,问题突然消失了。不过我仍然对解决原始问题感兴趣,因为强化版健康系统带来了一些好处。


应用程序运行结果如预期?诸如 /var/log/cloud-init-cmd 之类的日志没有显示任何错误? - Marcin
@Marcin 我的意思是端点和数据库都运行良好。我从EB控制台下载了完整的日志并仔细查看了它们。我只发现了2个来自eb-engine.log的错误,它们似乎不相关([ERROR] nginx: the configuration file /var/proxy/staging/nginx/nginx.conf syntax is ok nginx: configuration file /var/proxy/staging/nginx/nginx.conf test is successful[ERROR] Created symlink from /etc/systemd/system/multi-user.target.wants/worker.service to /etc/systemd/system/worker.service.)。名为 cloud-init-cmd 的文件甚至不是日志的一部分。 - Philip Fabianek
文件应该是/var/log/cfn-init-cmd.log,抱歉。 - Marcin
@Marcin 不,这个文件没有任何错误。 - Philip Fabianek
不知道为什么会出现错误。基本的健康报告只基于LB和EC2健康检查。增强版还监视实例内部的日志和其他指标。因此,可能在那里发现了一些错误,并将其报告为健康问题。 - Marcin
显示剩余3条评论
2个回答

7

我认为你遇到的问题可能是由于你的 settings.py 文件中 ALLOWED_HOSTS 设置引起的。

EB 向你的应用程序发送 HTTP 请求以查看它是否工作,但 Django 阻止任何不来自设置变量中指定主机的通信。但这里存在一个问题,EB 将请求发送到 ec2 实例的私有 IP。

最简单的解决方法是在你的 settings.py 文件中允许所有 HOSTS,像这样:

ALLOWED_HOSTS=['*']

这可能导致安全问题,但是这是最快的方法。现在,为了使其动态工作,因为EC2实例可以随时启动,私有IP会从一个实例更改为另一个实例。

要解决这个问题,您必须在部署过程开始时获取私有IP。

在您的settings.py文件的顶部放置以下函数:

import os
import requests
# Other imports ...

def is_ec2_linux():
"""Detect if we are running on an EC2 Linux Instance
   See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html
"""
    if os.path.isfile("/sys/hypervisor/uuid"):
        with open("/sys/hypervisor/uuid") as f:
            uuid = f.read()
            return uuid.startswith("ec2")
    return False

def get_token():
"""Set the autorization token to live for 6 hours (maximum)"""
    headers = {
        'X-aws-ec2-metadata-token-ttl-seconds': '21600',
    }
    response = requests.put('http://169.254.169.254/latest/api/token', headers=headers)
    return response.text


def get_linux_ec2_private_ip():
    """Get the private IP Address of the machine if running on an EC2 linux server.
See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html"""

    if not is_ec2_linux():
        return None
    try:
        token = get_token()
        headers = {
            'X-aws-ec2-metadata-token': f"{token}",
        }
        response = requests.get('http://169.254.169.254/latest/meta-data/local-ipv4', headers=headers)
        return response.text
    except:
        return None
    finally:
        if response:
            response.close()
# Other settings

最重要的函数是get_token()get_linux_ec2_private_ip(),第一个函数设置访问令牌并检索它供第二个函数使用以获取当前ec2实例IP。

获取到访问令牌后,将其添加到ALLOWED_HOSTS中。

ALLOWED_HOSTS = ['127.0.0.1', 'mywebsite.com']
private_ip = get_linux_ec2_private_ip()
if private_ip:
   ALLOWED_HOSTS.append(private_ip)

之后只需提交您的更改,如果您已设置EB CLI,则使用eb deploy重新部署它。


那个硬编码的IP地址169.254.169.254是什么? - DataGreed
2
这似乎是一个特殊保留的IP地址。在AWS EC2中,它用于在实例之间分发元数据。您可以在此处查看https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html - Sebastian Escalante

3
有一个包叫做django-ebhealthcheck,旨在通过获取您的ec2实例的本地ip并将其添加到ALLOWED_HOSTS中来解决这个问题。它非常简单易用,您只需将'ebhealthcheck.apps.EBHealthCheckConfig'添加到INSTALLED_APPS中即可。
包的github页面 - https://github.com/sjkingo/django-ebhealthcheck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接