【问题标题】:How to debug an Elixir application in production?如何在生产环境中调试 Elixir 应用程序?
【发布时间】:2018-03-22 20:54:54
【问题描述】:

这不是特别针对我当前的问题,而是一般来说。有时我有一个只发生在生产配置中的问题,我想在那里调试它。在 Elixir 中处理这个问题的最佳方法是什么?生产在没有图形环境(docker)的情况下运行。

在开发中我可以使用 IEX.pry,但由于混合在生产中不可用,这似乎不是一个选项。

对于 Erlang https://stackoverflow.com/a/21413344/1561489 提到 dbg 和 redbug,但即使它们可以使用,我也需要帮助才能将它们应用于 Elixir 代码。

【问题讨论】:

  • 使用特定的日志,您可以随时将默认的日志后端 - 控制台 - 替换为适合您需求的内容。
  • 在您想调试、重新部署和查看日志的任何地方添加Logger.error。我也想听听更好的方法。
  • 您还可以尝试找出生产系统和调试系统之间的确切差异并消除它们。据我所知,在 BEAM 代码方面没有“调试”构建这样的东西,因此两个环境之间的代码应该是相同的。
  • 您如何在生产环境中运行应用程序?

标签: erlang elixir phoenix-framework


【解决方案1】:

首先,使用iex -S mix 在您的开发机器上启动一个运行 iex 的本地节点。如果您不希望在本地运行的应用程序导致激活断点,则需要禁用该应用程序在本地启动。为此,您可以简单地注释掉mix.exs 中的application 函数或运行iex -S mix run --no-start

接下来,您需要使用Node.connect(:"remote@hostname") 从您的开发节点上的 iex 连接到在 docker 上运行的远程节点。为此,您必须确保远程计算机上的 epmd 和节点端口都可以从本地节点访问。

最后,一旦您的节点连接上,从本地 iex 运行 :debugger.start(),这将使用 GUI 打开调试器。现在在本地 iex 中,运行:int.ni(<Module you want to debug>),它将使模块对调试器可见,您可以继续添加断点并开始调试。

您可以在here找到包含步骤和屏幕截图的教程。

【讨论】:

  • 我想从 IEx 手册中添加以下内容:“连接到远程 shell 非常普遍,我们也通过命令行提供了快捷方式:$ iex --sname baz --remsh foo@HOST
【解决方案2】:

如果您在 AWS 上运行生产,那么您应该首先利用 CloudWatch 来发挥自己的优势。 在您的 elixir 代码中,像这样配置您的记录器:

config :logger,
  handle_otp_reports: true,
  handle_sasl_reports: true,
  metadata: [:application, :module, :function, :file, :line]

config :logger,
  backends: [
    {LoggerFileBackend, :shared_error}
  ]

config :logger, :shared_error,
  path: "#{logging_dir}/verbose-error.log",
  level: :error

在您的 Dockerfile 中,为 erl_crash.dump 的确切写入位置配置一个环境变量,例如: ERL_CRASH_DUMP=/opt/log/erl_crash.dump

然后在.ebextensions下的.config文件中配置awslogs如下:

files:
  "/etc/awslogs/config/stdout.conf":
    mode: "000755"
    owner: root
    group: root
    content: |
      [erl_crash.dump]
      log_group_name=/aws/elasticbeanstalk/your_app/erl_crash.dump
      log_stream_name={instance_id}
      file=/var/log/erl_crash.dump

      [verbose-error.log]
      log_group_name=/aws/elasticbeanstalk/your_app/verbose-error.log
      log_stream_name={instance_id}
      file=/var/log/verbose-error.log

并确保你在Dockerrun.aws.json下为你的docker设置了一个卷

  "Logging": "/var/log",
  "Volumes": [
    {
      "HostDirectory": "/var/log",
      "ContainerDirectory": "/opt/log"
    }
  ],

之后,您可以在CloudWatch 下查看您的错误消息。 现在,如果您使用ElasticBeanstalk(我上面的示例隐含暗示)与Docker 部署而不是AWS ECS,则std_input 的日志默认重定向到CloudWatch 内的/var/log/eb-docker/containers/eb-current-app/stdouterr.log

erl_crash.dump 的主要目的是至少知道您的应用程序何时崩溃,从而关闭容器。 AWS EB 通常会重新启动容器,从而使您对重新启动一无所知。这种理解也可以从其他 docker 相关的日志中获得,你可以配置警报来监听它们,并在你的 docker 必须重启时得到相应的通知。但是将erl_crash.dump 记录到 CloudWatch 的另一个好处是,如果需要,您可以随时将其导出到 S3,下载文件并将其导入到 :observer 中以分析问题所在。

如果在查阅日志后,您仍需要与生产应用程序进行更亲密的交互,那么您需要将remsh 用于您的节点。如果您使用distillery,您将使用您的版本配置您的生产应用程序的cookienode name,如下所示:

rel/confix.exs内,设置cookie:

environment :prod do
  set include_erts: false
  set include_src: false
  set cookie: :"my_cookie"
end

rel/templates/vm.args.eex 下设置变量:

-name <%= node_name %>
-setcookie <%= release.profile.cookie %>

rel/config.exs 中,您可以这样设置发布:

release :my_app do
  set version: "0.1.0"

  set overlays: [
    {:template, "rel/templates/vm.args.eex", "releases/<%= release_version %>/vm.args"}
  ]

  set overlay_vars: [
    node_name: "p@127.0.0.1",
  ]

然后您可以通过首先在包含 docker 容器的 EC2 实例中 ssh-ing 直接连接到在 docker 中运行的生产节点,然后运行以下命令:

CONTAINER_ID=$(sudo docker ps --format '{{.ID}}')
sudo docker exec -it $CONTAINER_ID bash -c "iex --name q@127.0.0.1 --cookie my_cookie"

进入后,您可以尝试四处寻找或if need be,后果自负地为您要检查的模块动态注入修改后的代码。一个简单的方法是在容器内创建一个文件并调用Node.spawn_link target_node, fn Code.eval_file(file_name, path) end

如果您的生产节点已经在运行并且您不知道 cookie,您可以进入正在运行的容器中并执行 ps aux &gt; t.log 并执行 cat t.log 以确定已应用和使用的随机 cookie相应地。

Docker 阻碍了epmd 与其他节点的通信。因此,最好的办法是使用Packer 创建您自己的AWS AMI 映像,然后进行裸机部署。

亚马逊最近向AWS ECSAWS VPC Networking Mode 发布了一项新功能,这可能有助于容器间epmd 通信,从而直接连接到您的节点。我还没试过,我可能错了。

如果您在 AWS 以外的提供商上运行,那么必须弄清楚如何使用 SSM agent 或其他服务轻松访问您的远程日志。

【讨论】:

  • 以上是一般概述,可能缺少迂腐的细节,留给读者作为练习:)
【解决方案3】:

我建议使用某种异常处理工具,到目前为止,我在 Sentry 上的体验非常好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-15
    • 2012-12-31
    • 2010-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-05
    • 2016-10-01
    相关资源
    最近更新 更多