250
285

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

初心者でもできる! ECS × ECR × CircleCIでRailsアプリケーションをコンテナデプロイ

Last updated at Posted at 2020-02-11

前書き

  • ECSのクラスターは起動タイプにFargateEC2がありますが、今回は使い慣れているEC2を使います。
  • 本記事はAWSの環境構築は全てマネジメントコンソールで行ってます。
    (Terraformで1発でやりたいような人は他の記事を見るか、一応GitHubにTerraformでコード化もしているのでそちらを参考に)
  • docker-composeは以下のような構成です
    • Nginxのコンテナ
    • Railsのコンテナ
    • MySQLのコンテナ

pumaとnginxをsocket通信させます。
※ 今回RailsはAPIモードで作成しています。
※ フロント部分がないためNginxはあまり意味がないかもしれませんが、練習のため

筆者のTwiiter

本記事で目指したいデプロイ構成

スクリーンショット 2020-02-11 10.08.44.png

(ECSのクラスターによって束ねられているEC2インスタンスや、VPC、RDS等は省略)

下準備

こちらからリポジトリをcloneしてください。
各Dockerfileと設定ファイルを用意しています。

$ git clone https://github.com/s14228so/ECS-Rails6.git
$ cd ECS-Rails6
$ docker-compose run app rails new . --force --database=mysql --api
$ vi config/database.yml
config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password #編集
  host: db #編集
# ...省略
production:
  <<: *default
  database: <%= ENV['DB_DATABASE'] %>
  adapter: mysql2
  encoding: utf8mb4
  charset: utf8mb4
  collation: utf8mb4_general_ci
  host: <%= ENV['DB_HOST'] %>
  username: <%= ENV['DB_USERNAME'] %>
  password: <%= ENV['DB_PASSWORD'] %>

本番環境用の環境変数は後ほどECSのタスク定義にて設定します。

Rails6から必要なそうなので追加します。(同じlocalhostなら問題ない気がするんですが、これがないとnginx(localhost:80)にアクセスするときにエラーが起きてしまいました。。わかる人いたら教えてください)

config/environments/development.rb
Rails.application.configure do
  config.hosts.clear #追加
$ cp docker/rails/puma.rb config/ (pumaの設定を上書き)
$ mkdir -p tmp/sockets  (socketファイルの置き場所を確保)
$ docker-compose up -d --build 
$ docker-compose run app rails db:create

http://localhost
にアクセスするとRailsのデフォルト画面が表示されるはずです。
localhost(:80)にアクセスするとnginxのwebサーバに(裏ではnginxがpumaにリクエストしている)
localhost:3000にアクセスするとpumaのアプリケーションサーバにリクエストを投げていることになります。

スクリーンショット 2020-02-05 8.11.50.png

scaffoldでjsonを返す簡易apiを作成します。

$  docker-compose run app rails g scaffold Post title:string
$  docker-compose run app rails db:migrate
seeds.rb
5.times.each do |i|
  Post.create(title: "test#{i + 1}")
end

$ docker-compose run app rails db:seed

下の画像のようにjsonが返ってきてればOKです!

スクリーンショット 2020-02-07 20.43.36.jpg

ECRへのpush

AWSのマネジメントコンソールを開きます。サービスタブをクリックしてECRと検索します。

RailsNginxのイメージ用のリポジトリをそれぞれ作成します。

リポジトリを作成をクリック
スクリーンショット 2020-02-05 9.23.52.png

まずはrailsのイメージから作成します。
リポジトリ名にはrails-apiを(任意のリポジトリ名で問題ありません)
スクリーンショット 2020-02-05 9.24.05.png

ECRヘのpushコマンドを表示します。
スクリーンショット 2020-02-05 9.28.59.png

プッシュコマンドの表示をクリックすると4つのコマンドが表示されるので一つずつターミナルで入力しましょう。

※ちなみに1つ目のコマンドでaws command not foundと言われる場合
aws-cliが入っていいないはずなので、でaws-cliをインストールしましょう。

$ curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
$ unzip awscli-bundle.zip
$ sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws




`※2番目のbuild時に以下のように-fでDockerfileのコンテキストを変えることに注意してください。`

$ docker build -t rails-api -f ./docker/rails/Dockerfile .





ECRヘのpushコマンドを4つ全て打ち終えたらECRのリポジトリにpushされていることを確認しましょう。


同様にnginxのDockerイメージも、ECRにリポジトリを作成してpushします。
先ほどと同じく2つ目のコマンドのみ以下のようコンテキストに注意が必要です。

$ docker build -t nginx -f ./docker/nginx/Dockerfile .



# VPCの作成
まずは`EIP`の設定から

VPCから左側のElastic IPタブをクリック
**新しいアドレスの割り当て**をクリック => **Amazon プール**を選択 => **割り当て**


さて、`VPC`を作成していきましょう。

ちなみに今回作成するサブネットとCIDRの構成はこんな感じ

|                  |  ap-northeast-1a |    ap-northeast-1c    |
|:-----------------|------------------:|:------------------:|
| Public             |     10.0.0.0/24         |     10.0.2.0/24            |
| Private           |            10.0.1.0/24  |       10.0.3.0/24        |


左上の**VPCダッシュボード**をクリック => **VPCウィザードの起動** => **パブリックとプライベート サブネットを持つ VPC**を選択

以下のように編集して作成します。
<img width="1249" alt="スクリーンショット 2020-02-07 4.25.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/303c0509-e270-fd1c-bb8b-0f194838fc7c.png">



RDSを作成する際にVPCが複数のAZで構成されている必要があるので
パブリックとプライベートサブネットそれぞれを1cのAZにも追加します。

**サブネット**のタブをクリック => **サブネットの作成**

<img width="1274" alt="スクリーンショット 2020-02-07 4.29.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/861067c1-d531-7360-53a1-de6bd5c5d5df.png">

プライベートなサブネットも同様に作成します。
<img width="1274" alt="スクリーンショット 2020-02-07 4.29.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/df78b4ba-450d-ab29-3b47-a46cfbcc6583.png">


public-1aサブネットのIPv4のアドレス指定動作を変更します。

<img width="583" alt="スクリーンショット 2020-02-07 4.34.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/825339cc-b4ea-d3ad-be0f-d742e9473666.png">

<img width="702" alt="スクリーンショット 2020-02-07 4.34.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/bc7d62cf-8b1f-ba7b-f218-aa63f0f3e0fa.png">
[パブリック IPv4 アドレスの自動割り当てを有効にする] チェックボックスをオンにし、[Save (保存)] を選択します。



ここで注意したいのは先ほど後から追加したpublic-1cですが、サブネット作成時にデフォルトで`プライベートルートテーブル`が選択されます。
public-1aのような`インターネットゲートウェイ`が設定されている`パブリックルートテーブル`にに修正します。

<img width="889" alt="スクリーンショット 2020-02-07 4.39.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/2bf131ca-8721-6ee6-7444-23e3a9e1f6e1.png">


public-1cを選択した状態で**アクション** => **ルートテーブルの編集**をクリック

<img width="683" alt="スクリーンショット 2020-02-07 4.38.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/157ff5d6-8fbf-7b41-6001-bca4bd5e4e29.png">


public-1aのルートテーブルのIDと同じルートテーブルのIDを選択して保存します。
<img width="1273" alt="スクリーンショット 2020-02-07 4.42.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/afc55868-4d85-be32-da9d-eb80203dae74.png">




# RDSの作成

エンジンのオプションでMySQLを選択
テンプレート => 無料枠

<img width="764" alt="スクリーンショット 2020-02-07 0.32.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/bd92e72e-a510-7e31-ee1f-83cf3dfac146.png">

=>VPCで先ほど作成したVPCを選択

<img width="786" alt="スクリーンショット 2020-02-07 5.33.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/d8640303-51fe-c21c-619c-2fe2bde88dc4.png">

データベースの作成をクリック!



# ECSでタスクの作成

マネジメントコンソールから**ECS**を検索

左タブの**タスク定義**を選択 => **新しいタスク定義の作成**をクリック


起動タイプの互換性: **EC2**を選択して次へ



<img width="1295" alt="スクリーンショット 2020-02-07 5.42.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/e9299076-71df-b612-41ce-f9fd0dbe2588.png">

タスク定義名に任意の名前を入力します。
ネットワークモードに**bridge**を選択

<img width="935" alt="スクリーンショット 2020-02-07 0.35.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/79c8ed57-3f09-f27c-73b2-2ed59991136f.png">

一番下の方までスクロールしていくと*ボリュームの追加*という項目があります。
・ボリュームの追加を選択
<img width="791" alt="スクリーンショット 2020-02-07 21.45.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/8322058f-f5f5-bda7-82c4-8cb38554151f.png">

ここでスコープをsharedに指定する`There is already a server bound to: <socket>`あとで怒られてしまいます。`task`にするように注意してください


中間の方まで戻リます。
タスクサイズは両方**512**を設定します。
**コンテナの追加**というボタンがあるのでクリック
ここで**nginx**と**rails**のコンテナをそれぞれ追加していきます。

まずはnginxのコンテナから。

コンテナ名はnginx
イメージにECRリポジトリに登録してあるnginxイメージのURIをコピペします。
ポートマッピングにはホストを0、コンテナポートを80に設定します。


<img width="834" alt="スクリーンショット 2020-02-07 5.49.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/d5d47f94-2cc9-25f2-93a2-2462b19cbcb4.png">


マウントポイントで先ほど作成したsocketsボリュームをマウントします。
また、Cloud Watch Logsにログを吐き出したいのでログ設定にチェックします

<img width="838" alt="スクリーンショット 2020-02-07 5.51.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/d3d004ec-c9e4-cea5-ce45-a0b5aa82f507.png">

他はとりあえず全て空欄でokです!


次にrailsのコンテナを作成します。

先ほどと同様にイメージにECRのrailsイメージのURIをコピペし、ボリュームマウントにsocketsを指定し(パスも同様に/app/tmp/sockets)、ログ設定にもチェックをいれましょう。

ポートマッピング: ホストを0、コンテナポートを3000

また、起動時のコマンドを追加します。作業ディレクトリは/appで。
<img width="764" alt="スクリーンショット 2020-02-07 6.09.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/0cc52bc8-5a6b-bc18-ca92-e53151bd6e6a.png">

bundle,exec,puma,-C,config/puma.rb 



環境変数を追加します。

<img width="811" alt="environmet.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/1bc331d5-4e77-584b-ec0a-4c80e9ca0d90.png">

DB_HOST: RDSのエンドポイント
DB_DATABASE: RDSのDB識別子
RAILS_MASTER_KEY: ローカルのconfig/master.keyの値を貼り付けます。


右下の**作成**ボタンをクリック!

# ロードバランサーの作成

マネジメントコンソールからEC2を検索 => 左タブのロードバランサーを選択
=> ロードバランサーの作成


ALBを選択して作成をクリック
<img width="1263" alt="スクリーンショット 2020-02-07 6.20.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/e288eef3-8001-2cc7-d422-5f5401127d14.png">


名前を適当に入力してAZの設定を行います。
今回は上で作成したVPCを選択し、パブリックサブネットのみ(public-1a、public-1c)を追加します。
<img width="1014" alt="スクリーンショット 2020-02-07 6.23.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/bf0c1819-7630-05ed-26f3-43a8523917fb.png">



・セキュリティグループの設定
新しくセキュリティグループを作成します。

<img width="1275" alt="スクリーンショット 2020-02-07 6.27.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/1102eab2-ab18-00f6-233a-c07d9f4b9397.png">


・ルーティングの設定
ターゲットグループの作成をします
<img width="1260" alt="スクリーンショット 2020-02-07 7.46.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/d989f021-cd86-6921-9176-ce4860be8ff1.png">

・手順 5: ターゲットの登録
ECSの設定で登録するのでここではターゲットは登録せずに「次の手順:確認」をクリック

作成!


# ECSでクラスタの作成
<img width="986" alt="スクリーンショット 2020-02-07 6.40.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/3f803375-d68f-c0c3-9928-5c194f203783.png">

ステップ1:クラスターテンプレートの選択
`EC2 Linux + ネットワーキング`を選択


ステップ2:クラスターの設定
EC2 インスタンスタイプには`t2.micro`を指定(画像ではm3.mediumにしてますが、料金がかなり高いのでt2.microをお勧めします)
キーペア: EC2インスタンスに接続するキーペアを指定します。

ネットワーキングの項目で今回作成したVPCを選択します。
<img width="986" alt="スクリーンショット 2020-02-07 6.40.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/a8136bf1-6e6d-2463-7bfd-e6c497ba2b97.png">

`セキュリティグループは新規作成`します。
後ほど、ここで新規作成したセキュリティグループを用いて、ECSによって起動するEC2インスタンス、RDS、ALBなどを全て繋ぎ合わせます。

作成をクリック!

作成が完了したら以下のような画面になります。
<img width="1173" alt="スクリーンショット 2020-02-07 6.47.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/933d4da0-edf1-2329-fd94-8901f042bec5.png">

クラスターを作成した時点でコンテナインスタンス(今回は2台)が立ち上がります。




# サービスの作成
クラスター一覧で先ほど作成したクラスターを選択します。
サービスという項目に作成ボタンがあるのでそちらをクリックします

<img width="872" alt="スクリーンショット 2020-02-07 18.59.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/8cd70024-27d9-56f0-2821-bb744bdb4a09.png">



ステップ 1: サービスの設定

タスク定義に作成済みのタスクを指定
クラスターに作成済みのクラスターを指定
サービス名に任意の名前を入力

サービス対応はレプリカを、
タスクの数は2を設定します。

ECSで言う**タスク**は**コンテナ群**を意味します。

レプリカはクラスター全体で必要なコンテナ数を指定してそれを維持します。
デーモンでは、各コンテナインスタンスにコンテナのコピーを1つ配置し維持します。

<img width="804" alt="スクリーンショット 2020-02-07 6.55.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/5968de33-4e26-1c89-26bd-e32102b9f6da.png">

他は変更せずに**次のステップ**へ

ステップ 2: ネットワーク構成
ロードバランシングで**Application Load Balancer**にチェックをいれます。

<img width="918" alt="スクリーンショット 2020-02-07 7.00.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/538cd956-c283-02a9-a1e9-bbf818e06729.png">
<img width="913" alt="スクリーンショット 2020-02-07 7.01.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/92356df6-a9bc-6ba5-dc1c-e64816c92b73.png">

サービスの検出の統合の有効化のチェックを外します。


ステップ 3: Auto Scaling (オプション)
・今回はサービスの必要数を直接調整しないをチェックして次のステップへ

サービスの作成をクリック!

このサービスの作成で、これまで作成してきたクラスタとタスク、ALBを全て繋ぎ合わせることができました。

## セキュリティグループの設定
ここが重要です!
今まで作成してきたコンポーネント間のトラフィックを許可するためにECSのクラスター作成時に一緒に新規作成したセキュリティグループのインバウンドを以下のように修正します。

<img width="1124" alt="スクリーンショット 2020-02-11 11.23.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/fefad064-24c7-2559-082d-76a90f74f826.png">

また、EC2インスタンスからRDSにアクセスできるようにします。
 [サービス]タブで、RDSを検索してクリック => 左側のメニューで、「データベース」をクリック。
次に、作成したデータベースを見つけてクリック => 下のタブでセキュリティグループをクリック



<img width="955" alt="スクリーンショット 2020-02-07 5.36.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/05612099-5dae-1262-aa54-d93b46043620.png">

以下のようにインバウンドを編集します。
EC2インスタンスからポート3306 への MySQLトラフィックを許可します。
こうすることで、EC2インスタンスからDBインスタンスに接続できるようにします。
(要はこれしないとrailsコンテナ内でrails db:migrateとか, mysql -u root -pできなくなるってことです)

<img width="1123" alt="スクリーンショット 2020-02-11 11.28.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/aec59435-b5cf-67d7-a2f6-a0eb36afea93.png">



## EC2インスタンスにSSHログイン

<img width="1040" alt="スクリーンショット 2020-02-07 7.14.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/7ed8f174-c9ad-ecc4-abcd-f2b07a6a9ce5.png">


IPv4 パブリック IPをコピーしておきます。

ターミナルを開きます。

$ ssh -i [キーペアのpath] ec2-user@[パブリック IP]


<img width="605" alt="スクリーンショット 2020-02-07 7.16.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/b1a16a61-302e-099e-6a78-069ac9c6d1e7.png">

$ docker ps

とするとコンテナが立ち上がっていることがわかります。
<img width="1438" alt="スクリーンショット 2020-02-07 7.18.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/0f7e367e-767e-3bbb-1af7-1ae24943b29a.png">
ちなみにdocker imagesとするとECRに登録してあるイメージが出てきます。

Railsのコンテナに入ってデータベースを作成しましょう。

$ docker exec -it [RailsコンテナのID] /bin/bash


$ echo $DB_DATABASE

とするとタスク定義の際に設定した環境変数の値が出力されているはずです。
環境変数でRAILS_ENVにproductionが設定されているのでRAILS_ENV=productionとかしなくでも大丈夫です。

$ rails db:create
$ rails db:migrate
$ rails db:seed


<img width="946" alt="スクリーンショット 2020-02-07 7.24.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/7d9b53d4-3a14-81dd-45cd-22266f112c83.png">


[**ALBのDNS名]/posts**にアクセスして以下のようにjsonが返ってきていれば大成功です!!!

<img width="763" alt="スクリーンショット 2020-02-07 7.27.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251768/46daa1a2-58ec-ee2f-c149-83da18dcfc8f.png">

※基本的にコンテナのログは`Cloud Watch Logs`に吐き出されているのでそちらを見ればデバッグできると思います。

# CircleCI での自動デプロイ化

記事が長くなってきたので、config.ymlの説明は省略します。

公式を参考に!
ECR: https://circleci.com/orbs/registry/orb/circleci/aws-ecr
ECS: https://circleci.com/orbs/registry/orb/circleci/aws-ecs?version=0.0.15


AWS_ECR_ACCOUNT_URLとか大文字になっているものは環境変数なのでCircleCIのプロジェクト設定の
`Environment Variables`で設定しておく必要があります。

```yml

version: 2.1
orbs:
  aws-ecr: circleci/aws-ecr@6.7.0
  aws-ecs: circleci/aws-ecs@1.1.0
workflows:
  # Nginxのデプロイ
  nginx-deploy:
    jobs:
      - aws-ecr/build-and-push-image:
          account-url: AWS_ECR_ACCOUNT_URL
          region: AWS_REGION
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          create-repo: true
          dockerfile: ./docker/nginx/Dockerfile
          repo: nginx
          tag: "${CIRCLE_SHA1}"
          filters:
            branches:
              only: master
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build-and-push-image
          family: 'nginx-rails-app' # ECSのタスク定義名
          cluster-name: '${ECS_ARN}'  #ECSのクラスターのARN
          service-name: 'test'  #サービス名
          container-image-name-updates: "container=nginx,tag=${CIRCLE_SHA1}"
  # Railsのデプロイ
  rails-deploy:
    jobs:
      - aws-ecr/build-and-push-image:
          account-url: AWS_ECR_ACCOUNT_URL
          region: AWS_REGION
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          create-repo: true
          dockerfile: ./docker/rails/Dockerfile
          repo: rails
          tag: "${CIRCLE_SHA1}"
          filters:
            branches:
              only: master
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build-and-push-image
          family: 'nginx-rails-app' # ECSのタスク定義名
          cluster-name: '${ECS_ARN}' #ECSのクラスターのARN
          service-name: 'test' #サービス名
          container-image-name-updates: "container=rails,tag=${CIRCLE_SHA1}"

  
#AWS_ECR_ACCOUNT_URL => ${アカウントID}.dkr.ecr.${リージョン}.amazonaws.com

ちなみにECSのクラスターのARNはcliで確認できます。

$ aws ecs describe-clusters --cluster
{
    "clusters": [
        {
            "status": "ACTIVE",
            "statistics": [],
            "tags": [],
            "clusterName": "Rails-Nginx",
            "settings": [
                {
                    "name": "containerInsights",
                    "value": "disabled"
                }
            ],
            "registeredContainerInstancesCount": 0,
            "pendingTasksCount": 0,
            "runningTasksCount": 0,
            "activeServicesCount": 0,
            "clusterArn": "arn:aws:ecs:ap-northeast-1:xxxxxxxxxxx:cluster/Rails-Nginx"
        }
    ],
    "failures": []
}

一応ARNの規則的には以下のようになります。
arn:aws:ecs:${リージョン名}:${アカウントID}:cluster/${アカウントID}"

250
285
7

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
250
285

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?