0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS CDK で Amazon Lightsail にブロックストレージを追加してみた!

0
Posted at

はじめに

前回 は、Amazon Lightsail(以降:Lightsail)に WordPress をインストールしました。
今回は、Lightsail にブロックストレージを追加する構成を試し、運用や性能の観点でどのような違いがあるかを整理します。
※本記事は 2026年4月時点の情報をもとに記載しています。実際の仕様や料金は公式サイトでご確認ください。

対象読者

  • AWS CDK を試してみたい方
  • WordPress を手軽に始めてみたい方
  • Lightsail のスペックや月額料金を知りたい方
  • Lightsail のブロックストレージに興味がある方

おことわり

構成図や構成図の注意点は前回の記事をご参照ください。

ブロックストレージの月額料金

Lightsail のブロックストレージは、1 GB あたり 0.10 USD です。
たとえば 100 GB を割り当てた場合、100 GB × 0.10 USD = 10 USD となります。

別途 Lightsail の料金 がかかります。


1. Lightsail 単体 vs Lightsail + ディスク追加の構成比較

構成概要

項目 Lightsail 単体 Lightsail + ディスク追加
構成 インスタンス内蔵ストレージのみ インスタンス + 別途ブロックストレージ
ディスク容量 プランにより固定 追加ディスクを個別に用意可能
管理対象 1 つのリソース 2 つのリソース(インスタンス + ディスク)
初期セットアップ難度 簡単 やや複雑

補足

Lightsail 単体構成でも WordPress の運用は可能です。
一方で、追加ディスクを使うと、用途ごとにデータ配置を分けやすくなります。


2. メリット・デメリット詳細比較

2.1 Lightsail 単体

メリット

# メリット 詳細
1 シンプルな構成 管理するリソースが 1 つで、運用を整理しやすくなります。
2 初期構築がしやすい インスタンス作成のみで開始できます。
3 追加ストレージ費用が不要 追加ディスクを用意しない場合、ストレージ費用は増えません。
4 構成が分かりやすい 障害時の確認対象を絞りやすいです。
5 学習コストが低い まず Lightsail を試してみたい場合に向いています。

デメリット

# デメリット 詳細
1 容量拡張の自由度が低い ストレージが不足した場合、構成の見直しが必要になる場合があります。
2 I/O が競合する可能性がある DB、メディア、Web コンテンツが同一領域にあるため、負荷が高い場合に I/O 競合が起こる可能性があります。
3 個別分離のバックアップ設計がしにくい データの役割ごとに保護対象を分けにくい場合があります。
4 障害時の切り分けがしにくい データ領域を分けていないため、障害時の影響範囲を分離しにくい場合があります。
5 データ増加の影響を受ける可能性がある メディアやデータ量の増加により、性能に影響する場合があります。

2.2 Lightsail + ディスク追加

メリット

# メリット 詳細
1 容量を増やしやすい 必要に応じて追加ディスクを用意できます。
2 I/O 競合を緩和しやすい OS・DB 領域とメディア領域を分ける構成にしやすくなります。
3 バックアップ対象を分けて考えやすい 役割ごとに保護方針を整理しやすくなります。
4 構成の意図を明確にしやすい どのデータをどこに置くかを分離できます。
5 障害対応の切り分けをしやすい インスタンスとディスクを別々の観点で扱えます。
6 wp-content の配置を工夫しやすい メディア領域を独立させる構成にしやすくなります。

デメリット

# デメリット 詳細
1 初期セットアップが複雑 ディスク接続、フォーマット、マウント設定が必要です。
2 管理負荷が増える 複数リソースの確認や運用が必要です。
3 設定ミスのリスクがある マウントやシンボリックリンク設定を誤ると影響が出る可能性があります。
4 追加コストが発生する ストレージ料金が上乗せされます。
5 一部の手動作業が残る場合がある ディスクのアタッチや初回確認を手動で行うことがあります。

3. セキュリティ面での比較

3.1 暗号化

項目 Lightsail 単体 Lightsail + ディスク追加
ボリューム暗号化 Lightsail の仕様に従って管理されます ディスクごとの仕様に従って管理されます
暗号化の管理 AWS 管理の仕組みを利用します AWS 管理の仕組みを利用します
キー管理の自由度 限定的です 限定的です
運用の柔軟性 標準的です データ配置を分けることで整理しやすくなります

3.2 アクセス制御

項目 Lightsail 単体 Lightsail + ディスク追加
ファイアウォール設定 インスタンス単位です インスタンス単位です
IAM ポリシー Lightsail の操作権限で管理します Lightsail の操作権限で管理します
細かな権限制御 限定的です 限定的です
運用上の分離 1 台に集約されます データ配置を分けやすくなります

3.3 バックアップ戦略

項目 Lightsail 単体 Lightsail + ディスク追加
スナップショット インスタンス全体を対象にできます ディスク単位でも検討しやすくなります
バックアップ取得時の影響 全体を対象にするため、設計上の考慮が必要です 対象を分けて考えやすくなります
自動スナップショット 設定可能です 設定可能です
段階的バックアップ やや設計しにくいです 比較的整理しやすくなります

3.4 障害対応

項目 Lightsail 単体 Lightsail + ディスク追加
ディスク障害時の対応 インスタンス全体の復旧を検討することがあります 障害箇所を切り分けやすくなります
OS 障害時の対応 ディスクへのアクセスに影響が出る場合があります データ領域を分けて扱いやすくなります
RTO 構成や運用に依存します 構成や運用に依存します
RPO スナップショット設計に依存します スナップショット設計に依存します

4. 動作速度比較

4.1 ディスク I/O 性能

ランダムアクセス(IOPS)

項目 Lightsail 単体 Lightsail + ディスク追加
リード IOPS 環境に依存します 環境に依存します
ライト IOPS 環境に依存します 環境に依存します
レイテンシ 構成に依存します 構成に依存します

シーケンシャルアクセス(スループット)

項目 Lightsail 単体 Lightsail + ディスク追加
リードスループット 環境に依存します 環境に依存します
ライトスループット 環境に依存します 環境に依存します

4.2 実運用シーンでの性能差

シーン Lightsail 単体 Lightsail + ディスク追加 補足
DB の読み取り(SELECT) 環境に依存します 環境に依存します 構成により差が出る場合があります
メディアアップロード 環境に依存します 環境に依存します 分離構成では影響を分けやすくなります
Web ページ表示 環境に依存します 環境に依存します キャッシュ設定の影響が大きいです
大量メディア操作 負荷が高いと遅くなる場合があります 分離により影響を抑えやすい場合があります 実測が必要です

4.3 ボトルネック分析

Lightsail 単体で I/O 競合が起こる可能性がある例

【I/O 競合の例】
DB クエリ(読み取り)
    ↓(同一領域で処理)
メディアアップロード(書き込み)
    ↓(同一領域で処理)
Web ページキャッシュ書き込み
    ↓(同一領域で処理)
→ 負荷が高い場合に遅延が発生する可能性があります

Lightsail + ディスク追加で役割を分けた例

【分離による整理】
インスタンス側 → DB 関連
別途ストレージ → メディア関連
→ 役割を分けて運用しやすくなります

CDK を試してみました

事前準備

実行環境は前回のとおりです。

ステップ 5 の「CDK 環境の初期化(ブートストラップ)」まで終わっていることを想定しています。


CDK ファイル作成とデプロイ

1. 作業ディレクトリの作成

lightsail-disk-wordpress というディレクトリを作成しています。
お好きな名前に変更してください。

> mkdir lightsail-disk-wordpress && cd lightsail-disk-wordpress
lightsail-disk-wordpress>

2. CDK プロジェクトの作成

lightsail-disk-wordpress> cdk init app --language=typescript
lightsail-disk-wordpress> npm install dotenv --save

※成功すると lightsail-disk-wordpress 内にいろいろファイルが作られます。


3. スタック(Stack)の修正

ここは Lightsail のディスク作成までを CDK で行い、アタッチは AWS CLI で実行する構成です。
そのため、コード中の説明もそれに合わせています。

lightsail-disk-wordpress/lib/lightsail-disk-wordpress-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lightsail from 'aws-cdk-lib/aws-lightsail';

export interface LightsailDiskWordPressStackProps extends cdk.StackProps {
  lightsailInstanceName: string;
  lightsailBundleId: string;
  lightsailBlueprintId: string;
  lightsailAutoSnapshotTime?: string;
  lightsailDiskName: string;
  lightsailDiskSize: string;
}

export class LightsailDiskWordPressStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: LightsailDiskWordPressStackProps) {
    super(scope, id, props);

    if (!props) {
      throw new Error('LightsailDiskWordPressStackProps must be provided');
    }

    const instanceName = props.lightsailInstanceName || 'WordPressInstance';
    const bundleId = props.lightsailBundleId || 'small_3_0';
    const bluePrintId = props.lightsailBlueprintId || 'wordpress';
    const snapshotTime = props?.lightsailAutoSnapshotTime || '18:00';
    const lightsailDiskName = props.lightsailDiskName || 'lightsail-disk-wordpress-media-bucket';
    const lightsailDiskSize = props.lightsailDiskSize || '100';

    // ==========================================
    // userData: setup.sh 生成&実行権付与
    // ==========================================
    const userDataScript = `#!/bin/bash

# setup.sh を /home/bitnami/setup.sh に生成
cat > /home/bitnami/setup.sh << 'SETUP_SCRIPT_EOF'
#!/bin/bash
set -e

LOG_FILE="/var/log/wordpress-setup.log"

# ==========================================
# ログファイル作成時に sudo 使用
# ==========================================
sudo touch "\$LOG_FILE"
sudo chown bitnami:bitnami "\$LOG_FILE"

log_output() {
  echo "\$@" | tee -a "\$LOG_FILE"
}

log_output "===== WordPress Media Storage Setup Started ====="
log_output "Timestamp: \$(date)"

# ==========================================
# 1. ディスク検出とフォーマット
# ==========================================
log_output "[Step 1] Detecting and formatting disk..."

DEVICE="/dev/xvdf" # 環境によって異なる場合があります
if [ ! -e "\$DEVICE" ]; then
  log_output "ERROR: Device \$DEVICE not found!"
  lsblk
  exit 1
fi

log_output "Found device: \$DEVICE"

if ! sudo blkid "\$DEVICE" > /dev/null 2>&1; then
  log_output "Formatting \$DEVICE..."
  sudo mkfs.ext4 "\$DEVICE" -F >> "\$LOG_FILE" 2>&1
else
  log_output "Device already formatted"
fi

# ==========================================
# 2. マウントポイント作成
# ==========================================
log_output "[Step 2] Creating mount point..."

MOUNT_POINT="/mnt/wordpress-media"
if [ ! -d "\$MOUNT_POINT" ]; then
  sudo mkdir -p "\$MOUNT_POINT"
  log_output "Mount point created: \$MOUNT_POINT"
else
  log_output "Mount point already exists: \$MOUNT_POINT"
fi

# ==========================================
# 3. ディスクのマウント
# ==========================================
log_output "[Step 3] Mounting disk..."

if ! mount | grep -q "\$MOUNT_POINT"; then
  sudo mount "\$DEVICE" "\$MOUNT_POINT" >> "\$LOG_FILE" 2>&1
  log_output "Disk mounted successfully"
else
  log_output "Disk already mounted"
fi

df -h "\$MOUNT_POINT" | tee -a "\$LOG_FILE"

# ==========================================
# 4. /etc/fstab に永続化設定を追加
# ==========================================
log_output "[Step 4] Adding to /etc/fstab..."

UUID=\$(sudo blkid -s UUID -o value "\$DEVICE")
log_output "Device UUID: \$UUID"

if ! grep -q "\$UUID" /etc/fstab; then
  echo "UUID=\$UUID \$MOUNT_POINT ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab >> "\$LOG_FILE" 2>&1
  log_output "fstab entry added"
else
  log_output "fstab entry already exists"
fi

# ==========================================
# 5. シンボリックリンク設定
# ==========================================
log_output "[Step 5] Setting up symbolic link..."

WP_CONTENT_LINK="/opt/bitnami/wordpress/wp-content"
WP_CONTENT_REAL="/bitnami/wordpress/wp-content"

if [ -L "\$WP_CONTENT_LINK" ]; then
  CURRENT_TARGET=\$(readlink "\$WP_CONTENT_LINK")
  log_output "Current symlink target: \$CURRENT_TARGET"

  if [ "\$CURRENT_TARGET" = "\$MOUNT_POINT" ]; then
    log_output "Symlink already points to \$MOUNT_POINT"
  else
    if [ -d "\$WP_CONTENT_REAL" ]; then
      log_output "Copying content from \$WP_CONTENT_REAL to \$MOUNT_POINT..."
      sudo cp -r "\$WP_CONTENT_REAL"/* "\$MOUNT_POINT/" 2>> "\$LOG_FILE" || true
    fi
    log_output "Removing old symlink..."
    sudo rm "\$WP_CONTENT_LINK"
    log_output "Creating new symlink..."
    sudo ln -s "\$MOUNT_POINT" "\$WP_CONTENT_LINK"
  fi
elif [ -d "\$WP_CONTENT_LINK" ]; then
  log_output "wp-content is a directory, converting to symlink..."
  if [ -d "\$WP_CONTENT_REAL" ]; then
    log_output "Copying content from \$WP_CONTENT_REAL to \$MOUNT_POINT..."
    sudo cp -r "\$WP_CONTENT_REAL"/* "\$MOUNT_POINT/" 2>> "\$LOG_FILE" || true
  fi
  sudo rm -rf "\$WP_CONTENT_LINK"
  sudo ln -s "\$MOUNT_POINT" "\$WP_CONTENT_LINK"
else
  log_output "Creating new symlink..."
  sudo ln -s "\$MOUNT_POINT" "\$WP_CONTENT_LINK"
fi

# ==========================================
# 6. パーミッション設定
# ==========================================
log_output "[Step 6] Setting permissions..."

sudo chown -R bitnami:daemon "\$MOUNT_POINT"
sudo chmod -R 755 "\$MOUNT_POINT"

log_output "Permissions set: bitnami:daemon 755"

# ==========================================
# 7. 検証
# ==========================================
log_output "[Step 7] Verifying setup..."

log_output "Symlink verification:"
ls -la "\$WP_CONTENT_LINK" | tee -a "\$LOG_FILE"

log_output ""
log_output "Symlink target:"
readlink "\$WP_CONTENT_LINK" | tee -a "\$LOG_FILE"

log_output ""
log_output "Disk space:"
df -h "\$MOUNT_POINT" | tee -a "\$LOG_FILE"

log_output ""
log_output "wp-content contents:"
ls -la "\$WP_CONTENT_LINK" | tee -a "\$LOG_FILE"

log_output ""
log_output "===== WordPress Media Storage Setup Completed Successfully ====="
log_output "Setup log: \$LOG_FILE"
SETUP_SCRIPT_EOF

# setup.sh に実行権を付与
chmod +x /home/bitnami/setup.sh

# 所有権を bitnami ユーザーに変更
chown bitnami:bitnami /home/bitnami/setup.sh

echo "setup.sh created at /home/bitnami/setup.sh"
echo "Permissions set to 755"
echo "Owner: bitnami:bitnami"
`;

    // ==========================================
    // 1. ブロックストレージ作成
    // ==========================================
    const cfnDisk = new lightsail.CfnDisk(this, 'WordPressMediaDisk', {
      diskName: lightsailDiskName,
      availabilityZone: this.availabilityZones[0],
      sizeInGb: parseInt(lightsailDiskSize, 10),
    });

    // ==========================================
    // 2. ファイアウォールルール定義
    // ==========================================
    const firewallRules: lightsail.CfnInstance.PortProperty[] = [
      { fromPort: 80, toPort: 80, protocol: 'tcp', accessFrom: 'Anywhere (Internet)', accessType: 'public' },
      { fromPort: 443, toPort: 443, protocol: 'tcp', accessFrom: 'Anywhere (Internet)', accessType: 'public' },
      { fromPort: 22, toPort: 22, protocol: 'tcp', cidrListAliases: ['lightsail-connect', 'lightsail-setup-ipv4'], accessType: 'public' },
    ];

    // ==========================================
    // 3. Lightsail インスタンス作成
    // ==========================================
    const wpInstance = new lightsail.CfnInstance(this, 'WordPressInstance', {
      instanceName: instanceName,
      blueprintId: bluePrintId,
      bundleId: bundleId,
      availabilityZone: this.availabilityZones[0],
      userData: userDataScript,
      networking: {
        ports: firewallRules,
      },
      addOns: [
        {
          addOnType: 'AutoSnapshot',
          autoSnapshotAddOnRequest: { snapshotTimeOfDay: snapshotTime },
        },
      ],
    });

    // ==========================================
    // 4. スタティック IP
    // ==========================================
    const staticIpName = `${instanceName}-static-ip`;
    const staticIp = new lightsail.CfnStaticIp(this, 'WordPressStaticIp', {
      staticIpName: staticIpName,
      attachedTo: wpInstance.instanceName,
    });

    staticIp.addDependency(wpInstance);

    // ==========================================
    // 出力
    // ==========================================
    new cdk.CfnOutput(this, 'LightsailInstanceName', {
      value: wpInstance.instanceName,
      description: 'Lightsailのインスタンス名',
    });

    new cdk.CfnOutput(this, 'LightsailStaticIpAddress', {
      value: staticIp.attrIpAddress,
      description: 'Lightsailのスタティック IP アドレス',
    });

    new cdk.CfnOutput(this, 'MediaDiskName', {
      value: cfnDisk.diskName!,
      description: 'Media Storage Disk Name',
    });

    new cdk.CfnOutput(this, 'MediaDiskSize', {
      value: `${cfnDisk.sizeInGb} GB`,
      description: 'Media Storage Size',
    });

    new cdk.CfnOutput(this, 'DiskAttachCommand', {
      value: `aws lightsail attach-disk --region ${this.region} --disk-name ${lightsailDiskName} --instance-name ${instanceName} --disk-path /dev/xvdf`,
      description: 'ディスク接続コマンド(CDK デプロイ後に実行してください)',
    });

    new cdk.CfnOutput(this, 'SetupLogPath', {
      value: '/var/log/wordpress-setup.log',
      description: 'セットアップログファイルのパス (SSH 接続後に確認可能)',
    });
  }
}

4. アプリ(App)

lightsail-disk-wordpress/bin/lightsail-disk-wordpress.ts
import * as cdk from 'aws-cdk-lib';
import { LightsailDiskWordPressStack } from '../lib/lightsail-disk-wordpress-stack';
import * as dotenv from 'dotenv';

dotenv.config(); // .envファイルを読み込む

const app = new cdk.App();

const lightsailInstanceName = process.env.LIGHTSAIL_INSTANCE_NAME;
const lightsailBundleId = process.env.LIGHTSAIL_BUNDLE_ID;
const lightsailBlueprintId = process.env.LIGHTSAIL_BLUEPRINT_ID;
const lightsailAutoSnapshotTime = process.env.LIGHTSAIL_AUTO_SNAPSHOT_TIME;
const lightsailDiskName = process.env.DISK_NAME;
const lightsailDiskSize = process.env.DISK_SIZE;
const cdkDefaultAccount = process.env.CDK_DEFAULT_ACCOUNT;
const cdkDefaultRegion = process.env.CDK_DEFAULT_REGION || 'us-east-1';

// 必須の環境変数のチェック
if (!lightsailInstanceName || !lightsailBundleId || !lightsailBlueprintId || !lightsailDiskName || !lightsailDiskSize) {
  throw new Error(
    '以下の環境変数が.envファイルに設定されていません: LIGHTSAIL_INSTANCE_NAME, LIGHTSAIL_BUNDLE_ID, LIGHTSAIL_BLUEPRINT_ID, DISK_NAME, DISK_SIZE'
  );
}

new LightsailDiskWordPressStack(app, 'LightsailDiskWordPressStack', {
  // 環境変数から設定値を読み込む
  lightsailInstanceName: lightsailInstanceName,
  lightsailBundleId: lightsailBundleId,
  lightsailBlueprintId: lightsailBlueprintId,
  lightsailAutoSnapshotTime: lightsailAutoSnapshotTime,
  lightsailDiskName: lightsailDiskName,
  lightsailDiskSize: lightsailDiskSize,
  env: {
    account: cdkDefaultAccount || process.env.CDK_DEFAULT_ACCOUNT,
    region: cdkDefaultRegion || process.env.CDK_DEFAULT_REGION,
  },
});

app.synth();

5. 環境変数

lightsail-disk-wordpress/.env
# AWS Configuration
CDK_DEFAULT_ACCOUNT=
CDK_DEFAULT_REGION=

# Lightsail Configuration
LIGHTSAIL_INSTANCE_NAME=WordpressDiskInstance

# 指定できる値は下記コマンドよりご確認ください。
# aws lightsail get-bundles --region ap-northeast-1 --query 'bundles[].{price:price,cpuCount:cpuCount,ramSizeInGb:ramSizeInGb,diskSizeInGb:diskSizeInGb,bundleId:bundleId,instanceType:instanceType,supportedPlatforms:supportedPlatforms[0]}' --output table
LIGHTSAIL_BUNDLE_ID=nano_3_0

# 指定できる値は下記コマンドよりご確認ください。
# aws lightsail get-blueprints --region ap-northeast-1 --query 'blueprints[].{blueprintId:blueprintId,name:name,group:group,productUrl:productUrl,platform:platform}' --output table
LIGHTSAIL_BLUEPRINT_ID=wordpress

# 自動スナップショットの時間 (HH:00 形式、UTC時間)
# 日本時間 (JST) 03:00 に実行したい場合は UTC 18:00
LIGHTSAIL_AUTO_SNAPSHOT_TIME=18:00

# Disk Configuration

# ディスク名 (ユニークである必要があります)
DISK_NAME=lightsail-disk-wordpress-media-bucket

# ディスクサイズ
DISK_SIZE=100

環境に合わせて変更してください。
SSH 接続は WordPress の管理者パスワード確認時に利用します。

6. デプロイ

lightsail-disk-wordpress> cdk deploy

時間がかかりますので気長に待ちましょう!

7. Lightsail インスタンスをブロックストレージにアタッチ

Lightsail の CDK まわりは、CfnDiskCfnInstance のような L1 コンストラクトを中心に扱います。
そのため、今回のように ディスク作成は CDK で行い、アタッチは AWS CLI で実行する形にしています。
Lightsail のディスク接続は、CloudFormation / CDK の抽象化状況により、記事執筆時点では CLI を併用する構成が分かりやすいためです。

この記事の構成では、CDK でディスク作成まで行い、インスタンスへのアタッチは AWS CLI で実行しています
なお、CDK のバージョンや利用する L1/L2 リソースによっては、別の実装方法を取れる場合があります。

lightsail-disk-wordpress> aws lightsail attach-disk --region [デプロイリージョン] --disk-name [ブロックストレージ名] --instance-name [Lightsail インスタンス名] --disk-path /dev/xvdf

例:ap-northeast-1 にデプロイされた Lightsail インスタンス名 WordpressDiskInstance に対して、ブロックストレージ名 lightsail-disk-wordpress-media-bucket をアタッチする場合

> aws lightsail attach-disk --region ap-northeast-1 --disk-name lightsail-disk-wordpress-media-bucket --instance-name WordpressDiskInstance --disk-path /dev/xvdf

8. セットアップ

ブロックストレージをアタッチした後、セットアップファイルを実行します。
SSH でログインし、以下のシェルファイルを実行します。

$ /home/bitnami/setup.sh

CDKでのインスタンス起動時点ではまだディスクがアタッチされていないため、UserDataでの自動実行ではなく、アタッチ後の手動実行スクリプトを生成する設計にしています。

9. WordPress の表示

Lightsail インスタンスに割り当てられた静的 IP アドレスをブラウザに入力すると、WordPress の初期画面が表示されます。

WordPress の初期画面
WordPress の初期画面


後始末

  1. 以下のコマンドを実行します。
lightsail-disk-wordpress> cdk destroy
  1. リソースが削除されます。

まとめ

今回は、CDK で Lightsail にブロックストレージを追加する構成を試しました。
ストレージを分けることで、運用設計を整理しやすくなり、障害対応やデータ配置の方針も考えやすくなります。
また、データ領域を分けることで、負荷状況によっては I/O 競合の緩和につながる可能性があります。
Lightsail は手軽に使える一方で、構成を少し工夫するだけでも扱いやすさが変わる点が特徴です。

この記事が、誰かのお役に立てば幸いです。

参考サイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?