• トップ
  • ブログ一覧
  • 【macOS】LocalStack+terraformでEC2+RDSを立ててみた
  • 【macOS】LocalStack+terraformでEC2+RDSを立ててみた

    ハク(エンジニア)ハク(エンジニア)
    2023.06.16

    IT技術

    注意

    • LocalStackのPro版(有料版)のAPIキーが必要
      • Pro版の体験版(14日間)があるので、そちらを使用しています
    • LocalStackとterraformとAWSのネットワーク設定周りは素人のため、間違っている箇所があるかもしれないがご容赦ください

    準備

    LocalStackをインストール

    • 参考: https://docs.localstack.cloud/getting-started/installation
      • 僕はLocalStack CLIでインストール
    • 下記ツールが必要のため、事前にインストール
      • python (version: 3.7 ~ 3.10)
      • pip
      • docker
    • LocalStackをインストール
      1brew install localstack
    • LocalStackがインストールされていることを確認
      1$ localstack --version
      22.1.0
    • LocalStack Pro版のAPIキーを環境変数に登録
      1export LOCALSTACK_API_KEY=xxxxxxxxxx
    • LocalStackを起動
      1$ localstack start -d
      2
      3     __                     _______ __             __
      4    / /   ____  _________ _/ / ___// /_____ ______/ /__
      5   / /   / __ \/ ___/ __ `/ /\__ \/ __/ __ `/ ___/ //_/
      6  / /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,<
      7 /_____/\____/\___/\__,_/_//____/\__/\__,_/\___/_/|_|
      8
      9 💻 LocalStack CLI 2.1.0
      10
      11[10:41:13] starting LocalStack in Docker mode 🐳               localstack.py:142
      122023-06-09T10:41:15.565  INFO --- [  MainThread] l.bootstrap.licensing      : Successfully activated API key
      132023-06-09T10:41:15.880  INFO --- [  MainThread] localstack.utils.bootstrap : Execution of "prepare_host" took 1896.32ms
      14[10:41:15] preparing environment                                bootstrap.py:630
      15[10:41:16] configuring container                                bootstrap.py:638
      16[10:41:17] starting container                                   bootstrap.py:645
      17[10:41:18] detaching                                            bootstrap.py:649
    • LocalStackが起動していることを確認
      1$ localstack status
      2┌─────────────────┬───────────────────────────────────────────────────────┐
      3Runtime version │ 2.1.1.dev4Docker image    │ tag: latest, id: 9ff2ae7ae1a0, 📆 2023-06-02T11:46:415Runtime status  │ ✔ running (name: "localstack_main", IP: 172.17.0.2)6└─────────────────┴───────────────────────────────────────────────────────┘
      7
      8$ docker ps
      9CONTAINER ID   IMAGE                       COMMAND                  CREATED         STATUS                   PORTS                                                                                                                                    NAMES
      105f55c6aea975   localstack/localstack-pro   "docker-entrypoint.sh"   9 minutes ago   Up 9 minutes (healthy)   127.0.0.1:443->443/tcp, 127.0.0.1:4510-4559->4510-4559/tcp, 0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp, 127.0.0.1:4566->4566/tcp, 5678/tcp   localstack_main
      • IPアドレスは必要なのでメモを取る

    terraformをインストール

    • 参考: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli
      • 僕はHomebrew on OS Xでインストール
    • terraformのリポジトリを追加
      1brew tap hashicorp/tap
    • terraformをインストール
      1brew install hashicorp/tap/terraform
    • terraformがインストールされていることを確認
      1$ terraform version
      2Terraform v1.4.6
      3on darwin_arm64
      4+ provider registry.terraform.io/hashicorp/aws v5.1.0
      5+ provider registry.terraform.io/hashicorp/http v3.3.0
      6+ provider registry.terraform.io/hashicorp/local v2.4.0
      7+ provider registry.terraform.io/hashicorp/tls v4.0.4

    EC2で使うイメージを用意する

    • 参考: https://docs.localstack.cloud/user-guide/aws/elastic-compute-cloud/#base-images
    • ubuntu:focalのイメージをpullします
      1docker pull ubuntu:focal
    • イメージがpullされていることを確認
      1$ docker images
      2REPOSITORY   TAG     IMAGE ID       CREATED       SIZE
      3... (中略) ...
      4ubuntu       focal   758cd4ebb217   8 weeks ago   65.7MB
      5... (中略) ...
    • LocalStack用のtagを追加
      1docker tag ubuntu:focal localstack-ec2/ubuntu-focal-ami:ami-000001
    • tagが追加されたことを確認
      1$ docker images
      2REPOSITORY                        TAG          IMAGE ID       CREATED       SIZE
      3... (中略) ...
      4localstack-ec2/ubuntu-focal-ami   ami-000001   758cd4ebb217   8 weeks ago   65.7MB
      5ubuntu                            focal        758cd4ebb217   8 weeks ago   65.7MB
      6... (中略) ...

    AWSプロフィールを設定

    • aws-cliで設定
      • AWS Access Key ID: dummy
      • AWS Secret Access Key: dummy
      • Default region name: ap-northeast-1
      • Default output format: json
      1$ aws configure --profile localstack
      2AWS Access Key ID [None]: dummy
      3AWS Secret Access Key [None]: dummy
      4Default region name [None]: ap-northeast-1
      5Default output format [None]: json
    • AWSプロフィールが設定されていることを確認
      1$ aws configure list --profile localstack
      2      Name                    Value             Type    Location
      3      ----                    -----             ----    --------
      4   profile               localstack           manual    --profile
      5access_key     ****************ummy shared-credentials-file
      6secret_key     ****************ummy shared-credentials-file
      7    region           ap-northeast-1      config-file    ~/.aws/config

    サーバー構成図

    1+----------------------------------------------+
    2| +--------------------------------+           |
    3| | RDS                            |           |
    4| | (MySQL)                        | <---+     |
    5| | port: determined by LocalStack |     |     |
    6| +--------------------------------+     |     |
    7|                                        |     |
    8|                                        |     |
    9|                                        |     |
    10| +------------+                +------------+ |
    11| | web-server |                | app-server | |
    12| | (nginx)    | -------------> | (golang)   | |
    13| | port: 80   |                | port: 8080 | |
    14| +------------+                +------------+ |
    15+----------------------------------------------+
    • RDS: LocalStackのコンテナー内にMySQLが起動する
    • web-server: 一つのdockerコンテナーとして作られる
      • web-serverの役割は通信の出入りで、リクエストが来たらapp-serverに通す(リバースプロキシ)
    • app-server: 一つのdockerコンテナーとして作られる
      • リクエストのIDをMySQLに格納されているデータを取得し、その結果を返却するプログラム

    terraformでEC2とRDSを構築

    • ディレクトリ構図
      1.
      2├── main.tf
      3├── output.tf
      4├── provider.tf
      5├── ec2
      6│   ├── keypair.tf
      7│   ├── main.tf
      8│   ├── output.tf
      9│   └── variables.tf
      10├── rds
      11│   ├── main.tf
      12│   └── variables.tf
      13└── vpc
      14    ├── main.tf
      15    └── output.tf
      • 上記のファイルはgit(url)に上げています
    • initとplanをします
      1$ terraform init
      2$ terraform plan
    • 内容を確認したらapplyを実行します
      1$ terraform apply
      • 途中でプロンプトが出るので、planと同じ内容出ていたらyesを打ちます
        1Do you want to perform these actions?
        2Terraform will perform the actions described above.
        3Only 'yes' will be accepted to approve.
        4
        5Enter a value: yes
    • EC2とRDSが起動していることを確認
      • EC2確認
        • dockerのコンテナーを確認
          1$ docker ps
          2CONTAINER ID   IMAGE                                        COMMAND                  CREATED          STATUS                    PORTS                                                                                                                                    NAMES
          3f16e0697c324   localstack-ec2/ubuntu-focal-ami:ami-000001   "sleep 43200"            17 minutes ago   Up 17 minutes             0.0.0.0:32983->22/tcp, 0.0.0.0:60385->80/tcp, 0.0.0.0:35709->8080/tcp                                                                    localstack-ec2.i-85a837419413c12d9
          4c9e63e3ae05b   localstack-ec2/ubuntu-focal-ami:ami-000001   "sleep 43200"            17 minutes ago   Up 17 minutes             0.0.0.0:22->22/tcp, 0.0.0.0:80->80/tcp, 0.0.0.0:8080->8080/tcp                                                                           localstack-ec2.i-0265072bfb2fbdc0a
          5ed08d7441d54   localstack/localstack-pro                    "docker-entrypoint.sh"   20 minutes ago   Up 20 minutes (healthy)   127.0.0.1:443->443/tcp, 127.0.0.1:4510-4559->4510-4559/tcp, 0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp, 127.0.0.1:4566->4566/tcp, 5678/tcp   localstack_main
          • dockerのコンテナーIDc9e63e3ae05bf16e0697c324は立てられたEC2で、起動していることが確認できた
            • c9e63e3ae05bはweb-serverとします
            • f16e0697c324はapp-serverとします
        • LocalStackのログを確認
          1$ localstack logs
          2... (中略) ...
          32023-06-09T03:59:02.385  INFO --- [   asgi_gw_4] l.s.ec2.vmmanager.docker   : Instance i-0265072bfb2fbdc0a will be accessible via SSH at: 127.0.0.1:22, 172.17.0.3:22
          42023-06-09T03:59:02.385  INFO --- [   asgi_gw_4] l.s.ec2.vmmanager.docker   : Instance i-0265072bfb2fbdc0a port mappings (container -> host): {'8080/tcp': 8080, '80/tcp': 80, '22/tcp': 22}
          52023-06-09T03:59:02.398  INFO --- [   asgi_gw_4] localstack.request.aws     : AWS ec2.RunInstances => 200
          62023-06-09T03:59:02.480  INFO --- [   asgi_gw_0] l.s.ec2.vmmanager.docker   : Instance i-85a837419413c12d9 will be accessible via SSH at: 127.0.0.1:32983, 172.17.0.4:22
          72023-06-09T03:59:02.480  INFO --- [   asgi_gw_0] l.s.ec2.vmmanager.docker   : Instance i-85a837419413c12d9 port mappings (container -> host): {'8080/tcp': 35709, '80/tcp': 60385, '22/tcp': 32983}
          82023-06-09T03:59:02.492  INFO --- [   asgi_gw_0] localstack.request.aws     : AWS ec2.RunInstances => 200
          9... (中略) ...
          • インスタンスIDi-0265072bfb2fbdc0ai-85a837419413c12d9は起動していることが確認できた
          • IPアドレスとポート番号は必要なのでメモを取る
      • RDS確認
        • LocalStackのコンテナーに入る
          • ↑の出力からコンテナーIDはed08d7441d54
          1docker exec -it ed08d7441d54 bash
        • プロセス確認
          1root@ed08d7441d54:/opt/code/localstack# ps aux
          2USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
          3root         1  0.0  0.1  11372  8628 ?        Ss   03:56   0:00 /opt/code/local
          4root        15  0.4  4.4 1921908 359844 ?      Sl   03:56   0:07 /opt/code/local
          5root      1354  0.0  0.0   2056   464 ?        Ss   03:58   0:00 /bin/sh -c mysq
          6mysql     1355  0.0  0.9 1418084 80080 ?       Sl   03:58   0:00 mysqld --no-def
          7root      3710  0.5  0.0   5844  3452 pts/0    Ss   04:28   0:00 bash
          8root      3716  0.0  0.0   8340  2840 pts/0    R+   04:28   0:00 ps aux
        • ポート番号はLocalStackのログで確認
          1$ localstack logs | grep rds
          2... (中略) ...
          32023-06-09T03:58:59.048  INFO --- [functhread38] l.s.rds.engine_mariadb     : Starting MySQL (MariaDB) RDS server on port 4510 (backend port 4510) - database "terraform_practice", user "terraform"
          4... (中略) ...
          • ポート番号は4510であることがわかりました
        • MySQLは起動していることが確認できた

    接続情報

    1type                docker-container-id     ip-address    port
    2ec2 (web-server)    c9e63e3ae05b            172.17.0.3    80->80; 8080->8080
    3ec2 (app-server)    f16e0697c324            172.17.0.4    60385->80; 35709->8080
    4rds                 ed08d7441d54            172.17.0.2    4510

    RDSデータ準備

    • MySQLにログイン
      1root@ed08d7441d54:/opt/code/localstack# mysql -h 127.0.0.1 -P 4510 -u terraform -D terraform_practice -p
      2Enter password:
      3Welcome to the MariaDB monitor.  Commands end with ; or \g.
      4Your MariaDB connection id is 10
      5Server version: 10.5.19-MariaDB-0+deb11u2 Debian 11
      6
      7Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
      8
      9Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    • テーブルを作成
      1MariaDB [terraform_practice]> CREATE TABLE practice (id INT, name VARCHAR(10), update_time DATETIME);
      2Query OK, 0 rows affected (0.022 sec)
    • データを挿入
      1MariaDB [terraform_practice]> INSERT INTO practice VALUES (1, "a", NOW()), (2, "b", NOW()), (3, "c", NOW());
      2Query OK, 3 rows affected (0.025 sec)
      3Records: 3  Duplicates: 0  Warnings: 0
    • 挿入したデータを確認
      1MariaDB [terraform_practice]> SELECT * FROM practice;
      2+------+------+---------------------+
      3| id   | name | update_time         |
      4+------+------+---------------------+
      5|    1 | a    | 2023-06-09 04:49:58 |
      6|    2 | b    | 2023-06-09 04:49:58 |
      7|    3 | c    | 2023-06-09 04:49:58 |
      8+------+------+---------------------+
      93 rows in set (0.001 sec)

    web-server設定

    • web-serverのコンテナーに入る
      1docker exec -it c9e63e3ae05b bash
    • インストール済みのプログラムを更新
      1apt-get update -y
    • 必要なプログラムやツールをインストール
      1apt-get install vim net-tools procps curl nginx -y
    • nginxがインストールされていることを確認
      1$ nginx -v
      2nginx version: nginx/1.18.0 (Ubuntu)
    • nginxを起動
      1nginx
    • nginxにアクセスできるかを確認
      • ホストマシンからcurlで確認
        1curl http://127.0.0.1/
      • dockerのコンテナー内からcurlで確認
        1curl http://172.17.0.3/
      • 出力は下記
        1<!DOCTYPE html>
        2<html>
        3<head>
        4<title>Welcome to nginx!</title>
        5<style>
        6    body {
        7        width: 35em;
        8        margin: 0 auto;
        9        font-family: Tahoma, Verdana, Arial, sans-serif;
        10    }
        11</style>
        12</head>
        13<body>
        14<h1>Welcome to nginx!</h1>
        15<p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p>
        16
        17<p>For online documentation and support please refer to
        18<a href="http://nginx.org/">nginx.org</a>.<br/>
        19Commercial support is available at
        20<a href="http://nginx.com/">nginx.com</a>.</p>
        21
        22<p><em>Thank you for using nginx.</em></p>
        23</body>
        24</html>
    • nginxの通信をすべてapp-serverに向くよう設定
      1$ vi / etc/nginx/sites-available/default
      /etc/nginx/sites-available/default
      1# before:
      2location / {
      3        # First attempt to serve request as file, then
      4        # as directory, then fall back to displaying a 404.
      5        try_files $uri $uri/ =404;
      6}
      7
      89
      10# after:
      11location / {
      12        # First attempt to serve request as file, then
      13        # as directory, then fall back to displaying a 404.
      14        # try_files $uri $uri/ =404;
      15        proxy_pass http://172.17.0.4:8080/;
      16}
    • nginxを再起動
      1nginx -s reload
    • リバースプロキシの設定反映されていることを確認
      • ホストマシンからcurlで確認
        1curl http://127.0.0.1/
      • dockerのコンテナー内からcurlで確認
        1curl http://172.17.0.3/
      • 出力は下記
        1<html>
        2<head><title>502 Bad Gateway</title></head>
        3<body>
        4<center><h1>502 Bad Gateway</h1></center>
        5<hr><center>nginx/1.18.0 (Ubuntu)</center>
        6</body>
        7</html>
      • ステータスコード502であれば正しく反映されている

    app-server設定

    • app-serverのコンテナーに入る
      1docker exec -it f16e0697c324 bash
    • インストール済みのプログラムを更新
      1apt-get update -y
    • 必要なプログラムやツールをインストール
      1apt-get install vim net-tools procps curl git golang-go -y
    • gitがインストールされていることを確認
      1$ git --version
      2git version 2.25.1
    • golangがインストールされていることを確認
      1$ go version
      2go version go1.13.8 linux/arm64
    • golangのMySQLのドライバーをインストール
      1go get -u github.com/go-sql-driver/mysql
    • スクリプトのディレクトリを作成
      1mkdir /root/src
    • スクリプトを作成
      1$ vi /root/src/main.go
      /root/src/main.go
      1package main
      2
      3import (
      4    "fmt"
      5    "strconv"
      6    "net/http"
      7    "database/sql"
      8
      9    _ "github.com/go-sql-driver/mysql"
      10)
      11
      12type PracticeDto struct {
      13    id          int
      14    name        string
      15    update_time string
      16}
      17
      18func getDataFromMysql(id int) PracticeDto {
      19    db, err := sql.Open("mysql", "terraform:terraform!@tcp(172.17.0.2:4510)/terraform_practice")
      20    if err != nil {
      21        panic(err.Error())
      22    }
      23    defer db.Close()
      24
      25    var dto PracticeDto
      26    err = db.QueryRow("SELECT * FROM practice WHERE id = ?", id).Scan(&dto.id, &dto.name, &dto.update_time)
      27    if err != nil {
      28        panic(err.Error())
      29    }
      30
      31    return dto
      32}
      33
      34func main() {
      35    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      36        id, _ := strconv.Atoi(r.FormValue("id"))
      37		mysql_res := getDataFromMysql(id)
      38
      39        fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path[1:])
      40		fmt.Fprintf(w, "Your requested id's (id: %d) result is [id: %d; name: %s; update_time: %s]\n", id, mysql_res.id, mysql_res.name, mysql_res.update_time)
      41    })
      42
      43    http.ListenAndServe(":8080", nil)
      44}
    • スクリプトファイルができたかを確認
      1$ ls -l /root/src
      2total 4
      3-rw-r--r-- 1 root root 1083 Jun  9 16:31 main.go
    • スクリプトをビルド
      1go build -o /root/src/main /root/src/main.go
    • ビルドしたファイルができたかを確認
      1ls -l /root/src/
      2total 7532
      3-rwxr-xr-x 1 root root 7815206 Jun  9 16:34 main
      4-rw-r--r-- 1 root root    1083 Jun  9 16:31 main.go
    • ビルドしたファイルを実行
      1/root/src/main
    • 動作確認
      • ホストマシンからcurlで確認
        1$ curl http://127.0.0.1:35709/dummy_uri\?id=1
      • dockerのコンテナー内からcurlで確認
        1curl http://172.17.0.4:8080/dummy_uri?id=1
      • 出力は下記
        1Hello, you've requested: dummy_uri
        2Your requested id's (id: 1) result is [id: 1; name: a; update_time: 2023-06-09 04:49:58]

    動作確認

    • ホストマシン → web-server → app-serverの経路で通信しているかを確認
      1$ curl http://127.0.0.1/test_uri\?id=2
      2Hello, you've requested: test_uri
      3Your requested id's (id: 2) result is [id: 2; name: b; update_time: 2023-06-09 04:49:58]
      • ホストマシン → web-server → app-server経路で通信していることが確認できた
    ハク(エンジニア)

    ハク(エンジニア)

    おすすめ記事