これは、アドベントカレンダー6日目の記事です。
5日目は、DSCのエンジンであるLCM(Local Configuration Manager) について説明しました。
今日は、コンフィグレーション(Configuration)構文と記述について見ていきましょう。今日からは、コード沢山 + PowerShell DSCのベストプラクティスも交えていきますよ。
- 事前知識
- PowerShell v5でのインテリセンス強化
- シンプルなコンフィグレーションサンプル
- コンフィグレーションの適用順序保証
- コンフィグレーション内でのPowerShell構文利用
- スクリプトリソースを使ったコンフィグレーション記述のポイント
- スクリプトリソースの問題点
- ネストされたコンフィグレーションの連動
- 異なるコンフィグレーションファイルのコンフィグレーションを連動
- コンフィグレーションによるNTPの設定/維持
- まとめ
事前知識
3日目の用語まとめでコンフィグレーションとは、「ロジックを呼び出してあるべき状態を宣言型構文で指示」するもの、Chefでいうレシピであると説明しました。
ざっくりいうと、「ノードのあるべき状態を記述したもの」と捉えて構わないでしょう。
標準リソース
コンフィグレーションを紹介するにあたり、PowerShell DSCにおいて標準で利用できるリソースは次の通りです。
Resource | 状態の対象 |
---|---|
ファイル(File) | ファイル |
アーカイブ(Archive) | Zip |
エンバイロンメント(Environment) | 環境変数 |
パッケージ(Package) | MSIインストール |
レジストリ(Registry) | レジストリ |
スクリプト(Script) | PowerShellスクリプト実行で指定 |
サービス(Service) | サービス状態 |
ユーザー(User) | ローカルユーザー |
グループ(Group) | ローカルグループ |
フィーチャー(WindowsFeature) | Windowsの機能 |
プロセス(WindowsProcess) | プロセスの起動 |
ログ(Log) | Verboseストリームへのログ表示 |
今回はこれらを使ってみましょう。
まさかの展開
実は私、すでにコンフィグレーション構文について説明していた。という。はい。
BuildInsiderにて、構文自体の説明は行っていたんですねー。なるべく丁寧に書いているので同じ内容に書くのは無駄な感じがします。
そこで、今回はもう少し実践的な話をしましょう。事前にBuildInsiderの記事にかかれているコンフィグレーション構文の基礎を知っている前提となります。
- PowerShell v5でのインテリセンス強化
- シンプルなコンフィグレーションサンプル
- コンフィグレーションの適用順序保証
- コンフィグレーション内でのPowerShell構文利用
- スクリプトリソースを使ったコンフィグレーション記述のポイント
- スクリプトリソースの問題点
- ネストされたコンフィグレーションの連動
- 異なるコンフィグレーションファイルのコンフィグレーションを連動
- コンフィグレーションによるNTPの設定/維持
PowerShell v5でのインテリセンス強化
PowerShell 4.0でコンフィグレーションを書くのはインテリセンスやタブ補完が弱く「使いにくい」のが現実です。v5では、インテリセンスもタブ補完もばっちり効き大幅に改善されたことが実感できます。ようやく捗ります。
インテリセンスは
Ctrl + Space
で入力可能な内容がポップアップ表示されますタブ補完は
Tab
キーを押すことで入力補完されます
リソースの補完
v4では、リソース以外の関係ないCmdletなども表示されていましたが、v5では利用可能なリソースだけインテリセンスに表示されます。
タブ補完もv4では壊れてましたがv5で正しく機能します。
リソースの定義表示
v4でもできましたが、リソース名でCtrl + Space
を押すと利用可能な定義が表示されます。一々定義を抽出する? 人間のやることではありません。
リソースが利用可能なキーワードのインテリセンス表示
v4までは、キーワードはタブ補完されても、インテリセンスが効きませんでした。v5ではばっちりインテリセンスが効きます! やった!
リソースのキーワードが利用可能な値のタブ補完
リソースのキーワードによっては、利用できる値が決まっていることがあります。v4まではコンフィグレーションを実行してコンパイルするまでエラーが検出できませんでしたが、v5ではタブ補完が効きます。*1
ここでTab
キーを押せば自動補完されます。
シンプルなコンフィグレーションサンプル
PowerShell 4.0にて標準で使えるリソースから、2つコンフィグレーションのサンプルを紹介しましょう。
サービスリソースの利用
「エラーなどで突然にWindowsサービスが停止!」は、当然ありえる話なのでサービスの起動保証がほしい思ったことは誰でもあるでしょう。DSCを使えば簡単にできます。
WinRM
、WinRM
サービスは、DSCの挙動の中心を支えるサービスです。このサービスの起動を保証するコンフィグレーションは次のように書けます。
どうですか? これだけシンプルなら、誰が見ても何をしたいのかおおよそ予測がつくのではないでしょうか。
たったこれだけで、WinRM
、WinRM
サービスが例えエラーを起こしても起動している状態を保証できるのです。つまりエラーからの自動的な状態復旧が満たされるのです。
DSCを使わない手はありません。*2
ユーザーリソースの利用
アクティブディレクトリ(ActiveDirectory、以降ADと呼びます) を使えば、ユーザーなんてサーバーで共通で持てる? いいえ、ADは状況によっては使う選択肢から外す必要があります。たとえば、サーバーの台数が増減するスケーラブルな環境がその一例です。
ADは、 イミュータブルインフラストラクチャ(Immutable Infrastructure)、いつでも破棄可能(Disposable)なサーバーを構築する場合、とても重たい存在です。何しろいつでもサーバーを捨てる、再構築するということは、なるべく状態を持たないよう(Stateless)にしないといけません。にも関わらず、ADに参加したコンピュータはディレクトリサービスという状態を持ちます(State-full)。ステートレスにしたい環境でステートフルを強いる(しかも容易に代替可能な機能のためだけに! )を使うのはナンセンスです。
そんな時には、ユーザーやグループの存在が保証される必要がありますよね? そこでDSCを使うと、ADなんて参加せずともローカルコンピュータでユーザーとグループの状態が保証できます。
例えば、リモート用にユーザーとグループを作るならこうかけます。
コンフィグレーション実行時に、PSCredentialを渡すことで、ユーザー名とパスワードを外から渡すことができます。そう、再利用すら可能なのです。
Remote -Credential (Get-Credential -Credential "RemoteUserName") -OutputPath Remote
コンフィグレーションの適用順序保証
コンフィグレーションの順序保証は DependsOn を使う
これは、PowerShell DSCのベストプラクティスです。
Chefでも散々言われていますが、DSCでもコンフィグレーションの適用順序はDependsOnで依存関係を保証します。
一見すると、上から順番に適用されいるように解釈してしまいそうですが、依存しているコンフィグレーションが実行されてから、依存するコンフィグレーションが実行される保証はありません。*3そのため、今回のようにグループに含めるユーザーが事前に存在していることを保証する場合は、DependsOn
を使って適用順序を厳密に定めてください。
DependsOnは、次のように記述します。PowerShell v5ではインテリセンスがばっちり聞いてタブで自動補完されるので大変はかどります。
リソース名1 コンフィグレーション名1 { # 何か素敵な定義 } リソース名2 コンフィグレーション名2 { # 何か素敵な定義 DependsOn = "[リソース名1]コンフィグレーション名1" }
コンフィグレーション内でのPowerShell構文利用
ここまでのサンプルでは、冗長なコンフィグレーション記述をしていました。
Configurationは、PowerShellのキーワードにすぎません。つまり、Configuraion構文内部でもPowerShellの通常構文は使えるのです。
とはいえ、あまり使いすぎると読みにくくなります。せっかく宣言型構文を使っているのに、可読性が落ちるのは意味がないので可読性は優先する必要があります。
そこで、可読性、再利用性、冗長性の排除からバランスを考えると、使っても以下の3つ程度に抑えるべきでしょう。
- 静的なprivate変数
- foreachなどループ構文の利用
- Cmdletでの外部リソースの利用
サービスに関するコンフィグレーションは、PowerShell構文を使うことでこのように書けます。
元のがこうです。
保証を担保したいサービスが増えれば増えるほど、ループで回したくなりますよね。
著しく可読性を損なわない限り、冗長な記述をPowershell構文を使ってシンプルにするのはありだと思います。むしろ使わないのはありえない。*4
スクリプトリソースを使ったコンフィグレーション記述のポイント
スクリプトリソースは、なんでもできます。でもあまりどう書けばいいのか、何が問題なのかは知られていないように思います。
そこでまず、スクリプトリソース利用時の基本構文について典型をお伝えしましょう。
スクリプトリソースの基本構文
必須なのは、SetScript
、SetScript
、SetScript
です。
そしてGetScriptセクションでは、SetScript
、SetScript
、SetScript
、SetScript
を定義します。
たとえば、ネットワークアダプタの6to4を無効するにはこう書きます。
TestScript
対象ノードの状態をテストするロジックを書きます。返却値は[bool]
である必要があります。
SetScript
対象ノードの状態を設定するロジックを書きます。テストがfalse
、つまりあるべき状態でなかった場合にのみ実行されます。
GetScript
後日説明しますが、Get-DSCConfiguration
にてノードの状態を取得する時に必要になります。
SetScript
には、SetScript
を当ててください。これは典型パターンですTestScript
には、TestScript
を当ててください。これは典型パターンですGetScript
には、GetScript
を当ててください。これは典型パターンですResult
には、TestScriptで設定したのと同じロジックを入れます
典型が多いのはいいのですが、冗長この上ないですね。必要悪とはこのことなのか...。
スクリプトリソースの問題点
スクリプトリソースの利用には、たくさん問題があります。
- ロジックをコンフィグレーションに直接定義
- 変数の参照は
$using:
スコープ - ポジショナルパラメータの利用
- 型の明示
- MOFの可読性低下
ロジックをコンフィグレーションに直接定義
これは、リソースコンセプトの破壊につながりかねないことです。極力やめましょう。
ではどうするのか? 再利用する場合は、リソースにするのです。コンポジットリソース(Composite Resource) などコンフィグレーションをリソースにして再利用する方法や、リソースを自分で定義してもいいでしょう。*5
ただ、その場限りで再利用もなく、シンプルな場合は許容できると思います。
スクリプトリソースを使ってコンフィグレーションを書きたくなったら、再利用する場合はリソースにしましょう
変数の参照は$using:
スコープ
Configuration構文内部でも、PowerShell構文は使えることを説明しました。変数が一番使いやすいでしょう。しかし、スクリプトリソースで変数を参照する時は注意が必要です。
スクリプトリソースの実態は、リモートで利用することが前提のスクリプトブロックです。この場合、ローカルとはコンフィグレーションを実行するDSCサーバー、リモートとはMOFを読み込むノードです。
そして、リモートで利用するスクリプトブロックでローカルの変数を参照するには、$using:
スコープでの参照が必要なのがPowerShellのルールです。もちろん、スクリプトブロック$using:
内部での変数は$using:
スコープは不要ですよ?
例えば、先ほどのスクリプトリソースでローカル変数を、コンフィグレーションで参照する場合はこう書きます。
ポジショナルパラメータの利用
これはリモートスクリプトブロックの制限ですが、ポジショナルパラメータの利用はできません。
先ほどの例なら、これはダメということです。
-State
パラメータを明示する必要があります。
型の明示
型は明示してください。暗黙の型変換はダメです。
たとえば、[String]を受け取るパラメータには、.ToString()
や.ToString()
で明示的にキャストが必要です。まぁ、型をあいまいにするなんて避けますよねーふつー(棒)
MOFの可読性低下
コンフィグレーションを実行した時にMOFファイルがコンパイル生成されます。MOFを読む機会は余りありませんが、ある時はあるのです。
先ほどのスクリプトリソースで生成されるMOFはこうです。読みにくくて仕方ありませんね。
ネストされたコンフィグレーションの連動
LCMには、コンフィグレーションを1つだけ持てます。
ではコンフィグレーション1つに全部の定義を書かないといけないのかというとそうではありません。ネスト(組み合わせ)が可能です。
コンフィグレーション構文の中で、Node
セクションは1回しか書けません。が、省略は可能です。(省略してMOFを生成するとNode
がデフォルトで入ります。)
そこで次のように、ネストしたコンフィグレーションでNode
セクションを書きます。
簡単ですね! ネスト対象の子コンフィグレーションにもパラメータが渡せます。
定義の表示も効きます! が、インテリセンスとタブ補完はv5でも効きません。しょぼーん。
ネストコンフィグレーションでのDependsOn
v4では、ネストされたコンフィグレーションではコンフィグレーションを跨いだDependsOn
が使えず依存関係の保証ができない制限があります。
これはv5でコンフィグレーションを跨いでDependsOn
できるようになっているので、是非使ってください。
異なるコンフィグレーションファイルのコンフィグレーションを連動
PowerShellには、読み込んだファイルの実行を現在のスコープで実施するために.
(ドットソース - DotSource)というオペレータがあります。
これを使うことで、別ファイルに記述されたコンフィグレーションを、読み込み先のスコープで利用できます。
で、こうかと思うじゃないですか?
これはだめです。Remoteコンフィグレーションのパラメータ渡しでInvalidOperationExceptionが発生します。
DependsOn
をはずせば問題ありません。このため、依存関係のあるコンフィグレーションは1つにまとめておく必要があります。
子コンフィグレーションのファイルが異なる場合、親であるネストしたコンフィグレーションからはパラメータが渡せません。
同一ファイルに記述された子コンフィグレーションなら渡せるのですが...。
これに対して、PowerShellは一度定義したコンフィグレーションをリソースとして利用できる仕組みを持っています。それがコンポジットリソース
です。詳細は明日紹介しましょう。
コンフィグレーションによるNTPの設定/維持
Windowsに限らずLinuxなどでも大事なものがNTPによる時刻同期です。特にAWSやAzureなど仮想環境ではほっておくと時刻がどんどんずれます。
謎社では、数多くのWebサーバーを用いて1つのサービスを提供しています。大量のサーバーがあっても「時間が同期されていることを保証」することは正常なサービス提供の要です。
DSCがないころは、スクリプトでNTPの維持していましたが正直二度とやりたくないです。*6
今回、NTPをDSCでチューニングから維持まで行うコンフィグレーションを公開しましょう。*7かなり長いですが、本番でも利用しているフェイルオーバーNTPまで定義した信頼のあるものです。かなり使えますよ、というかないと話になりません。
まとめ
おおよそコンフィグレーションで抑えるべきポイントは網羅したと思います。基本的には罠といえる罠はそんなにありません。
v4では「インテリセンス」「タブ補完」「DependsOn」「パラメータ参照」などの機能が足りないのは事実です。これもv5でほとんど改善されるので是非心待ちにしましょう。
明日は、MOFの生成について説明します。