はじめに
こんにちは、IaC技術推進部の山路と申します。本記事では、Alibaba Cloudに関する簡単な紹介と、Alibaba Cloudを利用する場合のユースケースの一つについて紹介します。また、ユースケースに関連して、マルチクラウド構成でAlibaba Cloudを利用することを想定し、Alibaba CloudとAzureをVPNで接続する環境を、Terraformを用いて構築する例も紹介します。
Alibaba Cloudとは
Alibaba Cloudは、アリババグループの運営しているクラウドプロバイダーサービスです。アリババグループではEコマースを中心とした多数の企業が含まれており、日本でもなじみのあるものといえばAliPayがまず挙げられるかと思います。元々はAlibaba Groupで利用していたプライベートクラウド環境を、サービスとしてパブリックに利用できるようにしたものであり、Alibaba Groupで利用・開発された巨大なインフラストラクチャサービスを利用することができます。
Alibaba Cloudの特徴
Alibaba Cloudには以下に記載するように様々な特徴が見られます。
- インフラストラクチャ
- 中国国内のリージョンが多い: 本記事を書いている時点で、Alibaba Cloudで利用できる全21リージョン中9リージョンが中国国内になる(ただし香港はグローバルリージョン扱いとなります)※1
- 世界最大規模のEコマース取引日の通信に耐えうる強固な基盤: 中国で記念日にあたる「独身の日」には、2019年度の売り上げは4.1兆円、ピークトランザクションは約54万/秒という大量のアクセスが発生
- サービス
- CDNノードは2800以上あり、そのうち中国本土には2300以上のノードが存在
- 他のクラウドベンダーには見られない独自のサービスを展開
- Anti-DDoS Premium: Alibaba Cloud以外も適用対象とするAnti-DDoSサービス
- Image Search: 画像データを与えるだけで商品画像検索を実現できるサービス
- ビジネス
- IaaSプロバイダーとして世界第3位: AWS、Azureに続いて世界第3位のシェアを持つIaaSプロバイダーです。なおアジア太平洋地域(APAC)では第1位のシェアとなります。
※参考リンク:
※1:Alibaba Cloudのドキュメントなどでは若干記載が異なっていることがありますが、Alibaba CloudのCloud Shellからコマンドを実行すると、以下のように出力されます。
shell@Alicloud:~$ aliyun ecs DescribeRegions | grep RegionId "RegionId": "cn-qingdao" # 中国(青島) "RegionId": "cn-beijing" # 中国(北京) "RegionId": "cn-zhangjiakou" # 中国(張家口) "RegionId": "cn-huhehaote" # 中国(フフホト) "RegionId": "cn-hangzhou" # 中国(杭州) "RegionId": "cn-shanghai" # 中国(上海) "RegionId": "cn-shenzhen" # 中国(深セン) "RegionId": "cn-heyuan" # 中国(河源) "RegionId": "cn-chengdu" # 中国(成都) "RegionId": "cn-hongkong" # 中国(香港) "RegionId": "ap-northeast-1" # 日本(東京) "RegionId": "ap-southeast-1" # シンガポール "RegionId": "ap-southeast-2" # オーストラリア(シドニー) "RegionId": "ap-southeast-3" # マレーシア(クアラルンプール) "RegionId": "ap-southeast-5" # インドネシア(ジャカルタ) "RegionId": "ap-south-1" # インド(ムンバイ) "RegionId": "us-east-1" # 米国(バージニア) "RegionId": "us-west-1" # 米国(シリコンバレー) "RegionId": "eu-west-1" # イギリス(ロンドン) "RegionId": "me-east-1" # UAE(ドバイ) "RegionId": "eu-central-1" # ドイツ(フランクフルト) shell@Alicloud:~$
Alibaba Cloudを利用するケース:中国向けビジネス展開での利用
上記の特徴を眺めたとき、真っ先に浮かぶユースケースとしては中国国内へのビジネス展開を行う際に利用する場合が挙げられるかと思います。
中国向けにビジネスを展開する場合、障壁となる要素がいくつか存在します。例えば中国国内でWebサイトを開設する場合はICP(Internet Content Provider)と呼ばれる中国特有のライセンスを取得する必要があります。このライセンスを取得するには中国国内企業が発行することのできる許可証が必要となり、現地法人や中国企業との提携を結ぶ必要があります。
また、日中間でのネットワークが不安定であるため、日中間で専用線を設置することを検討する必要もあります。
※参考リンク:
このような障壁も影響し、多くのクラウドベンダーでは中国向けビジネスを展開することが難しくなってしまいます。代表的な障壁としては以下のようなものが挙げられます。
1. サーバー:多くのベンダーは中国国内で利用できるリージョンが存在しません(香港は除く)。また中国国内にリージョンを所有するベンダーは、中国企業と提携することでサービスを提供しており、中国専用のアカウントや管理コンソール画面から操作する必要があります。
2. ネットワーク:中国国内外での通信は基本的にインターネット経由となります。例えば中国リージョンと日本リージョンとの仮想ネットワーク間通信を行う場合、仮想ネットワーク間でのピアリング接続は利用することができません。また専用線を利用する場合も、利用できる専用線サービスに制限がある場合があります。
3. 法律:ICPライセンスの申請に対するサポートは多くのベンダーでは実施しておらず、利用者が別途申請する必要があります。
一方、Alibaba Cloudを利用することで、上記障害の多くを克服することが可能になります。
1. サーバー:中国国内に多くのリージョンを所有しています。また中国国内・国外のリージョンを共通のアカウントで利用することができます。
2. ネットワーク:Alibaba Cloudにも仮想ネットワーク間のピアリング機能はまだ備わっていませんが、CEN(Cloud Enterprise Network)の機能を利用すると、仮想ネットワーク間通信をAlibaba Cloudのネットワークインフラを利用して実現することができます。またExpress Connectという専用線サービスも利用することができます。
3. 法律:Alibaba CloudにはICPライセンスの登録をサポートするサービスが存在します。
Alibaba Cloudを含むマルチクラウド環境の構成
上記の通り、中国向けのビジネスを展開する場合、Alibaba Cloudは有効な選択肢の一つとなりそうです。しかし一方で、多くのユーザーや企業は既に何らかの形でクラウドを利用しているため、既存のクラウド環境とAlibaba Cloudとを組み合わせて利用したいというケースが多いと予想されます。
このように複数のクラウド環境を利用する、いわゆるマルチクラウドを扱う上では、単一のクラウド環境を利用する場合と比べ、様々な問題が発生します。これには各クラウドの仕様の把握やクラウド間ネットワークの設計に加え、システムの構成管理に対しても考慮が必要となります。
例えばシステムの構成管理については、管理対象のクラウドが複数になることで運用が煩雑になります。新しいリソースを作成する際に管理ポータル画面から処理を行うと、2つ以上のWebブラウザ画面を行き来しながら作業するため、作業ミスの発生リスクも高まります。また利用するクラウドの仕様を把握し、クラウド間の連携についての知識も必要となるため、プロジェクト内のメンバー間で技術的な習熟度が異なる場合、設計・構築時の考慮漏れや作業ミスが発生する可能性も上昇します。後者に関してはマルチクラウドに限らない問題ですが、複数のクラウドが管理対象となれば、設計漏れや作業ミスなどが発生する可能性が高くなることは容易に想像できます。
このような構成管理・運用の問題を軽減するため、マルチクラウドに対応した構成管理ツールであるTerraformを利用し、Alibaba Cloudを含むマルチクラウド環境の構築を試してみました。
※参考リンク:
Terraformとは
TerraformはHashiCorp社が開発しているインフラストラクチャの構成管理・リソース作成ツールです。Terraformではtfファイルに記載された内容に基づいて、クラウドリソースやアプリケーション環境などの管理を行うことができます。Terraformを利用することで、インフラをコードとして定義することが可能となり、IaC(Infrastructure as Code)を実現する助けにもなります。
マルチクラウドの構成管理にTerraformを利用することで得られるメリットの一つは、複数のクラウドにまたがるインフラの定義をTerraformに集約することができる点です。Terraformは現時点でも100以上のクラウドベンダーやサービスに対応しており、これらをtfファイル上で同じようなリソースとして扱うことができます。これにより、複数のクラウドを構築・管理するうえでの相互運用性に役立つことが期待できます。
また、TerraformのようなIaCツールを利用することで得られるメリットの一つに、メンバー間の技術習熟度の差を埋めることに役立つ点があります。Terraformでは基本的にtfファイルで定義したものしかリソースが作成されないため、tfファイルの定義が正しければ想定通りのシステムが構成されることが期待できます。またレビューの際も、レビューの対象となるものは基本的にtfファイルの内容に絞られるので、設計・構築の考慮漏れやミスの発生する確率を軽減することができます。さらにTerraformでリソースを作成する場合はterraform apply
という共通のコマンドを実行するだけなので、構築時の作業ミスが発生する余地はないと言えます。
TerraformとAlibaba Cloudとの関係
Terraformは複数のクラウドプロバイダーやアプリケーションに対応しており、Alibaba Cloudにも対応しています。またAlibaba Cloudの公式ドキュメントではTerraformを利用する例も紹介されていたり、Terraform Module Web GUIという機能が備わっています。後者はAlibaba CloudのWebコンソール画面からTerraformを操作し、作成するリソースを定義することができる機能になります。このように、Alibaba CloudとTerraformとは連携が強く、Alibaba Cloudリソースを管理するうえでTerraformが利用できることが期待できます。
※Terraform Module Web GUIの画面の例
このような背景から、Alibaba Cloudを含むマルチクラウド環境を管理することを考えた場合、Terraformを利用することは有力な選択肢の一つになるのではないかと考えています。
※参考リンク:
Terraformを用いたマルチクラウド環境の構築
ここからはTerraformを利用し、Alibaba Cloudを含むマルチクラウド環境の構築について紹介します。ここでは、Alibaba Cloudのみを構築する場合、Alibaba Cloud+AzureをVPNで接続した環境を構築する場合の2つを紹介します。
検証環境情報
- 操作端末: Windows 10
- バージョン情報
- Terraform version: v0.12.18
- provider.alicloud: v1.84.0
- provider.azurerm: v2.12.0
- Terraform version: v0.12.18
- その他情報
- 利用リージョン:Alibaba Cloud・Azureどちらも日本
Alibaba Cloud環境をTerraformで作成する
構築環境情報
まずはAlibaba Cloudを利用した環境の構築例です。ここではシンプルに、以下のリソースを含むような環境を構築します。
リソース名 | 用途 | 個数 |
---|---|---|
VPC | Alibaba Cloud上で利用する仮想ネットワーク | 1 |
Vswitch | 仮想ネットワーク上で分割して利用するサブネット | 1 |
ECS | 仮想マシン(今回はCentOSを利用) | 1 |
Security Group | VPCに対して適用し、IPアドレス単位で通信制御を行う | 1 |
Security Group Rule | Security Groupで設定する各ルール (ローカルのIPアドレスのInboundのみを許可) | 1 |
※検証環境構成図
デプロイ作業
それでは実際の構築手順になります。今回は以下のようなtfファイルを用いてリソースの作成を行います。
# providerの指定 provider "alicloud" { access_key = "<Alibaba CloudのAccessKeyId>" secret_key = "<Alibaba CloudのAccessKeySecret>" region = "ap-northeast-1" // 日本(東京) } # VPCの定義 resource "alicloud_vpc" "vpc" { name = "terraform_test_vpc" cidr_block = "10.0.0.0/16" } # Vswitchの定義 resource "alicloud_vswitch" "vsw" { vpc_id = alicloud_vpc.vpc.id cidr_block = "10.0.0.0/24" availability_zone = "ap-northeast-1a" depends_on = [alicloud_vpc.vpc] } # Security Groupの定義 resource "alicloud_security_group" "sg" { name = "terraform_test_sg" vpc_id = alicloud_vpc.vpc.id depends_on = [alicloud_vswitch.vsw] } # Security Groupで設定するルールの定義(ローカルからのアクセスを許可) resource "alicloud_security_group_rule" "allow_local" { type = "ingress" ip_protocol = "all" nic_type = "intranet" policy = "accept" port_range = "*" priority = 100 security_group_id = alicloud_security_group.sg.id cidr_ip = "<Local IP Address>" // ローカルのIPアドレスを指定 depends_on = [alicloud_security_group.sg] } # ECSの定義 resource "alicloud_instance" "ecs" { instance_name = "terraform_test_ecs" image_id = "centos_7_7_x64_20G_alibase_20200220.vhd" instance_type = "ecs.sn1.medium" system_disk_category = "cloud_ssd" security_groups = [alicloud_security_group.sg.id] vswitch_id = alicloud_vswitch.vsw.id internet_max_bandwidth_out = 10 //0以上の値を指定するとECSにパブリックIPが付与される host_name = "terraformtest" password = "<パスワードを指定>" } #### SSH key pairを利用する場合は、以下のリソースを作成 #### #### 利用時はalicloud_instance.ecs.passwordを削除する #### /* resource "alicloud_key_pair" "key" { key_name = "my_public_key" public_key = "<公開鍵ファイル情報>" } resource "alicloud_key_pair_attachment" "attach" { key_name = alicloud_key_pair.key.id instance_ids = [alicloud_instance.ecs.id] } */ # リソース作成後の出力を定義 output "instance_ip_addr" { value = alicloud_instance.ecs.public_ip }
※参考リンク:
上記tfファイルを用意したのち、terraform apply
コマンドを実行して作成を開始します。今回はoutput
としてECSインスタンスに付与されたパブリックIPアドレスを指定し、リソースの作成完了後に表示されるアドレスを指定してSSH接続を行えるかの確認を行いました。
# terraform initによる初期化 C:\terraform_article\alibaba>terraform init Initializing the backend... (中略) * provider.alicloud: version = "~> 1.84" Terraform has been successfully initialized! C:\terraform_article\alibaba> # terraform validateによるtfファイル定義の確認 C:\terraform_article\alibaba>terraform validate Success! The configuration is valid. # terraform planによる作成リソースの確認 C:\terraform_article\alibaba>terraform plan (中略) Plan: 5 to add, 0 to change, 0 to destroy. # terraform apply C:\terraform_article\alibaba>terraform apply (中略) Plan: 5 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 # yesを入力 (中略) Apply complete! Resources: 5 added, 0 changed, 0 destroyed. Outputs: instance_ip_addr = 47.74.39.220
上記のようにリソース柵瀬宇井が完了したので、Outputs:
に出力されたIPアドレスにSSH接続を行います。
# SSH接続 $ ssh root@47.74.39.220 The authenticity of host '47.74.39.220 (47.74.39.220)' can't be established. ECDSA key fingerprint is SHA256:q+lHAUGI1qUsDvYvHfIFLtosQSW9w035E7cMN4G7Suc. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '47.74.39.220' (ECDSA) to the list of known hosts. root@47.74.39.220's password: Welcome to Alibaba Cloud Elastic Compute Service ! [root@terraformtest ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:16:3e:00:1c:97 brd ff:ff:ff:ff:ff:ff inet 10.0.0.140/24 brd 10.0.0.255 scope global dynamic eth0 valid_lft 315359930sec preferred_lft 315359930sec [root@terraformtest ~]# lsb_release -a LSB Version: :core-4.1-amd64:core-4.1-noarch Distributor ID: CentOS Description: CentOS Linux release 7.7.1908 (Core) Release: 7.7.1908 Codename: Core
ECSへのSSHによるアクセスが確認できたので、作成したリソースを削除します。
# terraform destroy C:\terraform_article\alibaba>terraform destroy (中略) Plan: 0 to add, 0 to change, 5 to destroy. Do you really want to destroy all resources? Terraform will destroy all your managed infrastructure, as shown above. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes # yesを入力 (中略) Destroy complete! Resources: 5 destroyed.
Alibaba Cloud+Azure環境をTerraformで作成する
構築環境情報
次にAlibaba Cloudに加えてAzureの環境を用意する場合を紹介します。今回はVPN Gatewayを双方の環境に用意し、接続するような構成とします。最終的には各クラウド上の仮想マシンから対向へのPing疎通が確認できることをゴールとします。
プロバイダー | リソース名 | 用途 | 個数 |
---|---|---|---|
Alibaba Cloud | - | - | - |
VPC | Alibaba Cloud上で利用する仮想ネットワーク | 1 | |
Vswitch | 仮想ネットワークのアドレス範囲を分割して利用する | 1 | |
ECS | Elastic Compute Service。仮想マシン | 1 | |
Security Group | VPCに対して適用し、IPアドレス単位で通信制御を行う | 1 | |
Security Group Rule | Security Groupで設定する各ルール。ローカルからのSSHログイン、VPN対向からのInboud通信を許可 | 2 | |
VPN Gateway | VPN接続を実現するAlibaba Cloudのゲートウェイ | 1 | |
Customer Gateway | VPN接続先情報を登録するリソース | 1 | |
VPN Connection | VPN接続を定義するリソース | 1 | |
VPN Route Entry | 対向側へのルーティングを実現するために利用 | 1 | |
Azure | - | - | - |
Resource Group | Azure上で作成するリソースを管理するグループ | 1 | |
Virtual Network | Azure上で利用する仮想ネットワーク | 1 | |
Subnet | 仮想ネットワークのアドレスを分割して利用。AzureはGatewaySubnetというVPN Gateway専用のサブネットが必要 | 2 | |
Network Security Group | サブネットまたはNicに適用し、IPアドレス単位で通信制御を行う | 1 | |
Public IP | パブリックIPアドレス。VMとVPN Gatewayに割りあてる | 2 | |
NIC | VMに割りあてるNic | 1 | |
Virtual Machine | 仮想マシン | 1 | |
Virtual Network Gateway | VPN接続に利用するAzure側のゲートウェイ | 1 | |
Local Network Gateway | VPN接続先の情報を登録するリソース | 1 | |
Virtual Network Gateway Connection | VPN接続の定義するリソース | 1 |
※検証環境構成図
デプロイ作業
今回は以下の3つのtfファイルを利用して、上記リソースの作成を行います。少々長いのでファイルの内容は折りたたんでおります。
tfファイル
# main.tf # Azure Resource Groupの定義 resource "azurerm_resource_group" "rg" { name = var.azure_resource_group_name location = var.azure_location } # Azure 仮想ネットワークの定義 resource "azurerm_virtual_network" "vnet" { name = var.azure_vnet_name address_space = [var.azure_vnet_addr] location = var.azure_location resource_group_name = var.azure_resource_group_name depends_on = [azurerm_resource_group.rg] } # Azure サブネットの定義 resource "azurerm_subnet" "subnet" { name = var.azure_subnet_name resource_group_name = var.azure_resource_group_name virtual_network_name = var.azure_vnet_name address_prefixes = [var.azure_subnet_addr] depends_on = [azurerm_virtual_network.vnet] } # Azure NSGと通信ルールの定義 resource "azurerm_network_security_group" "nsg" { name = var.azure_sg_name location = var.azure_location resource_group_name = var.azure_resource_group_name depends_on = [azurerm_subnet.subnet] security_rule { name = "SSH" priority = 1001 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "22" source_address_prefix = var.sg_ssh_source_addr destination_address_prefix = "*" } } # Azure NSGとサブネットの紐づけを定義 resource "azurerm_subnet_network_security_group_association" "nsg_subnet_association" { subnet_id = azurerm_subnet.subnet.id network_security_group_id = azurerm_network_security_group.nsg.id depends_on = [azurerm_network_security_group.nsg, azurerm_subnet.subnet] } # Azure 仮想マシン用パブリックIPの定義 resource "azurerm_public_ip" "publicip" { name = var.azure_pip_name location = var.azure_location resource_group_name = var.azure_resource_group_name allocation_method = "Dynamic" depends_on = [azurerm_resource_group.rg] } # Azure ネットワークインターフェイスの定義 resource "azurerm_network_interface" "nic" { name = var.azure_nic_name location = var.azure_location resource_group_name = var.azure_resource_group_name depends_on = [azurerm_public_ip.publicip] ip_configuration { name = var.azure_ipconfig_name subnet_id = azurerm_subnet.subnet.id private_ip_address_allocation = "Dynamic" public_ip_address_id = azurerm_public_ip.publicip.id } } # Azure 仮想マシンの定義 resource "azurerm_virtual_machine" "vm" { name = var.azure_vm_name location = var.azure_location resource_group_name = var.azure_resource_group_name network_interface_ids = [azurerm_network_interface.nic.id] vm_size = var.azure_vm_size depends_on = [azurerm_network_interface.nic] storage_os_disk { name = var.azure_os_disk_name caching = "ReadWrite" create_option = "FromImage" managed_disk_type = var.azure_os_disk_type } storage_image_reference { publisher = var.azure_os_publisher offer = var.azure_os_offer sku = var.azure_os_sku version = var.azure_os_version } os_profile { computer_name = var.azure_vm_hostname admin_username = var.azure_vm_admin_name admin_password = var.azure_vm_admin_password } os_profile_linux_config { disable_password_authentication = false } } # SSH key pairを利用する場合は、azurerm_virtual_machine.vm.os_profile_linux_configを変更 # 利用時はazurerm_virtual_machine.vm.os_profile.admin_passwordを削除する /* os_profile_linux_config { disable_password_authentication = true ssh_keys = var.ssh_keys } */ # Alibaba Cloud 仮想ネットワークの定義 resource "alicloud_vpc" "vpc" { name = var.alibaba_vpc_name cidr_block = var.alibaba_vpc_cidr } # Alibaba Cloud Vswitchの定義 resource "alicloud_vswitch" "vsw" { vpc_id = alicloud_vpc.vpc.id cidr_block = var.alibaba_vswitch_cidr availability_zone = var.alibaba_zone depends_on = [alicloud_vpc.vpc] } # Alibaba Cloud セキュリティグループの定義 resource "alicloud_security_group" "sg" { name = var.alibaba_sg_name vpc_id = alicloud_vpc.vpc.id depends_on = [alicloud_vswitch.vsw] } # Alibaba Cloud セキュリティグループルールの定義 resource "alicloud_security_group_rule" "allow_local" { type = "ingress" ip_protocol = "all" nic_type = "intranet" policy = "accept" port_range = "*" priority = 100 security_group_id = alicloud_security_group.sg.id cidr_ip = var.sg_ssh_source_addr depends_on = [alicloud_security_group.sg] } # Alibaba Cloud 仮想マシンの定義 resource "alicloud_instance" "ecs" { instance_name = var.alibaba_ecs_name image_id = var.alibaba_image_id instance_type = var.alibaba_ecs_instance_type system_disk_category = var.alibaba_system_disk_type security_groups = [alicloud_security_group.sg.id] vswitch_id = alicloud_vswitch.vsw.id internet_max_bandwidth_out = 10 host_name = var.alibaba_ecs_hostname password = var.alibaba_ecs_password } # SSH key pairを利用する場合は、以下のリソースを作成 # 利用時はalicloud_instance.ecs.passwordを削除する /* resource "alicloud_key_pair" "key" { key_name = "my_public_key" public_key = "<公開鍵ファイル情報>" } resource "alicloud_key_pair_attachment" "attach" { key_name = alicloud_key_pair.key.id instance_ids = [alicloud_instance.ecs.id] } */ # Azure Virtual Network Gateway用パブリックIPアドレスの定義 resource "azurerm_public_ip" "vpngw_ip" { name = var.azure_vpngw_ip_name location = var.azure_location resource_group_name = var.azure_resource_group_name allocation_method = "Dynamic" depends_on = [azurerm_resource_group.rg] } # Azure Virtual Network Gateway用サブネットの定義 resource "azurerm_subnet" "gw_subnet" { name = "GatewaySubnet" resource_group_name = var.azure_resource_group_name virtual_network_name = var.azure_vnet_name address_prefixes = [var.azure_gw_subnet_address_prefix] depends_on = [azurerm_virtual_network.vnet] } # Azure Virtual Network Gatewayの定義 resource "azurerm_virtual_network_gateway" "vgw" { name = var.azure_vpngw_name location = var.azure_location resource_group_name = var.azure_resource_group_name type = "Vpn" vpn_type = "RouteBased" active_active = false enable_bgp = false sku = var.azure_vpngw_sku depends_on = [azurerm_subnet.gw_subnet, azurerm_public_ip.vpngw_ip] ip_configuration { name = var.azure_vpngw_ipconfig_name public_ip_address_id = azurerm_public_ip.vpngw_ip.id private_ip_address_allocation = "Dynamic" subnet_id = azurerm_subnet.gw_subnet.id } } # Azure Virtual Network Gateway パブリックIPアドレスのデータを定義 # Alibaba Cloudリソースから参照するために利用 data "azurerm_public_ip" "azure_vpngw_ip" { name = azurerm_public_ip.vpngw_ip.name resource_group_name = var.azure_resource_group_name } # Alibaba VPN Gatewayの定義 resource "alicloud_vpn_gateway" "vpngw" { name = var.alibaba_vpngw_name vpc_id = alicloud_vpc.vpc.id bandwidth = "10" enable_ssl = true instance_charge_type = "PostPaid" vswitch_id = alicloud_vswitch.vsw.id depends_on = [azurerm_virtual_network_gateway.vgw] } # Azure Local Network Gatewayの定義 resource "azurerm_local_network_gateway" "lgw" { name = var.azure_localgw_name resource_group_name = var.azure_resource_group_name location = var.azure_location gateway_address = alicloud_vpn_gateway.vpngw.internet_ip address_space = [var.alibaba_vswitch_cidr] depends_on = [alicloud_vpn_gateway.vpngw] } # Azure Virtual Network Gateway Connectionの定義 resource "azurerm_virtual_network_gateway_connection" "vgw_connect" { name = var.azure_vpngw_connect_name location = var.azure_location resource_group_name = var.azure_resource_group_name type = "IPsec" virtual_network_gateway_id = azurerm_virtual_network_gateway.vgw.id local_network_gateway_id = azurerm_local_network_gateway.lgw.id shared_key = var.vpngw_connect_shared_key depends_on = [azurerm_local_network_gateway.lgw] } # Alibaba Cloud Customer Gatewayの定義 resource "alicloud_vpn_customer_gateway" "cgw" { name = var.alibaba_customergw_name ip_address = data.azurerm_public_ip.azure_vpngw_ip.ip_address depends_on = [azurerm_virtual_network_gateway_connection.vgw_connect] } # Alibaba Cloud VPN Connectionの定義 resource "alicloud_vpn_connection" "ipsec" { name = var.alibaba_ipsec_name vpn_gateway_id = alicloud_vpn_gateway.vpngw.id customer_gateway_id = alicloud_vpn_customer_gateway.cgw.id local_subnet = [var.alibaba_vswitch_cidr] remote_subnet = [var.azure_subnet_addr] effect_immediately = true depends_on = [alicloud_vpn_customer_gateway.cgw] ike_config { psk = var.vpngw_connect_shared_key ike_auth_alg = "sha256" ike_enc_alg = "3des" ike_version = "ikev2" ike_mode = "main" ike_lifetime = 86400 } ipsec_config { ipsec_enc_alg = "3des" ipsec_auth_alg = "sha256" ipsec_lifetime = 86400 } } # Alibaba Cloud VPN Route Entryの定義 resource "alicloud_vpn_route_entry" "vpn_routing" { vpn_gateway_id = alicloud_vpn_gateway.vpngw.id route_dest = var.azure_subnet_addr // azure subnet next_hop = alicloud_vpn_connection.ipsec.id weight = 0 publish_vpc = true depends_on = [alicloud_vpn_connection.ipsec] } # Alibaba Cloud セキュリティグループルール(VPN対向からのアクセス用) resource "alicloud_security_group_rule" "allow_remote_cidr" { type = "ingress" ip_protocol = "all" nic_type = "intranet" policy = "accept" port_range = "*" priority = 99 security_group_id = alicloud_security_group.sg.id cidr_ip = var.azure_subnet_addr depends_on = [alicloud_vpn_route_entry.vpn_routing] } # Azure 仮想マシン用パブリックIPのデータ定義 # 出力データで利用 data "azurerm_public_ip" "publicip" { name = azurerm_public_ip.publicip.name resource_group_name = azurerm_virtual_machine.vm.resource_group_name } # リソース作成後の出力(Azure)の定義 output "azure_vm_ip_addr" { value = data.azurerm_public_ip.publicip.ip_address } # リソース作成後の出力(Alibaba Cloud)の定義 output "alibaba_ecs_ip_addr" { value = alicloud_instance.ecs.public_ip }
# provider.tf provider "azurerm" { features {} subscription_id = "<サブスクリプションID>" client_id = "<アプリケーションID>" client_secret = "<パスワード>" tenant_id = "<テナントID>" } provider "alicloud" { access_key = "<Alibaba CloudのAccessKeyId>" secret_key = "<Alibaba CloudのAccessKeySecret>" region = "ap-northeast-1" // 日本(東京) }
# variables.tf # Azure Resource Groupの定義 variable "azure_resource_group_name" { default = "terraform_rg" } variable "azure_location" { default = "japaneast" } # Azure 仮想ネットワークの定義 variable "azure_vnet_name" { default = "terraform_vnet" } variable "azure_vnet_addr" { default = "10.0.0.0/16" } # Azure サブネットの定義 variable "azure_subnet_name" { default = "terraform_subnet" } variable "azure_subnet_addr" { default = "10.0.0.0/24" } # Azure NSGと通信ルールの定義 variable "azure_sg_name" { default = "terraform_sg" } variable "sg_ssh_source_addr" { default = "<ローカルのパブリックIPアドレスを指定>" } # Azure 仮想マシン用パブリックIPの定義 variable "azure_pip_name" { default = "terraform_vm_pip" } # Azure ネットワークインターフェイスの定義 variable "azure_nic_name" { default = "terraform_vm_nic" } variable "azure_ipconfig_name" { default = "terraform_vm_nic_ipconfig" } # Azure 仮想マシンの定義 variable "azure_vm_name" { default = "terraform_vm" } variable "azure_vm_size" { default = "Standard_D2s_v3" } variable "azure_os_disk_name" { default = "terraform_osdisk" } variable "azure_os_disk_type" { default = "StandardSSD_LRS" } variable "azure_os_publisher" { default = "OpenLogic" } variable "azure_os_offer" { default = "CentOS" } variable "azure_os_sku" { default = "7.7" } variable "azure_os_version" { default = "latest" } variable "azure_vm_hostname" { default = "terraform-vm" } variable "azure_vm_admin_name" { default = "<ユーザー名を指定>" } variable "azure_vm_admin_password" { default = "<ログインパスワードを指定>" } # Alibaba Cloud 仮想ネットワークの定義 variable "alibaba_vpc_name" { default = "terraform_vpc" } variable "alibaba_vpc_cidr" { default = "192.168.0.0/16" } # Alibaba Cloud Vswitchの定義 variable "alibaba_vswitch_cidr" { default = "192.168.0.0/24" } variable "alibaba_zone" { default = "ap-northeast-1a" } # Alibaba Cloud セキュリティグループの定義 variable "alibaba_sg_name" { default = "terraform_sg" } # Alibaba Cloud 仮想マシンの定義 variable "alibaba_ecs_name" { default = "terraform_ecs" } variable "alibaba_image_id" { default = "centos_7_7_x64_20G_alibase_20200220.vhd" } variable "alibaba_ecs_instance_type" { default = "ecs.sn1.medium" } variable "alibaba_system_disk_type" { default = "cloud_ssd" } variable "alibaba_ecs_hostname" { default = "terraform-ecs" } variable "alibaba_ecs_password" { default = "<ログインパスワードを指定>" } # Azure Virtual Network Gateway用パブリックIPアドレスの定義 variable "azure_vpngw_ip_name" { default = "terraform_vpngw_ip" } # Azure Virtual Network Gateway用サブネットの定義 variable "azure_gw_subnet_address_prefix" { default = "10.0.255.0/24" } # Azure Virtual Network Gatewayの定義 variable "azure_vpngw_name" { default = "terraform_vpngw" } variable "azure_vpngw_sku" { default = "Basic" } variable "azure_vpngw_ipconfig_name" { default = "terraform_vpngw_ipconfig" } # Alibaba Cloud VPN Gatewayの定義 variable "alibaba_vpngw_name" { default = "terraform_vpngw" } # Azure Local Network Gatewayの定義 variable "azure_localgw_name" { default = "terraform_localgw" } # Azure Virtual Network Gateway Connectionの定義 variable "azure_vpngw_connect_name" { default = "terraform_vpngw_connect" } variable "vpngw_connect_shared_key" { default = "test1234" } # Alibaba Cloud Customer Gatewayの定義 variable "alibaba_customergw_name" { default = "terraform_customergw" } # Alibaba Cloud VPN Connectionの定義 variable "alibaba_ipsec_name" { default = "terraform_ipsec" }
※参考リンク:
上記tfファイルを使ってterraform apply
コマンドを実行することで、リソースの作成が行われます。ただし上記tfファイルを利用した場合、1点注意したい点があります。上記tfファイルを用いて検証環境を作成するには、現状ではterraform apply
コマンドを2回実行する必要があります。これについては後述いたします。
今回もoutput
には仮想マシンに付与されたパブリックIPアドレスを指定しており、リソースの作成完了後に各仮想マシンにSSH接続を行います。そして対向の仮想マシンの持つプライベートIPアドレス宛にPingコマンドを実行し、Ping応答が返ってくるかを確認しました。
# terraform initによる初期化 C:\terraform_article\alibaba_azure>terraform init (中略) * provider.alicloud: version = "~> 1.84" * provider.azurerm: version = "~> 2.12" Terraform has been successfully initialized! # terraform validateによるtfファイル定義の確認 C:\terraform_article\alibaba_azure>terraform validate Success! The configuration is valid. # terraform planによる作成リソースの確認 C:\terraform_article\alibaba_azure>terraform plan (中略) Plan: 23 to add, 0 to change, 0 to destroy. # terraform apply(1回目) C:\terraform_article\alibaba_azure>terraform apply (中略) Plan: 23 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 # yesを入力 (中略) Error: expected ip_address to contain a valid IP, got: on main.tf line 274, in resource "alicloud_vpn_customer_gateway" "cgw": 274: resource "alicloud_vpn_customer_gateway" "cgw" { C:\terraform_article\alibaba_azure> # terraform apply(2回目) C:\terraform_article\alibaba_azure>terraform apply (中略) Plan: 4 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 # yesを入力 (中略) Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: alibaba_ecs_ip_addr = 47.245.2.176 azure_vm_ip_addr = 52.185.130.132
上記のようにリソースの作成が完了したので、Alibaba Cloud・Azureの仮想マシンにSSHでログインし、それぞれ対向の仮想マシンに対してPing疎通ができるかを確認します。
# Alibaba Cloud ECSに接続 $ ssh root@47.245.2.176 Welcome to Alibaba Cloud Elastic Compute Service ! [root@terraform-ecs ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:16:3e:00:48:21 brd ff:ff:ff:ff:ff:ff inet 192.168.0.83/24 brd 192.168.0.255 scope global dynamic eth0 valid_lft 315357559sec preferred_lft 315357559sec # Azure VMに接続 $ ssh testadm@52.185.130.132 [testadm@terraform-vm ~]$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:0d:3a:cd:76:41 brd ff:ff:ff:ff:ff:ff inet 10.0.0.4/24 brd 10.0.0.255 scope global noprefixroute eth0 valid_lft forever preferred_lft forever inet6 fe80::20d:3aff:fecd:7641/64 scope link valid_lft forever preferred_lft forever # Alibaba CloudからAzure VMへPing疎通を確認 [root@terraform-ecs ~]# ping 10.0.0.4 PING 10.0.0.4 (10.0.0.4) 56(84) bytes of data. 64 bytes from 10.0.0.4: icmp_seq=2 ttl=63 time=11.9 ms 64 bytes from 10.0.0.4: icmp_seq=3 ttl=63 time=15.1 ms 64 bytes from 10.0.0.4: icmp_seq=4 ttl=63 time=25.3 ms 64 bytes from 10.0.0.4: icmp_seq=5 ttl=63 time=35.1 ms 64 bytes from 10.0.0.4: icmp_seq=6 ttl=63 time=17.5 ms ^C --- 10.0.0.4 ping statistics --- 6 packets transmitted, 5 received, 16% packet loss, time 5005ms rtt min/avg/max/mdev = 11.902/20.995/35.113/8.335 ms [root@terraform-ecs ~]# # AzureからAlibaba Cloud ECSへPing疎通を確認 [testadm@terraform-vm ~]$ ping 192.168.0.83 PING 192.168.0.83 (192.168.0.83) 56(84) bytes of data. 64 bytes from 192.168.0.83: icmp_seq=1 ttl=63 time=11.7 ms 64 bytes from 192.168.0.83: icmp_seq=2 ttl=63 time=21.2 ms 64 bytes from 192.168.0.83: icmp_seq=3 ttl=63 time=11.5 ms 64 bytes from 192.168.0.83: icmp_seq=4 ttl=63 time=11.5 ms 64 bytes from 192.168.0.83: icmp_seq=5 ttl=63 time=11.8 ms ^C --- 192.168.0.83 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4006ms rtt min/avg/max/mdev = 11.580/13.604/21.282/3.841 ms [testadm@terraform-vm ~]$
上記のようにPing疎通できることが確認できました。最後にリソースを削除して終了します。
# terraform destroy C:\terraform_article\alibaba_azure>terraform destroy (中略) Plan: 0 to add, 0 to change, 23 to destroy. Do you really want to destroy all resources? Terraform will destroy all your managed infrastructure, as shown above. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes # yesを入力 (中略) Destroy complete! Resources: 23 destroyed. C:\terraform_article\alibaba_azure>
terraform apply
を2回実行する理由について
前章にてterraform apply
コマンドを2回実行する必要がある、と記載していますが、この理由としては、1度目のterraform apply
コマンド実行時に以下のようなエラーが発生するからです。
Error: expected ip_address to contain a valid IP, got: on main.tf line 274, in resource "alicloud_vpn_customer_gateway" "cgw": 274: resource "alicloud_vpn_customer_gateway" "cgw" {
上記エラーでは、Alibaba CloudのCustomer Gatewayを作成するのに必要な、対向側(ここではAzure)のVPN GatewayのパブリックIPアドレスがうまく渡せていないために発生しています。しかし1度目のterraform apply
実行後、terraform show
コマンドを実行すると、AzureのVirtual Network Gatewayとそれに割り当てるPublic IPリソースは作成されております。
# terraform show C:\terraform_article\alibaba_azure>terraform show (出力から一部抜粋) # azurerm_public_ip.vpngw_ip: resource "azurerm_public_ip" "vpngw_ip" { allocation_method = "Dynamic" id = "/subscriptions/<サブスクリプションID>/resourceGroups/terraform_rg/providers/Microsoft.Network/publicIPAddresses/terraform_vpngw_ip" idle_timeout_in_minutes = 4 ip_version = "IPv4" location = "japaneast" name = "terraform_vpngw_ip" resource_group_name = "terraform_rg" sku = "Basic" } # azurerm_virtual_network_gateway.vgw: resource "azurerm_virtual_network_gateway" "vgw" { active_active = false enable_bgp = false generation = "Generation1" id = "/subscriptions/<サブスクリプションID>/resourceGroups/terraform_rg/providers/Microsoft.Network/virtualNetworkGateways/terraform_vpngw" location = "japaneast" name = "terraform_vpngw" resource_group_name = "terraform_rg" sku = "Basic" type = "Vpn" vpn_type = "RouteBased" bgp_settings { asn = 65515 peer_weight = 0 peering_address = "10.0.255.254" } ip_configuration { name = "terraform_vpngw_ipconfig" private_ip_address_allocation = "Dynamic" public_ip_address_id = "/subscriptions/<サブスクリプションID>/resourceGroups/terraform_rg/providers/Microsoft.Network/publicIPAddresses/terraform_vpngw_ip" subnet_id = "/subscriptions/<サブスクリプションID>/resourceGroups/terraform_rg/providers/Microsoft.Network/virtualNetworks/terraform_vnet/subnets/GatewaySubnet" } }
またAzureポータルを確認すると、Public IPが付与されていることが確認できます。
パブリックIPアドレス自体はAzureのVirtual Network Gatewayに割り当てられているため、ここでもう一度terraform apply
コマンドを実行すると、今度は処理が最後まで実行されます。
# terraform apply(2回目) C:\terraform_article\alibaba_azure>terraform apply (中略) Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: alibaba_ecs_ip_addr = 47.245.2.176 azure_vm_ip_addr = 52.185.130.132
上記エラーに関連する情報として、AzureのパブリックIPアドレスはallocated_method
がdynamic
の場合、それらがデバイスにアタッチされるまではIPアドレスが割り振られないという仕様があります。これを回避するためにTerraform公式ドキュメントのこちらの情報をもとに、以下の2つの方法を試しましたが、現時点では解決できておりません。このエラーについての原因は現在調査中で、原因と対応が分かった際は更新いたします。
data.azurerm_public_ip
を利用→改善無し- Public IP作成時のパブリックIPアドレスの割り当て方式(
allocated_method
)をstatic
に変更→VPN Gatewayにはdynamic
のみ利用可能のため適用できず
※参考リンク:
今回はあくまで検証が目的でしたので、terraform apply
コマンドを再実行する形で良しとしました。しかし本番環境を管理する場合は当然色々と考えなければなりません。幸いVPN Gatewayは繰り返し作成・削除を行うような対象のリソースである場合は少ないかと思いますので、例えばVPN Gatewayとその周辺リソースは別のtfファイルで管理して個別に実行する、といった形で運用を行うのも一つの方法かと思います。
最後に
今回はAlibaba Cloudの環境構築をTerraformで実行することをメインに、さらにマルチクラウド環境をTerraformで作成することを紹介しました。最後に検証を行う中での気づきなどを記しておきます。
マルチクラウドを扱う難しさ
私は今回初めてTerraformで複数のクラウドプロバイダーを対象にリソースを作成することを試したのですが、その中でいくつか難しいと感じる場面に遭遇しました。
クラウドプロバイダー間の仕様の違い
これは当然のことなのですが、クラウドプロバイダーにはそれぞれ特有の仕様があり、同じ機能を実装する際に異なるリソースを用意する必要も多々あります。例えばAlibaba CloudとAzureでは、仮想マシンを1台動かせる環境を用意するのに作成が必要なリソースは、以下のように異なります。
プロバイダー | リソース名 | 用途 |
---|---|---|
Alibaba Cloud | - | - |
VPC | Alibaba Cloud上で利用する仮想ネットワーク | |
Vswitch | 仮想ネットワークのアドレス範囲を分割して利用する | |
ECS | 仮想マシン | |
Security Group | VPCに対して適用し、IPアドレス単位で通信制御を行う | |
Security Group Rule | Security Groupで設定する各ルール | |
Azure | - | - |
Resource Group | Azure上で作成するリソースを管理するグループ | |
Virtual Network | Azure上で利用する仮想ネットワーク | |
Subnet | 仮想ネットワークのアドレスを分割して利用。 | |
Network Security Group | サブネットまたはNicに適用し、IPアドレス単位で通信制御を行う | |
Public IP | パブリックIPアドレス。VMとVPN Gatewayに割りあてる | |
NIC | 仮想マシンに割りあてるNic | |
Virtual Machine | 仮想マシン |
このような違いは、ベンダー間の仕様が異なることだけでなく、それに関連するTerraformのリソース定義の仕方にも影響します。これらの仕様の違いを把握することは、マルチクラウド環境を管理するためには当たり前のように必要となる知識ですが、扱うクラウドプロバイダーの数が増えることにより習得する範囲も増えるため、すぐに習得するのは難しいだろうと思います。
各リソース間の連携
各プロバイダーの仕様の違いを抑えるだけでなく、それらを組み合わせて利用したときにもトラブルが発生しました。前章で紹介したAzure Virtual Network Gatewayに関連するエラーが最も代表的な例ですが、他のリソースが所持するデータを参照して作成されるリソースの場合、参照元のリソースが作成されていないタイミングでリソースの作成を開始してしまい、エラーが発生する場合があります。
このエラーは、作成するリソース間での作成完了のタイミングが異なることが影響します。例えばAzure Virtual Network Gatewayは作成完了までにおよそ45分ほどかかり、他のリソースと作成完了のタイミングが合いません。またそれに加えて、Terraformが返すリソース作成完了のタイミングと、実際のクラウドプロバイダー側でのリソース作成完了との間にギャップがある場合もあります。
このようなリソース間での作成タイミングの問題を防ぐため、対象のリソース定義にdepends_on
パラメータを与えることで、依存関係にあるリソースを明示的に指定することができます。
Terraformのフォルダ構成をどうする?
今回マルチクラウド環境を用意する際、本記事内に載せたtfファイルのほかに、各リソースをモジュールとして別フォルダに分け、ひとつのtfファイルからそれらモジュールを呼び出す形で各リソースを作成する構成も試していました。以下にフォルダ構成の概要図を記載します。
※Terraformフォルダ構成
複数のファイルやフォルダに分ける理由として、Terraformの各リソースをモジュールとしていくつかの塊に分けることで、tfファイルの可読性の向上や学習コストを抑制することが望めます。そうしてインフラ環境やそれらを定義するコードの管理コストを抑える効果を期待できます。
これまでTerraformのフォルダ構成については、Terraformの公式ドキュメントをはじめ、様々な形で資料として公開されています。しかしマルチクラウドに関する記載はまだそれほど多くなく、実際設計をする段階でどのようにフォルダを分けるべきかは検討が必要になります。
今回はクラウドプロバイダー毎にフォルダを分け、さらにその中でいくつかのモジュールに分けてみましたが、例えば本番環境と検証環境はどのように分けて管理するか(フォルダを分けるのか、Workspace機能を利用するのか)、変更が頻繁に走るリソースとそれ以外のリソースは同じフォルダ階層に置くべきなのか、など、まだまだ検討しなければならない要素はありそうです。