「Knowledge」をTerraformを使用してECS(fagate)構成に変更してみた

# はじめに
自分の学習記録用にQiita:Team の オープンソースクローン 「Knowledge」を単一のEC2の中で
使用していたのですが、学習も兼ねてTerraformでECS環境へ移行してみました。

## 移行後の構成イメージ
![AWS構成_knowledge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626780/4ed279d9-baa7-a903-4246-08f352bce745.png)


## 参考サイト
みんな大好きDevelopers.ioさんの以下のサイトを参考にしました。
こちらのサイトだと手動で作成していたのですが、
ちょうどTerraformの勉強をしていたので、Terraformを使用してみました。

[Amazon ECS + RDS(PostgreSQL) で Qiita:Team の オープンソースクローン Knowledge(Docker版) を構築する](https://dev.classmethod.jp/articles/knowledge-docker-ecs-rds/)


## おおまかな手順の流れ
1. Cloud9実行環境構築

1. Terraformで環境作成

1. ECRにイメージをアップロード

1. タスク定義作成

1. Terraformでサービス作成

1. DBデータ移行

***
## １．Cloud9実行環境構築
前提として別の記事でCloud9実行環境構築は作成しているので、
そちらの記事を参照ください。

### (1) Homebrewインストール
homebrewについては以下を参照
[homebrewとは何者か。仕組みについて調べてみた](https://qiita.com/omega999/items/6f65217b81ad3fffe7e6)

```
$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"
```

### (2) HomebrewにPATHを通す
1を実行すると、Terminalの最後に、PATHが通っていないと警告が表示されます。
そこで、以下コマンドを順に実行してPATHを通します。

```
$ test -d ~/.linuxbrew && PATH="$HOME/.linuxbrew/bin:$HOME/.linuxbrew/sbin:$PATH"
$ test -d /home/linuxbrew/.linuxbrew && PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:$PATH"
$ test -r ~/.bash_profile && echo "export PATH='$(brew --prefix)/bin:$(brew --prefix)/sbin'":'"$PATH"' >>~/.bash_profile
$ echo "export PATH='$(brew --prefix)/bin:$(brew --prefix)/sbin'":'"$PATH"' >>~/.profile
```

### (3) 動作確認
新たにTerminalを開いて確認します。

```bash
$ brew
Example usage:
  brew search [TEXT|/REGEX/]
  brew info [FORMULA...]
  brew install FORMULA...
  brew update
  brew upgrade [FORMULA...]
  brew uninstall FORMULA...
  brew list [FORMULA...]

Troubleshooting:
  brew config
  brew doctor
  brew install --verbose --debug FORMULA

Contributing:
  brew create [URL [--no-fetch]]
  brew edit [FORMULA...]

Further help:
  brew commands
  brew help [COMMAND]
  man brew
  https://docs.brew.sh
```

簡単な手順でCloud9でもHomebrewを実行できるようになりました。


### (4)  tfenvをインストール
Terraformのバージョンを切り替えることができるtfenvをインストールします。
tfenvをインストールしていれば、新しいTerraformのバージョンがリリースされても、Cloud9上で旧バージョンのインストールを維持したまま、新バージョンを試すことができます。

```bash
$ brew install tfenv
```

### (5) tfenvで利用できるTerraformのバージョンを確認
以下のコマンドで、tfenvを使ってインストールできるTerraformのバージョンを確認します。

```bash
$ tfenv list-remote
```

### (6) tfenvでTerraformをインストール
最新のバージョンでインストールするには、以下のコマンドを実行します。

```bash
$ tfenv install latest
```

バージョンを指定する場合は、以下のコマンドに倣います。

```bash
$ tfenv install 0.11.13
```

### (7) tfenvでTerraformのバージョンを選択
tfenvでバージョンを指定すると、terraformコマンドを実行した時に、指定したバージョンが使われるようになります。1つしかバージョンをインストールしていなくても、コマンドを実行する必要があります。

```bash
$ tfenv use latest
```
バージョンを指定する場合は、以下のコマンドに倣います。

```bash
$ tfenv use 0.11.13
```

### (8) 動作確認
Terraformのバージョン確認コマンドを実行して、バージョンを表す文字列が返ることを確認します。

```bash
$ terraform --version
Terraform v0.12.0-beta2
```

これでCloud9でもTerraformが使えるようになりました。Cloud9をEC2モードで動かしている場合は、EC2にIAM Roleをアタッチすれば、TerraformでAWSのリソースを構築できるようになります。



## ２．Terraformで環境作成
### (1) 今回作成する予定のディレクトリ構成
サンプルで作成したgithubをクローンして使ってみてください
https://github.com/sentail-yasu/knowledge-ecs-sample

開発環境でしか作らない想定だったので、以下の構成で作りました。
適宜変更してください

```
├── envs
│   ├── dev
│   │   ├── backend.tf
│   │   ├── main.tf
│   │   └── provider.tf
│   │   └── variables.t
├── acm.tf
├── ec2.tf
├── ecs.tf　←ここだけはこの章でで作らないように
├── lb.tf
├── network.tf
├── rds.tf
├── s3.tf
├── security_group.tf
└── variables.tf

```
### (2) 定義作成

envs/dev/backend.tf

```
terraform {
  required_version = ">= 0.12"

  backend "s3" {
    bucket  = "tfstate-bucket"　←自分用のS3バケットを作成して変更
    region  = "ap-northeast-1"
    key     = "dev/terraform.tfstate" ←任意で変更
    encrypt = true
  }
}
```

envs/dev/main.tf

```
module "dev" {
  source             = "../../"

  vpc_cidr           = var.vpc_cidr
  subnet_cidr        = var.subnet_cidr
  name               = var.name
  rds_instance_class = var.rds_instance_class
  database_name      = var.database_name
  db_username        = var.db_username
  db_password        = var.db_password

  region             = var.region
  ami_id             = var.ami_id
  instance_count     = var.instance_count
  public_subnets     = var.public_subnets
  private_subnets    = var.private_subnets
  instance_type      = var.instance_type
  key_pair           = var.key_pair
  app_name           = var.app_name
  bastion_name       = var.bastion_name

}
```

envs/dev/provider.tf

```
provider "aws" {
  region  = "ap-northeast-1"
}

```

envs/dev/variables.tf

```
variable "vpc_cidr"{
  default = "10.1.0.0/16"
}

variable "subnet_cidr" {
  type = "map"

  default = {
    public-a  = "10.1.10.0/24"
    public-c  = "10.1.20.0/24"
    private-a = "10.1.100.0/24"
    private-c = "10.1.200.0/24"
  }
}

variable "name" {
  default = "knowledge"
}

variable "rds_instance_class" {
  default = "db.t2.small"
}
variable "database_name" {
  default = "knowledge"
}

variable "db_username" {
  default = "postgres"
}

variable "db_password" {}

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

variable "ami_id" {
  default = "ami-0f310fced6141e627"　←AmazonLinux2 x86-64
}

variable "instance_count" {
  default = 2
}

variable "public_subnets" {
  default = {
    "0" = "subnet-xxxx"　←一回実行してサブネット作成後に手動で入れてください
    "1" = "subnet-xxxx"　←一回実行してサブネット作成後に手動で入れてください
  }
}
variable "private_subnets" {
  default = {
    "0" = "subnet-xxxx"　←一回実行してサブネット作成後に手動で入れてください
    "1" = "subnet-xxxx"　←一回実行してサブネット作成後に手動で入れてください
  }
}

variable "instance_type" {
  default = "t3.micro"
}

variable "key_pair" {
  default = "your key pair"
}

variable "app_name" {
  default = "web"
}

variable "bastion_name" {
  default = "bastion"
}

```

acm.tf

```
data aws_route53_zone route53-zone {
  name         = "your domain." ←自分で取得したドメインを入力
  private_zone = false
}

resource aws_acm_certificate cert {
  domain_name       = "www.your domain."
  validation_method = "DNS"
}

resource aws_route53_record cert_validation {
  zone_id = data.aws_route53_zone.route53-zone.zone_id
  name    = aws_acm_certificate.cert.domain_validation_options.0.resource_record_name
  type    = aws_acm_certificate.cert.domain_validation_options.0.resource_record_type
  records = [aws_acm_certificate.cert.domain_validation_options.0.resource_record_value]
  ttl     = 60
}

resource aws_acm_certificate_validation cert {
  certificate_arn = aws_acm_certificate.cert.arn
  validation_record_fqdns = [aws_route53_record.cert_validation.fqdn]
}
```

ec2.tf

```
resource "aws_instance" "bastion_ec2" {
  count                  = 1
  ami                    = var.ami_id
  instance_type          = var.instance_type
  key_name               = var.key_pair
  subnet_id              = lookup(var.public_subnets, count.index % 2)
  vpc_security_group_ids = ["${aws_security_group.bastion_security_group.id}"]

  tags = {
    Name = "${var.name}_${var.bastion_name}_${count.index + 1}"
  }
}

```

lb.tf

```
// albを作成。
resource "aws_alb" "alb" {
  name                       = "${var.name}-alb"
  security_groups            = ["${aws_security_group.alb.id}"]
  subnets = [
    "${aws_subnet.public-a.id}",
    "${aws_subnet.public-c.id}",
  ]
  internal                   = false
  enable_deletion_protection = false

  access_logs {
    bucket = "${var.name}-alb-logs-01"
  }
}

// albのターゲットグループ
resource "aws_alb_target_group" "alb" {
  name     = "${var.name}-tg"
  port     = 8080
  protocol = "HTTP"
  vpc_id   = aws_vpc.vpc.id
  target_type = "ip"

  health_check {
    interval            = 60
    path                = "/"
    // NOTE: defaultはtraffic-port
    //port                = 80
    protocol            = "HTTP"
    timeout             = 20
    unhealthy_threshold = 4
    matcher             = 302
  }
}

// 443ポートの設定。今回は事前にAWS Certificate Managerで作成済みの証明書を設定。
resource "aws_alb_listener" "alb_443" {
  load_balancer_arn = "${aws_alb.alb.arn}"
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2015-05"
  certificate_arn   = "${aws_acm_certificate.cert.arn}"

  default_action {
    target_group_arn = "${aws_alb_target_group.alb.arn}"
    type             = "forward"
  }
}

resource "aws_alb_listener" "alb" {
  load_balancer_arn = "${aws_alb.alb.arn}"
  port              = "80"
  protocol          = "HTTP"

  default_action {
    target_group_arn = "${aws_alb_target_group.alb.arn}"
    type             = "forward"
  }
}

output "alb" {
  value = {
    dns_name         = "${aws_alb.alb.dns_name}"
    arn              = "${aws_alb.alb.arn}"
    target_group_arn = "${aws_alb_target_group.alb.arn}"
  }
}

```

network.tf

```
resource "aws_vpc" "vpc" {
  cidr_block           = var.vpc_cidr
  instance_tenancy     = "default"
  enable_dns_support   = true

  tags = {
    Name = "${var.name}-vpc"
  }
}

# EIP
resource "aws_eip" "nat" {
    vpc = true
}

# NatGateway
resource "aws_nat_gateway" "nat" {
  allocation_id = aws_eip.nat.id
  subnet_id = aws_subnet.public-a.id
}

# Subnet
resource "aws_subnet" "public-a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = var.subnet_cidr["public-a"]
  map_public_ip_on_launch = true
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "public-subnet-1a"
  }
}

resource "aws_subnet" "public-c" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = var.subnet_cidr["public-c"]
  map_public_ip_on_launch = true
  availability_zone = "ap-northeast-1c"

  tags = {
    Name = "public-subnet-1c"
  }
}

resource "aws_subnet" "private-a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = var.subnet_cidr["private-a"]
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "private-subunet-1a"
  }
}

resource "aws_subnet" "private-c" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = var.subnet_cidr["private-c"]
  availability_zone = "ap-northeast-1c"

  tags = {
    Name = "private-subunet-1c"
  }
}

# db_subnet_group
resource "aws_db_subnet_group" "db-subnet" {
    name = "db-subnet"
    description = "test db subnet"
    subnet_ids = ["${aws_subnet.private-a.id}", "${aws_subnet.private-c.id}"]
}

# Internet Gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.name}-igw"
  }
}

# public Route Table
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.name}-rt"
  }
}

# public Route
resource "aws_route" "public_route" {
  gateway_id             = aws_internet_gateway.igw.id
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
}

# パブリックルートテーブルとpublic-a関連付け
resource "aws_route_table_association" "public_a" {
    subnet_id      = aws_subnet.public-a.id
    route_table_id = aws_route_table.public.id
}

# パブリックルートテーブルとpublic-c関連付け
resource "aws_route_table_association" "public_c" {
    subnet_id      = aws_subnet.public-c.id
    route_table_id = aws_route_table.public.id
}

# private Route Table
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.vpc.id
}

# private Route
resource "aws_route" "private_route" {
  nat_gateway_id             = aws_nat_gateway.nat.id
  route_table_id         = aws_route_table.private.id
  destination_cidr_block = "0.0.0.0/0"
}

# パブリックルートテーブルとprivate-a関連付け
resource "aws_route_table_association" "private-a" {
    subnet_id = aws_subnet.private-a.id
    route_table_id = aws_route_table.private.id
}

# パブリックルートテーブルとprivate-c関連付け
resource "aws_route_table_association" "private-c" {
    subnet_id = aws_subnet.private-c.id
    route_table_id = aws_route_table.private.id
}

output "vpc_id" {
  value = aws_vpc.vpc.id
}

output "subnet_public_a_id" {
  value = aws_subnet.public-a.id
}

output "subnet_public_c_id" {
  value = aws_subnet.public-c.id
}

output "subnet_private_a_id" {
  value = aws_subnet.private-a.id
}

output "subnet_private_c_id" {
  value = aws_subnet.private-c.id
}

```

rds.tf

```
resource "aws_db_parameter_group" "db-pg" {
    name = "knowledge"
    family = "postgres11.5"
    description = "knowledge"

    parameter {
        name = "log_min_duration_statement"
        value = "100"
    }
}

resource "aws_db_instance" "posgre" {
    identifier = "knowledge"
    allocated_storage = 10
    engine = "postgres"
    engine_version = "11.5"
    instance_class = var.rds_instance_class
    name = var.database_name
    username = var.db_username
    password = var.db_password
    db_subnet_group_name = "${aws_db_subnet_group.db-subnet.name}"
    vpc_security_group_ids = ["${aws_security_group.posgre_security_group.id}"]
    parameter_group_name = "${aws_db_parameter_group.db-pg.name}"
    multi_az = false
    backup_retention_period = "7"
    backup_window = "19:00-19:30"
    apply_immediately = "true"
    auto_minor_version_upgrade = false
}

output "rds_endpoint" {
    value = "${aws_db_instance.posgre.address}"
}

```

s3.tf

```
# プライベートバケットの定義
resource "aws_s3_bucket" "alb-logs" {
  # バケット名は世界で1意にしなければならない
  bucket = "${var.name}-alb-logs-01"

  versioning {
    enabled = true
  }

  # 暗号化を有効
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}

# ブロックパブリックアクセス
resource "aws_s3_bucket_public_access_block" "alb-logs" {
  bucket                  = aws_s3_bucket.alb-logs.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

```

security_group.tf

```
# RDSに設定するセキュリティグループ
resource "aws_security_group" "posgre_security_group" {
  name   = "${var.name}-posgre-sg"
  vpc_id = aws_vpc.vpc.id

  ingress {
    from_port       = "5432"
    to_port         = "5432"
    protocol        = "tcp"
    cidr_blocks     = ["10.1.0.0/16"]
  }

  egress {
      from_port      = 0
      to_port        = 0
      protocol       = "-1"
      cidr_blocks    = ["0.0.0.0/0"]
  }
  tags = {
    Name = "${var.name}-posgre-sg"
  }
}

resource "aws_security_group" "alb" {
  name   = "${var.name}-alb-sg"
  vpc_id = aws_vpc.vpc.id

  ingress {
    from_port       = "443"
    to_port         = "443"
    protocol        = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
      from_port      = 0
      to_port        = 0
      protocol       = "-1"
      cidr_blocks    = ["0.0.0.0/0"]
  }
  tags = {
    Name = "${var.name}-alb-sg"
  }
}

output "db_sg_id" {
  value = aws_security_group.posgre_security_group.id
}

# bastionに設定するセキュリティグループ
resource "aws_security_group" "bastion_security_group" {
  name   = "${var.name}-${var.bastion_name}-sg"
  vpc_id = aws_vpc.vpc.id

  ingress {
    from_port   = "22"
    to_port     = "22"
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = "0"
    to_port     = "0"
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "${var.name}-${var.bastion_name}-sg"
  }
}

output "bastion_sg_id" {
  value = aws_security_group.bastion_security_group.id
}

# ecsに設定するセキュリティグループ
resource "aws_security_group" "ecs" {
  name   = "${var.name}-ecs-sg"
  vpc_id = aws_vpc.vpc.id

  ingress {
    from_port       = "8080"
    to_port         = "8080"
    protocol        = "tcp"
    cidr_blocks     = ["10.1.0.0/16"]
  }

  egress {
      from_port      = 0
      to_port        = 0
      protocol       = "-1"
      cidr_blocks    = ["0.0.0.0/0"]
  }
  tags = {
    Name = "${var.name}-ecs-sg"
  }
}

```

variables.tf

```
variable "vpc_cidr"{}
variable "subnet_cidr" {}
variable "name" {}
variable "rds_instance_class" {}
variable "database_name" {}
variable "db_username" {}
variable "db_password" {}

variable "region" {}
variable "ami_id" {}
variable "instance_count" {}
variable "public_subnets" {}
variable "private_subnets" {}
variable "instance_type" {}
variable "key_pair" {}
variable "app_name" {}
variable "bastion_name" {}

```

### (3) Terraform実行
実行するディレクトリに移動後、Terraformで環境構築
※なぜかACMのところで1回目は失敗してしまうのですが、
　2回目の実行で成功します。
　以下の記事の方も同じ事象みたいです
　https://qiita.com/pokotyan/items/6a00b5cdfe8811b4c832
```
$ cd terraform/envs/dev
$ terraform init
$ terraform plan
$ terraform apply
```

***
## ３．ECRにイメージをアップロード

### (1) 公式DockerからDockerイメージをダウンロード
以下のリポジトリをクローンする
https://github.com/support-project/docker-knowledge

### (2) データベース接続先の変更
volumes/knowledge/custom_connection.xmlでDB接続先設定をカスタマイズできるようですので、作成したPostgreSQLの情報を使ってこのファイルを編集します。

変更後のイメージ

```
<connectionConfig>
        <name>custom</name>
        <driverClass>org.postgresql.Driver</driverClass>
        <URL>jdbc:postgresql://RDSのエンドポイント:ポート番号/DB名</URL>　←ここを変更
        <user>postgres</user>
        <password>mysql123</password>
        <schema>public</schema>
        <maxConn>0</maxConn>
        <autocommit>false</autocommit>
</connectionConfig>
```

### (3) Dockerfile の編集
さきほど編集した custom_connection.xml が コンテナ起動時に適用されるようにします。

```
VOLUME [ "/root/.knowledge" ]
COPY ./volumes/knowledge/custom_connection.xml /root/.knowledge/　←ここを追加
EXPOSE 8080
```
### (4) ECRのリポジトリ作成
前提としてAWSコンソールにログインしてECRリポジトリを作成しておく

### (5) ECRへログイン

```
aws ecr get-login --no-include-email --region ap-northeast-1 > login.sh
bash login.sh
```

### (6) Docker Image の作成

```
docker build -t knowledge .
```

### (7) タグ付け

```
docker tag knowledge:latest ECRのリポジトリ名/knowledge:latest
```

### (8) プッシュ

```
docker push ECRのリポジトリ名/knowledge:latest
```

***
## ４．タスク定義作成

### (1) task_definition.jsonを作成

```
$ vi  task_definition.json
```

以下の内容を環境に置き換えて作成

```
{
    "family": "sample",
    "executionRoleArn": "arn:aws:iam::自分の環境に変更:role/ecsTaskExecutionRole",
    "networkMode": "awsvpc",
    "containerDefinitions": [
        {
            "name": "knowledge",
            "image": "自分のECRのリポジトリ/knowledge:latest",
            "ulimits": [
                {
                    "name": "nofile",
                    "softLimit": 65536,
                    "hardLimit": 65536
                }
            ],
            "portMappings": [
                {
                    "protocol": "tcp",
                    "containerPort": 8080
                }
            ],
            "memoryReservation": 256,
            "essential": true,
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/knowledge",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-stream-prefix": "knowledge"
                }
            }
        }
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "512",
    "memory": "2048"
}
```

### (2) タスク定義作成

```
$ aws ecs register-task-definition --cli-input-json file://$PWD/task-definitions.json
```

***
## ５．Terraformでサービス作成

### (1) 定義作成

ecs.tf

```
## cluster設定
resource "aws_ecs_cluster" "ecs_cluster" {
  name = "${var.name}-cluster"
}

resource "aws_ecs_service" "ecs" {
  name                               = "${var.name}-01"
  cluster                            = "${aws_ecs_cluster.ecs_cluster.id}"
  task_definition                    = "knowledge-fagate:1"
  desired_count                      = 2
  launch_type                        = "FARGATE"
  deployment_minimum_healthy_percent = 100
  deployment_maximum_percent         = 200

  network_configuration {
    subnets = [
        "${aws_subnet.private-a.id}",
        "${aws_subnet.private-c.id}"
    ]

    security_groups = [
      "${aws_security_group.ecs.id}"
    ]

    assign_public_ip = "false"
  }

  health_check_grace_period_seconds = 0

  load_balancer {
    target_group_arn = "${aws_alb_target_group.alb.arn}"
    container_name   = "knowledge"
    container_port   = 8080
  }

  scheduling_strategy = "REPLICA"

  deployment_controller {
    type = "CODE_DEPLOY"
  }
  // deployやautoscaleで動的に変化する値を差分だしたくないので無視する
  lifecycle {
    ignore_changes = [
      "desired_count",
      "task_definition",
      "load_balancer",
    ]
  }
  propagate_tags = "TASK_DEFINITION"
}
```

### (2) オプトイン設定を定義
どうやらタスク定義はARNがオプトインの設定を変更しない場合、ARNが長すぎてエラーになってしまうため、以下の公式ドキュメントに従い、自分が使用しているIAMだけオプトイン設定を変更する。
https://aws.amazon.com/jp/blogs/compute/migrating-your-amazon-ecs-deployment-to-the-new-arn-and-resource-id-format-2/

### (3) Terraform実行


```
$ cd terraform/envs/dev
$ terraform init
$ terraform plan
$ terraform apply
```

### (4) 実行後確認
ECSのサービスが無事出来ていれば完璧


**
## ６．DBデータ移行

### (1) 移行前のRDSにログインできるEC2にログイン
※割愛してますが、組み込みのKnowledgeのposgreにログインできなかったため、
　わざわざRDSを建て、GUIからDBの移行しました...

### (2) postgresql11のインストール
こちらのpostgres環境はバージョン11であったため、
以下のコマンドでインストール実施

```
$ sudo rpm -ivh --nodeps https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
$ sed -i "s/\$releasever/7/g" "/etc/yum.repos.d/pgdg-redhat-all.repo"
$ sudo yum install -y postgresql11
```

### (3) 以下のコマンドでdump取得

```
pg_dump -U ユーザ名 -h ホスト名 -p 5432 DB名 -f ファイル名.dump
```

### (4) 移行後のRDSにログインできるEC2にログイン

### (5) postgresql11のインストール
こちらのpostgres環境はバージョン11であったため、
以下のコマンドでインストール実施

```
$ sudo rpm -ivh --nodeps https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
$ sed -i "s/\$releasever/7/g" "/etc/yum.repos.d/pgdg-redhat-all.repo"
$ sudo yum install -y postgresql11
```

### (6) 以下のコマンドでdumpから復元

なんか色々エラーが起きてたけど、無視しました...
そのせいなのかユーザが復元されませんでした

```
psql -U ユーザ名 -h ホスト名 -d データベース名 -p 5432 -f ファイル名.dump
```

### (7) 画像を読み込めるように再度修正
なぜか画像が読み込めなくてなっていたので、
DBに直接ログインして以下のSQL文を発行する。
※正直ここは環境特有かも

```
UPDATE knowledges
SET content = REPLACE(content, '/knowledge/open.file/download?', '/open.file/download?');
```

以上で、移行完了です。


## 今後の課題
Terraformの実行もECRにイメージをアップするところも
手動でやってしまったので、CI/CDツールをもっと活かして自動化していきたいと思います。