我对Terraform 的后端(=http)进行了调查

在Terraform的后端有许多选择。

根据Terraform Backend Types中所列举的,Terraform的远程后端有多种选择。

    • artifactory

 

    • azurerm

 

    • consul

 

    • etcd

 

    • etcdv3

 

    • gcs

 

    • http

 

    • manta

 

    • s3

 

    swift

嗯,就这样吧。
这次我调查了这个。

顺便提一下,默认情况下是使用本地后端,并保存在名为terraform.tfstate的文件中。

我想查一下关于HTTP的信息。

我认为比较实用的是S3和Swift,但我决定去仔细研究一下HTTP。

後端(http編程)

我看了写在这里的“示例用法”和“示例参考”,完全不懂呢。

在HTTP的情况下,我应该送什么,并且要求什么样的回应才可以呢?完全不清楚。

因此,首先搭建了一个简单的测试服务器,然后使用Charles进行捕获,看是否能够理解。

首先需要更改tf文件的后端设置。

    1. 我之前用Terraform有一个.tf文件,

 

    然后我只是简单地在文件中添加了后端。

这样做的话就会变成这样。

provider "openstack" {
  auth_url          = "*****"
  user_name         = "*****"
  tenant_id         = "*****"
  tenant_name       = "*****"
  password          = "*****"
  user_domain_id    = "*****"
  project_domain_id = "*****"
}

terraform {
  backend "http" {
    address = "http://localhost.com:5000"
  }
}

resource "openstack_compute_keypair_v2" "my_kp_1" {
  name = "terraform-kp-1"
}

只需要一种选择,对于以下原文进行中文释义:足したのは。

加起来的是。

terraform {
  backend "http" {
    address = "http://localhost.com:5000"
  }
}

這一部分。

在backend中出现了一个名为localhost.com的主机名,但是通过hosts文件将其解析为127.0.0.1。

为什么不能保持localhost呢?原因是如果保持localhost的话,Charles就无法进行捕获了。

作为这个解决方案,从浏览器中,或者在localhost后面加上”.”进行截图可能是可行的,但是在Terraform中似乎不行。

接下来创建一个请求目标

我希望localhost.com:5000能够收到一些HTTP请求。

所以,我尝试制作了一个这样的Flask应用程序。


from flask import make_response, Flask

app = Flask(__name__)


@app.route('/')
def hello():
    return make_response()


if __name__ == "__main__":
    app.run(debug=True)

我会把这个启动起来。

% python /path/to/flaskapp.py

如果是这样,那么它应该会在 localhost:5000 上等待。后端已经搭建好了,对吧。

尝试执行terraform init。

现在,在这个状态下,让我们尝试运行 terraform init。

% terraform init

Initializing the backend...

Initializing provider plugins...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.openstack: version = "~> 1.12"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

哦,看起来成功了呢。好像是进行了terraform init。

这时发生了什么?

我会用Charles查看这个请求/响应的内容。

在terraform初始化时的请求

GET / HTTP/1.1
Host: localhost.com:5000
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

terraform init的响应

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 0
Server: Werkzeug/0.14.1 Python/2.7.10
Date: Wed, 19 Dec 2018 08:49:27 GMT
Connection: close

原来如此。这意味着在后端指定的URL上执行GET请求。

我认为是因为处于init阶段,所以响应为空也是可以的。

嗯,其实只是 Flask 只能以这种方式返回响应而已。

我试着执行terraform apply。

那么接下来让我们尝试一下“terraform apply”。

% terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + openstack_compute_keypair_v2.my_kp_1
      id:          <computed>
      fingerprint: <computed>
      name:        "terraform-kp-1"
      private_key: <computed>
      public_key:  <computed>
      region:      <computed>


Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

openstack_compute_keypair_v2.my_kp_1: Creating...
  fingerprint: "" => "<computed>"
  name:        "" => "terraform-kp-1"
  private_key: "" => "<computed>"
  public_key:  "" => "<computed>"
  region:      "" => "<computed>"
openstack_compute_keypair_v2.my_kp_1: Creation complete after 1s (ID: terraform-kp-1)
Failed to save state: HTTP error: 405


Error: Failed to persist state to backend.

The error shown above has prevented Terraform from writing the updated state
to the configured backend. To allow for recovery, the state has been written
to the file "errored.tfstate" in the current working directory.

Running "terraform apply" again at this point will create a forked state,
making it harder to recover.

To retry writing this state, use the following command:
    terraform state push errored.tfstate

出现了错误。嗯,这是可以理解的。

Failed to save state: HTTP error: 405
Error: Failed to persist state to backend.

因为我没有创建过POST帖子。

好的,让我们来看一下Charles捕获的这个情况。

申请执行terraform apply时的请求。

POST / HTTP/1.1
Host: localhost.com:5000
User-Agent: Go-http-client/1.1
Content-Length: 3281
Content-Md5: BzoOkMeCZ1DPgz266a8Ckg==
Content-Type: application/json
Accept-Encoding: gzip

{
    "version": 3,
    "terraform_version": "0.11.10",
    "serial": 1,
    "lineage": "0159803e-4f38-ddaf-d403-4118c48cff6a",
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "openstack_compute_keypair_v2.my_kp_1": {
                    "type": "openstack_compute_keypair_v2",
                    "depends_on": [],
                    "primary": {
                        "id": "terraform-kp-1",
                        "attributes": {
                            "fingerprint": "4d:6a:92:2c:f2:44:db:84:12:76:5d:e9:2d:d9:c7:06",
                            "id": "terraform-kp-1",
                            "name": "terraform-kp-1",
                            "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAA(略)-----END RSA PRIVATE KEY-----\n",
                            "public_key": "ssh-rsa AAAAB3NzaC(略) Generated-by-Nova\n",
                            "region": ""
                        },
                        "meta": {},
                        "tainted": false
                    },
                    "deposed": [],
                    "provider": "provider.openstack"
                }
            },
            "depends_on": []
        }
    ]
}

由于当然的原因,响应是405错误(不允许的方法),所以不会在这里显示。

总之,

terraform initすると、backendに対してはGETが飛ぶ。この時レスポンスBODYは空でOKである模様

terraform applyした場合に、apply後のterraform.state相当のJSONがリクエストBODYとしてPOSTされる

这表明了一种动作。

http后端的目标地址可以是这样的行为吗?

那么,对于http后台,大概可以这样创建Web应用程序。

    • POSTが飛んできたら、リクエストBODYをJSON化してストアしておく。これは常に上書く。

 

    • GETが飛んできたら、ストアしたJSONをレスポンスBODYに入れて返す。もしストアしたデータがない場合は空レスポンスでOK

 

    あとロック/アンロックとかもあるようなので、これはどこかでステートごとにフラグでも持たせておいて、フラグに応じてレスポンスコードを変える

总之,就是在后端页面上写着…

Stores the state using a simple REST client.

State will be fetched via GET, updated via POST, and purged with DELETE. The method used for updating is configurable.

只需要一种选择,请用中文确切地转述以下信息:”・・・・・・这个信息是这样的”

    • REST

 

    GET/POST/DELETE

因为写着”需要读行间”,所以这句话是在说什么。嗯,确实会这样。

然而,实际上需要考虑一些能够识别用户的方式,比如认证(这里指的是与提供者本身的认证不同),但是既然已经基本了解了其运作原理,本文将到此为止。

等等,我会制定一个实际上这样做也许不错的方案并写下来试试。

bannerAds