TerraformでプライベートなRedshiftクラスターを作る

今回はRedshiftクラスターをVPC内に構築し踏み台サーバーからアクセスしてみたいと思います。 今回のRedshiftクラスターはインターネットに接続せず、プライベートで利用します。 そのため、外部からアクセスする手段として踏み台サーバーとしてEC2インスタンスを立ち上げ、それだけが通信できるようにします。 今回はTerraformを用いて構築します。
2021.07.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

踏み台サーバーとは

踏み台サーバーはプライベートなサーバーにアクセスするために経由されるサーバーのことです。 今回はRedshiftクラスターはインターネットに接続していないため、EC2インスタンスを踏み台としてアクセスします。 こうすることで通信先を限定することができ、アクセスの管理がしやすくなります。 以下は今回の構成図です。

Redshiftクラスターにアクセスする場合は、EC2インスタンスを経由しなければアクセスできないようになっています。

Terraformでデプロイする

ネットワーク

network.tf

resource "aws_vpc" "example" {
  cidr_block = "10.1.0.0/16"
  enable_dns_hostnames = true
}

resource "aws_internet_gateway" "example" {
  vpc_id = aws_vpc.example.id
}

resource "aws_subnet" "redshift_1a" {
  cidr_block = "10.1.1.0/24"
  availability_zone = "ap-northeast-1a"
  vpc_id = aws_vpc.example.id 
}

resource "aws_subnet" "bastion" {
cidr_block = "10.1.10.0/24"
  availability_zone = "ap-northeast-1a"
  vpc_id = aws_vpc.example.id 
}

resource "aws_route_table" "main" {
  vpc_id = aws_vpc.example.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.example.id  
  }
}

resource "aws_route_table_association" "bastion_main" {
  subnet_id = aws_subnet.bastion.id
  route_table_id = aws_route_table.main.id
}

今回はサブネットを2つ作り、1つはRedshift用、1つを踏み台サーバー用(bastion)にしています。 インターネットゲートウェイを設置していますが踏み台サーバーのあるサブネットからしかアクセスできません。 ポイントとしてはVPCの設定でenable_dns_hostnamestrueにしておくことです。 こうすることでVPC内からでもエンドポイントのドメイン名を解決できます。

Redshift

redshift.tf

resource "aws_security_group" "redshift" {
  name        = "redshift"
  vpc_id      = aws_vpc.example.id
  description = "redshift cluster"

  ingress {
    from_port = 5439
    to_port = 5439
    protocol = "tcp"
    security_groups = [aws_security_group.bastion.id]
  }
}

resource "aws_redshift_cluster" "example" {
  cluster_identifier     = "example-cluster"
  database_name          = "example"
  master_username        = "exampleuser"
  master_password        = "SuperSecr3t"
  node_type              = "dc2.large"
  cluster_type           = "multi-node"
  number_of_nodes = 2
  publicly_accessible = false
  cluster_subnet_group_name = aws_redshift_subnet_group.example.id
  vpc_security_group_ids = [aws_security_group.redshift.id]
  skip_final_snapshot = true
}

resource "aws_redshift_subnet_group" "example" {
  name       = "example"
  subnet_ids = [aws_subnet.redshift_1a.id]
}

Redshiftクラスターとそれ用のセキュリティグループの設定をしています。 ポイントとしては以下の通りです。

  • セキュリティグループを設定しアクセス制御
    • 踏み台サーバー用のセキュリティグループからのアクセスのみを許可
  • マルチノード構成のためノード数を2以上に
  • publicly_accessibleで外部との通信を二重で拒否

注意点

  • ポート番号は何も指定しないと5439になります。
  • 検証用なのでskip_final_snapshottrueにしていますが、本番で使う場合は適宜設定してください。
  • 簡略化のためにユーザー名とパスワードをコードに埋め込んでいますが、これは危険です。
    • AWS Secrets Mangerなどを利用して安全に保管しましょう

EC2

ec2.tf

resource "aws_security_group" "bastion" {
  name        = "bastion"
  vpc_id      = aws_vpc.example.id
  description = "bastion server"

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


resource "aws_instance" "bastion" {
  ami           = "ami-0ca38c7440de1749a" # Amazon Linux 2 64_x86
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.bastion.id
  iam_instance_profile = aws_iam_instance_profile.systems_manager.name

  associate_public_ip_address = true

  vpc_security_group_ids = [
    aws_security_group.bastion.id
  ]
}

今回は割愛しますが、EC2インスタンスへはSessionManager経由で接続します。 そのためのIAMロールは適宜設定してください。 ポイントとしては以下の通りです。

  • 踏み台サーバー用のセキュリティグループを設定
    • 今回はSessionManager経由なのでアウトバウンドの設定のみで十分
  • 外部とのアクセス用にパブリックIPを割り当てる。

接続してみる

エンドポイントはマネージドコンソールかTerraformから確認することができます。 今回はTerraformから確認します。

Redshiftクラスターのエンドポイントを確認する

$ terraform state show 'aws_redshift_cluster.example' | grep endpoint
endpoint = "example-cluster.XXXXXX.ap-northeast-1.redshift.amazonaws.com:5439"

はじめにドメイン名が解決されているか確認してみます。
以下ではSession Managerで接続したEC2インスタンス内で作業を行なっていきます。

プライベートアドレスで解決されているか確認

$ nslookup example-cluster.XXXXXX.ap-northeast-1.redshift.amazonaws.com
Server:         10.1.0.2
Address:        10.1.0.2#53

Non-authoritative answer:
Name:   example-cluster.XXXXXX.ap-northeast-1.redshift.amazonaws.com
Address: 10.1.1.214

IPアドレスの範囲的にクラスターはap-northeast-1aにプロビジョニングされたようです。 マネージドコンソールから確認するとリーダーノード1つとコンピュートノード2つが作成されています。 リーダーノードのIPアドレスが返されています。

ここでパブリックIPが設定されいますが、これは外部からアクセス可能であるかにかかわらず表示されるものです。

パブリックおよびプライベートクラスターノードの IP アドレスは、クラスターがパブリックにアクセス可能であるかどうかに関係なく表示されます。

参考

psqlでRedshiftクラスターに接続

sh-4.2$ sudo yum install postgresql
sh-4.2$ psql -h example-cluster.XXXXXX.ap-northeast-1.redshift.amazonaws.com -p 5439 -U exampleuser -d example
Password for user exampleuser:
psql (9.2.24, server 8.0.2)
WARNING: psql version 9.2, server version 8.0.
         Some psql features might not work.
SSL connection (cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.

example=# \l
                     List of databases
     name     |    owner    | encoding | access privileges
--------------+-------------+----------+-------------------
 dev          | rdsdb       | UNICODE  |
 example      | exampleuser | UNICODE  |
 padb_harvest | rdsdb       | UNICODE  |
 template0    | rdsdb       | UNICODE  | rdsdb=CT/rdsdb
 template1    | rdsdb       | UNICODE  | rdsdb=CT/rdsdb
(5 rows)

無事に接続できました。 データベースも作成されています。

感想

Terraformを使用してプライベートなRedshiftクラスターを構築することができました。 VPCの設定も同時にできるので便利だと思います。 AdminerなどのWeb上で動くSQLクライアントを踏み台サーバーに設置すればHTTP経由でもアクセスできるので便利かもしれません。