打包应用程序以供部署

一旦应用程序构建用于生产,它仍然需要打包才能部署到服务器上。打包 Swift 应用程序以进行部署有多种策略。

Docker

如今,使用容器技术(如 Docker)打包应用程序是最流行的方法之一。

使用 Docker 的工具,我们可以将应用程序构建并打包为 Docker 镜像,将其发布到 Docker 仓库,然后直接在服务器上或支持 Docker 部署的平台(如 Kubernetes)上启动它。包括 AWS、GCP、Azure、IBM 在内的许多公共云提供商都鼓励这种部署方式。

下面是一个在 CentOS 上构建和打包应用程序的 Dockerfile 示例:

#------- build -------
FROM swift:centos8 as builder

# 设置工作区
RUN mkdir /workspace
WORKDIR /workspace

# 将源代码复制到 docker 镜像
COPY . /workspace

RUN swift build -c release --static-swift-stdlib

#------- package -------
FROM centos
# 复制可执行文件
COPY --from=builder /workspace/.build/release/<executable-name> /

# 设置入口点(应用程序名称)
CMD ["<executable-name>"]

要使用 Dockerfile 从应用程序的源位置创建本地 Docker 镜像,请使用 docker build 命令,例如:

$ docker build . -t <my-app>:<my-app-version>

要测试本地镜像,请使用 docker run 命令,例如:

$ docker run <my-app>:<my-app-version>

最后,使用 docker push 命令将应用程序的 Docker 镜像发布到您选择的 Docker 仓库,例如:

$ docker tag <my-app>:<my-app-version> <docker-hub-user>/<my-app>:<my-app-version>
$ docker push <docker-hub-user>/<my-app>:<my-app-version>

此时,应用程序的 Docker 镜像已准备好部署到运行 docker 的服务器主机,或支持 Docker 部署的平台。

有关 Docker 的更多完整信息,请参阅 Docker 的文档

Distroless

Distroless 是 Google 的一个项目,旨在创建仅包含应用程序及其运行时依赖项的最小镜像。它们不包含包管理器、shell 或任何其他您期望在标准 Linux 发行版中找到的程序。

由于 distroless 支持 Docker 并基于 Debian,将 Swift 应用程序打包到其中的过程与上述 Docker 过程十分相似。下面是一个在 distroless 的 C++ 基础镜像上构建和打包应用程序的 Dockerfile 示例:

#------- build -------
# 使用 Ubuntu Bionic 构建,因为它与 Debian 运行时兼容
FROM swift:bionic as builder

# 设置工作区
RUN mkdir /workspace
WORKDIR /workspace

# 将源代码复制到 docker 镜像
COPY . /workspace

RUN swift build -c release --static-swift-stdlib

#------- package -------
# 在 distroless C++ 上运行,因为它包含
# Swift 程序需要的所有(*)运行时依赖项
FROM gcr.io/distroless/cc-debian10
# 复制可执行文件
COPY --from=builder /workspace/.build/release/<executable-name> /

# 设置入口点(应用程序名称)
CMD ["<executable-name>"]

注意,上述使用 gcr.io/distroless/cc-debian10 作为运行时镜像,这对于不使用 FoundationNetworkingFoundationXML 的 Swift 程序应该有效。为了提供更完整的支持,我们(社区)可以向 distroless 提交 PR,引入一个包含 libcurllibxml 的 Swift 基础镜像,分别用于 FoundationNetworkingFoundationXML

归档(Tarball、 ZIP 文件等)

由于在 Mac 或 Windows 上不(尚未)支持交叉编译 Swift 到 Linux,我们需要使用虚拟化技术(如 Docker)来编译我们目标运行在 Linux 上的应用程序。

这并不意味着我们必须将应用程序也打包为 Docker 镜像才能部署。虽然使用 Docker 镜像进行部署方便且流行,但应用程序也可以使用简单且轻量的归档格式(如 tarball 或 ZIP 文件)打包,然后上传到服务器,在服务器上解压并运行。

以下是使用 Docker 和 tar 构建和打包应用程序以在 Ubuntu 服务器上部署的示例:

首先,从应用程序的源位置使用 docker run 命令构建它:

$ docker run --rm \
  -v "$PWD:/workspace" \
  -w /workspace \
  swift:bionic \
  /bin/bash -cl "swift build -c release --static-swift-stdlib"

注意,我们正在绑定挂载源目录,以便构建将构建产物写入本地驱动器,我们稍后将从中打包它们。

接下来,我们可以创建一个包含应用程序可执行文件的暂存区:

$ docker run --rm \
  -v "$PWD:/workspace" \
  -w /workspace \
  swift:bionic  \
  /bin/bash -cl ' \
     rm -rf .build/install && mkdir -p .build/install && \
     cp -P .build/release/<executable-name> .build/install/'

注意,此命令可以与上面的构建命令结合使用——我们将它们分开以使示例更具可读性。

最后,从暂存目录创建一个 tarball:

$ tar cvzf <my-app>-<my-app-version>.tar.gz -C .build/install .

我们可以通过将 tarball 解压到一个目录并在 Docker 运行时容器中运行应用程序来测试 tarball 的完整性:

$ cd <extracted directory>
$ docker run -v "$PWD:/app" -w /app bionic ./<executable-name>

可以使用 scp 等工具将应用程序的 tarball 部署到目标服务器,或者在更复杂的设置中使用配置管理系统(如 chefpuppetansible 等)。

源代码分发

另一种在动态语言(如 Ruby 或 Javascript)中流行的分发技术是将源代码分发到服务器,然后在服务器上编译。

要直接在服务器上构建 Swift 应用程序,服务器必须安装正确的 Swift 工具链。Swift.org 发布了适用于各种 Linux 发行版的工具链,请确保使用与您的服务器 Linux 版本和所需 Swift 版本匹配的工具链。

这种方法的主要优点是简单。额外的优点是服务器具有完整的工具链(例如调试器),可以帮助在服务器上“实时”排除问题。

这种方法的主要缺点是服务器具有完整的工具链(例如编译器),这意味着精通技术的攻击者可能会找到执行代码的方法。他们还可能获得对源代码的访问权限,这而这些代码可能涉及敏感信息。如果应用程序代码需要从私有或受保护的存储库中克隆,服务器需要访问凭据,这增加了额外的攻击面。

在大多数情况下,由于这些安全问题,不建议使用源代码分发。

静态链接和 Curl/XML

注意: 如果您使用 -static-stdlib 编译并使用 Curl 与 FoundationNetworking 或 XML 与 FoundationXML,您必须在目标系统上安装 libcurl 和/或 libxml2 才能使其工作。