如何在Ubuntu 16.04上使用Distillery和edeliver自动化Elixir-Phoenix部署

2018-09-20 14:51:38 浏览数 (1)

介绍

Elixir构建于Erlang编程语言之上,是一种功能性编程语言,因其专注于开发人员的工作效率以及因为编写高度并发和可伸缩的应用程序而易于使用而闻名。

Phoenix是一个基于Elixir构建的Web框架,允许创建高性能的Web应用程序。

当与两个额外的工具 - Distillery和edeliver结合使用时,您可以完全自动化Phoenix项目从开发环境到生产服务器的部署。

Distillery将Elixir应用程序编译成一个包,然后您可以在其他地方部署。它还生成允许热交换代码的软件包,这意味着您可以在不停机的情况下升级实时应用程序。所有这一切都可以在您配置很少或没有配置的情况下完成,这使得Distillery与许多其他选项区别开来。

edeliver通过处理重复性任务(如构建应用程序,将构建的包传输到服务器,迁移数据库以及启动/更新服务器)来自动执行此构建和部署过程。如果需要,您甚至可以配置edeliver以允许中间分段设置。

在本教程中,您将在本地开发机器和生产服务器上安装Erlang,Elixir和Phoenix 1.3,您将简化两个位置之间的SSH通信,然后您将创建一个示例Phoenix项目来构建和使用edeliver进行部署。最后,您将使用Nginx反向代理和SSL证书保护生产服务器。

要完成本教程,你需要具备以下内容:

  • 一台已经设置好可以使用sudo命令的非root账号的Ubuntu 服务器,并且已开启防火墙。没有服务器的同学可以在这里购买,不过我个人更推荐您使用免费的腾讯云开发者实验室进行试验,学会安装后在购买服务器。
  • SSL证书:如何设置此证书取决于你是否拥有可解析该服务器的域名。
    • 如果你有域名,保护你网站的最简单方法是使用腾讯云SSL证书服务,它提供免费的可信证书。腾讯云SSL证书安装操作指南进行设置。
    • 如果你没有域名,建议您先去这里注册一个域名,如果你只是使用此配置进行测试或个人使用,则可以使用自签名证书,不需要购买域名。自签名证书提供了相同类型的加密,但没有域名验证公告。关于自签名证书,你可以参考为Apache创建自签名SSL证书和如何为Nginx创建自签名SSL证书这两篇文章。

在本教程结束时,您将拥有一个可以执行以下操作的命令:

  • 构建与您的生产环境兼容的Phoenix版本
  • 将发布部署到您的生产环境
  • 在生产环境中启动您的应用程序
  • 通过在没有任何停机时间的情况下部署新版本来热交换当前的生产版本

先决条件

在开始之前,请确保您具有以下内容:

  • 一个基于Ubuntu的本地开发机器。虽然本教程的说明是为基于Ubuntu的本地开发机器编写的,但此部署过程的一个优点是它完全独立于生产环境。
  • 在具有至少1GB RAM的Ubuntu 16.04生产服务器上具有sudo权限的非root用户帐户。
    • 由于我们的目标是自动化部署过程,因此在执行安装教程的第4步时不要输入SSH密码。此外,请确保允许使用命令sudo ufw allow 4000在安装教程的步骤7中访问端口4000。这是我们在本教程中用于测试Phoenix的端口。
  • 在生产服务器上安装Nginx。
  • 完全注册的域名。本教程将用example.com贯穿始终。如果你没有域名,建议您先去这里注册一个域名,您需要将域名解析到您的服务器,您可以使用腾讯云云解析进行快速设置。
  • 为您的服务器设置了以下两个DNS记录。
    • 一个用example.com指向服务器的公共IP地址的A记录。
    • 一个用www.example.com指向服务器的公共IP地址的A记录。
  • 通过在Ubuntu 16.04教程中使用Nginx服务器块进行设置,使用SSL证书保护Nginx。请务必在Nginx安装教程的第4步中选择选项2Redirect,因为这将在我们在本教程中创建的生产服务器上提供自动重定向到HTTPS。

第1步 - 在本地开发机器上安装Elixir和Phoenix

因为Elixir在Erlang VM上运行,所以我们需要先安装VM才能安装Elixir。由于我们要确保使用最新的稳定版Erlang,我们将从Erlang Solutions存储库安装Erlang。

首先,下载并将Erlang Solutions存储库添加到本地开发计算机。

代码语言:javascript复制
cd ~
wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i erlang-solutions_1.0_all.deb

现在,更新您的包列表并安装esl-erlang包,该包提供Erlang编程语言以及有用的工具,库和中间件,统称为Erlang / OTP平台。

代码语言:javascript复制
sudo apt-get update
sudo apt-get install esl-erlang

然后,安装Elixir。

代码语言:javascript复制
sudo apt-get install elixir

接下来,使用Mix - 与Elixir捆绑在一起的构建工具来创建Elixir项目和管理依赖项 - 安装Elixir自己的包管理器Hex,稍后您将使用它来安装Phoenix。

命令的local部分告诉Mix在本地安装hex

代码语言:javascript复制
mix local.hex

提示确认安装时,请输入Y

代码语言:javascript复制
OutputAre you sure you want to install "https://repo.hex.pm/installs/1.5.0/hex-0.17.1.ez"? [Yn] Y
* creating .mix/archives/hex-0.17.1

现在,使用Hex安装Phoenix 1.3.0 Mix存档,这是一个Zip文件,其中包含生成新基础Phoenix项目所需的所有内容。

代码语言:javascript复制
mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new-1.3.0.ez

同样,当提示确认安装时,请输入Y

代码语言:javascript复制
OutputAre you sure you want to install "https://github.com/phoenixframework/archives/raw/master/phx_new-1.3.0.ez"? [Yn] Y
* creating .mix/archives/phx_new-1.3.0

警告:如果您从phx_new.ez存档中安装Phoenix ,您将获得最新版本的Phoenix,它可能与我们在本教程中使用的版本不同 - 1.3.0。然后,您必须将本教程改编为您正在使用的Phoenix版本。

在本地开发机器上安装Elixir和Phoenix后,让我们在生产服务器上安装我们需要的部件。

第2步 - 在生产服务器上安装Elixir和Phoenix

因为我们需要我们的Phoenix项目在本地开发机器和生产服务器上运行,所以我们需要在两个地方安装所有相同的语言和工具。

使用步骤1中的相同命令,下载并将Erlang Solutions存储库添加到生产服务器。

代码语言:javascript复制
cd ~
wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i erlang-solutions_1.0_all.deb

更新包列表并安装esl-erlang包。

代码语言:javascript复制
sudo apt-get update
sudo apt-get install esl-erlang

安装Elixir。

代码语言:javascript复制
sudo apt-get install elixir

使用Mix安装Hex。

代码语言:javascript复制
mix local.hex

提示确认安装时,请输入Y

代码语言:javascript复制
OutputAre you sure you want to install "https://repo.hex.pm/installs/1.5.0/hex-0.17.1.ez"? [Yn] Y
* creating .mix/archives/hex-0.17.1

本地开发机器和生产服务器现在都可以运行Phoenix了,但是通过设置SSH主机别名,可以更轻松地从本地开发机器连接到生产服务器。

步骤3 - 设置SSH主机别名

由于我们的目标是完全自动化的部署过程,因此我们在初始生产服务器设置期间生成了一个SSH密钥对,该密钥对不会提示输入密码。

现在,我们可以使用该命令ssh -i ~/.ssh/private_key_filesammy@example.com从本地开发机器连接到生产服务器。

在这里,我们作为用户sammy连接到example.com。该-i标志告诉SSH去使用位于~/.ssh/private_key_file该连接的私钥文件。

我们可以通过设置一个SSH主机别名来自然知道连接到生产服务器时要使用的私钥,用户和域,从而使这个命令 - 以及部署过程本身 - 更简单。

在本地开发机器上打开~/.ssh/config以进行编辑。

代码语言:javascript复制
nano ~/.ssh/config

并且,按以下行复制。

代码语言:javascript复制
Host example.com 
    HostName example.com
    User sammy
    IdentityFile ~/.ssh/private_key_file

注意:如果您的config文件中已包含某些内容,请添加一个空行,将此新配置与任何现有配置分开。

Host行提供标识此特定配置的别名。为了便于记忆,我们使用了我们的域名。该HostName行告诉SSH要连接的主机。该User行让SSH知道要连接哪个用户,而IdentityFile告诉SSH使用哪个私钥文件。

保存更改并关闭文件。

最后,通过连接到生产服务器来测试配置。

代码语言:javascript复制
ssh example.com

您应该能够在不指定用户,私钥文件或域的情况下建立连接。如果无法连接,请按照屏幕上的消息并回溯前面的步骤以解决问题。

现在我们已经简化了与生产服务器的连接,我们可以创建一个示例Phoenix项目进行部署。

第4步 - 创建测试项目

默认情况下,当您创建一个新的Phoenix项目时,它配置了PostgreSQL数据库适配器和Brunch,一个基于JavaScript的Web应用程序构建工具。为了避免这种额外的复杂性,我们将通过分别传入--no-ecto--no-brunch标志来创建一个名为myproject没有数据库适配器且没有Brunch 的简单Phoenix项目。

切换到您的主目录并创建新项目。

代码语言:javascript复制
cd ~
mix phx.new --no-ecto --no-brunch myproject

输出包括Phoenix作为myproject项目脚手架创建的目录和文件,确认您要安装所需依赖项的提示,以及有关如何启动Phoenix内置服务器的说明。

输入Y提示确认安装时。

代码语言:javascript复制
Output* creating myproject/config/config.exs
* creating myproject/config/dev.exs
* creating myproject/config/prod.exs
...
​
Fetch and install dependencies? [Yn] Y
* running mix deps.get
* running mix deps.compile
​
We are all set! Go into your application by running:
​
    $ cd myproject
​
Start your Phoenix app with:
​
    $ mix phx.server
​
You can also run your app inside IEx (Interactive Elixir) as:
​
    $ iex -S mix phx.server

现在,让我们看看我们的测试项目是否正常工作。

进入myproject目录并运行mix phx.server命令以编译项目并启动服务器。

代码语言:javascript复制
cd ~/myproject
mix phx.server

输出告诉您Phoenix编译的文件的数量和类型,为您提供有关它在此过程中遇到的问题的警告,如果成功,将告诉您到达项目的位置。

第一次在本地开发机器上编译基于Elixir的应用程序时,系统会提示您安装Rebar,这是Mix依赖的Erlang的构建和依赖工具。在提示符下输入Y

代码语言:javascript复制
Output==> file_system
Compiling 6 files (.ex)
Generated file_system app
...
Could not find "rebar3", which is needed to build dependency :ranch
I can install a local copy which is just used by Mix
Shall I install rebar3? (if running non-interactively, use "mix local.rebar --force") [Yn] Y
...
Compiling 11 files (.ex)
Generated myproject app
[info] Running MyprojectWeb.Endpoint with Cowboy using http://0.0.0.0:4000

要测试当前设置,请将Web浏览器指向http:// localhost:4000。您应该会看到默认的欢迎您来到Phoenix的Phoenix Framework主页。如果没有,请确保防火墙允许连接端口4000,然后查看终端输出以获取进一步说明。

一旦您确认一切正常,请按CTRL C两次以停止服务器,以便在步骤5中准备好进一步配置。

现在您已经拥有一个功能齐全的本地Phoenix项目,让我们将其配置为使用Distillery和edeliver。

步骤5 - 配置项目以使用Distillery和edeliver

Phoenix项目存储配置详细信息,例如在config/prod.exs中的项目运行的端口和项目的主机URL,因此我们将首先编辑该文件,告诉Phoenix如何在生产环境中访问项目。

在本地开发计算机上打开config/prod.exs以进行编辑。

代码语言:javascript复制
nano ~/myproject/config/prod.exs

找到以下代码块:

代码语言:javascript复制
...
config :myproject, MyprojectWeb.Endpoint,
  load_from_system_env: true,
  url: [host: "example.com", port: 80],
  cache_static_manifest: "priv/static/cache_manifest.json"
...

load_from_system_env被设置为true,Phoenix应该默认从环境变量PORT获得该项目运行的端口。这称为HTTP端口。

url: [host]url: [port]被用来生成项目中的链接。在设置代理端点在与Phoenix项目不同的端口上公开的代理时,HTTP和URL之间的这种差异特别有用。

为简单起见,我们将在myproject运行的HTTP端口中进行硬编码。这将减少移动部件的数量,这反过来将提高我们的自动部署过程的可靠性。

除了我们将要修改的默认选项之外,我们还将添加两个新选项。

server选项告诉Distillery将项目配置为在启动时引导HTTP服务器,这是我们在完全自动化的部署过程中所需要的。

code_reloader选项告诉项目在项目代码更改时刷新所有连接的Web浏览器。虽然这在开发中可能是一个非常有用的功能,但它不适用于生产环境,因此我们将其关闭。

现在,修改默认配置。

代码语言:javascript复制
...
config :myproject, MyprojectWeb.Endpoint,
  http: [port: 4000],
  url: [host: "example.com", port: 80],
  cache_static_manifest: "priv/static/manifest.json",
  server: true,
  code_reloader: false
...

注意:为避免潜在的配置问题,请在继续之前仔细检查是否已在行cache_static_manifest末尾添加了一个,

完成更改后保存并关闭config/prod.exs

当我们在步骤4中创建myproject项目时,当我们使用edeliver将代码更改推送到构建服务器时,Phoenix会自动生成我们在步骤6中需要的文件.gitignore

默认情况下,该.gitignore文件告诉Git忽略依赖项并构建文件,以便存储库不会变得不必要地大。此外,该文件告诉Git忽略所有Phoenix项目目录中的prod.secret.exs文件,在config中的文件包含非常敏感的信息,如生产数据库密码和用于签署令牌的应用程序机密。

由于myproject项目需要生产服务器上的prod.secret.exs在正常运行而我们无法使用Git将其移动到那里,我们必须手动将其传输到服务器。

在生产服务器上的主目录中,创建一个名为的新目录app_config。这是你要存放prod.secret.exs的地方。

代码语言:javascript复制
cd ~
mkdir app_config

现在,用scp复制prod.secret.exs到生产服务器上的目录app_config

代码语言:javascript复制
scp ~/myproject/config/prod.secret.exs example.com:/home/sammy/app_config/prod.secret.exs

最后,通过列出生产服务器上的app_config内容来验证是否发生了传输。

代码语言:javascript复制
ls ~/app_config

如果未在输出中看到prod.secret.exs,请查看本地开发计算机上的终端以获取其他信息。

通过生产服务器上的prod.secret.exs后,我们已经可以将构建所需的Distillery和部署过程所需的edeliver添加到mix.exs(其为myproject项目的主要配置文件)来进行安装。

在本地开发机器上打开mix.exs

代码语言:javascript复制
nano ~/myproject/mix.exs

现在,找到以下代码块:

mix.exs中的依赖关系:

代码语言:javascript复制
  ...
  defp deps do
    [
      {:phoenix, "~> 1.3.0"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_html, "~> 2.10"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:cowboy, "~> 1.0"}
    ]
  end
  ...

deps是一个私有函数,它明确定义了我们myproject项目的所有依赖项。虽然并非严格要求,但它确实有助于保持项目配置的有序性。

添加edeliverdistillery到依赖性列表。

mix.exs中的依赖关系:

代码语言:javascript复制
  ...
  defp deps do
    [
      {:phoenix, "~> 1.3.0"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_html, "~> 2.10"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:cowboy, "~> 1.0"},
      {:edeliver, "~> 1.4.3"},
      {:distillery, "~> 1.4"}
    ]
  end
  ...

注意:为了避免潜在的配置问题,仔细检检查你是否已经在新的前行的末尾edeliver条目加入了一个,。

保存更改并关闭mix.exs

现在,告诉mix我们获取新的依赖项,以便它们在运行时可用。

代码语言:javascript复制
cd ~/myproject/
mix deps.get

输出告诉我们edeliverdistillery已成功添加到我们的项目中。

代码语言:javascript复制
OutputResolving Hex dependencies...
Dependency resolution completed:
  ...
* Getting edeliver (Hex package)
  Checking package (https://repo.hex.pm/tarballs/edeliver-1.4.4.tar)
  Fetched package
* Getting distillery (Hex package)
  Checking package (https://repo.hex.pm/tarballs/distillery-1.5.2.tar)
  Fetched package

最后,在本地开发机器上重启Phoenix的服务器以测试当前配置。

代码语言:javascript复制
mix phx.server

将浏览器指向http:// localhost:4000。您应该会看到在步骤4中看到的默认Phoenix主页。如果没有,请重新跟踪前面的步骤并查看本地开发机器的终端以获取其他信息。

当您准备好继续时,请按CTRL C两次以停止服务器,以便在下一步中准备好进一步配置。

安装了Distillery和edeliver后,我们就可以配置它们进行部署了。

第6步 - 配置Edeliver和Distillery

Distillery需要一个默认情况下不生成的构建配置文件。但是,我们可以通过运行mix release.init生成默认配置。

进入本地开发计算机上的目录myproject并生成配置文件。

代码语言:javascript复制
cd ~/myproject
mix release.init

输出确认文件已创建,并包含有关如何编辑和构建版本的进一步说明。

代码语言:javascript复制
OutputAn example config file has been placed in rel/config.exs, review it,
make edits as needed/desired, and then run `mix release` to build the release

执行热升级时,edeliver将在目录rel/myproject中查找版本,但Distillery 默认将版本放在目录_build中。因此,让我们修改Distillery的默认配置文件rel/config.exs,将生产版本放在正确的位置。

在编辑器中打开rel/config.exs

代码语言:javascript复制
nano rel/config.exs

找到以下部分:

代码语言:javascript复制
...
environment :prod do
  set include_erts: true
  set include_src: false
  set cookie: :"f3a1[Q^31~]3~N=|T|T=0NvN;h7OHK!%%c.}$)iP9!X|TS[X@sqG=m`yBYVt4/`:"
end
...

该块告诉Distillery我们希望它如何构建自包含的生产发布包。include_erts表示我们是否要捆绑Erlang运行时系统,这在目标系统没有安装Erlang或Elixir时很有用。include_src表示我们是否要包含源代码文件。并且,该cookie值用于验证Erlang节点以便彼此通信。

关闭文件。

我们现在已准备好配置edeliver,但我们必须手动创建其配置文件。

进入本地开发计算机上的目录myproject并创建一个名为的新目录.deliver,然后打开一个新文件.deliver/config进行编辑。

代码语言:javascript复制
cd ~/myproject
mkdir .deliver
nano .deliver/config

在此文件中,我们将指定构建和生产服务器的详细信息。由于我们在构建和生产中使用相同的服务器,因此我们的主机和用户在构建和生产方面是相同的。此外,我们将在app_build目录中执行构建并将已编译的生产文件放在app_release目录中。

将以下内容复制到文件中。

代码语言:javascript复制
APP="myproject"

BUILD_HOST="example.com"
BUILD_USER="sammy"
BUILD_AT="/home/sammy/app_build"

PRODUCTION_HOSTS="example.com" 
PRODUCTION_USER="sammy" 
DELIVER_TO="/home/sammy/app_release" 

接下来,我们将在build文件夹和prod.secret.exs中创建一个符号链接,我们在步骤5中将文件传输到生产服务器上的app_config目录。此符号链接在edeliver挂钩内创建。在构建,阶段和部署过程中的每个点上,edeliver都会调用特定的挂钩。对于我们的自动部署设置,我们正在侦听在edeliver获取依赖关系并开始编译之前调用的钩子pre_erlang_get_and_update_deps

附加以下内容到.deliver/config

代码语言:javascript复制
pre_erlang_get_and_update_deps() {
  local _prod_secret_path="/home/sammy/app_config/prod.secret.exs"
  if [ "$TARGET_MIX_ENV" = "prod" ]; then
    __sync_remote "
      ln -sfn '$_prod_secret_path' '$BUILD_AT/config/prod.secret.exs'
    "
  fi
}

完成编辑后保存并关闭文件。

因为edeliver使用Git将代码从最新提交推送到构建服务器以进行进一步操作,所以部署之前的最后一步是为我们的项目创建一个Git存储库。

在本地开发计算机上的目录myproject中,使用该git init命令创建一个空的Git存储库。

代码语言:javascript复制
cd ~/myproject
git init

在我们将文件添加到Git索引之前,我们还需要将包含我们的发布tar包的目录添加到.gitignore文件中。否则,在几次发布后,Git存储库的大小会变得非常大。

代码语言:javascript复制
echo ".deliver/releases/" >> .gitignore

接下来,将myproject项目中的完整文件集添加到Git临时区域,以便它们将包含在下一次提交中。

代码语言:javascript复制
git add .

现在,设置Git应该与此存储库关联的标识。这将帮助您跟踪项目的更改来自何处。

代码语言:javascript复制
git config user.email "you@example.com"
git config user.name "Your Name"

最后,使用-m选项描述提交原因,将文件提交到存储库。

代码语言:javascript复制
git commit -m "Setting up automated deployment"

输出重复提交您的提交消息,然后报告更改的文件数,插入的行数以及添加到存储库的文件的名称。

代码语言:javascript复制
Output[master (root-commit) e58b766] Setting up automated deployment
 39 files changed, 2344 insertions( )
 create mode 100644 .deliver/config
...

我们的项目现在致力于Git和Distillery以及edeliver的完全配置,我们已准备好进行首次部署。

第7步 - 部署项目

此部署过程的一个好处是,您几乎可以在本地开发计算机上执行所有操作,很少需要触摸生产服务器。

现在让我的myproject项目通过将项目推送到生产服务器来解决所有问题。

首先,在本地开发机器上使用mix以构建项目版本并使用edeliver将其传输到构建服务器。

代码语言:javascript复制
cd ~/myproject
mix edeliver build release

输出实时更新构建过程的每个步骤,如果一切按预期工作,则告诉您构建成功。

代码语言:javascript复制
OutputBUILDING RELEASE OF MYPROJECT APP ON BUILD HOST

-----> Authorizing hosts
-----> Ensuring hosts are ready to accept git pushes
-----> Pushing new commits with git to: sammy@example.com
-----> Resetting remote hosts to fc86f878d96...
-----> Cleaning generated files from last build
-----> Fetching / Updating dependencies
-----> Compiling sources
-----> Generating release
-----> Copying release 0.0.1 to local release store
-----> Copying myproject.tar.gz to release store

RELEASE BUILD OF MYPROJECT WAS SUCCESSFUL!

如果您的构建不成功,edeliver将指示它遇到问题时尝试执行的代码行。您可以使用该信息来解决问题。

构建完成后,将版本传输到生产服务器。

代码语言:javascript复制
mix edeliver deploy release to production

输出再次实时更新了流程的每个步骤,如果一切正常,则告诉您构建已发布到生产中。

代码语言:javascript复制
OutputDEPLOYING RELEASE OF MYPROJECT APP TO PRODUCTION HOSTS

-----> Authorizing hosts
-----> Uploading archive of release 0.0.1 from local release store
-----> Extracting archive myproject.0.1.tar.gz

DEPLOYED RELEASE TO PRODUCTION!

如果遇到问题部署,请检查终端中的输出以获取其他信息。

最后,在生产服务器上启动项目myproject

代码语言:javascript复制
mix edeliver start production

输出告诉用户项目正在运行,正在运行的主机以及它在生产服务器上使用的发行版的路径。回应将是START DONE!

代码语言:javascript复制
OutputEDELIVER MYPROJECT WITH START COMMAND

-----> starting production servers

production node:

  user    : sammy
  host    : example.com
  path    : /home/sammy/app_release
  response:

START DONE!

通过将浏览器指向http://example.com:4000来测试部署过程。您应该再次看到默认的Phoenix Framework主页。如果不这样做,请仔细检查生产服务器上的端口4000是否已打开,然后查阅本地开发计算机的终端以获取其他信息。

既然我们已经验证了完整的构建和部署过程,那么让我们的设置更进一步,即在生产服务器上执行代码更新而不会出现任何停机。

第8步 - 在没有生产停机的情况下升级项目

我们的构建和部署过程的一个特性是能够热交换代码,在生产服务器上更新项目而不会出现任何停机。让我们对项目进行一些更改来试试这个。

打开项目的主页文件进行编辑。

代码语言:javascript复制
nano ~/myproject/lib/myproject_web/templates/page/index.html.eex

找到以下行:

代码语言:javascript复制
...
<h2><%= gettext "Welcome to %{name}", name: "Phoenix!" %></h2>
...

现在,用以下内容替换该行:

代码语言:javascript复制
<h2>Hello, World!</h2>

保存并关闭文件。

现在我们已经更新了代码库,我们还需要增加应用程序版本。如有必要,版本号可以更轻松地跟踪版本并回滚到以前的版本。

在本地开发机器上打开mix.exs

代码语言:javascript复制
nano ~/myproject/mix.exs

找到以下块:

代码语言:javascript复制
  ...
  def project do
    [app: :myproject,
     version: "0.0.1",
     elixir: "~> 1.2",
     elixirc_paths: elixirc_paths(Mix.env),
     compilers: [:phoenix, :gettext]    Mix.compilers,
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end
  ...

将版本0.0.1增加到0.0.2

mix.exs

代码语言:javascript复制
  ...
  def project do
    [app: :myproject,
     version: "0.0.2",
     elixir: "~> 1.2",
     elixirc_paths: elixirc_paths(Mix.env),
     compilers: [:phoenix, :gettext]    Mix.compilers,
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end
  ...

然后,保存并关闭该文件。

现在我们需要添加并提交我们对Git的更改,以便edeliver知道它应该将它们推送到构建服务器。

代码语言:javascript复制
git add .
git commit -m "Changed welcome message"

最后,我们准备热交换我们的变化。这一次,我们有一个命令,相当于我们在步骤7中使用的三个相关命令。

使用一个命令,在生产服务器上构建,部署和重新启动应用程序。

代码语言:javascript复制
mix edeliver upgrade production

再一次,输出实时地引导我们完成整个过程的每一步,如果成功,则结束于,UPGRADE DONE!

代码语言:javascript复制
OutputEDELIVER MYPROJECT WITH UPGRADE COMMAND

-----> Upgrading to revision 2fc28b6 from branch master
-----> Detecting release versions on production hosts
-----> Deploying upgrades to 1 online hosts
-----> Checking whether installed version 0.0.1 is in release store
-----> Building the upgrade from version 0.0.1
-----> Authorizing hosts
-----> Validating * version 0.0.1 is in local release store
-----> Ensuring hosts are ready to accept git pushes
-----> Pushing new commits with git to: sammy@example.com
-----> Resetting remote hosts to 2fc28b6...
-----> Cleaning generated files from last build
-----> Checking out 2fc28b6...
-----> Fetching / Updating dependencies
-----> Compiling sources
-----> Checking version of new release
-----> Uploading archive of release 0.0.1 from local release store
-----> Extracting archive myproject_0.0.1.tar.gz
-----> Generating release
-----> Removing built release 0.0.1 from remote release directory
-----> Copying release 0.0.2 to local release store
-----> Copying myproject.tar.gz to release store
-----> Upgrading production hosts to version 0.0.2
-----> Authorizing hosts
-----> Uploading archive of release 0.0.2 from local release store
-----> Upgrading release to 0.0.2

UPGRADE DONE!

要验证一切正常,请在浏览器中重新加载http://example.com:4000。您应该看到新消息。如果不这样做,请重新跟踪前面的步骤并检查终端是否有其他错误和警告消息。

部署过程现在已经简化为一个命令,我们也使用了Erlang最着名的功能之一 热交换代码。最后,让我们通过将其置于Nginx代理之后来强化我们的生产应用程序。

步骤9 - 在生产服务器上设置反向代理

虽然我们可以直接将我们的应用程序暴露给Internet,但反向代理将提供更好的安全性。为了便于配置,支持SSL,以及设置自定义HTTP响应头的功能,我们将使用Nginx作为代理。

如果您在先决条件中设置了使用Nginx服务器块加密,您应该已经在生产服务器上为我们的项目创建了一个单独的Nginx服务器块。

打开该服务器块的配置文件以进行编辑。

代码语言:javascript复制
sudo nano /etc/nginx/sites-available/example.com

首先,我们需要告诉Nginx我们的Phoenix项目所在的位置以及它监听的端口。由于我们在4000本地端口服务我们的项目,我们告诉Nginx我们的代理端点在127.0.0.1:4000

将以下代码复制到默认服务器配置块上方的配置文件中。

代码语言:javascript复制
upstream phoenix {
    server 127.0.0.1:4000;
}

现在,在同一个文件中,找到以下代码块:

代码语言:javascript复制
    ...
        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }
    ...

为了使代理工作,我们需要告诉Nginx将所有与Web服务器的连接重定向到我们的Phoenix项目,包括请求头,客户端代理的服务器的IP地址以及客户端的IP地址本身。

我们还将配置Nginx以通过WebSockets转发传入请求,WebSockets是Web服务器和客户端之间的消息传递协议,用于将标准无状态HTTP连接升级为持久性HTTP连接。

Phoenix有一个名为Channels的功能,我们在本教程中没有探讨过,但Channels需要支持WebSockets。如果没有此配置,Channels将无法工作,因为WebSocket请求不会进入服务器。

用以下内容替换上一个块location

代码语言:javascript复制
  location / {
    allow all;

    # Proxy Headers
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Cluster-Client-Ip $remote_addr;

    # WebSockets
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_pass http://phoenix;
  }

保存并关闭该文件以继续。

现在,验证新的Nginx配置。

代码语言:javascript复制
sudo nginx -t

Nginx应该报告语法正常并且测试成功。如果没有,请按照屏幕上的消息解决问题。

重新启动Nginx以传播更改。

代码语言:javascript复制
sudo systemctl restart nginx

最后,出于安全考虑,禁止通过端口4000上的HTTP访问您的应用程序。

代码语言:javascript复制
sudo ufw delete allow 4000

然后,检查UFW的状态。

代码语言:javascript复制
sudo ufw status

防火墙此时应仅允许SSH和Nginx访问。

代码语言:javascript复制
OutputStatus: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)

最后,通过指向浏览器https://example.com测试一切正常。

您现在拥有完全自动化的构建和部署过程,并且生产服务器通过反向代理和SSL证书进行保护。

结论

即使我们设置了edeliver来构建和部署我们的Phoenix项目到一个命令的生产服务器,你仍然可以做更多的事情。

大多数生产Phoenix应用程序使用数据库。

如果您的生产基础架构由Phoenix节点集群组成,则可以使用edeliver一次部署到所有节点并在所有节点上执行热交换。

或者,如果您希望设置具有更高的可靠性,则可以创建完整的分段基础结构并使用edeliver来管理分段和部署过程。

想要了解更多关于Elixir-Phoenix部署的相关教程,请前往腾讯云 社区学习更多知识。


参考文献:《How To Automate Elixir-Phoenix Deployment with Distillery and edeliver on Ubuntu 16.04》

0 人点赞