この記事は「Oracle Cloud その2 Advent Calendar 2018」の12月19日の記事として書かれています。
コードを使用してインフラストラクチャのトポロジを定義するHashicorp社のTerraformを使ってOracle Cloud InfrastructureでInfrastructure as Code(IaC)を実現することができます。
この記事では、Terraformを使ってOCI File Storage ServiceにLinuxインスタンスからマウントしてみたことを紹介します。
今回構築する構成
Oracle LinuxのVMインスタンス2つから同じFile Storageをマウントする構成を以下のファイルから作成します。
ファイル名 | 内容 |
---|---|
data_sources.tf | 作成したリソース情報の取得 |
export.tf | File Storage Exportの作成 |
export_set.tf | File Storage ExportSetの作成 |
file_system.tf | File Storageの作成 |
instance.tf | Computeインスタンスの作成 |
mount_target.tf | File Storage Mount Targetの作成 |
network.tf | VCN/InternetGateway/RouteTableの作成 |
provider.tf | Terraform OCI Provider設定 |
security_list.tf | セキュリティリストの作成 |
variables.tf | 変数・値の定義 |
(すべてのファイルは本ページの最下部にまとめて記載/env-vars.ps1を除く) |
準備作業
環境を構築をする上で、使用する環境の情報および作成するインスタンスへの接続に必要な情報を準備します。
準備に必要な情報は
「Oracle Cloud Advent Calendar 2018」の12月10日の記事
「Terraform を 使って Oracle Cloud環境構築 (2018/12/10)」を参照し、Terraform の動作テスト~環境変数の設定まで実施してください。
ネットワーク構築
VCN,Subnet,Internet Gateway,Route Tableを作成(network.tf)
NFS通信を有効とするSecurity Listを作成します。
# Protocols are specified as protocol numbers.
# http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
resource "oci_core_security_list" "TF_security_list" {
compartment_id = "${var.compartment_ocid}"
display_name = "TF_security_list"
vcn_id = "${oci_core_virtual_network.TF_vcn.id}"
// Allow all outbound requests
egress_security_rules = [
{
destination = "0.0.0.0/0"
protocol = "all"
},
]
// See https://docs.us-phoenix-1.oraclecloud.com/Content/File/Tasks/creatingfilesystems.htm.
// Specific security list rules are required to allow mount targets to work properly.
ingress_security_rules = [
{
// Allowing inbound SSH traffic to instances in the subnet from any source
protocol = "6"
source = "0.0.0.0/0"
tcp_options {
"min" = 22
"max" = 22
}
},
{
// Allowing inbound ICMP traffic of a specific type and code from any source
protocol = 1
source = "0.0.0.0/0"
icmp_options {
"type" = 3
"code" = 4
}
},
{
// Allowing inbound ICMP traffic of a specific type from within our VCN
protocol = 1
source = "${var.TF_vcn-cidr}"
icmp_options {
"type" = 3
}
},
]
}
File Storage、Mount Target、Storage export / export setの作成
変数の追加
作成するリソースの名前、パス、サイズを指定します。
variable "file_system_1_display_name" {
default = "TF_fs_1"
}
variable "mount_target_1_display_name" {
default = "my_mount_target_1"
}
variable "export_path_fs1_mt1" {
default = "/mnt/fs1"
}
variable "export_set_name_1" {
default = "export set for mount target 1"
}
variable "max_byte" {
default = 23843202333
}
variable "max_files" {
default = 223442
}
variable "export_read_write_access_source" {
default = "10.0.0.0/8"
}
variable "export_read_only_access_source" {
default = "0.0.0.0/0"
}
variable "export_path_fs1_mt1" {
default = "/mnt/fs1"
}
variable "export_set_name_1" {
default = "export set for mount target 1"
}
File Storage の作成
resource "oci_file_storage_file_system" "TF_fs_1" {
#Required
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
#Optional
display_name = "${var.file_system_1_display_name}"
}
Mount Targetの作成
resource "oci_file_storage_mount_target" "TF_mount_target_1" {
#Required
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
subnet_id = "${oci_core_subnet.TF_subnet.id}"
#Optional
display_name = "${var.mount_target_1_display_name}"
}
export / export set の作成
resource "oci_file_storage_export" "TF_export_fs1_mt1" {
#Required
export_set_id = "${oci_file_storage_export_set.TF_export_set_1.id}"
file_system_id = "${oci_file_storage_file_system.TF_fs_1.id}"
path = "${var.export_path_fs1_mt1}"
export_options = [
{
source = "${var.export_read_write_access_source}"
access = "READ_WRITE"
identity_squash = "NONE"
require_privileged_source_port = true
},
{
source = "${var.export_read_only_access_source}"
access = "READ_ONLY"
identity_squash = "ALL"
require_privileged_source_port = true
},
]
}
resource "oci_file_storage_export_set" "TF_export_set_1" {
# Required
mount_target_id = "${oci_file_storage_mount_target.TF_mount_target_1.id}"
# Optional
display_name = "${var.export_set_name_1}"
max_fs_stat_bytes = "${var.max_byte}"
max_fs_stat_files = "${var.max_files}"
}
インスタンス の作成とFile Storageのマウント
変数の追加(Linux VM/VM.Standard2.1 シェイプ を 2つ作成)
variable "NumInstances" {
default = "2"
}
variable "instance_image_ocid" {
type = "map"
default = {
// See https://docs.us-phoenix-1.oraclecloud.com/images/
// Oracle-provided image "Oracle-Linux-7.5-2018.05.09-1"
us-ashburn-1 = "ocid1.image.oc1.iad.aaaaaaaaXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
}
variable "instance_shape" {
default = "VM.Standard2.1"
}
locals {
mount_target_1_ip_address = "${lookup(data.oci_core_private_ips.ip_mount_target1.private_ips[0], "ip_address")}"
}
「NumInstances
」で作成するインスタンスの数を指定します。
そのほか作成するインスタンスのシェイプ、イメージを指定します。
data_sources.tfへの追加
# Gets the list of file systems in the compartment
data "oci_file_storage_file_systems" "file_systems" {
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
}
# Gets the list of mount targets in the compartment
data "oci_file_storage_mount_targets" "mount_targets" {
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
}
# Gets the list of exports in the compartment
data "oci_file_storage_exports" "exports" {
compartment_id = "${var.compartment_ocid}"
}
# Gets a list of snapshots for a particular file system
data "oci_file_storage_snapshots" "snapshots" {
file_system_id = "${oci_file_storage_file_system.TF_fs_1.id}"
}
# Gets a list of export sets in a compartment and availability domain
data "oci_file_storage_export_sets" "export_sets" {
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
}
data "oci_core_private_ips" ip_mount_target1 {
subnet_id = "${oci_file_storage_mount_target.TF_mount_target_1.subnet_id}"
filter {
name = "id"
values = ["${oci_file_storage_mount_target.TF_mount_target_1.private_ip_ids.0}"]
}
}
作成したFile Storageに関してマウントに必要な情報を取得します。
resource "oci_core_instance" "TF_instance" {
count = "${var.NumInstances}"
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
display_name = "TF instance${count.index}"
hostname_label = "TFinstance${count.index}"
shape = "${var.instance_shape}"
subnet_id = "${oci_core_subnet.TF_subnet.id}"
metadata {
ssh_authorized_keys = "${var.ssh_public_key}"
}
source_details {
source_type = "image"
source_id = "${var.instance_image_ocid[var.region]}"
}
timeouts {
create = "60m"
}
}
resource "null_resource" "mount_fss_on_instance" {
depends_on = ["oci_core_instance.TF_instance",
"oci_file_storage_export.TF_export_fs1_mt1",
]
count = "${var.NumInstances}"
provisioner "remote-exec" {
connection {
agent = false
timeout = "15m"
host = "${oci_core_instance.TF_instance.*.public_ip[count.index % var.NumInstances]}"
user = "opc"
private_key = "${var.ssh_private_key}"
}
inline = [
"sudo yum -y install nfs-utils > nfs-utils-install.log",
"sudo mkdir -p /mnt/fs1",
"sudo mount ${local.mount_target_1_ip_address}:${var.export_path_fs1_mt1} ${var.export_path_fs1_mt1}",
]
}
}
「resource
」 「null_resource
」でOS起動後にマウント用のディレクトリ作成、マウントの実行をするコマンドを記述します。
環境構築の実行
すべてのファイルが用意できたら以下のコマンドを実行し環境を作成します。
- terraform init
- terraform plan -out plan01
- terraform apply "plan01"
作成されたインスタンスの確認
instance0 への接続
10.0.1.5:/mnt/fs1 が /mnt/fs1 にマウントされていることを確認
[opc@tfinstance0 ~]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 7.2G 0 7.2G 0% /dev
tmpfs 7.3G 0 7.3G 0% /dev/shm
tmpfs 7.3G 8.6M 7.3G 1% /run
tmpfs 7.3G 0 7.3G 0% /sys/fs/cgroup
/dev/sda3 39G 2.0G 37G 6% /
/dev/sda1 512M 9.8M 502M 2% /boot/efi
10.0.1.5:/mnt/fs1 23G 0 23G 0% /mnt/fs1
tmpfs 1.5G 0 1.5G 0% /run/user/1000
instance1 への接続
10.0.1.5:/mnt/fs1 が /mnt/fs1 にマウントされていることを確認
[opc@tfinstance1 ~]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 7.2G 0 7.2G 0% /dev
tmpfs 7.3G 0 7.3G 0% /dev/shm
tmpfs 7.3G 8.6M 7.3G 1% /run
tmpfs 7.3G 0 7.3G 0% /sys/fs/cgroup
/dev/sda3 39G 2.0G 37G 6% /
/dev/sda1 512M 9.8M 502M 2% /boot/efi
10.0.1.5:/mnt/fs1 23G 0 23G 0% /mnt/fs1
tmpfs 1.5G 0 1.5G 0% /run/user/1000
instance0 で作成したディレクトリに instance1 でファイルを作成し、そのファイルが instance0 で 確認できることを確認
[opc@tfinstance0 ~]$ ls /mnt/fs1
[opc@tfinstance0 ~]$ sudo mkdir /mnt/fs1/test01
[opc@tfinstance1 ~]$ ls /mnt/fs1/
test01
[opc@tfinstance1 ~]$ sudo touch /mnt/fs1/test01/sample.txt
[opc@tfinstance0 ~]$ ls /mnt/fs1/test01/
sample.txt
おわりに
terraform を使って 複数インスタンスの共有用ファイルシステムとしてFile Storage Serviceがマウントできることを確認しました。
# Gets the list of file systems in the compartment
data "oci_file_storage_file_systems" "file_systems" {
#Required
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
#Optional fields. Used by the service to filter the results when returning data to the client.
#display_name = "TF_fs_1"
#id = "ocid1.filesystem.oc1.phx.aaaaaaaaXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
#state = "DELETED"
}
# Gets the list of mount targets in the compartment
data "oci_file_storage_mount_targets" "mount_targets" {
#Required
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
#Optional fields. Used by the service to filter the results when returning data to the client.
#display_name = "${var.mount_target_display_name}"
#export_set_id = "${var.mount_target_export_set_id}"
#id = "${var.mount_target_id}"
#state = "${var.mount_target_state}"
}
# Gets the list of exports in the compartment
data "oci_file_storage_exports" "exports" {
#Required
compartment_id = "${var.compartment_ocid}"
#Optional fields. Used by the service to filter the results when returning data to the client.
#export_set_id = "${oci_file_storage_mount_target.TF_mount_target_1.export_set_id}"
#file_system_id = "${oci_file_storage_file_system.TF_fs.id}"
#id = "${var.export_id}"
#state = "${var.export_state}"
}
# Gets a list of snapshots for a particular file system
data "oci_file_storage_snapshots" "snapshots" {
#Required
file_system_id = "${oci_file_storage_file_system.TF_fs_1.id}"
#Optional fields. Used by the service to filter the results when returning data to the client.
#id = "${var.snapshot_id}"
#state = "${var.snapshot_state}"
}
# Gets a list of export sets in a compartment and availability domain
data "oci_file_storage_export_sets" "export_sets" {
#Required
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
#Optional fields. Used by the service to filter the results when returning data to the client.
#display_name = "${var.export_set_display_name}"
#id = "${var.export_set_id}"
#state = "${var.export_set_state}"
}
data "oci_core_private_ips" ip_mount_target1 {
subnet_id = "${oci_file_storage_mount_target.TF_mount_target_1.subnet_id}"
filter {
name = "id"
values = ["${oci_file_storage_mount_target.TF_mount_target_1.private_ip_ids.0}"]
}
}
resource "oci_file_storage_export" "TF_export_fs1_mt1" {
#Required
export_set_id = "${oci_file_storage_export_set.TF_export_set_1.id}"
file_system_id = "${oci_file_storage_file_system.TF_fs_1.id}"
path = "${var.export_path_fs1_mt1}"
export_options = [
{
source = "${var.export_read_write_access_source}"
access = "READ_WRITE"
identity_squash = "NONE"
require_privileged_source_port = true
},
{
source = "${var.export_read_only_access_source}"
access = "READ_ONLY"
identity_squash = "ALL"
require_privileged_source_port = true
},
]
}
resource "oci_file_storage_export_set" "TF_export_set_1" {
# Required
mount_target_id = "${oci_file_storage_mount_target.TF_mount_target_1.id}"
# Optional
display_name = "${var.export_set_name_1}"
max_fs_stat_bytes = "${var.max_byte}"
max_fs_stat_files = "${var.max_files}"
}
resource "oci_file_storage_file_system" "TF_fs_1" {
#Required
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
#Optional
display_name = "${var.file_system_1_display_name}"
}
resource "oci_core_instance" "TF_instance" {
count = "${var.NumInstances}"
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
display_name = "TF instance${count.index}"
hostname_label = "TFinstance${count.index}"
shape = "${var.instance_shape}"
subnet_id = "${oci_core_subnet.TF_subnet.id}"
metadata {
ssh_authorized_keys = "${var.ssh_public_key}"
}
source_details {
source_type = "image"
source_id = "${var.instance_image_ocid[var.region]}"
}
timeouts {
create = "60m"
}
}
resource "null_resource" "mount_fss_on_instance" {
depends_on = ["oci_core_instance.TF_instance",
"oci_file_storage_export.TF_export_fs1_mt1",
]
count = "${var.NumInstances}"
provisioner "remote-exec" {
connection {
agent = false
timeout = "15m"
host = "${oci_core_instance.TF_instance.*.public_ip[count.index % var.NumInstances]}"
user = "opc"
private_key = "${var.ssh_private_key}"
}
inline = [
"sudo yum -y install nfs-utils > nfs-utils-install.log",
"sudo mkdir -p /mnt/fs1",
"sudo mount ${local.mount_target_1_ip_address}:${var.export_path_fs1_mt1} ${var.export_path_fs1_mt1}",
]
}
}
resource "oci_file_storage_mount_target" "TF_mount_target_1" {
#Required
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
compartment_id = "${var.compartment_ocid}"
subnet_id = "${oci_core_subnet.TF_subnet.id}"
#Optional
display_name = "${var.mount_target_1_display_name}"
}
resource "oci_core_virtual_network" "TF_vcn" {
cidr_block = "${var.TF_vcn-cidr}"
dns_label = "TFvcn"
compartment_id = "${var.compartment_ocid}"
display_name = "TFvcn"
dns_label = "TFvcn"
}
resource "oci_core_internet_gateway" "TF_internet_gateway" {
compartment_id = "${var.compartment_ocid}"
display_name = "TF internet gateway"
vcn_id = "${oci_core_virtual_network.TF_vcn.id}"
}
resource "oci_core_route_table" "TF_route_table" {
compartment_id = "${var.compartment_ocid}"
vcn_id = "${oci_core_virtual_network.TF_vcn.id}"
display_name = "TF route table"
route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = "${oci_core_internet_gateway.TF_internet_gateway.id}"
}
}
resource "oci_core_subnet" "TF_subnet" {
availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[var.availability_domain - 1],"name")}"
cidr_block = "${var.TF_subnet_cidr}"
display_name = "TFsubnet"
dns_label = "TFsubnet"
compartment_id = "${var.compartment_ocid}"
vcn_id = "${oci_core_virtual_network.TF_vcn.id}"
security_list_ids = ["${oci_core_security_list.TF_security_list.id}"]
route_table_id = "${oci_core_route_table.TF_route_table.id}"
}
provider "oci" {
version = ">= 3.0.0"
tenancy_ocid = "${var.tenancy_ocid}"
user_ocid = "${var.user_ocid}"
fingerprint = "${var.fingerprint}"
private_key_path = "${var.private_key_path}"
region = "${var.region}"
}
# Protocols are specified as protocol numbers.
# http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
resource "oci_core_security_list" "TF_security_list" {
compartment_id = "${var.compartment_ocid}"
display_name = "TF_security_list"
vcn_id = "${oci_core_virtual_network.TF_vcn.id}"
// Allow all outbound requests
egress_security_rules = [
{
destination = "0.0.0.0/0"
protocol = "all"
},
]
// See https://docs.us-phoenix-1.oraclecloud.com/Content/File/Tasks/creatingfilesystems.htm.
// Specific security list rules are required to allow mount targets to work properly.
ingress_security_rules = [
{
protocol = "6"
source = "${var.TF_vcn-cidr}"
tcp_options {
"min" = 2048
"max" = 2050
}
},
{
protocol = "6"
source = "${var.TF_vcn-cidr}"
tcp_options {
source_port_range {
"min" = 2048
"max" = 2050
}
}
},
{
protocol = "6"
source = "${var.TF_vcn-cidr}"
tcp_options {
"min" = 111
"max" = 111
}
},
{
// Allowing inbound SSH traffic to instances in the subnet from any source
protocol = "6"
source = "0.0.0.0/0"
tcp_options {
"min" = 22
"max" = 22
}
},
{
// Allowing inbound ICMP traffic of a specific type and code from any source
protocol = 1
source = "0.0.0.0/0"
icmp_options {
"type" = 3
"code" = 4
}
},
{
// Allowing inbound ICMP traffic of a specific type from within our VCN
protocol = 1
source = "${var.TF_vcn-cidr}"
icmp_options {
"type" = 3
}
},
]
}
variable "tenancy_ocid" {}
variable "user_ocid" {}
variable "fingerprint" {}
variable "private_key_path" {}
variable "region" {}
variable "compartment_ocid" {}
# Refer https://docs.us-phoenix-1.oraclecloud.com/Content/Compute/Tasks/managingkeypairs.htm on how to setup SSH key pairs for compute instances
variable "ssh_public_key" {}
variable "ssh_private_key" {}
variable "NumInstances" {
default = "2"
}
# Choose an Availability Domain
variable "availability_domain" {
default = "2"
}
variable "TF_vcn-cidr" {
default = "10.0.0.0/16"
}
variable "TF_subnet_cidr" {
default = "10.0.1.0/24"
}
variable "file_system_1_display_name" {
default = "TF_fs_1"
}
variable "mount_target_1_display_name" {
default = "my_mount_target_1"
}
variable "export_path_fs1_mt1" {
default = "/mnt/fs1"
}
variable "export_set_name_1" {
default = "export set for mount target 1"
}
variable "max_byte" {
default = 23843202333
}
variable "max_files" {
default = 223442
}
variable "export_read_write_access_source" {
default = "10.0.0.0/8"
}
variable "export_read_only_access_source" {
default = "0.0.0.0/0"
}
variable "instance_image_ocid" {
type = "map"
default = {
// See https://docs.us-phoenix-1.oraclecloud.com/images/
// Oracle-provided image "Oracle-Linux-7.5-2018.05.09-1"
eu-frankfurt-1 = "ocid1.image.oc1.eu-frankfurt-1.aaaaaaaazregkysspxnktw35k4r5vzwurxk6myu44umqthjeakbkvxvxdlkq"
us-ashburn-1 = "ocid1.image.oc1.iad.aaaaaaaa6ybn2lkqp2ejhijhehf5i65spqh3igt53iyvncyjmo7uhm5235ca"
uk-london-1 = "ocid1.image.oc1.uk-london-1.aaaaaaaayodsld656eh5stds5mo4hrmwuhk2ugin4eyfpgoiiskqfxll6a4a"
us-phoenix-1 = "ocid1.image.oc1.phx.aaaaaaaaozjbzisykoybkppaiwviyfzusjzokq7jzwxi7nvwdiopk7ligoia"
}
}
variable "instance_shape" {
default = "VM.Standard2.1"
}
locals {
mount_target_1_ip_address = "${lookup(data.oci_core_private_ips.ip_mount_target1.private_ips[0], "ip_address")}"
}