ビビリフクロウの足跡

いつもお世話になっているインターネットへの恩返し

cdktfをOpenStackでつかってみた

はじめに

プロジェクトでAWS CDKを使う機会が増えています。linterやテストコード書いてCI回したり、静的型付けを実施できたりと、今までインフラの世界では通用しなかった高級言語のナレッジが適用できるようになって嬉しいですよね! そこで、自宅で立てているOpenStackでも同様の恩恵が得られないかと調べていたところ、cdktfで取り急ぎ高級言語化できそうだったので、今回試してみました。。。

実際に使ってみた

cdktfはterraformをcdkフレームワークにくるんだプロダクトです。terraformにはOpenStackのプロバイダも用意されているので、このプロバイダを使ってOpenStackもcdkの世界につれてくることができます!

まずはcdktfプロジェクトを作成しましょう。

$ mkdir cdk-openstack
$ cd cdk-openstack
$ npm install -g cdktf-cli
$ npx cdktf init --template="typescript"  --local
Note: By supplying '--local' option you have chosen local storage mode for storing the state of your stack.
This means that your Terraform state file will be stored locally on disk in a file 'terraform.tfstate' in the root of your project.

We will now set up the project. Please enter the details for your project.
If you want to exit, press ^C.

Project Name: (default: 'cdk-openstack') 
Project Description: (default: 'A simple getting started project for cdktf.') 
npm notice created a lockfile as package-lock.json. You should commit this file.
+ cdktf@0.0.18
+ constructs@3.2.51
added 4 packages from 4 contributors and audited 4 packages in 0.482s
found 0 vulnerabilities

npm WARN eslint-plugin-react@7.21.5 requires a peer of eslint@^3 || ^4 || ^5 || ^6 || ^7 but none is installed. You must install peer dependencies yourself.

+ cdktf-cli@0.0.18
+ @types/node@14.14.10
+ typescript@4.1.2
added 226 packages from 142 contributors and audited 231 packages in 8.713s

54 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities


> cdk-openstack@1.0.0 build /home/ubuntu/workspaces/gitlab-com/cdk-openstack
> cdktf get && tsc

Generated typescript constructs in the output directory: .gen
========================================================================================================

  Your cdktf typescript project is ready!

  cat help              Print this message

  Compile:
    npm run compile     Compile typescript code to javascript (or "npm run watch")
    npm run watch       Watch for changes and compile typescript in the background
    npm run build       cdktf get and compile typescript

  Synthesize:
    cdktf synth         Synthesize Terraform resources from stacks to cdktf.out/ (ready for 'terraform apply')

  Diff:
    cdktf diff          Perform a diff (terraform plan) for the given stack

  Deploy:
    cdktf deploy        Deploy the given stack

  Destroy:
    cdktf destroy       Destroy the stack


 Upgrades:
   npm run get           Import/update Terraform providers and modules (you should check-in this directory)
   npm run upgrade       Upgrade cdktf modules to latest version
   npm run upgrade:next  Upgrade cdktf modules to latest "@next" version (last commit)

 Use Prebuilt Providers:

  You can add one or multiple of the prebuilt providers listed below:

  npm install -a @cdktf/provider-aws
  npm install -a @cdktf/provider-google
  npm install -a @cdktf/provider-azurerm
  npm install -a @cdktf/provider-docker
  npm install -a @cdktf/provider-github
  npm install -a @cdktf/provider-null

  Check for an up to date list here https://github.com/terraform-cdk-providers

========================================================================================================

$

できたプロジェクト内部はこんな感じです。

$ ls
cdktf.json  help  main.d.ts  main.js  main.ts  node_modules  package-lock.json  package.json  tsconfig.json
$

cdktf.json をのぞいてみると、awsプロバイダしか定義されていないのがわかります。

{
  "language": "typescript",
  "app": "npm run --silent compile && node main.js",
  "terraformProviders": [
    "aws@~> 2.0"
  ],
  "context": {
    "excludeStackIdFromLogicalIds": "true",
"allowSepCharsInLogicalIds": "true"
  }
}

なので、 terraformProviders から aws を削除し、terraform-provider-openstack/openstack@1.33.0 を追加し、cdktf get を叩きます。

{
  "language": "typescript",
  "app": "npm run --silent compile && node main.js",
  "terraformProviders": [
    "terraform-provider-openstack/openstack@1.33.0"
  ],
  "context": {
    "excludeStackIdFromLogicalIds": "true",
"allowSepCharsInLogicalIds": "true"
  }
}
$ npx cdktf get
Generated typescript constructs in the output directory: .gen
$ 

さて、そうしたら実際にインフラコードを書いていきましょう! main.ts をのぞいてみると、、、

import { Construct } from 'constructs';
import { App, TerraformStack } from 'cdktf';

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    // define resources here

  }
}

const app = new App();
new MyStack(app, 'cdk-openstack');
app.synth();

とにもかくにもopenstackモジュールをimportしましょう。以下のimport文を追加します。

import * as openstack from './.gen/providers/openstack';

そうしたら、 // define resources here 以降に認証情報やOpenStackリソースを定義していきます。まずは認証情報から、以下のように与えます。

new OpenstackProvider(this, "openstack-provider", {
      userName: process.env.OS_USERNAME,
      password: process.env.OS_PASSWORD,
      tenantName: process.env.OS_PROJECT_NAME,
      userDomainName: process.env.OS_USER_DOMAIN_NAME,
      projectDomainName: process.env.OS_PROJECT_DOMAIN_NAME,
      authUrl: process.env.OS_AUTH_URL,
      cacertFile: process.env.OS_CACERT
});

openrc認証情報を使えるように、環境変数でパラメータを与えておきます。

次に、リソース定義をこんな感じで定義していきますー。

    // define network for octavia
    const octaviaNetwork = new openstack.NetworkingNetworkV2(this, "OctaviaVlanProviderNetwork", {
      name: "lb-mgmt-net",
      external: true,
      shared: true,
      mtu: 1450,
      segments: [
        {
          physicalNetwork: "octavia_vlan_provider",
          segmentationId: 121,
          networkType: "vlan"
        }
      ]
    });

    // define subnet for octavia
    const octaviaSubnetCidr = "192.168.121.0/24";
    new openstack.NetworkingSubnetV2(this, "OctaviaVlanProviderNetworkSubnet", {
      name: "lb-mgmt-subnet",
      cidr: octaviaSubnetCidr,
      networkId: octaviaNetwork.id,
      allocationPool: [
        {
          start: "192.168.121.100",
          end: "192.168.121.254"
        }
      ],
      gatewayIp: "192.168.121.1"
    });

    // define security group for amphora
    const amphoraSecurityGroup = new openstack.NetworkingSecgroupV2(this, "AmphoraSecurityGroup", {
      name: "amphora-sg",
    });

    const amphoraSgIngressMaps = [
      {
        protocol: "tcp",
        portRangeMax: 22,
        portRangeMin: 22,
        remoteIpPrefix: "0.0.0.0/0"
      },
      {
        protocol: "tcp",
        portRangeMax: 9443,
        portRangeMin: 9443,
        remoteIpPrefix: octaviaSubnetCidr
      },
      {
        protocol: "tcp",
        portRangeMax: 5555,
        portRangeMin: 5555,
        remoteIpPrefix: octaviaSubnetCidr
      }
    ];
    for (const [index, amphoraSgIngressMap] of amphoraSgIngressMaps.entries()) {
      new openstack.NetworkingSecgroupRuleV2(this, `AmphoraSgRules${index}`, {
        securityGroupId: amphoraSecurityGroup.id,
        protocol: amphoraSgIngressMap.protocol,
        portRangeMax: amphoraSgIngressMap.portRangeMax,
        portRangeMin: amphoraSgIngressMap.portRangeMin,
        remoteIpPrefix: amphoraSgIngressMap.remoteIpPrefix,
        ethertype: "IPv4",
        direction: "ingress"
      });
    }

あとは、openrc認証情報ファイルを読み込んで、npm run build および cdktf deploy を実行します。

$ npm run build

> cdk-openstack@1.0.0 build /home/ubuntu/workspaces/gitlab-com/cdk-openstack
> cdktf get && tsc

Generated typescript constructs in the output directory: .gen
$
$ npx cdktf deploy
Deploying Stack: cdk-openstack
Resources
 ✔ OPENSTACK_NETWORKING OctaviaVlanProvider openstack_networking_network_v2.OctaviaVlanProviderNetwork
 ✔ OPENSTACK_NETWORKING AmphoraSgRules02    openstack_networking_secgroup_rule_v2.AmphoraSgRules0
 ✔ OPENSTACK_NETWORKING AmphoraSgRules12    openstack_networking_secgroup_rule_v2.AmphoraSgRules1
 ✔ OPENSTACK_NETWORKING AmphoraSgRules22    openstack_networking_secgroup_rule_v2.AmphoraSgRules2
 ✔ OPENSTACK_NETWORKING AmphoraSecurityGrou openstack_networking_secgroup_v2.AmphoraSecurityGroup
 ✔ OPENSTACK_NETWORKING OctaviaVlanProvider openstack_networking_subnet_v2.OctaviaVlanProviderNetworkSubnet

Summary: 6 created, 0 updated, 0 destroyed.
$ 

ちゃんとデプロイされました!!

所感

terraform providerが用意されているプラットフォームについてはこれで一応インフラコードを高級言語化できそうで良い感じですね! linterは少なくともGitLab CIで実行できるようにしておきたいものです。。。