2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Terraform の Backend(=http)について調べてみた

Last updated at Posted at 2018-12-19

TerraformのBackendにはいろいろある

Terraform Backend Types にあるように、TerraformのRemote Backendっていろいろあります。

  • artifactory
  • azurerm
  • consul
  • etcd
  • etcdv3
  • gcs
  • http
  • manta
  • s3
  • swift

とまぁこのくらい。
今回はこれについて調べてみました。

ちなみにデフォルトはLocal Backendになっていて、 terraform.tfstateというファイルに保存されます。

http について調べてみる

実用的っぽいと思ったのはs3, swiftあたりだったのですが、あえていろいろ考えて http を調べてみました。

Backend (http編)

ここにかかれている Example Usage, Example Referencing を見たのですが、全然分かりませんね。

何を送って、何を返してくれればOKなんでしょう、httpの場合。さっぱりです。

ということで、まずは簡単なテストサーバを立てて、それを Charles でキャプって見るとわかるかなと思いまして、こういうことをやりました。

まずtfファイルのbackend設定を変える

  1. 自分が今までTerraformで使っていた .tf があった
  2. で、それに backend を単に足してみた

そうするとこうなります。

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"
  }
}

の部分です。

localhost.com というホスト名がbackend内に出てきますが、これはhostsで127.0.0.1に解決させています。

なんでlocalhostのままではダメなのか・・・というと、localhostのままだとCharlesでキャプチャしてくれないのですよね。

この解決策として、ブラウザからとかだと localhsot.(末尾に".")でキャプチャしてもらえたりしますが、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 で待ち受けてくれるはずです。backendが出来ましたね。

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 init時のリクエスト

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

なるほど。backendで指定したURLにGETが飛ぶわけですね。

init時だからだと思いますが、レスポンスは空でOKのようです。

(まぁ、レスポンスはこういうふうにしか返さないってだけなんですけどね、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エラー(Method Not Allowed)なのでここには載せません。

つまるところ、

  • terraform initすると、backendに対してはGETが飛ぶ。この時レスポンスBODYは空でOKである模様
  • terraform applyした場合に、apply後のterraform.state相当のJSONがリクエストBODYとしてPOSTされる

という挙動であることがわかります。

http Backendの宛先はこんな挙動で良いのでは?

ということは、http Backend向けには、おそらくこういうWebアプリケーションを作ればいいということですね。

  • POSTが飛んできたら、リクエストBODYをJSON化してストアしておく。これは常に上書く。
  • GETが飛んできたら、ストアしたJSONをレスポンスBODYに入れて返す。もしストアしたデータがない場合は空レスポンスでOK
  • あとロック/アンロックとかもあるようなので、これはどこかでステートごとにフラグでも持たせておいて、フラグに応じてレスポンスコードを変える

つまるところ、Backendのページに書かれていた・・・

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

って書いてんだから行間読め と言っているのでしょう。まぁ、確かにそうなるわな。

しかしながら、実際にはユーザを識別するための何かしらとか、認証とか考えないといけないのでしょうけど(Provider自体の認証とは別にという意味です)、まずは大体の挙動はわかったので本記事はここまでにします。

そのうち、実際にこうすればいいんじゃないかという案を作って書いてみます。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?