導入
Hashicorp Configuration Language (HCL), Terraformで使用されている言語であり、他のプログラミング言語に存在する多くの便利な構造や機能を提供します。インフラストラクチャーコードでループを使用することで、コードの重複を大幅に削減し、可读性を向上させ、将来のリファクタリングをより簡単にし、より幅広い灵活性を提供することができます。HCLはまた、他の言語では配列と辞書として知られているリストやマップなどの一般的なデータ構造や、実行パスの分岐に使用する条件演算子も提供しています。
Terraform独自の機能として、依存関係を手動で指定することができます。あなたのコードを実行すると、既にコードを実行する際に構築される実行グラフには、大多数のシーンで正しい链接が含まれていますが、Terraformが認識できない依存関係を強制する必要があるかもしれません。
この記事では、HCLが提供するデータ構造、リソースに使用するループ機能(count
キー、for_each
、for
)、既知と未知の値を処理する条件演算子、以及びリソース間の依存関係について説明します。
前提条件
- デジタルオceanの个人アクセストックンを作成することができます。これはDigitalOcean控制面板上で行えます。操作方法についてはDigitalOcean製品ドキュメント、How to Create a Personal Access Tokenを参照してください。
-
Terraformをlocal machineにインストールし、DigitalOceanプロVIDERを使うプロジェクトを設定します。Terraformを使用するためには、How To Use Terraform with DigitalOceanのテスト1とテスト2を完了し、プロジェクトフォルダをterraform-flexibilityに名づけてください。Step 2の中では、providerの設定時に
pvt_key
変数やSSH key資源を含めなくても良いです。
注釈:本教程はTerraform 1.0.2でテストされています。
HCLのデータ型
HCLでのloopや他の機能を知りたい場合は先に、 availble data types と heir uses を見ておくと良いです。
Hashicorp Configuration Languageは primitive 型とcomplex 型を支持します。基本の型であるstring、number、boolean値などが primitive 型であり、他の型から派生された型ではない。另一方面、complex型は複数の値を一つに集めた型です。これらの中で2種類のcomplex型があります:structural types(構造型)とcollection types(集合型)。structural typesは異なる型の値をグループ化してくれるのです。HCLで使われている主な例はresource definitionsです。complex typesも複数の値を集めたものですが、structural typesと比べて同じ型の値だけを集めます。HCLで利用可能な3種類のcollection types(集合型)はlists(リスト)、maps(マップ)およびsets(セット)です。
lists(リスト)は他のプログラミング言語のarray(配列)と似ています。同じ型の要素を含むknown number of elements( Known number of elements)を持ち、0から始まる整数索引でアクセスすることができます。次のステップで deploying Droplets(部署Droplets)の名前を示す例です:
Lists
list variable declaration( list变量宣言)は[]
( array notation)( array notation)で名前を指定し、0から始まる整数索引でアクセスできます。以下はHCLでのlist variable declaration( list变量宣言)の例です:
“`hcl
# type: list
# default: [“myapp”]
droplets = [“myapp”]
“`
Maps
マップはキー値ペアの集合で、それぞれの値はそのキーを使って取り出すことができます。キーを指定する方法には二種類あります: コロン (:
) を使用した場合や等号 (=
) を使用した場合です。両方の場合でも、値は引号内に enclose されています。コロンを使用した場合、キーもencloseされます。以下は等号を使用した Map 定義です:
環境ごとにDroplet名を含むMapを等号で記述しています:
数字で始まるキーは、コロン構文を使用してください:
Sets
Sets do not support element ordering, meaning that iterating through sets is not guaranteed to yield the same order each time and that their elements cannot be accessed in a targeted way. They contain unique elements repeated exactly once, and specifying the same element multiple times will result in them being coalesced with only one instance being present in the set.
Set declaration is similar to list declaration, the only difference being the type of the variable:
Now that you’ve learned about the types of data structures HCL provides and reviewed the syntax of lists, maps, and sets, which we’ll use throughout this tutorial, you’ll move on to trying some flexible ways of deploying multiple instances of the same resource in Terraform.
リソースの数を設定するには、count
キーを使用します
この节では、同じ資源を複数回作成する方法を見ていきます。count
キーは全てのresource固有のパラメーターであり、resourceを何個にするか指定することができます。これを示すため、Droplet resourceを作成し、それを前節で作成したプロジェクトディRECTORY内に保存します。ファイル名はdroplets.tf
です。作成して編集します:
以下の行を追加:
このコードはDroplet resourceを定義し、Ubuntu 20.04を使うと、1GB RAMと1CPU coreを持つtest_droplet
を定義します。
注意:count
の値は3
に設定されていることにより、Terraformは同じresourceを3回作成することを試みます。ファイルを保存し閉じてください。
完了後、プロジェクトをplanningすることで、Terraformが実行する操作を見ることができます:
出力は次のようになります:
Terraformは、test_droplet
リソースの3つのインスタンスを作成し、すべて同じ名前 web
を持つことを示しています。可能であるにも関わらず、推奨されていませんので、各インスタンスの名前をユニークにするDroplet定義を変更しましょう。droplets.tf
を編集します。
選択中の行を変更します。
ファイルを保存し閉じます。
count
オブジェクトは、現在の反復のインデックスを持つindex
パラメータを提供します。現在のインデックスは、文字列の埋め込みを使用して、変数を代入して動的に文字列を構築することで、Dropletの名前に代入されます。プロジェクトを再計画して変更を確認することができます。
出力は以下のようになります。
ここで、test_droplet
の3つのインスタンスは、名前にインデックスを持つようになって、追跡することができます。
これまでに、count
キーを使用してリソースの複数のインスタンスを作成する方法を学び、プロビジョニング中にインスタンスのインデックスを取得し使用する方法も学びました。次に、Dropletの名前をリストから取得する方法を学びます。
リストからDroplet名を取得する
同じリソースの複数のインスタンスにカスタム名を割り当てる必要がある場合、定義したリスト変数から動的に取得することができます。このチュートリアルの残りの部分で、Dropletの名前のリストからデプロイを自動化するいくつかの方法を見ていきます。これにより灵活性と使用の簡便性を高めます。
まず、Droplet名を含むリストを定義する必要があります。variables.tf
という名前のファイルを作成し、編集してください。
以下の行を追加してください。
ファイルを保存して閉じます。このコードは、first
、second
、third
、fourth
を含むdroplet_names
という名前のリストを定義します。
droplets.tf
を編集してください。
選択された行を変更してください。
灵活性を高めるために、定数の数の要素を手動で指定する代わりに、droplet_names
リストの長さをcount
パラメータに渡します。これは常にリスト内の要素数を返します。名前には、count.index
の位置のリストの要素を取得し、配列のbracket記法を使用します。終わったらファイルを保存して閉じます。
プロジェクトの計画を再試行してみてください。これにより、以下のような出力を受け取ることができます。
これらの変更の結果、4つのDropletが部署され、droplet_names
リストの要素の名前順に順次に名付けられます。
ここで、count
の特徴や構文を学び、リストと共にリソースインスタンスを変更する方法を使用しました。今度は、count
の欠点を理解し、それを克服する方法を学びます。
count
の欠点を理解する
countの使用方法を理解したため、今は、countが使用されているリストを変更する際の欠点を見てみましょう。
Dropletをクラウド上にデプロイする試みにしましょう。
質問にyes
を入力してください。出力の終わりはこのようになります。
OutputApply complete! Resources: 4 added, 0 changed, 0 destroyed.
次に、droplet_names
リストを拡大して1つのもっと Droplet インスタンスを作成しましょう。variables.tf
を編集します。
リストの先頭に新しい要素を追加します。
修正を完了したら、ファイルを保存して閉じます。
プロジェクトを計画する:
出力は次のようになります:
出力は、Terraformは最初に四つのDropletsを順序付けたリストと見なしています。これらはインデックス番号で識別されます(Droplets)。このように、Terraformは初めて四つのDropletsを以下のように見なします:
Index Number | 0 | 1 | 2 | 3 |
---|---|---|---|---|
Droplet Name | first | second | third | fourth |
新しいDroplet zero
が先頭に追加された後、内部的には次のような表示になります:
Index Number | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
Droplet Name | zero | first | second | third | fourth |
最初の四つDropletsは一つずつ右に移動します。Terraformはその二つの状態をテーブルに比较します:位置 0
は最初は first だったが、第二张表では異なっているので、更新操作を計画します。この方法は続いて位置 4
まで行われます(ここでは初めてのDropletであり、第一张表には comparable 要素が存在しなかった), そのため新規作成操作を計画します。
これは、リストの末尾以外に新しい要素を追加すると、資源が更新されていなくなった場合でも更新操作が計画されます。同様に、droplet_names
リストの一部を削除した場合も更新行動が計画されます。
使用しているcount
を使うと、同じ资源を同じ数で同じように deploy する場合は簡単な解決策です。恒常数量の恒常型の同じ resource を deploy する場合でも、同じ attribute を変更する場合は、後々の教程で見ているfor_each
loop が绝佳な選択です。この場合、同じ attribute を変更する必要がありません。
Current Resource Referencing (self
)
Another drawback of count
is that it’s not possible to reference an arbitrary instance of a resource by its index in some cases.
The main example is destroy-time provisioners, which run when the resource is planned to be destroyed. The reason is that the requested instance may not exist (it’s already destroyed) or would create a mutual dependency cycle. In such situations, instead of accessing the object through the list of instances, you can access only the current resource through the self
keyword.
test_droplet
定義にて、破棄時のローカルプロビジョニングを追加し、実行時にメッセージを表示します。droplets.tf
を編集してください:
以下の強調線の行を追加します:
ファイルを保存して閉じます。
local-exec
プロビジョニングャーは、Terraformが実行されているlocal machineでコマンドを実行します。when
パラメーターが destroy
に設定されているため、リソースが破棄される际にだけ実行されます。これが実行するコマンドは、stdout
に文字列をechoし、現在のリソース名をself.name
を使用して置換します。
次節でDropletsを異なる方法で作成しますので、現在デプロイされているものを破棄します。以下のコマンドを実行します:
要求されたらyes
を入力してください。local-exec
プロビジョニングャーが4回実行されることがあります:
このステップで、count
の欠点を学びました。次に、それを克服し、より多くの変数型に適用できるfor_each
ループ構文について学びます。
変数型に対してfor_each
を使用してループ
この節で、for_each
ループ、その構文、以及び、リソースの定義時に多くのインスタンスを扱う際の柔軟性を向上させる方法について考えます。
`for_each`は各リソースに存在するパラメーターであり、`count`とは異なり、作成するインスタンスの数を要求するものではなく、マップやセットを受け入れます。与えられたコレクションの各要素に一度だけ traversed され、それぞれの要素に対応するインスタンスが作成されます。`for_each`は、`each`キーワード下でkeyとvalueを属性として利用可能にします(ペアのkeyとvalueはそれぞれ`each.key`と`each.value`となります)。セットが与えられた場合、keyとvalueは同じになります。
`each`オブジェクトに現在の要素を提供しているため、リストのように要素に直接アクセスする必要はなく、`for_each`を使用することで、リストで行っていたような操作が可能です。集合にはそれは可能ではないですが、内部では順序を持たないため。リストを渡すこともできますが、まず`toset`関数を使用して集合に変換する必要があります。
`for_each`を使用する主な利点は、3つのコレクションデータ型を枚举することだけでなく、影響を受ける要素だけに変更、作成、または削除を行うことです。入力内の要素の順序を変更すると、アクションが計画されないし、入力から要素を追加、削除、または変更すると、影響を受ける要素に対する適切なアクションの計画のみが行われます。
今回は、`count`から`for_each`に変換したDropletリソースを見て、実践上の機能を確認しましょう。編集用に`droplets.tf`を開くために以下のコマンドを実行してください。
ハイライトされた行を修正します。
`local-exec`プロビジョナーを削除することができます。終わったら、ファイルを保存して閉じてください。
最初行はcount
を置き換えて、for_each
を呼び出すことで、droplet_names
のリストをset型に変換しています(toset
関数を使用して自動変換されます)。Droplet名はeach.value
で指定しますが、今回はdroplet_names
はset型なので、それらの要素は個別に扱われます。
プロジェクトを計画するためには以下を実行します:
出力は次のようになります:
与えたcount
を使った場合は、Terraformは各インスタンスを個別に考えていませんし、それらは次々に作成されます。各インスタンスは、与えたset型内のString要素を示す括号で表示されます(それぞれのresource名の後方に示されます)。
実行してCloudに適用します:
入力した時はyes
を入力します。完了したら、droplet_names
から一つの元素を削除してみます。variables.tf
を編集します:
以下は、上記のテキストを日本語に訳しています。
Modify the list to look like this:
保存して閉じる。
プロジェクトを再び作り直してみたら、次のような出力が得られます:
この時点で、Terraform は仅に削除されたインスタンス (zero
) を破壊し、他の全てのインスタンスは変更されませんが正しい動作です。
このスtepで、for_each
を使った方法、その利点を知ったし、次にfor
ループの使い方を学びます。次のスtepでは、for
ループの構文と使用法を見ていきます。そして、特定のタスクを自動化する場合にどのように使えるかを知っておくと良いです。
使用するfor
loop
The for
loop works with collections, creating a new collection by applying a transformation to each element of the input. The exact type of the output will depend on whether the loop is surrounded by square brackets ([]
) or curly braces ({}
), which give a list or a map, respectively. It is suitable for querying resources and forming structured outputs for further processing.
The general syntax of the for
loop is:
其他プログラミング言語と同様に、あなたは初めて変数を名前付け(element
)し、 enumerating the collection specified by the collection
. The body of the loop is the transformational step, and the optional if
clause can be used for filtering the input collection.
次に、使い方を見てみます。例を見てみます。输出を保存します。以下の行をoutputs.tf
ファイルに追加します:
このコードは、 deploying Droplet names and their IP addresses to a file named outputs.tf
. Create it for editing by running the following command:
Add the following lines to output pairs of deployed Droplet names and their IP addresses:
This code specifies an output called ip_addresses
, and specifies a for
loop that iterates over the instances of the test_droplet
resource you’ve been customizing in the previous steps. Because the loop is surrounded by curly brackets, its output will be a map. The transformational step for maps is similar to lambda functions in other programming languages, and here it creates a key-value pair by combining the instance name as the key with its private IP as its value.
Save and close the file, then refresh the Terraform state to account for the new output by running:
The Terraform refresh
command updates the local state with the actual infrastructure state in the cloud.
Then, check the contents of the outputs:
Terraformでは、ip_addresses
の出力内容を示しています。これはfor
ループにより構成されたマップである。(実際の順序はお客様に合っていますが、例として述べておきます。)loopは全ての要素に対応していますから、手動で入力する必要はなく、自動的に新しいDropletもこの出力に自動的に反映されます。
正方形括号内にfor
を記載することで、出力はリストになります。例如、DropletのIPアドレスだけを出力する場合は、次のように書きます:
ここで、変換の步驟はinstance.ip_address
属性を選択します。これは以下のような出力を与えます:
前述の通り、if
句を使用して入力コレクションを filtering できます。ここでは、instance.region
と fra1
を比較します。これは instance.region
が fra1
と等価であるかどうかをチェックします。そうでなければ、変換は行わず skip されます。このコードの出力は前の例と同じです, 因为根据test_droplet
资源定义,所有Droplet实例都在fra1
区域。因此,这个代码的输出将与前面的例子相同。
条件式も他の値(Droplet サイズや分布など)をフィルター링する時に有用です。
次のセクションでリソースを異なる方法で作成するために、現在デプロイされているリソースを破棄するために以下のコマンドを実行してください:
提示されたときにyes
を入力してプロセスを完了してください。
これまでにfor
ループ、その構文、および使用例を説明しました。次に、条件と、count
と一緒に使用する方法を学びます。
ディレクティブと条件
前のセクションのうちの一つでcount
キーとその機能について見ました。次に、後でTerraformコードで使用することができる3項条件演算子について学び、それがcount
とどのように組み合わされるか学びます。
3項演算子の構文は以下の通りです:
condition
はboolean値(真或者偽)を計算する式です。条件が真の場合、式の評価結果はvalue_if_true
になります。その一方、条件が偽の場合、結果はvalue_if_false
になります。
3項演算子の主な用途は、変数の内容に応じて单一のリソースの作成を有効または無効にすることです。これは、比較の結果(1
または0
)を想定リソースのcount
キーに渡して実現できます。
ternary 演算子を使用してリストやセットから单一の要素を取得している場合、one
関数を使用することができます。与えられたコレクションが空である場合、null
を返します。そうでない場合、コレクションの单一の要素を返します。もし複数の要素がある場合は、エラーを投げます。
「variables.tf」を編集して、変数 create_droplet
を追加しましょう。まず、variables.tf
を開いてください:
強調された行を追加してください:
このコードは create_droplet
変数を bool
型に定義します。ファイルを保存して閉じてください。
次に、Droplet 宣言を変更するために、droplets.tf
を編集してください。以下のように droplets.tf
を開くためのコマンドを実行してください:
以下のようにファイルを修正してください:
変数 count
については、create_droplet
変数が真の場合 1
、假の場合 0
を返す ternary 演算子を使用します。これにより、Droplet の提供が行われないため、IP アドレスを出力することはありません。完了したら、droplets.tf
を保存して閉じてください。
変数が false
に設定されている状態でプロジェクトの実行計画を作成してください。以下のように実行してください:
以下のような出力が受け取られます:
因为 create_droplet
传入了 false
的值,count
的实例数是 0
,所以不会有 Droplet 创建,因此不会有 IP 地址输出。
これまで、ternary conditional operator(三元条件演算子)とcount
キーを使って必要な資源を deployment(部署)する方法を見ていた。次に、资源的明确の依存性設定を学びましょう。
Resource Dependency Settings
Terraformは资源間の依存関係を自動的に検出し、適当な順序で構成させてくれる。大半の場合、Terraformはexprressions(表达式)を扫描してグラフを作成し、相互の依存関係を自動的に判別します。
ただし、一资源がcloud provider(云提供商)へのアクセス控制設定を deploy(部署)しており、その资源の provisioning(提供)に先立ての必要がある場合、Terraformには明示的に依存関係が存在していることがわからない。そのような場合、Terraformはその依存関係を manual specification(手続き指定)してくれます。
depends_on
は各resource(资源)に availble(有効)で、特定resource(资源)をspecify(指定)してhidden dependency relationship(隐藏依赖关系)をmanually specify(手动指定)。Hidden dependency relationships(隐藏依赖关系)はresource(资源)が他resource(资源)のbehavior(行为)を利用していないが、behavioral dependency(行为依赖)を持つ場合に起こります。そのような場合、Terraformはconnect them one way(连接它们的一种方式)します。
以下はdepends_on
を指定する例を見てみましょう:
他のリソースへの参照のリストを受け付け、任意の表現を受け付けることはない。
depends_on
は稀に使用するべきで、すべての他の選択肢が尽きた場合だけ使用するべきです。これの使用は、Terraformの自動依存性検出システムの範囲を超えて宣言しようとしていることを示します; これはリソースが必要以上の数のリソースに明示的に依存していることを示すかもしれません。
ここでは、depends_on
キーを使用してリソースに追加の依存関係を明示的に設定する方法以及その使用时机について学びました。
結論
この記事では、HCLがコードの柔軟性とスケーラビリティを向上させる機能について述べました。例えば、count
を使用して部署するリソースのインスタンス数を指定すること、for_each
を使用して集合データ型をループしてインスタンスをカスタマイズする高度な方法などです。正しく使用することで、コードの重複を大幅に削減し、デプロイされたインフラの管理の操作負荷を大幅に軽減することができます。
また、条件と三項演算子について学びました。これらは、リソースが部署されるか否かを制御するために使用することができます。Terraformの自動依存性分析システムは相当に有効ですが、depends_on
キーを使用してリソースの依存関係を手動で指定する必要がある場合もあるかもしれません。
このチュートリアルは、Terraformを使用してインフラの管理シリーズの一部です。このシリーズでは、Terraformについての様々なトピックをカバーしています。初めてTerraformをインストールするから、複雑なプロジェクトを管理するまでです。