Terrafromを使って、Alibaba Cloud ECS + RDS によるMagentoをさくっとデプロイしてみる

## はじめに

これは[Alibaba CloudのkubernetesやServerless製品（SAEなど）でサービス立ち上げてみよう by Alibaba Cloud Advent Calendar 2023](https://qiita.com/advent-calendar/2023/alibaba1) の記事です。
私は日頃からAlibabaCloudによるソリューションや開発手法、展開に取り組んでおり、日々中国や全世界から最先端の技術取得に励んでいます。
**そうした活動をもっと外部へと発信していこう**と始めたのがこのAdventCalendarです。メンバー一人一人が書いた記事を通して、少しでも多くの方にAlibabaCloudの興味を持って頂ければ幸いです。
さて、本記事は**Terrafromを使って、Alibaba Cloud ECS + RDS によるMagentoをさくっとデプロイしてみる話**です。
[Magento](https://github.com/magento/magento2) はオープンソースで人気のあるECプラットフォームです。Magento はオープンソース版もあり、必要なすべての e コマース機能が付帯されているため、スモールスタートから大規模Webサイト向けに使いやすく、色々な実績があります。

最近 [この記事](https://qiita.com/ohiro18/items/178ecbac3b7cdaaf160d)　を書いたのですが、よくよく考えてみたら、システム全体は、ACK(Alibaba Cloud Container Service for Kubernetes) 上にてWebサイトやデータベースをインストール、構築しています。そのため、メンテナンスや拡張には適していません。

なので、次の手順は ECS（Elastic Compute Service）インスタンスとApsaraDB RDS for MySQLインスタンスに基づいてMagentoWebサイトを構築します。必要なすべてのリソースは、一般的なインフラストラクチャ構成を迅速に展開するために使用します。といっても、コンソールでポチポチ操作するのも面倒なので、TerraformことInfrastructure as Codeベースで構築します。


## このチュートリアルについて

### 対象となる方: 

このガイドは、次の方を対象としています。
- Alibaba Cloud, ECS(Elastic Compute Service), ApsaraDB RDS, VPC(Virtual Private Cloud) とMagento、Terraform に関する基本的な知識を持つこと

### 前提条件:

- Alibaba cloud アカウントを持つこと
- Alibaba CloudでECS(Elastic Compute Service), ApsaraDB RDS, VPC(Virtual Private Cloud) を有効化していること

## terraformでリソースを準備

terraformには[AlibabaCloud Provider](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs) を参考しながら、AlibabaCloudの関連リソースを管理します。本記事は次のような構成図で作成を進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/76194468-6099-11fc-fdf5-02f9d1af486b.png)


### 作業環境にterraformをインストール

使用する OS に基づいて最新の terraform をインストールします。詳細については、[Terraformのインストール](https://developer.hashicorp.com/terraform/install) を参照してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/e9faa837-2384-b29a-190c-4a8b1b8bf746.png)


### terraform構成ファイルを準備

今回、MagentoをAlibaba Cloud リソースとしてTerraformでデプロイするにあたり、Alibaba Cloud リソースとその依存関係を Alibaba Cloud Providerに基づいて以下の表のように定義し、設定します。

![04_Terraform_Alibaba_Cloud_Provider_Dependency_Overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/938b18e1-ee68-8868-0bf3-a865d47186c1.png)

以下のように関連する設定ファイルを準備します。 `main.tf`は、リソース情報と依存関係を定義します。

```
provider "alicloud" {
  access_key    = "${var.access_key}"
  secret_key    = "${var.secret_key}"
  region        = "${var.region}"
}

data "alicloud_instance_types" "ecs_types" {
  availability_zone     = "${var.zone}"
  cpu_core_count        = "${var.cpu_core_count}"
  memory_size           = "${var.memory_size}"
}

resource "alicloud_vpc" "vpc" {
  vpc_name      = "${var.project_name}-vpc"
  cidr_block    = "${var.vpc_cidr}"
  description   = "Magento demo vpc"
}

resource "alicloud_vswitch" "vsw" {
  vswitch_name      = "${var.project_name}-vswitch"
  vpc_id            = "${alicloud_vpc.vpc.id}"
  cidr_block        = "${var.vswitch_cidr}"
  zone_id           = "${var.zone}"
  description       = "Magento demo vswitch"
}

resource "alicloud_security_group" "magento_server" {
  name          = "${var.project_name}-security-group"
  description   = "Enable HTTP access via port 80"
  vpc_id        = "${alicloud_vpc.vpc.id}"
}

resource "alicloud_security_group_rule" "allow_ssh" {
  type              = "ingress"
  ip_protocol       = "tcp"
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "22/22"
  priority          = 1
  security_group_id = "${alicloud_security_group.magento_server.id}"
  cidr_ip           = "${var.allow_ssh_ip}"
}

resource "alicloud_security_group_rule" "allow_http" {
  type              = "ingress"
  ip_protocol       = "tcp"
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "80/80"
  priority          = 1
  security_group_id = "${alicloud_security_group.magento_server.id}"
  cidr_ip           = "0.0.0.0/0"
}

resource "alicloud_instance" "magento_instance" {
  availability_zone          = "${var.zone}"
  security_groups            = ["${alicloud_security_group.magento_server.id}"]
  instance_type              = "${data.alicloud_instance_types.ecs_types.instance_types.0.id}"
  system_disk_category       = "cloud_efficiency"
  image_id                   = "centos_7_9_x64_20G_alibase_20210824.vhd"
  instance_name              = "${var.project_name}-ecs"
  vswitch_id                 = "${alicloud_vswitch.vsw.id}"
  internet_max_bandwidth_out = 5
  instance_charge_type       = "PostPaid"
  key_name                   = "${var.ecs_key_name}"
  user_data                  = "${file("magento.sh")}"
}

resource "alicloud_db_instance" "magento_db" {
  engine                    = "MySQL"
  engine_version            = "5.7"
  zone_id                   = "${var.zone}"
  instance_type             = "${var.db_instance_type}"
  instance_storage          = "30"
  instance_charge_type      = "Postpaid"
  instance_name             = "${var.project_name}-rds-mysql"
  vswitch_id                = "${alicloud_vswitch.vsw.id}"
  security_ips              = ["${alicloud_instance.magento_instance.private_ip}"]
  monitoring_period         = "60"
}

resource "alicloud_db_account" "account" {
  db_instance_id      = "${alicloud_db_instance.magento_db.id}"
  account_name        = "${var.db_account}"
  account_password    = "${var.db_password}"
  account_description = "Magento demo db account"
}

resource "alicloud_db_database" "db" {
  instance_id = "${alicloud_db_instance.magento_db.id}"
  name        = "${var.database_name}"
  description = "Magento demo database"
}

resource "alicloud_db_account_privilege" "privilege" {
  instance_id  = "${alicloud_db_instance.magento_db.id}"
  account_name = "${var.db_account}"
  db_names     = ["${var.database_name}"]
  privilege    = "ReadWrite"
}
```

`variables.tf`は変数とデフォルト値（存在する場合）を定義します。

```
variable "access_key" {

}

variable "secret_key" {

}

variable "region" {
    default = "ap-northeast-1"
}

variable "zone" {
    default = "ap-northeast-1a"
}

variable "cpu_core_count" {
    default = "2"
}

variable "memory_size" {
    default = "4"
}

variable "project_name" {
    default = "magento-demo"
}

variable "vpc_cidr" {
  default = "172.16.0.0/12"
}

variable "vswitch_cidr" {
  default = "172.16.0.0/21"
}

variable "allow_ssh_ip" {
    
}

variable "ecs_key_name" {

}

variable "db_instance_type" {
    default = "mysql.n2.medium.1"
}

variable "db_account" {
    default = "magento"
}

variable "db_password" {

}

variable "database_name" {
    default = "magento"
}
```


`outputs.tf`は作成プロセスが完了後の出力値を定義します。 Magentoを手動で構成するため、以下の情報を知っておく必要があります。

- ECS(Elastic Compute Service) インスタンスのパブリックIPアドレス
- ECS(Elastic Compute Service) インスタンスのプライベートIPアドレス
- RDSMySQLインスタンスの接続情報

```
output "Magento-Server-Public-IP" {
  value = "${alicloud_instance.magento_instance.public_ip}"
}

output "Magento-Server-Private-IP" {
  value = "${alicloud_instance.magento_instance.private_ip}"
}

output "Magento-DB-Connection-String" {
  value = "${alicloud_db_instance.magento_db.connection_string}"
}
```

`versions.tf`はterraformのバージョン要件を定義します。

```
terraform {
  required_version = ">= 1.6.6"
}
```

`config.tfvars`はデフォルト値を持たない変数の特定の値を定義します。この時点で、情報が不明の場合はあとから入力することもできますが、その際にインタラクティブ入力とすることもできます。

```
access_key = "xxxxxxxxxxxxxx"
secret_key = "xxxxxxxxxxxxxx"
allow_ssh_ip = "xxxxxxxxxxxxxx"
ecs_key_name = "xxxxxxxxxxxxxx"
db_password = "xxxxxxxxxxxxxx"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/07b635c3-e608-1ad2-2a63-7e37a6078a63.png)


これらのterraform構成ファイルに加えて、ECS（Elastic Compute Service）インスタンスの準備ができたら、Magento作業環境の初期操作を定義するシェルスクリプトファイルも必要です。

[Magentoクイックインストール](https://devdocs.magento.com/guides/v2.4/install-gde/composer.html) に従って、最新のMagentoを必要に応じて更新できます。 たとえば、[Alibaba Help Doc](https://www.alibabacloud.com/help/ja/ecs/use-cases/manually-build-a-magento-e-commerce-website-on-an-ecs-instance) に基づいたMagento2.1.0を例とします。

```bash
#!/bin/bash

# Install Apache
echo "Apache phase" > /root/test.log
yum install httpd -y
httpd -v >> /root/test.log

# Update Apache configuration file - httpd.conf
line=`sed -n '/conf.modules.d/=' /etc/httpd/conf/httpd.conf`
sed -i "${line}a LoadModule rewrite_module modules/mod_rewrite.so\n" /etc/httpd/conf/httpd.conf
sed -i "/\"\/var\/www\/html\">/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/" /etc/httpd/conf/httpd.conf


# Start Apache service
systemctl start httpd
systemctl enable httpd

# Install PHP
echo "Php phase" >> /root/test.log
yum -y install https://repo.ius.io/ius-release-el7.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm unzip
rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
yum -y install php70w php70w-pdo php70w-mysqlnd php70w-opcache php70w-xml php70w-gd php70w-mcrypt php70w-devel php70w-intl php70w-mbstring php70w-bcmath php70w-json php70w-iconv php70w-soap

php -v >> /root/test.log

# Update PHP configuration file - php.ini
sed -i '$a\\nmemory_limit = 1024M\ndate.timezone = Asia\/Tokyo' /etc/php.ini

# Restart Apache
systemctl restart httpd

# Install Composer
echo "Composer phase" >> /root/test.log
wget https://getcomposer.org/download/latest-1.x/composer.phar
chmod +x composer.phar
cp composer.phar /usr/bin/composer

# Install Git
echo "Git phase" >> /root/test.log
yum -y install git

# Install Magento
echo "Magento phase" >> /root/test.log
cd /var/www/html/
git clone https://github.com/magento/magento2.git
cd magento2 &&  git checkout tags/2.1.0 -b 2.1.0
shopt -s dotglob nullglob && mv /var/www/html/magento2/* /var/www/html/ && cd ..
chown -R :apache /var/www/html
find /var/www/html -type f -print0 | xargs -r0 chmod 640
find /var/www/html -type d -print0 | xargs -r0 chmod 750
chmod -R g+w /var/www/html/{pub,var}
chmod -R g+w /var/www/html/{app/etc,vendor}
chmod 750 /var/www/html/bin/magento

# Check Apache service status
systemctl status httpd >> /root/test.log
echo "Done Successfully" >> /root/test.log
echo "Please run 'cd /var/www/html/ && composer install' manually to finish magento installation." >> /root/test.log
```

### terraformで実行

上記ファイルの設定が全て整ったら、次のように6ファイルあると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/ed552dd8-4499-625c-c41a-582c71cc9696.png)

`terraform init`を実行して、必要なプロバイダーをダウンロードし、作業ディレクトリを初期化します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/b5517a3f-0437-7316-b94c-414b55c71892.png)


`terraform plan -var-file="config.tfvars"` でアクションプランを生成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/324e19a8-4869-803b-8429-ac282ec14b9a.png)


上記、terraform planが問題なければ。今度は `terraform apply -var-file="config.tfvars"` でアクションプランを実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/23cde401-fb31-0bdd-f7aa-a4fe21fab533.png)


実行が問題なく完了したら、以下のようなメッセージが出力されます。

```
......
Apply complete! Resources: 10 added, 0 changed, 0 destroyed.

Outputs:

Magento-DB-Connection-String = "rm-xxxxxxxxxxx.mysql.japan.rds.aliyuncs.com"
Magento-Server-Private-IP = "xxxxx"
Magento-Server-Public-IP = "xxxxx"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/5629fe5f-bd73-95b7-7135-e8c92b37de6a.png)

このTerrafromによる実行が、Alibaba Cloud関連リソースでちゃんと反映されてるか、１つ１つずつ確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/5e30be1d-c945-e204-bb5e-6371ecca3919.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/da99bd9d-8334-e8e0-392f-7a261f801f74.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/3c692e33-2eeb-7504-845b-9d7cabd95c1d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/9c055c90-7d63-cf30-06f9-388a190ff391.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/68123479-8831-11ee-8759-9a1dd31c27c9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/9a89cf53-5887-9e20-5134-4ef40e32097d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/9cdbf90b-cee6-dd8f-e008-e4e2be8df5ef.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/0e033091-5dcc-3b06-873f-7d718f10b2a4.png)

Alibaba Cloud関連リソースもちゃんと作成されていますね。
それでは早速作成したECSのPublic IPをコピーしながら、Webブラウザに貼り付けてPort80で開いてみます。すると、「Autoload error Vendor autoload is not found. Please run 'composer install' under application root directory.」という画像のようなエラーが発生します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/cb7b002c-929a-dc21-e912-0a3ba38a84e6.png)


上記で定義した `allow_ssh_ip`から ECS へログインします。 他のIPアドレスを使用している場合は、セキュリティグループによってブロックされます。なので、最初に ECS の `/root/test.log` のログを確認しておく必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/fe93f7ac-a896-adda-47f7-e588af3ea9b0.png)

ログから、「Please run 'cd /var/www/html/ && composer install' manually to finish magento installation.」 とあったので、 `cd /var/www/html/ && composer install`を実行して、Magentoのインストール処理を完了させます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/a2c1330d-4aa7-0d7c-f3c6-6ba1fb37b070.png)


Magentoのインストール処理が完了したら、`systemctl status httpd` でステータスを確認します。無事` active (running)` であれば、次のステップへ進みます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/82eddd20-4f7c-0f03-df43-ea486254cdb8.png)


作成したECSのPublicIPを80ポートで再度アクセスすると、Magentoセットアップウィザードが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/df660e9e-efab-ca3d-c56c-863e3c6bb640.png)



## Magentoサーバーをインストール

Magentoセットアップウィザードの [Agree and Setup Magento] ボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/1e9be012-607e-fac5-0315-b836ad7be9eb.png)


Step 1の画面に入ります。[Start Readiness Check] ボタンをクリックして、 準備チェックを開始します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/4b947b43-1b91-6a2d-82dc-8227c38028f9.png)

Nextボタンをクリックし、Step 2の画面に入ります。Add a Databaseで、先ほどのデータベースサーバーホストの出力で `Magento-DB-Connection-String`を使用して、データベース接続を構成します。もし値を忘れた場合は、RDSインスタンス管理ページから取得できます。Database名、User、Passowordは上記のterraformの変数として定義されていますので、それに合わせて入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/a9671c30-2af2-9f69-caf7-06def7b22ead.png)


Step 3 でWeb構成を行い、管理者アドレスを更新し、セッション情報をDBに保存します。Advanced Optionsボタンもクリックし、「Session Save」を `Db` に選定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/dbf02053-d403-5458-e02e-dca6dd4b6bd4.png)


Step 4 で構成を好きに設定します。今回はデフォルト値を使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/be552ec2-4a61-69da-3bf0-5088811020b8.png)


Step 5 で管理者アカウントを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/114bf395-15af-6f6b-a89b-fca4b4df9fc4.png)


tep 6、上記の構成で問題なければ、Magentoをインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/eece83a1-e0cc-b1b4-726e-e1b7a4b1e06d.png)

インストールが無事成功したら、次のような画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/94b48386-ae17-ee13-c75f-8a1e96f40b6b.png)



Magentoストアのホームページを確認します。まだ設定していない状態なので、以下のようなページになっていると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/87056f04-7675-0a2a-99b0-34378ac07bbd.png)


Magento の管理者管理ページを確認し、上記の手順で作成した管理者アカウントでログインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/f54c3d99-8edb-7fbd-dcbd-182df77aaefe.png)

ログイン成功したら、このような Dashboard が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355017/d7bd6343-708b-e406-78d5-77b08618a79e.png)


## さいごに
今回はECSとApsaraDB RDS for MySQLを使って構築しましたが、スケーラビリティを考えるなら、SAE + PolarDB、 コストを考えると、SAE + サーバレスRDSという組み合わせが良いかもしれません。Alibaba CloudでMagentoを利用をする際は、本記事を参考にいただければ幸いです。
※本当は全てSAEで構築したかったけど、SAEでTerraformに対する理解がまだ追いついてなかったので、そこは次に向けて学習中。。