# 部署 Spring 引导应用程序

Spring 在部署应用程序时，Boot 的灵活打包选项提供了很多选择。你可以将 Spring 引导应用程序部署到各种云平台，部署到虚拟机/真机，或者使它们在 UNIX 系统中完全可执行。

本节将介绍一些更常见的部署场景。

## 1. 部署到云上

Spring Boot 的可执行 JAR 已经为大多数流行的云 PaaS（Platform-as-a-Service，即平台即服务）提供商准备好了。这些供应商往往要求你“带上自己的容器”。它们管理应用程序进程（而不是专门的 Java 应用程序），因此它们需要一个中间层，将*你的*应用程序适应于运行进程的*Cloud’s*概念。

Heroku 和 Cloud Foundry 这两家颇受欢迎的云供应商采用了一种“BuildPack”方法。BuildPack 将部署的代码封装在应用程序*开始*所需的任何内容中。它可能是一个 JDK 和对`java`的调用、一个嵌入式 Web 服务器或一个成熟的应用程序服务器。BuildPack 是可插入的，但在理想情况下，你应该能够通过尽可能少的自定义来实现它。这减少了不在你控制范围内的功能的占用。它最大限度地减少了开发环境和生产环境之间的差异。

理想情况下，你的应用程序，比如 Spring 可执行的引导程序 jar，包含了在其中运行所需的所有内容。

在这一部分中，我们将研究如何在“Getting Started”部分中获得[我们开发的应用程序](getting-started.html#getting-started.first-application)并在云中运行。

### 1.1.Cloud Foundry

Cloud Foundry 提供了默认的构建包，如果没有指定其他构建包，这些构建包就会发挥作用。Cloud Foundry[Java BuildPack](https://github.com/cloudfoundry/java-buildpack)对 Spring 应用程序具有出色的支持，包括 Spring 启动。你可以部署独立的可执行应用程序以及传统的`.war`打包应用程序。

一旦你构建了你的应用程序（例如，通过使用`mvn clean package`）并[安装了`cf`命令行工具]（https://DOCS.cloudfoundry.org/cf-cli/install-go-cli.html），就可以使用`cf push`命令部署你的应用程序，并替换到你编译的`.jar`的路径。在推送应用程序之前，一定要[用`cf`命令行客户端登录]（https://DOCS.cloudfoundry.org/cf-cli/getting-started.html#login）。下面的一行显示了使用`cf push`命令部署应用程序的情况：

```
$ cf push acloudyspringtime -p target/demo-0.0.1-SNAPSHOT.jar
```

|   |在前面的示例中，我们用`acloudyspringtime`替换你给出的任何值`cf`作为应用程序的名称。|
|---|-----------------------------------------------------------------------------------------------------------------------------|

有关更多选项，请参见[`cf push`文档]（https://DOCS.cloudfoundry.org/cf-cli/getting-started.html#push）。如果在同一目录中存在一个 Cloud Foundry[`manifest.yml`]（https://DOCS.cloudfoundry.org/devguide/deploy-apps/manifest.html）文件，则将考虑该文件。

此时，`cf`开始上载应用程序，生成类似于以下示例的输出：

```
Uploading acloudyspringtime... OK
Preparing to start acloudyspringtime... OK
-----> Downloaded app package (8.9M)
-----> Java Buildpack Version: v3.12 (offline) | https://github.com/cloudfoundry/java-buildpack.git#6f25b7e
-----> Downloading Open Jdk JRE 1.8.0_121 from https://java-buildpack.cloudfoundry.org/openjdk/trusty/x86_64/openjdk-1.8.0_121.tar.gz (found in cache)
       Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (1.6s)
-----> Downloading Open JDK Like Memory Calculator 2.0.2_RELEASE from https://java-buildpack.cloudfoundry.org/memory-calculator/trusty/x86_64/memory-calculator-2.0.2_RELEASE.tar.gz (found in cache)
       Memory Settings: -Xss349K -Xmx681574K -XX:MaxMetaspaceSize=104857K -Xms681574K -XX:MetaspaceSize=104857K
-----> Downloading Container Certificate Trust Store 1.0.0_RELEASE from https://java-buildpack.cloudfoundry.org/container-certificate-trust-store/container-certificate-trust-store-1.0.0_RELEASE.jar (found in cache)
       Adding certificates to .java-buildpack/container_certificate_trust_store/truststore.jks (0.6s)
-----> Downloading Spring Auto Reconfiguration 1.10.0_RELEASE from https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-1.10.0_RELEASE.jar (found in cache)
Checking status of app 'acloudyspringtime'...
  0 of 1 instances running (1 starting)
  ...
  0 of 1 instances running (1 starting)
  ...
  0 of 1 instances running (1 starting)
  ...
  1 of 1 instances running (1 running)

App started
```

恭喜你！应用程序现已上线！

一旦你的应用程序处于活动状态，你就可以使用`cf apps`命令来验证部署的应用程序的状态，如以下示例所示：

```
$ cf apps
Getting applications in ...
OK

name                 requested state   instances   memory   disk   urls
...
acloudyspringtime    started           1/1         512M     1G     acloudyspringtime.cfapps.io
...
```

一旦 Cloud Foundry 确认你的应用程序已经部署，你就应该能够在给定的 URI 中找到该应用程序。在前面的示例中，你可以在`https://acloudyspringtime.cfapps.io/`处找到它。

#### 1.1.1.对服务的约束

默认情况下，有关正在运行的应用程序的元数据以及服务连接信息作为环境变量公开给应用程序（例如：`$VCAP_SERVICES`）。这一架构决定是由于 Cloud Foundry 的多语种（任何语言和平台都可以作为 buildpack 支持）的特性。过程范围的环境变量与语言无关。

环境变量并不总是最简单的 API，因此 Spring 引导会自动提取它们，并将数据平坦化为可通过 Spring 的`Environment`抽象访问的属性，如以下示例所示：

```
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
public class MyBean implements EnvironmentAware {

    private String instanceId;

    @Override
    public void setEnvironment(Environment environment) {
        this.instanceId = environment.getProperty("vcap.application.instance_id");
    }

    // ...

}

```

所有 Cloud Foundry 属性的前缀都是`vcap`。你可以使用`vcap`属性来访问应用程序信息（例如应用程序的公共 URL）和服务信息（例如数据库凭据）。有关完整的详细信息，请参见[“CloudFoundryVCapEnvironmentPostProcessor”](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.html)Javadoc。

|   |[Java CFEnv](https://github.com/pivotal-cf/java-cfenv/)项目更适合于配置数据源等任务。|
|---|-------------------------------------------------------------------------------------------------------------------------------|

### 1.2.库贝内特斯

Spring 通过检查`"*_SERVICE_HOST"`和`"*_SERVICE_PORT"`变量的环境，引导自动检测 Kubernetes 部署环境。你可以使用`spring.main.cloud-platform`配置属性重写此检测。

Spring Boot 帮助你[管理应用程序的状态](features.html#features.spring-application.application-availability)并用[使用致动器的 HTTP Kubernetes 探测](actuator.html#actuator.endpoints.kubernetes-probes)导出它。

#### 1.2.1.Kubernetes 容器生命周期

当 Kubernetes 删除一个应用程序实例时，关闭过程涉及多个并发的子系统：关闭钩子、取消注册服务、从负载平衡器中删除实例…，因为这种关闭过程是并行进行的（而且是由于分布式系统的性质），有一个窗口，在此期间，流量可以路由到一个也已开始关机处理的 pod。

你可以在 Prestop 处理程序中配置睡眠执行，以避免请求被路由到已经开始关闭的 POD。这种睡眠时间应该足够长，新的请求可以停止路由到 POD，其持续时间也会因部署而异。可以使用 POD 配置文件中的 PodSpec 对 Prestop 处理程序进行配置，具体如下：

```
spec:
  containers:
  - name: "example-container"
    image: "example-image"
    lifecycle:
      preStop:
        exec:
          command: ["sh", "-c", "sleep 10"]
```

一旦预停钩子完成，SIGTERM 将被发送到容器，[优雅的关机](web.html#web.graceful-shutdown)将开始，允许任何剩余的飞行中请求完成。

|   |当 Kubernetes 向 POD 发送 SIGTERM 信号时，它会等待一个指定的时间，称为终止宽限期（默认为 30 秒）。<br/>如果容器在宽限期之后仍在运行，它们被发送 SIGKILL 信号并被强制删除。<br/>如果 POD 关闭所需的时间超过 30 秒，这可能是因为你增加了`spring.lifecycle.timeout-per-shutdown-phase`，请确保通过在 POD YAML 中设置`terminationGracePeriodSeconds`选项来增加终止宽限期。|
|---| |

### 1.3.赫罗库

Heroku 是另一个流行的 PaaS 平台。为了定制 Heroku 构建，你提供了`Procfile`，它提供了部署应用程序所需的咒语。Heroku 为 Java 应用程序分配一个`port`，然后确保对外部 URI 的路由工作。

你必须配置你的应用程序以侦听正确的端口。下面的示例显示了我们的 Starter REST 应用程序的`Procfile`:

```
web: java -Dserver.port=$PORT -jar target/demo-0.0.1-SNAPSHOT.jar
```

Spring 引导使`-D`参数作为可从 Spring `Environment`实例访问的属性可用。`server.port`配置属性被馈送到嵌入式 Tomcat、 Jetty 或 Undertow 实例，然后在启动时使用端口。`$PORT`环境变量由 Heroku PaaS 分配给我们。

这应该是你所需要的一切。Heroku 部署最常见的部署工作流是`git push`到生产的代码，如以下示例所示：

```
$ git push heroku main
```

这将导致以下情况：

```
Initializing repository, done.
Counting objects: 95, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (78/78), done.
Writing objects: 100% (95/95), 8.66 MiB | 606.00 KiB/s, done.
Total 95 (delta 31), reused 0 (delta 0)

-----> Java app detected
-----> Installing OpenJDK 1.8... done
-----> Installing Maven 3.3.1... done
-----> Installing settings.xml... done
-----> Executing: mvn -B -DskipTests=true clean install

       [INFO] Scanning for projects...
       Downloading: https://repo.spring.io/...
       Downloaded: https://repo.spring.io/... (818 B at 1.8 KB/sec)
        ....
       Downloaded: https://s3pository.heroku.com/jvm/... (152 KB at 595.3 KB/sec)
       [INFO] Installing /tmp/build_0c35a5d2-a067-4abc-a232-14b1fb7a8229/target/...
       [INFO] Installing /tmp/build_0c35a5d2-a067-4abc-a232-14b1fb7a8229/pom.xml ...
       [INFO] ------------------------------------------------------------------------
       [INFO] BUILD SUCCESS
       [INFO] ------------------------------------------------------------------------
       [INFO] Total time: 59.358s
       [INFO] Finished at: Fri Mar 07 07:28:25 UTC 2014
       [INFO] Final Memory: 20M/493M
       [INFO] ------------------------------------------------------------------------

-----> Discovering process types
       Procfile declares types -> web

-----> Compressing... done, 70.4MB
-----> Launching... done, v6
       https://agile-sierra-1405.herokuapp.com/ deployed to Heroku

To [email protected]:agile-sierra-1405.git
 * [new branch]      main -> main
```

你的应用程序现在应该已经在 Heroku 上启动并运行了。有关更多详细信息，请参见[Deploying Spring Boot Applications to Heroku](https://devcenter.heroku.com/articles/deploying-spring-boot-apps-to-heroku)。

### 1.4.OpenShift

[OpenShift](https://www.openshift.com/)有许多资源描述如何部署 Spring 引导应用程序，包括：

* [使用 S2i Builder](https://blog.openshift.com/using-openshift-enterprise-grade-spring-boot-deployments/)

* [建筑指南](https://access.redhat.com/documentation/en-us/reference_architectures/2017/html-single/spring_boot_microservices_on_red_hat_openshift_container_platform_3/)

* [作为传统的 Web 应用程序在 Wildfly 上运行](https://blog.openshift.com/using-spring-boot-on-openshift/)

* [OpenShift Commons 简报](https://blog.openshift.com/openshift-commons-briefing-96-cloud-native-applications-spring-rhoar/)

### 1.5.亚马逊网络服务

Amazon Web Services 提供了多种方式来安装 Spring 基于启动的应用程序，既可以是传统的 Web 应用程序，也可以是带有嵌入式 Web 服务器的可执行文件 jar。这些选择包括：

* AWS 弹性豆茎

* AWS 代码部署

* AWS Ops Works

* AWS 云形成

* AWS 容器注册表

每一种都有不同的特点和定价模式。在本文中，我们描述了使用 AWS 弹性豆茎的方法。

#### 1.5.1.AWS 弹性豆茎

正如在官方[Elastic Beanstalk Java 指南](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Java.html)中所描述的，部署 Java 应用程序有两个主要选项。你可以使用“ Tomcat 平台”或“Java SE 平台”。

##### 使用 Tomcat 平台

此选项适用于生成 WAR 文件的 Spring 引导项目。不需要特殊配置。你只需要按照官方的指南去做就行了。

##### 使用 Java SE 平台

此选项适用于生成 jar 文件并运行嵌入式 Web 容器的 Spring 引导项目。Elastic Beanstalk 环境在端口 80 上运行一个 Nginx 实例，以代理在端口 5000 上运行的实际应用程序。要配置它，请在`application.properties`文件中添加以下行：

```
server.port=5000
```

|   |上传二进制文件，而不是源文件<br/><br/>默认情况下，Elastic Beanstalk 上传源文件并在 AWS 中进行编译。<br/>但是，最好是上传二进制文件。<br/>要做到这一点，在你的`.elasticbeanstalk/config.yml`文件中添加类似于以下的行：<br/>```<br/>deploy:<br/>    artifact: target/demo-0.0.1-SNAPSHOT.jar<br/>```|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |通过设置环境类型<br/><br/>来降低成本，默认情况下，弹性 Beanstalk 环境是负载平衡的，<br/>负载平衡器有很大的成本，<br/>为了避免这种成本，请将环境类型设置为“单实例”，如[亚马逊文档](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-create-wizard.html#environments-create-wizard-capacity)中所述，<br/>还可以通过使用 CLI 和以下命令创建单个实例环境：<br/><br/>```<br/>eb create -s<br/>```|
|---| |

#### 1.5.2.摘要

这是访问 AWS 的最简单的方法之一，但还有更多的事情要做，例如如何将 Elastic Beanstalk 集成到任何 CI/CD 工具中，使用 Elastic Beanstalk Maven 插件而不是 CLI，等等。有一个[blog post](https://exampledriven.wordpress.com/2017/01/09/spring-boot-aws-elastic-beanstalk-example/)更详细地介绍了这些主题。

### 1.6.CloudCaptain 和亚马逊网络服务

[CloudCaptain](https://cloudcaptain.sh/)的工作原理是将你的 Spring 引导可执行文件 jar 或 WAR 转换为一个最小的 VM 映像，该映像可以在 VirtualBox 或 AWS 上不变地部署。CloudCaptain 具有用于 Spring 引导的深度集成，并使用来自 Spring 引导配置文件的信息来自动配置端口和健康检查 URL。CloudCaptain 为它生成的映像以及它提供的所有资源（实例、安全组、弹性负载均衡器等）利用了这些信息。

一旦你创建了[CloudCaptain 帐户](https://console.cloudcaptain.sh)，将其连接到你的 AWS 帐户，安装最新版本的 CloudCaptain 客户端，并确保该应用程序已由 Maven 或 Gradle 构建（例如，通过使用`mvn clean package`），你可以使用类似于以下的命令将 Spring 引导应用程序部署到 AWS:

```
$ boxfuse run myapp-1.0.jar -env=prod
```

有关更多选项，请参见[`boxfuse run`文档]（https://cloudcaptain.sh/DOCS/commandline/run.html）。如果当前目录中存在[`boxfuse.conf`]（https://cloudcaptain.sh/DOCS/commandline/#configuration）文件，则考虑该文件。

|   |默认情况下，CloudCaptain 在启动时激活一个名为`boxfuse`的 Spring 配置文件。<br/>如果你的可执行文件 jar 或战争包含一个[`application-boxfuse.properties`]（https://cloudcaptain.sh/DOCS/payloads/springboot.html#configuration）文件，Cloudcaptain 的配置基于它所包含的属性。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

此时，CloudCaptain 将为你的应用程序创建一个映像，将其上载，并在 AWS 上配置和启动必要的资源，其输出结果类似于以下示例：

```
Fusing Image for myapp-1.0.jar ...
Image fused in 00:06.838s (53937 K) -> axelfontaine/myapp:1.0
Creating axelfontaine/myapp ...
Pushing axelfontaine/myapp:1.0 ...
Verifying axelfontaine/myapp:1.0 ...
Creating Elastic IP ...
Mapping myapp-axelfontaine.boxfuse.io to 52.28.233.167 ...
Waiting for AWS to create an AMI for axelfontaine/myapp:1.0 in eu-central-1 (this may take up to 50 seconds) ...
AMI created in 00:23.557s -> ami-d23f38cf
Creating security group boxfuse-sg_axelfontaine/myapp:1.0 ...
Launching t2.micro instance of axelfontaine/myapp:1.0 (ami-d23f38cf) in eu-central-1 ...
Instance launched in 00:30.306s -> i-92ef9f53
Waiting for AWS to boot Instance i-92ef9f53 and Payload to start at https://52.28.235.61/ ...
Payload started in 00:29.266s -> https://52.28.235.61/
Remapping Elastic IP 52.28.233.167 to i-92ef9f53 ...
Waiting 15s for AWS to complete Elastic IP Zero Downtime transition ...
Deployment completed successfully. axelfontaine/myapp:1.0 is up and running at https://myapp-axelfontaine.boxfuse.io/
```

你的应用程序现在应该已经启动并在 AWS 上运行。

请参阅[deploying Spring Boot apps on EC2](https://cloudcaptain.sh/blog/spring-boot-ec2.html)上的博客文章以及[documentation for the CloudCaptain Spring Boot integration](https://cloudcaptain.sh/docs/payloads/springboot.html)，以开始使用 Maven 构建来运行该应用程序。

### 1.7.天蓝色

这[入门指南](https://spring.io/guides/gs/spring-boot-for-azure/)将引导你将 Spring 引导应用程序部署到[Azure Spring Cloud](https://azure.microsoft.com/en-us/services/spring-cloud/)或[Azure 应用程序服务](https://docs.microsoft.com/en-us/azure/app-service/overview)。

### 1.8.谷歌云

Google Cloud 有几种可用于启动 Spring 启动应用程序的选项。最容易开始使用的可能是 App Engine，但你也可以找到在带有容器引擎的容器中或在带有计算引擎的虚拟机上运行 Spring boot 的方法。

要在 App Engine 中运行，你可以首先在 UI 中创建一个项目，该项目为你设置唯一的标识符，还可以设置 HTTP 路由。将一个 Java 应用程序添加到项目中，并将其保持为空，然后使用[Google Cloud SDK](https://cloud.google.com/sdk/install)将你的 Spring 启动应用程序从命令行或 CI 构建推入该插槽。

App Engine 标准要求你使用战争包装。按照[这些步骤](https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java8/springboot-helloworld/README.md)将 App Engine 标准应用程序部署到 Google Cloud。

或者，App Engine Flex 要求你创建`app.yaml`文件来描述应用程序所需的资源。通常，将此文件放入`src/main/appengine`，它应该类似于以下文件：

```
service: "default"

runtime: "java"
env: "flex"

runtime_config:
  jdk: "openjdk8"

handlers:
- url: "/.*"
  script: "this field is required, but ignored"

manual_scaling:
  instances: 1

health_check:
  enable_health_check: false

env_variables:
  ENCRYPT_KEY: "your_encryption_key_here"
```

你可以通过将项目 ID 添加到构建配置中来部署应用程序（例如，使用 Maven 插件），如下例所示：

```
<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>appengine-maven-plugin</artifactId>
    <version>1.3.0</version>
    <configuration>
        <project>myproject</project>
    </configuration>
</plugin>
```

然后使用`mvn appengine:deploy`进行部署（如果需要首先进行身份验证，则生成失败）。

## 2. 安装 Spring 启动应用程序

Spring 除了通过使用`java -jar`运行启动应用程序外，还可以为 UNIX 系统制作完全可执行的应用程序。完全可执行的 jar 可以像任何其他可执行的二进制文件一样执行，也可以[注册为`init.d`或`systemd`]（#deployment.installing.nix-services）。这有助于在公共生产环境中安装和管理 Spring 引导应用程序。

|   |完全可执行的 JAR 通过在文件的前面嵌入一个额外的脚本来工作。<br/>目前，一些工具不接受这种格式，因此你可能不能总是使用这种技术。<br/>，例如，`jar -xf`可能无法自动提取已完全可执行的 jar 或 WAR，<br/>建议仅在打算直接执行时才使 jar 或 WAR 完全可执行，而不是使用`java -jar`运行它，或者将其部署到 Servlet 容器中。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |不能使 ZIP64 格式的 jar 文件完全可执行。<br/>尝试这样做将导致 jar 文件在直接执行或使用`java -jar`执行时被报告为已损坏。<br/>包含一个或多个 ZIP64 格式嵌套 JAR 的标准格式 jar 文件可以完全可执行。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

要使用 Maven 创建一个“完全可执行的” jar，请使用以下插件配置：

```
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <executable>true</executable>
    </configuration>
</plugin>
```

下面的示例展示了等效的 Gradle 配置：

```
bootJar {
    launchScript()
}
```

然后，你可以通过输入`./my-application.jar`（其中`my-application`是工件的名称）来运行应用程序。包含 jar 的目录用作应用程序的工作目录。

### 2.1.支持的操作系统

默认脚本支持大多数 Linux 发行版，并在 Centos 和 Ubuntu 上进行测试。其他平台，如 OS X 和 FreeBSD，需要使用自定义`embeddedLaunchScript`。

### 2.2.Unix/Linux 服务

Spring 通过使用`init.d`或`systemd`，启动应用程序可以很容易地作为 UNIX/Linux 服务启动。

#### 2.2.1.作为 init.d 服务（系统 V）安装

如果你将 Spring boot 的 Maven 或 Gradle 插件配置为生成[fully executable jar](#deployment.installing)，并且不使用自定义的`embeddedLaunchScript`，那么你的应用程序可以用作`init.d`服务。为此，将 jar 符号链接到`init.d`以支持标准`start`、`stop`、`restart`和`status`命令。

该脚本支持以下功能：

* 作为 OWNS jar 文件的用户启动服务

* 使用`/var/run/<appname>/<appname>.pid`跟踪应用程序的 PID

* 将控制台日志写入`/var/log/<appname>.log`

假设在`/var/myapp`中安装了 Spring 引导应用程序，要将 Spring 引导应用程序安装为`init.d`服务，请创建一个符号链接，如下所示：

```
$ sudo ln -s /var/myapp/myapp.jar /etc/init.d/myapp
```

一旦安装，你就可以用通常的方式启动和停止服务。例如，在基于 Debian 的系统上，你可以用以下命令启动它：

```
$ service myapp start
```

|   |如果应用程序无法启动，请检查写入`/var/log/<appname>.log`的日志文件中的错误。|
|---|------------------------------------------------------------------------------------------------------|

你还可以使用标准的操作系统工具标记应用程序自动启动。例如，在 Debian 上，你可以使用以下命令：

```
$ update-rc.d myapp defaults <priority>
```

##### 获得 init.d 服务

|   |以下是关于如何保护作为 init.d 服务运行的 Spring 引导应用程序的一组指导原则。<br/>它并不是要详尽列出所有应该做的事情，以加强应用程序及其运行的环境。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

当作为 root 执行时，就像使用 root 启动 init.d 服务时一样，默认的可执行脚本以`RUN_AS_USER`环境变量中指定的用户的身份运行应用程序。当环境变量未被设置时，OWNS 该 jar 文件的用户被用来代替。永远不应该将 Spring 引导应用程序运行为`root`，因此`RUN_AS_USER`永远不应该是 root，并且应用程序的 jar 文件永远不应该由 root 拥有。相反，创建一个特定的用户来运行你的应用程序，并设置`RUN_AS_USER`环境变量，或者使用`chown`使其成为 jar 文件的所有者，如以下示例所示：

```
$ chown bootapp:bootapp your-app.jar
```

在这种情况下，默认的可执行脚本以`bootapp`用户的身份运行应用程序。

|   |为了减少应用程序的用户帐户遭到破坏的可能性，你应该考虑防止它使用登录 shell。<br/>例如，你可以将帐户的 shell 设置为`/usr/sbin/nologin`。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

你还应该采取措施，防止修改应用程序的 jar 文件。首先，配置其权限，使其不能被写入，只能由其所有者读取或执行，如以下示例所示：

```
$ chmod 500 your-app.jar
```

其次，如果你的应用程序或正在运行它的帐户遭到破坏，你还应该采取措施限制损害。如果攻击者确实获得了访问权限，他们可以使 jar 文件可写并更改其内容。防止这种情况发生的一种方法是使用`chattr`使其不可更改，如下例所示：

```
$ sudo chattr +i your-app.jar
```

这将阻止任何用户（包括根用户）修改 jar。

如果使用 root 来控制应用程序的服务，并且你[使用`.conf`文件]（#deployment.installing.nix-services.script-customization.when-running.conf-file）来定制其启动，则根用户将读取`.conf`文件并对其进行评估。它应该得到相应的保护。使用`chmod`使文件只能由所有者读取，并使用`chown`使根用户成为所有者，如以下示例所示：

```
$ chmod 400 your-app.conf
$ sudo chown root:root your-app.conf
```

#### 2.2.2.安装为 Systemd 服务

`systemd`是 System v init 系统的继承者，现在被许多现代 Linux 发行版使用。虽然可以继续使用`init.d`带有`systemd`的脚本，但也可以通过使用`systemd`’service’脚本启动 Spring 启动应用程序。

假设在`/var/myapp`中安装了 Spring 引导应用程序，要将 Spring 引导应用程序安装为`systemd`服务，请创建一个名为`myapp.service`的脚本，并将其放置在`/etc/systemd/system`目录中。下面的脚本提供了一个示例：

```
[Unit]
Description=myapp
After=syslog.target

[Service]
User=myapp
ExecStart=/var/myapp/myapp.jar
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target
```

|   |请记住更改应用程序的`Description`、`User`和`ExecStart`字段。|
|---|------------------------------------------------------------------------------------------|

|   |`ExecStart`字段不声明脚本操作命令，这意味着默认情况下使用`run`命令。|
|---|------------------------------------------------------------------------------------------------------------------------|

请注意，与作为`init.d`服务运行不同，运行应用程序的用户、PID 文件和控制台日志文件由`systemd`本身管理，因此必须通过在“服务”脚本中使用适当的字段进行配置。有关更多详细信息，请咨询[服务单元配置手册页](https://www.freedesktop.org/software/systemd/man/systemd.service.html)。

要标记应用程序在系统启动时自动启动，请使用以下命令：

```
$ systemctl enable myapp.service
```

运行`man systemctl`获取更多详细信息。

#### 2.2.3.自定义启动脚本

由 Maven 或 Gradle 插件编写的默认嵌入式启动脚本可以通过多种方式进行定制。对于大多数人来说，使用默认脚本并进行一些自定义通常就足够了。如果你发现无法自定义你需要的内容，请使用`embeddedLaunchScript`选项完全编写自己的文件。

##### 在编写开始脚本时自定义 #####

当开始脚本被写入 jar 文件时，定制它的元素通常是有意义的。例如，init.d 脚本可以提供一个“描述”。由于你预先知道描述（并且它不需要更改），因此在生成 jar 时也可以提供它。

要定制编写的元素，可以使用 Spring boot Maven 插件的`embeddedLaunchScriptProperties`选项或 Spring boot Gradle 插件的`properties`属性`launchScript`]（https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/ Gradle-plugin/reference/htmlsinger/#packaging-executable-configing-ling-launation-launch-script）。

默认脚本支持以下属性替换：

|           Name           |说明|                                   Gradle default                                   |                       Maven default                        |
|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|------------------------------------------------------------|
|          `mode`          |脚本模式。|                                       `auto`                                       |                           `auto`                           |
|    `initInfoProvides`    |“init info”的`Provides`部分|                                 `${task.baseName}`                                 |                  `${project.artifactId}`                   |
| `initInfoRequiredStart`  |`Required-Start`“init info”部分。|                           `$remote_fs $syslog $network`                            |               `$remote_fs $syslog $network`                |
|  `initInfoRequiredStop`  |`Required-Stop`“init info”部分。|                           `$remote_fs $syslog $network`                            |               `$remote_fs $syslog $network`                |
|  `initInfoDefaultStart`  |`Default-Start`“init info”部分。|                                     `2 3 4 5`                                      |                         `2 3 4 5`                          |
|  `initInfoDefaultStop`   |`Default-Stop`“init info”部分。|                                      `0 1 6`                                       |                          `0 1 6`                           |
|`initInfoShortDescription`|`Short-Description`“init info”部分。|Single-line version of `${project.description}` (falling back to `${task.baseName}`)|                     `${project.name}`                      |
|  `initInfoDescription`   |`Description`“init info”部分。|           `${project.description}` (falling back to `${task.baseName}`)            |`${project.description}` (falling back to `${project.name}`)|
|   `initInfoChkconfig`    |`chkconfig`“init info”部分|                                    `2345 99 01`                                    |                        `2345 99 01`                        |
|       `confFolder`       |`CONF_FOLDER`的默认值|                             Folder containing the jar                              |                 Folder containing the jar                  |
|   `inlinedConfScript`    |引用在默认启动脚本中应该内联的文件脚本。<br/>在加载任何外部配置文件之前，这可以用于设置环境变量，例如`JAVA_OPTS`|                                                                                    |                                                            |
|       `logFolder`        |`LOG_FOLDER`的默认值。<br/>仅对`init.d`服务有效|                                                                                    |                                                            |
|      `logFilename`       |`LOG_FILENAME`的默认值。<br/>仅对`init.d`服务有效|                                                                                    |                                                            |
|       `pidFolder`        |`PID_FOLDER`的默认值。<br/>仅对`init.d`服务有效|                                                                                    |                                                            |
|      `pidFilename`       |`PID_FOLDER`中 PID 文件名称的默认值。<br/>仅对`init.d`服务有效|                                                                                    |                                                            |
|   `useStartStopDaemon`   |当`start-stop-daemon`命令可用时，是否应该使用它来控制进程|                                       `true`                                       |                           `true`                           |
|      `stopWaitTime`      |`STOP_WAIT_TIME`的默认值（以秒为单位）。<br/>仅对`init.d`服务有效|                                         60                                         |                             60                             |

##### 在脚本运行时自定义脚本 #####

对于需要自定义*之后* jar 已编写的脚本的项，可以使用环境变量或[配置文件](#deployment.installing.nix-services.script-customization.when-running.conf-file)。

默认脚本支持以下环境属性：

|       Variable        |说明|
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|        `MODE`         |操作的“模式”。<br/>默认值取决于构建 jar 的方式，但通常是`auto`（这意味着它试图通过检查来猜测它是否是一个 init 脚本）如果它是一个名为`init.d`的目录中的符号链接）。<br/>你可以显式地将它设置为`service`，这样 `stop|start|status|restart` commands work or to `run` if you want to run the script in the foreground.|
|     `RUN_AS_USER`     |将用于运行该应用程序的用户。<br/>当未设置时，将使用 OWNS jar 文件的用户。|
|`USE_START_STOP_DAEMON`|是否应该使用`start-stop-daemon`命令来控制进程。<br/>默认为`true`。|
|     `PID_FOLDER`      |PID 文件夹的根名（默认情况下`/var/run`）。|
|     `LOG_FOLDER`      |将日志文件放入其中的文件夹名称（默认情况下`/var/log`）。|
|     `CONF_FOLDER`     |读取.conf 文件的文件夹的名称（默认情况下与 jar-file 文件相同）。|
|    `LOG_FILENAME`     |在`LOG_FOLDER`（默认情况下`<appname>.log`）中的日志文件的名称。|
|      `APP_NAME`       |应用程序的名称。<br/>如果 jar 是从符号链接运行的，则脚本将猜测应用程序的名称。<br/>如果不是符号链接，或者你希望显式设置应用程序名称，这将非常有用。|
|      `RUN_ARGS`       |要传递给程序（ Spring 引导应用程序）的参数。|
|      `JAVA_HOME`      |默认情况下，`java`可执行文件的位置是通过使用`PATH`发现的，但是如果在`$JAVA_HOME/bin/java`处有一个可执行文件，则可以显式地设置它。|
|      `JAVA_OPTS`      |在 JVM 启动时传递给它的选项。|
|       `JARFILE`       |jar 文件的明确位置，以防该脚本被用来启动一个 jar 文件，该 jar 文件实际上并未被嵌入。|
|        `DEBUG`        |如果不是空的，则在 shell 进程上设置`-x`标志，允许你查看脚本中的逻辑。|
|   `STOP_WAIT_TIME`    |在强制关机之前停止应用程序所需的等待时间（默认情况下`60`）。|

|   |`PID_FOLDER`，`LOG_FOLDER`，和`LOG_FILENAME`变量仅对`init.d`服务有效。<br/>对于`systemd`，通过使用’service’脚本进行等效的定制。有关更多详细信息，请参见<234r="232"/>。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

除了`JARFILE`和`APP_NAME`之外，可以使用`.conf`文件配置上一节中列出的设置。该文件预计将位于 jar 文件的旁边，并且具有相同的名称，但后缀为`.conf`，而不是`.jar`。例如，名为`/var/myapp/myapp.jar`的 jar 使用名为`/var/myapp/myapp.conf`的配置文件，如以下示例所示：

myapp.conf

```
JAVA_OPTS=-Xmx1024M
LOG_FOLDER=/custom/log/folder
```

|   |如果不喜欢将配置文件放在 jar 文件旁边，则可以设置`CONF_FOLDER`环境变量来定制配置文件的位置。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------|

要了解如何适当地保护此文件，请参见[获得 init.d 服务的指导方针](#deployment.installing.nix-services.init-d.securing)。

### 2.3.Microsoft Windows 服务

Spring 启动应用程序可以通过使用[`winsw`]作为 Windows 服务启动。

a（[单独维护的样本](https://github.com/snicoll/spring-boot-daemon)）逐步描述了如何为 Spring 引导应用程序创建 Windows 服务。

## 3. 接下来要读什么？

有关 PaaS 可以提供的特性的更多信息，请参见[Cloud Foundry](https://www.cloudfoundry.org/)、[Heroku](https://www.heroku.com/)、[OpenShift](https://www.openshift.com)和[Boxfuse](https://boxfuse.com)网站。这些只是最受欢迎的 Java PaaS 提供商中的四个。由于 Spring 启动非常适合基于云的部署，因此你也可以自由地考虑其他提供商。

下一节继续介绍 *[Spring Boot CLI](cli.html#cli)*，或者你可以提前阅读有关 *[构建工具插件](build-tool-plugins.html#build-tool-plugins)* 的内容。

