Elixir实战:13 运行系统 (2) OTP 发布
zhezhongyun 2025-01-05 00:37 78 浏览
OTP 发行版是一个独立的、编译的、可运行的系统,包含系统所需的最小 OTP 应用集。OTP 发行版可以选择性地包含最小的 Erlang 运行时二进制文件,这使得该发行版完全自给自足。发行版不包含工件,例如源代码、文档文件或测试。
这种方法提供了各种好处。首先,您可以在开发机器或构建服务器上构建系统,并仅交付二进制工件。主机机器不需要安装任何工具。如果您将最小的 Erlang 运行时嵌入到发布中,您甚至不需要在生产服务器上安装 Elixir 和 Erlang。运行系统所需的所有内容都将成为您的发布包的一部分。此外,发布简化了一些操作任务,例如连接到正在运行的系统并在系统上下文中执行自定义 Elixir 代码。最后,发布为系统的系统在线升级(和降级)铺平了道路,这在 Erlang 中称为发布处理。
13.2.1 构建发布
要构建一个发布版本,您需要编译您的主 OTP 应用程序及其所有依赖项。然后,您需要将所有二进制文件与 Erlang 运行时一起包含在发布版本中。这可以通过 mix release 命令完成(https://hexdocs.pm/mix/Mix.Tasks.Release.xhtml)。
让我们看看它的实际效果。前往待办事项文件夹,并运行发布命令:
$ mix release
* assembling todo-0.1.0 on MIX_ENV=dev
* using config/runtime.exs to configure the release at runtime
Release created at _build/dev/rel/todo
...
这将在开发 Mix 环境中构建发布。由于 release 旨在运行在生产环境中,您通常希望在生产环境中构建它。您可以通过在命令前加上 MIX_ENV=prod 来实现。或者,您可以在 mix.exs 中强制为 release 任务设置默认环境。
清单 13.1 强制执行 release 任务的生产环境 (todo_release/mix.exs)
defmodule Todo.MixProject do
...
def cli do
[
preferred_envs: [release: :prod]
]
end
...
end
cli 函数可用于为不同的 Mix 任务配置默认的 Mix 环境。该函数必须返回一个包含支持选项的关键字列表。 :preferred_envs 选项是一个关键字列表,其中每个键是任务名称(以原子形式提供),值是该任务所需的默认环境。
通过此更改,您可以调用 mix release ,这将编译您的项目在生产环境中,然后生成发布版本:
$ mix release
* assembling todo-0.1.0 on MIX_ENV=prod
...
在 mix release 完成后,发布将位于_build/prod/rel/todo/子文件夹中。我们稍后会讨论发布的内容,但首先,让我们看看您如何使用它。
13.2.2 使用发布
与发布交互的主要工具是位于 _build/prod/rel/todo/bin/todo 的 shell 脚本。您可以使用它执行各种任务,例如:
- 启动系统并在前台运行 iex shell。
- 将系统作为后台进程启动。
- 停止运行系统。
- 将远程 shell 附加到正在运行的系统。
验证发布是否有效的最简单方法是将系统与 iex shell 一起在前台启动:
$ RELEASE_NODE="todo@localhost" _build/prod/rel/todo/bin/todo start_iex
Starting database worker.
Starting database worker.
Starting database worker.
Starting to-do cache.
iex(todo@localhost)1>
在这里, RELEASE_NODE 操作系统环境变量被设置为所需的节点名称。如果没有它,Elixir 将根据主机名选择一个默认值。为了使示例在不同的机器上工作,选择了使用 localhost 作为主机部分的硬编码值。请注意,这是一个短节点名称。如果您想使用长名称,还需要将 RELEASE_DISTRIBUTION 操作系统环境变量设置为值 name 。有关如何配置发布的更多详细信息,请参阅 mix release 文档。
该版本不再依赖于您系统的 Erlang 和 Elixir。它是完全独立的;您可以将_build/prod/rel/todo 子文件夹的内容复制到另一台未安装 Elixir 和 Erlang 的机器上,它仍然可以正常工作。当然,由于该版本包含 Erlang 运行时二进制文件,目标机器必须使用相同的操作系统和架构。
要将系统作为后台进程启动,您可以使用 daemon 命令:
$ RELEASE_NODE="todo@localhost" _build/prod/rel/todo/bin/todo daemon
这与之前提到的分离进程不同。相反,系统是通过 run_erl 工具启动的 (https://erlang.org/doc/man/run_erl.xhtml)。该工具将标准输出重定向到位于 _build/prod/rel/todo/tmp/log 文件夹中的日志文件,这使您能够分析系统的控制台输出。
一旦系统在后台运行,您可以启动到节点的远程 shell:
$ RELEASE_NODE="todo@localhost" _build/prod/rel/todo/bin/todo remote
iex(todo@localhost)1>
此时,您在生产节点的上下文中运行了一个 iex shell 会话。按两次 Ctrl-C 退出 shell 会停止远程 shell,但 todo 节点仍将继续运行。
如果系统作为后台进程运行,并且您想停止它,可以使用 stop 命令:
$ RELEASE_NODE="todo@localhost" _build/prod/rel/todo/bin/todo stop
也可以直接附加到正在运行的进程的外壳上。附加提供了一个重要的好处:它捕获了正在运行的节点的标准输出。无论正在运行的节点打印什么——例如,通过 IO.puts ——都可以在附加的进程中看到(而远程外壳则不是这样)。
让我们看看它的实际操作。首先,我们将在后台启动发布,同时运行 iex 。这可以通过 daemon_iex 命令完成:
$ RELEASE_NODE="todo@localhost" _build/prod/rel/todo/bin/todo daemon_iex
现在,我们可以使用 to_erl 工具附加到 shell:
$ _build/prod/rel/todo/erts-13.0/bin/to_erl _build/prod/rel/todo/tmp/pipe/
iex(todo@localhost)1>
[memory_usage: 70117728, process_count: 230] ?
捕获控制台的标准输出
在第 10 章中,您添加了一个定期打印内存使用情况和进程计数到标准输出的作业。当您连接到 shell 时,该作业的输出是可见的。相反,当运行远程 shell 时,这个输出将不可见。
在附加到 shell 时要小心。与远程 shell 不同,附加的 shell 在运行节点的上下文中运行。您只是通过操作系统管道附加到运行节点。因此,您一次只能有一个附加会话。此外,您可能会通过按 Ctrl-\ 意外停止运行节点。您应该按 Ctrl-D 从运行节点中分离,而不停止它。
todo 脚本可以执行各种其他命令。要获取帮助,只需调用 _build/prod/rel/todo/bin/todo 而不带任何参数。这将把帮助信息打印到标准输出。最后,关于构建发布的更多细节,请查看官方 Mix 文档,网址为 https://hexdocs.pm/mix/Mix.Tasks.Release.xhtml。
13.2.3 发布内容
让我们花一些时间讨论一下您发布的结构。一个完全独立的发布由以下部分组成:
- 编译的 OTP 应用程序是运行您的系统所需的
- 一个包含将传递给虚拟机的参数的文件
- 一个描述需要启动哪些 OTP 应用程序的启动脚本
- 一个包含 OTP 应用程序环境变量的配置文件
- 一个帮助的 shell 脚本,用于启动、停止和与系统交互
- Erlang 运行时二进制文件
在这种情况下,所有这些都位于 _build/prod/rel/todo 文件夹中的某个地方。让我们仔细看看发布的一些重要部分。
编译后的二进制文件
所有必需应用程序的编译版本位于 _build/prod/rel/todo/lib 文件夹中:
$ ls -1 _build/prod/rel/todo/lib
asn1-5.1
compiler-8.3
cowboy-2.10.0
cowboy_telemetry-0.4.0
cowlib-2.12.1
crypto-5.2
eex-1.15.0
elixir-1.15.0
iex-1.15.0
kernel-9.0
logger-1.15.0
mime-2.0.3
plug-1.14.2
plug_cowboy-2.6.1
plug_crypto-1.2.5
poolboy-1.5.2
public_key-1.14
ranch-1.8.0
runtime_tools-2.0
sasl-4.2.1
ssl-11.0
stdlib-5.0
telemetry-1.2.1
todo-0.1.0
此列表包括您所有的运行时依赖项,包括直接依赖项(在 mix.exs 中指定)和间接依赖项(依赖项的依赖项)。此外,一些 OTP 应用程序,如 kernel 、 stdlib 和 elixir ,会自动包含在发布中。这些是任何基于 Elixir 的系统所需的核心 OTP 应用程序。最后, iex 应用程序也被包含在内,这使得可以运行远程 iex shell。
在这些文件夹中,有一个 ebin 子文件夹,编译后的二进制文件与.app 文件一起存放。每个 OTP 应用程序文件夹还可能包含 priv 文件夹,里面有额外的特定于应用程序的文件。
提示 如果您需要在发布中包含其他文件,最好的方法是在项目根目录下创建一个 priv 文件夹。该文件夹如果存在,将自动出现在应用程序文件夹下的发布中。当您需要访问 priv 文件夹中的文件时,可以调用 Application.app_ dir(:an_app_name, "priv") 来查找该文件夹的绝对路径。
将所有所需的 OTP 应用程序捆绑在一起使得发布独立。因为系统包含所有所需的二进制文件(包括 Elixir 和 Erlang 标准库),在目标主机上不需要其他任何东西。
您可以通过查看负载路径来证明这一点:
$ RELEASE_NODE="todo@localhost" _build/prod/rel/todo/bin/todo start_iex
iex(todo@localhost)1> :code.get_path() ?
[~c"ch13/todo_release/_build/prod/rel/todo/lib/../releases/0.1.0/consolidated",
~c"ch13/todo_release/_build/prod/rel/todo/lib/kernel-9.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/stdlib-5.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/compiler-8.3/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/elixir-1.15.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/sasl-4.2.1/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/logger-1.15.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/crypto-5.2/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/cowlib-2.12.1/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/asn1-5.1/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/public_key-1.14/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/ssl-11.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/ranch-1.8.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/cowboy-2.10.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/telemetry-1.2.1/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/cowboy_telemetry-0.4.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/eex-1.15.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/mime-2.0.3/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/plug_crypto-1.2.5/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/plug-1.14.2/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/plug_cowboy-2.6.1/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/poolboy-1.5.2/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/runtime_tools-2.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/todo-0.1.0/ebin",
~c"ch13/todo_release/_build/prod/rel/todo/lib/iex-1.15.0/ebin"]
? 检索负载路径列表
注意所有加载路径都指向发布文件夹。相比之下,当你启动一个普通的 iex -S mix shell 并运行 :code.get_path/0 时,你会看到一个更长的加载路径列表,其中一些指向构建文件夹,其他指向系统的 Elixir 和 Erlang 安装路径。这应该让你相信你的发布是自包含的。运行时只会在发布文件夹中查找模块。
此外,最小的 Erlang 二进制文件已包含在发布中。它们位于_build/prod/rel/todo/erts-X.Y,其中 X.Y 对应于运行时版本号(与 Erlang 版本号无关)。Erlang 运行时的包含使得该发布完全独立。此外,它允许您在同一台机器上运行多个由不同 Elixir 或 Erlang 版本驱动的系统。
配置
配置文件位于 _build/prod/rel/todo/releases/0.1.0 文件夹中,0.1.0 对应于您的 todo 应用程序的版本(如 mix.exs 中所提供)。此文件夹中最相关的两个文件是 vm.args 和 env.sh。
vm.args 文件可用于向 Erlang 运行时提供标志,例如 +P 标志,它设置最大运行进程数。env.sh 文件可用于设置环境变量,例如前面提到的 RELEASE_NODE 和 RELEASE_DISTRIBUTION 。有关如何提供您自己版本的这些文件的更多详细信息,请参见 https://hexdocs.pm/mix/Mix.Tasks.Release.xhtml#module-vm-args-and-env-sh-env-bat。
13.2.4 在 Docker 容器中打包
在生产中运行系统有多种方式。您可以将其部署到平台即服务(PaaS),例如 Heroku、Fly.io 或 Gigalixir,或者您可以在 Kubernetes 集群中运行它。另一个选项是通过服务管理器(如 systemd)将系统作为服务运行。
无论您选择哪种部署策略,都应努力将系统作为 OTP 发布运行。在大多数情况下,这意味着在前台启动发布。因此,有效的启动命令是 start_iex 或 start 。
前面的命令还会启动 iex 会话。这允许您附加到正在运行的 BEAM 节点的 iex shell,并在捕获节点的标准输出的同时与生产系统进行交互。另一方面,这种方法是有风险的,因为您可能会意外地停止节点(通过按两次 Ctrl-C)。
相反, start 命令将以前台模式启动系统,但不带 iex 会话。因此,您将无法附加到主 iex shell。您仍然可以通过建立远程 iex shell 会话与运行中的系统进行交互,但在这种情况下,节点的标准输出不会被捕获。
具体的部署步骤取决于所选择的策略。可供选择的选项太多,无法一一覆盖。关于一些流行选择的良好基本介绍可以在 Phoenix web 框架的部署指南中找到(https://hexdocs.pm/phoenix/deployment.xhtml)。
作为一个小例子,让我们看看如何在 Docker 容器中运行待办事项系统。Docker 是许多团队选择的热门选项,因为它有助于自动化部署,支持在本地运行类似生产的版本,并为各种部署选项铺平道路,特别是在云计算领域。本部分假设您对 Docker 有一定的了解。如果不是这样,您可以查看官方的入门指南,网址是 https://docs.docker.com/get-started/。
Elixir 项目的 Docker 镜像通常分为两个阶段构建。在第一个阶段,通常称为构建阶段,您需要编译代码并组装 OTP 发布。然后,在第二个阶段,您将发布复制到最终镜像中,该镜像将部署到目标主机。最终镜像不包含构建工具,例如 Erlang 和 Elixir。这些工具不需要,因为 OTP 发布本身包含所需的最小 Erlang 和 Elixir 二进制文件集。
要构建 Docker 镜像,我们需要在项目根目录中创建一个名为 Dockerfile 的文件。以下列表展示了第一个构建阶段,该阶段生成 OTP 版本。
清单 13.2 构建阶段 (todo_release/Dockerfile)
ARG ELIXIR="1.15.4" ?
ARG ERLANG="26.0.2" ?
ARG DEBIAN="bookworm-20230612-slim" ?
ARG OS="debian-${DEBIAN}" ?
FROM "hexpm/elixir:${ELIXIR}-erlang-${ERLANG}-${OS}" as builder ?
WORKDIR /todo
ENV MIX_ENV="prod" ?
RUN mix local.hex --force && mix local.rebar --force ?
COPY mix.exs mix.lock ./ ?
COPY config config ?
COPY lib lib ?
RUN mix deps.get --only prod ?
RUN mix release ?
...
基础镜像
? 默认使用生产混合环境
? 安装构建工具
? 复制所需的源文件
获取生产依赖
构建发布
本示例中使用的基础 Docker 镜像由 Hex 包管理器团队维护(https://hub.docker.com/r/hexpm/elixir)。
值得注意的是,为了简洁起见,这个 Docker 文件过于简单,因为它没有利用 Docker 层缓存。因此,任何源文件的更改都将需要对整个项目进行重新编译,包括所有依赖项。要获取更精细的构建镜像的方法,请查看由 Phoenix Web 框架生成的 Dockerfile(https://hexdocs.pm/phoenix/releases.xhtml#containers)。
接下来,让我们开始构建最终图像。
清单 13.3 构建最终镜像 (todo_release/Dockerfile)
ARG DEBIAN="bookworm-20230612-slim"
...
FROM debian:${DEBIAN} ?
WORKDIR "/todo"
RUN apt-get update -y && apt-get install -y openssl locales
COPY \ ?
--from=builder \ ?
--chown=nobody:root \ ?
/todo/_build/prod/rel/todo ./ ?
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
ENV LANG="en_US.UTF-8"
ENV LANGUAGE="en_US:en"
ENV LC_ALL="en_US.UTF-8"
CMD ["/todo/bin/todo", "start_iex"] ?
基础镜像
复制构建的发布版本
? 定义启动命令
首先要注意的是,基础镜像是 Debian,而不是 Elixir 或 Erlang。使用与构建镜像相同的基础操作系统非常重要。否则,您可能会因不兼容而遇到崩溃。
要构建最终镜像,您需要从构建阶段复制 OTP 版本,配置区域设置,并定义默认启动命令。在此示例中,选择了 start_iex 命令,这使得可以附加到正在运行的 shell。
此时,您可以构建映像:
$ docker build . -t elixir-in-action/todo
接下来,您可以启动容器:
$ docker run \
--rm -it \
--name todo_system \
-p "5454:5454" \ ?
elixir-in-action/todo
? 将 http 端口发布到主机
您现在可以在本地与系统进行交互:
$ curl -d "" \
"http://localhost:5454/add_entry?list=bob&date=2023-12-19&title=Dentist"
OK
$ curl "http://localhost:5454/entries?list=bob&date=2023-12-19"
2023-12-19 Dentist
与构建阶段一样,生产镜像过于简单。特别是,它不支持通过分布式 Erlang 进行集群,也不支持建立远程 shell(通过 --remsh 开关)。这可以通过一些工作来解决,但为了简洁起见,这里不讨论。如果您想从多个容器建立 Erlang 集群,特别是当它们在 Kubernetes 集群中运行时,请查看 libcluster 库(https://hexdocs.pm/libcluster/)。
这结束了发布的话题。一旦您的系统启动并运行,了解如何分析其行为是很有用的。
相关推荐
- 激光手术矫正视力对眼睛到底有没有伤害?
-
因为大家询问到很多关于“基质不能完全愈合”的问题,有必要在这里再详细解释一下。谢谢@珍惜年少时光提出的疑问:因为手头刚好在看组织学,其中提到:”角膜基质约占角膜的全厚度的90%,主要成分是胶原板层,...
- OneCode核心概念解析——View(视图)
-
什么是视图?在前面的章节中介绍过,Page相关的概念,Page是用户交互的入口,具有Url唯一性。但Page还只是一个抽象的容器,而View则是一个具备了具体业务能力的特殊的Page,它可以是一个...
- 精品博文图文详解Xilinx ISE14.7 安装教程
-
在软件安装之前,得准备好软件安装包,可从Xilinx官网上下载:http://china.xilinx.com/support/download/index.html/content/xilinx/z...
- 卡片项目管理(Web)(卡片设计的流程)
-
简洁的HTML文档卡片管理,简单框架个人本地离线使用。将个人工具类的文档整理使用。优化方向:添加图片、瀑布式布局、颜色修改、毛玻璃效果等。<!DOCTYPEhtml><html...
- GolangWeb框架Iris项目实战-JWT和中间件(Middleware)的使用EP07
-
前文再续,上一回我们完成了用户的登录逻辑,将之前用户管理模块中添加的用户账号进行账号和密码的校验,过程中使用图形验证码强制进行人机交互,防止账号的密码被暴力破解。本回我们需要为登录成功的用户生成Tok...
- sitemap 网站地图是什么格式?有什么好处?
-
sitemap网站地图方便搜索引擎发现和爬取网页站点地图是一种xml文件,或者是txt,是将网站的所有网址列在这个文件中,为了方便搜索引擎发现并收录的。sitemap网站地图分两种:用于用户导...
- 如何在HarmonyOS NEXT中处理页面间的数据传递?
-
大家好,前两天的Mate70的发布,让人热血沸腾啊,不想错过,自学的小伙伴一起啊,今天分享的学习笔记是关于页面间数据伟递的问题,在HarmonyOSNEXT5.0中,页面间的数据传递可以有很多种...
- 从 Element UI 源码的构建流程来看前端 UI 库设计
-
作者:前端森林转发链接:https://mp.weixin.qq.com/s/ziDMLDJcvx07aM6xoEyWHQ引言由于业务需要,近期团队要搞一套自己的UI组件库,框架方面还是Vue。而业界...
- jq+ajax+bootstrap改了一个动态分页的表格
-
最近在维护一个很古老的项目,里面是用jq的dataTable方法实现一个分页的表格,不过这些表格的分页是本地分页。现在想要的是点击分页去请求数据。经过多次的修改,以失败告终。分页的不准确,还会有这个错...
- 学习ES6- 入门Vue(大量源代码及笔记,带你起飞)
-
ES6学习网站:https://es6.ruanyifeng.com/箭头函数普通函数//普通函数this指向调用时所在的对象(可变)letfn=functionfn(a,b){...
- 青锋微服务架构之-Ant Design Pro 基本配置
-
青锋(msxy)-Gitee.com1、更换AntDesignPro的logo和名称需要修改文件所在位置:/config/defaultSetting.jsconstproSett...
- 大数据调度服务监控平台(大数据调度服务监控平台官网)
-
简介SmartKettle是针对上述企业的痛点,对kettle的使用做了一些包装、优化,使其在web端也能具备基础的kettle作业、转换的配置、调度、监控,能在很大一定程度上协助企业完成不同...
- Flask博客实战 - 实现博客首页视图及样式
-
本套教程是一个Flask实战类教程,html/css/javascript等相关技术栈不会过多的去详细解释,那么就需要各位初学者尽可能的先去掌握这些基础知识,当然本套教程不需要你对其非常精通,但最起码...
- Web自动化测试:模拟鼠标操作(ActionChains)
-
在日常的测试中,经常会遇到需要鼠标去操作的一些事情,比如说悬浮菜单、拖动验证码等,这一节我们来学习如何使用webdriver模拟鼠标的操作首页模拟鼠标的操作要首先引入ActionChains的包fro...
- DCS F-16C 中文指南 16.9ILS仪表降落系统教程
-
10–ILS教程我们的ILS(仪表着陆进近)将到达Batumi巴统机场。ILS频率:110.30跑道航向:120磁航向/126真航向无线电塔频率:131.0001.设置雷达高度表开关打开(前)并...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML常用标签 (29)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- HTML button formtarget 属性 (30)
- CSS 水平对齐 (Horizontal Align) (30)