概要
Pulumiは Indrastracture as Code を実現するソフトウェア。 Azure Resource Manager(Template) や Terraform で出来ることと同じですが、一般的なプログラム言語(Node.js,JavaScript,TypeScript,Python,GO)で記述できるのが強みでしょうか、、、
この記事 は terraform を利用しましたが、今回は Pulumi を用いて Azure環境上に Ubuntu のVMを作成し、SSH接続してみました。
実行環境
macOS Sonoma 14.0
python 3.8.12
Azure CLI 2.53.0
pulumi v3.91.1
Pulumi IaC
作業環境の準備
Pulumiの状態管理をローカルで行います
## Azureへのログイン
az login
## 作業場所の定義
$ mkdir ~/MyDevelops/Pulumi/vm_azure_python
$ cd ~/MyDevelops/Pulumi/vm_azure_python
プロジェクトの作成
## 「vm-azure-python」からのプロジェクト作成
$ pulumi new vm-azure-python
This command will walk you through creating a new Pulumi project.
Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.
project name (vm_azure_python):
project description (A Python program to deploy a virtual machine on Azure):
Created project 'vm_azure_python'
stack name (dev):
Created stack 'dev'
Enter your passphrase to protect config/secrets:
Re-enter your passphrase to confirm:
azure-native:location: The Azure location to deploy into (WestUS2): japaneast
adminUsername: The user account to create on the VM (pulumiuser):
osImage: The Azure URN of the base image to use for the VM (Debian:debian-11:11:latest): Canonical:UbuntuServer:18.04-LTS:latest
servicePort: The HTTP service port to expose on the VM (80): 8080
sshPublicKey: The public key data to use for SSH authentication:
vmName: The DNS hostname prefix to use for the VM (my-server): vm-ituru-ubuntu
vmSize: The machine size to use for the VM (Standard_A1_v2): Standard_B2s
Saved config
Installing dependencies...
Creating virtual environment...
Finished creating virtual environment
Updating pip, setuptools, and wheel in virtual environment...
:
中略
:
Your new project is ready to go! ✨
To perform an initial deployment, run `pulumi up`
プロジェクト作成後のフォルダ構成
## フォルダ・ファイルの確認
$ ls -l
total 80
drwxr-xr-x 13 ituru staff 416 10 30 22:51 ./
drwxr-xr-x 10 ituru staff 320 10 30 21:45 ../
-rw-r--r-- 1 ituru staff 12 10 30 21:46 .gitignore
drwxr-xr-x 8 ituru staff 256 10 30 23:15 .pulumi/
-rw-r--r-- 1 ituru staff 974 10 30 23:27 Pulumi.dev.yaml
-rw-r--r-- 1 ituru staff 145 10 30 21:46 Pulumi.yaml
-rw-r--r-- 1 ituru staff 5505 10 30 23:42 __main__.py
drwxr-xr-x 3 ituru staff 96 10 30 23:51 __pycache__/
-rw-r--r-- 1 ituru staff 82 10 30 21:46 requirements.txt
drwxr-xr-x 6 ituru staff 192 10 30 21:52 venv/
設定情報の確認
encryptionsalt: v1:LGF1nzVAmSs=:v1:rcevXbWYrtXUqi88...............
config:
azure-native:location: japaneast
vm_azure_python:adminUsername: pulumiuser
vm_azure_python:osImage: Canonical:UbuntuServer:18.04-LTS:latest
vm_azure_python:servicePort: "8080"
vm_azure_python:vmName: vm-ituru-ubuntu
vm_azure_python:vmSize: Standard_B2s
sshキー(秘密鍵・公開鍵)の作成
## カレントディレクトリに鍵保存ディレクトリの作成
$ mkdir .ssh
$ cd .ssh
## sshキーの作成
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/ituru/.ssh/id_rsa): /Users/ituru/MyDevelops/Pulumi/vm_azure_python/.ssh/id_rsa
Enter passphrase (empty for no passphrase): <-- パスフレーズの入力(任意)
Enter same passphrase again: <-- もう一度、パスフレーズの入力(任意)
Your identification has been saved in /Users/ituru/MyDevelops/Pulumi/vm_azure_python/.ssh/id_rsa
Your public key has been saved in /Users/ituru/MyDevelops/Pulumi/vm_azure_python/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:0j4NAXLGmq86AUj/k9wMqOIK3aP7777777777777777 ituru@IsseinoMacBook-Pro.local
The key's randomart image is:
+---[RSA 3072]----+
| +.ooOo.+ |
| o + =.ooo. |
|o + Eo + . |
|o. ooo . + . |
| .. o.* S . |
|.o.. =.* o |
|+ ..o.o o . |
|.....o . |
|o +=. |
+----[SHA256]-----+
## 生成された鍵の確認
$ ls -l
total 16
drwxr-xr-x 4 ituru staff 128 10 30 22:51 ./
drwxr-xr-x 13 ituru staff 416 10 30 22:51 ../
-rw------- 1 ituru staff 2622 10 30 22:51 id_rsa
-rw-r--r-- 1 ituru staff 584 10 30 22:51 id_rsa.pub
$ cd ..
作成するリソース(仮想マシン)への公開鍵の設定
必要な構成変数 'vm_azure_python:sshPublicKey' の作成(公開鍵の設定)
$ pulumi config set vm_azure_python:sshPublicKey "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQ............"
設定情報の確認、sshのPublicKeyの設定を確認できます。
encryptionsalt: v1:LGF1nzVAmSs=:v1:rcevXbWYrtXUqi88...............
config:
azure-native:location: japaneast
vm_azure_python:adminUsername: pulumiuser
vm_azure_python:osImage: Canonical:UbuntuServer:18.04-LTS:latest
vm_azure_python:servicePort: "8080"
vm_azure_python:sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQ...........
vm_azure_python:vmName: vm-ituru-ubuntu
vm_azure_python:vmSize: Standard_B2s
構築コードの編集
作成したプロジェクト内の __main__.py ファイルの編集
import pulumi
from pulumi_azure_native import resources, network, compute
from pulumi_random import random_string
import base64
# Import the program's configuration settings.
config = pulumi.Config()
vm_name = config.get("vmName")
vm_size = config.get("vmSize")
os_image = config.get("osImage")
admin_username = config.get("adminUsername")
service_port = config.get("servicePort")
ssh_public_key = config.require("sshPublicKey")
os_image_publisher, os_image_offer, os_image_sku, os_image_version = os_image.split(":")
# Create a resource group.
resource_group = resources.ResourceGroup("rg_ituru_linuxvm_")
# Create a virtual network.
virtual_network = network.VirtualNetwork(
"network",
resource_group_name=resource_group.name,
address_space=network.AddressSpaceArgs(
address_prefixes=[
"10.0.0.0/16",
],
),
subnets=[
network.SubnetArgs(
name=f"{vm_name}-subnet",
address_prefix="10.0.1.0/24",
),
],
)
# Use a random string to give the VM a unique DNS name.
domain_name_label = random_string.RandomString(
"domain-label",
length=8,
upper=False,
special=False,
).result.apply(lambda result: f"{vm_name}-{result}")
# Create a public IP address for the VM.
public_ip = network.PublicIPAddress(
"public-ip",
resource_group_name=resource_group.name,
public_ip_allocation_method=network.IpAllocationMethod.DYNAMIC,
dns_settings=network.PublicIPAddressDnsSettingsArgs(
domain_name_label=domain_name_label,
),
)
# Create a security group allowing inbound access over ports 8080 (for HTTP) and 22 (for SSH).
security_group = network.NetworkSecurityGroup(
"security-group",
resource_group_name=resource_group.name,
security_rules=[
network.SecurityRuleArgs(
name=f"{vm_name}-securityrule",
priority=1000,
direction=network.AccessRuleDirection.INBOUND,
access="Allow",
protocol="Tcp",
source_port_range="*",
source_address_prefix="123.123.123.123",
destination_address_prefix="*",
destination_port_ranges=[
service_port,
"22",
],
),
],
)
# Create a network interface with the virtual network, IP address, and security group.
network_interface = network.NetworkInterface(
"network-interface",
resource_group_name=resource_group.name,
network_security_group=network.NetworkSecurityGroupArgs(
id=security_group.id,
),
ip_configurations=[
network.NetworkInterfaceIPConfigurationArgs(
name=f"{vm_name}-ipconfiguration",
private_ip_allocation_method=network.IpAllocationMethod.DYNAMIC,
subnet=network.SubnetArgs(
id=virtual_network.subnets.apply(lambda subnets: subnets[0].id),
),
public_ip_address=network.PublicIPAddressArgs(
id=public_ip.id,
),
),
],
)
# Define a script to be run when the VM starts up.
init_script = f"""#!/bin/bash
echo '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello, world!</title>
</head>
<body>
<h1>Hello, world! 👋</h1>
<p>Deployed with 💜 by <a href="https://pulumi.com/">Pulumi</a>.</p>
</body>
</html>' > index.html
sudo python3 -m http.server {service_port} &
"""
# Create the virtual machine.
vm = compute.VirtualMachine(
"vm",
resource_group_name=resource_group.name,
network_profile=compute.NetworkProfileArgs(
network_interfaces=[
compute.NetworkInterfaceReferenceArgs(
id=network_interface.id,
primary=True,
)
]
),
hardware_profile=compute.HardwareProfileArgs(
vm_size=vm_size,
),
os_profile=compute.OSProfileArgs(
computer_name=vm_name,
admin_username=admin_username,
custom_data=base64.b64encode(bytes(init_script, "utf-8")).decode("utf-8"),
linux_configuration=compute.LinuxConfigurationArgs(
disable_password_authentication=True,
ssh=compute.SshConfigurationArgs(
public_keys=[
compute.SshPublicKeyArgs(
key_data=ssh_public_key,
path=f"/home/{admin_username}/.ssh/authorized_keys",
),
],
),
),
),
storage_profile=compute.StorageProfileArgs(
os_disk=compute.OSDiskArgs(
name=f"{vm_name}-osdisk",
create_option=compute.DiskCreateOption.FROM_IMAGE,
),
image_reference=compute.ImageReferenceArgs(
publisher=os_image_publisher,
offer=os_image_offer,
sku=os_image_sku,
version=os_image_version,
),
),
)
# Once the machine is created, fetch its IP address and DNS hostname.
vm_address = vm.id.apply(
lambda id: network.get_public_ip_address_output(
resource_group_name=resource_group.name,
public_ip_address_name=public_ip.name,
)
)
# Export the VM's hostname, public IP address, and HTTP URL.
pulumi.export("ip", vm_address.ip_address)
pulumi.export("hostname", vm_address.dns_settings.apply(lambda settings: settings.fqdn))
pulumi.export(
"url",
vm_address.dns_settings.apply(
lambda settings: f"http://{settings.fqdn}:{service_port}"
),
)
構築とテスト
Linux仮想マシンの構築
## Deploy Stack
$ pulumi up
Enter your passphrase to unlock config/secrets
(set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember): *************
Enter your passphrase to unlock config/secrets
Previewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack vm_azure_python-dev create
+ ├─ random:index:RandomString domain-label create
+ ├─ azure-native:resources:ResourceGroup rg_ituru_linuxvm_ create
+ ├─ azure-native:network:VirtualNetwork network create
+ ├─ azure-native:network:NetworkSecurityGroup security-group create
+ ├─ azure-native:network:PublicIPAddress public-ip create
+ ├─ azure-native:network:NetworkInterface network-interface create
+ └─ azure-native:compute:VirtualMachine vm create
Outputs:
hostname: output<string>
ip : output<string>
url : output<string>
Resources:
+ 8 to create
Do you want to perform this update? yes
Updating (dev):
Type Name Status
+ pulumi:pulumi:Stack vm_azure_python-dev created (91s)
+ ├─ random:index:RandomString domain-label created (0.00s)
+ ├─ azure-native:resources:ResourceGroup rg_ituru_linuxvm_ created (0.45s)
+ ├─ azure-native:network:VirtualNetwork network created (13s)
+ ├─ azure-native:network:NetworkSecurityGroup security-group created (4s)
+ ├─ azure-native:network:PublicIPAddress public-ip created (7s)
+ ├─ azure-native:network:NetworkInterface network-interface created (0.56s)
+ └─ azure-native:compute:VirtualMachine vm created (76s)
Outputs:
hostname: "vm-ituru-ubuntu-as6zrbef.japaneast.cloudapp.azure.com"
ip : "52.140.201.159"
url : "http://vm-ituru-ubuntu-as6zrbef.japaneast.cloudapp.azure.com:8080"
Resources:
+ 8 created
Duration: 1m34s
接続テスト
Webアクセス
$ curl http://vm-ituru-ubuntu-as6zrbef.japaneast.cloudapp.azure.com:8080
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello, world!</title>
</head>
<body>
<h1>Hello, world! 👋</h1>
<p>Deployed with 💜 by <a href="https://pulumi.com/">Pulumi</a>.</p>
</body>
</html>
SSH接続
## 秘密鍵を指定して ssh接続
$ ssh -i ./.ssh/id_rsa pulumiuser@52.140.201.159
The authenticity of host '52.140.201.159 (52.140.201.159)' can't be established.
ED25519 key fingerprint is SHA256:s5fcGz4UslCjpbdXXSiP5wOILvldhhYStnmYvdcQC7o.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '52.140.201.159' (ED25519) to the list of known hosts.
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-1109-azure x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon Oct 30 14:53:56 UTC 2023
System load: 0.44 Processes: 135
Usage of /: 4.5% of 28.89GB Users logged in: 0
Memory usage: 5% IP address for eth0: 10.0.1.4
Swap usage: 0%
Expanded Security Maintenance for Infrastructure is not enabled.
0 updates can be applied immediately.
Enable ESM Infra to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
pulumiuser@vm-ituru-ubuntu:~$ <--- SSH接続完了
## OSの確認
pulumiuser@vm-ituru-ubuntu:~$ uname -a
Linux vm-ituru-ubuntu 5.4.0-1109-azure #115~18.04.1-Ubuntu SMP Mon May 22 20:06:37 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
pulumiuser@vm-ituru-ubuntu:~$ exit
後片付け
リソースの全削除
$ pulumi destroy
まとめ
Pulumiで構築(プログラミング言語でコードを記載)すると単体テスト(同じプログラミング言語を利用して)はやりやすいかもしれませんね。
参考記事
以下の記事を参考にさせていただきました。感謝申し上げます。