インフラストラクチャのコードは混乱しがちです。果てしなく続くYAMLファイル、目が痛くなるJSON、そしてガムテープと祈りでつながれたbashスクリプト。ですが、もしインフラストラクチャのコードに強く型付けされた言語の安全性と表現力を持ち込むことができたらどうでしょうか?

ここで登場するのがKotlinとArrow-ktです。KotlinのDSL構築能力とArrow-ktの関数型プログラミングツールを使えば、次のようなIaCソリューションを作成できます:

  • 型安全: 本番サーバーが炎上する前にコンパイル時にエラーをキャッチ
  • コンポーザブル: シンプルで再利用可能なコンポーネントから複雑なインフラを構築
  • 表現力豊か: 人間にとって理解しやすい方法でインフラを記述

準備を整える

始める前に、ツールを準備しましょう。必要なものは:

  • Kotlin(できれば1.5.0以降)
  • Arrow-kt(バージョン1.0.1を使用します)
  • お気に入りのIDE(Kotlin開発にはIntelliJ IDEAが強く推奨されます)

build.gradle.ktsファイルに次の依存関係を追加します:


dependencies {
    implementation("io.arrow-kt:arrow-core:1.0.1")
    implementation("io.arrow-kt:arrow-fx-coroutines:1.0.1")
}

DSLを一つずつ構築する

まず、インフラの基本的な構成要素を定義することから始めましょう。サーバーとネットワークのシンプルなモデルを作成します。

1. ドメインの定義


sealed class Resource
data class Server(val name: String, val size: String) : Resource()
data class Network(val name: String, val cidr: String) : Resource()

これで作業するための基本的な構造ができました。次に、これらのリソースを定義するためのDSLを作成しましょう。

2. DSLの作成


class Infrastructure {
    private val resources = mutableListOf()

    fun server(name: String, init: ServerBuilder.() -> Unit) {
        val builder = ServerBuilder(name)
        builder.init()
        resources.add(builder.build())
    }

    fun network(name: String, init: NetworkBuilder.() -> Unit) {
        val builder = NetworkBuilder(name)
        builder.init()
        resources.add(builder.build())
    }
}

class ServerBuilder(private val name: String) {
    var size: String = "t2.micro"

    fun build() = Server(name, size)
}

class NetworkBuilder(private val name: String) {
    var cidr: String = "10.0.0.0/16"

    fun build() = Network(name, cidr)
}

fun infrastructure(init: Infrastructure.() -> Unit): Infrastructure {
    val infrastructure = Infrastructure()
    infrastructure.init()
    return infrastructure
}

これで次のようにインフラを定義できます:


val myInfra = infrastructure {
    server("web-server") {
        size = "t2.small"
    }
    network("main-vpc") {
        cidr = "172.16.0.0/16"
    }
}

Arrow-ktで型安全性を追加する

DSLは良い感じですが、Arrow-ktの関数型プログラミングの良さを加えてさらにレベルアップしましょう。

1. 検証されたリソース

まず、ArrowのValidatedを使ってリソースが正しく定義されていることを確認します:


import arrow.core.*

sealed class ValidationError
object InvalidServerName : ValidationError()
object InvalidNetworkCIDR : ValidationError()

fun Server.validate(): ValidatedNel =
    if (name.isNotBlank()) this.validNel()
    else InvalidServerName.invalidNel()

fun Network.validate(): ValidatedNel =
    if (cidr.matches(Regex("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$"))) this.validNel()
    else InvalidNetworkCIDR.invalidNel()

2. 検証の合成

次に、Infrastructureクラスをこれらの検証を使用するように更新します:


class Infrastructure {
    private val resources = mutableListOf()

    fun validateAll(): ValidatedNel> =
        resources.traverse { resource ->
            when (resource) {
                is Server -> resource.validate()
                is Network -> resource.validate()
            }
        }

    // ... クラスの残りは同じ
}

さらに進める: リソースの依存関係

実際のインフラはしばしばリソース間に依存関係があります。これをArrowのKleisliを使ってモデル化しましょう:


import arrow.core.*
import arrow.fx.coroutines.*

typealias ResourceDep = Kleisli

fun server(name: String): ResourceDep = Kleisli { infra ->
    infra.resources.filterIsInstance().find { it.name == name }.toOption()
}

fun network(name: String): ResourceDep = Kleisli { infra ->
    infra.resources.filterIsInstance().find { it.name == name }.toOption()
}

fun attachToNetwork(server: ResourceDep, network: ResourceDep): ResourceDep =
    Kleisli { infra ->
        val s = server.run(infra).getOrElse { return@Kleisli None }
        val n = network.run(infra).getOrElse { return@Kleisli None }
        println("Attaching ${s.name} to ${n.name}")
        Some(Unit)
    }

これでDSLで依存関係を表現できます。構成の力このアプローチの美しい点の一つは、シンプルな部分から複雑なインフラを簡単に構成できることです。Webアプリケーションのための高レベルの抽象化を作成しましょう。まとめ型安全なインフラDSLで可能なことの表面をかじっただけです。Kotlinの言語機能とArrow-ktの関数型プログラミングツールキットを活用することで、強力で表現力豊かで安全なインフラ定義方法を作成しました。主なポイント:型安全性はエラーを早期にキャッチし、高価なランタイムエラーを防ぎます。コンポーザビリティにより、シンプルで再利用可能な部分から複雑なインフラを構築できます。ValidatedKleisliのような関数型プログラミングの概念は、複雑な関係や制約をモデル化するための強力なツールを提供します。考えるべきことインフラDSLを開発し続ける中で、次の質問を考えてみてください:このDSLをどのように拡張して異なるクラウドプロバイダーをサポートしますか?このアプローチを使用してCloudFormationテンプレートやTerraform構成を生成できますか?DSLにコスト見積もりをどのように組み込むことができますか?既存のIaCツールをKotlinで再現するだけでなく、エラーを早期にキャッチし、意図を明確にするより表現力豊かで型安全なインフラ定義方法を作成することが目標です。コーディングを楽しんでください、そしてサーバーが常に稼働し、レイテンシーが低いことを願っています!