5
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?

Japan AWS Jr. ChampionsAdvent Calendar 2024

Day 14

Private Hosted Zoneへのアクセスをローカルで再現して爆速開発を実現する

Posted at

この記事は、Japan AWS Jr. Champions Advent Calendar 2024の14日目の記事です。

はじめに

株式会社Finatextでサーバーサイド・AWSエンジニアをしている @takuma5884rbbです。

私が普段触れているシステムは、AWS上に構築されたマイクロサービスアーキテクチャで運用されています。機能ごとにモジュールを分けて開発・デプロイを行うマイクロサービスアーキテクチャは、開発サイクルを高速化し、メンテナンスやスケーリングを簡素化します。一方で、モジュール間のインターフェースや接続方法について考慮する必要があり、開発手法にある種の制約を与えることもあります。本記事では、そういったケースへの対処法の一つをご紹介します。

AWSを用いたソフトウェア開発とその課題

AWS上にデプロイされた複数のコンテナ(サーバー)を接続する際に考慮する点として、ネットワークアクセスの制限は外せないでしょう。

この課題に対するソリューションの一つが、Amazon Route53におけるPrivate Hosted Zoneです。

Private Hosted Zoneは設定したVPCのみからアクセスできるHosted Zoneです。

つまり、

  1. アーキテクチャ内に存在するモジュールを特定のVPCに配置する
  2. そのVPCを関連づけたHosted Zoneを作成
  3. そのHosted Zoneを用いてDNSレコードを作成
  4. それらのDNSレコードを用いて、各種モジュールへアクセスできるようにルーティングを設定

という手順を踏むことで、システム内部の安全な通信を確立させることができます。

一方で、アプリケーション開発のサイクルにおいて全ての作業をAWS上で行うわけではありません。例えば開発用EC2インスタンスを建ててその中で開発するとなると追加のコストがかかりますし、ローカルで開発したコードをAWS環境にデプロイして動作確認を行うにしても、CDパイプラインを用意したとしても十数分のリードタイムが生じてしまうでしょう。

したがって、

ローカル環境にてコーディングから動作確認まで完結する

状態を作ることができれば、ソフトウェアの開発サイクルを高速に回すことができます。

次のセクションから、そのための方法の一つをご紹介します。

本編

AWS Systems Manager Session Managerを用いた接続方法

まずは、ローカルからPrivate Hosted Zoneへの接続を実現します。

Private Hosted Zoneが特定のVPCからしかアクセスできない以上、そのVPC上に起動しているインスタンスに接続することを考えます。ここでは、AWS Systems Manager Session Managerを用います。

AWS CLIを用いる場合はこんな感じ

aws ssm start-session --target {インスタンスID} --document-name \
    AWS-StartPortForwardingSessionToRemoteHost \
    --parameters {endpoint},portNumber={リモート側のポート番号},localPortNumber={ローカルで使用したいポート番号} --region {region}

Goでスクリプトを書く場合はこんな感じ

func (c *ssmClient) StartSession(ctx context.Context, target, host, port string) (string, error) {
	input := &ssm.StartSessionInput{
		Target: aws.String(target),
		Parameters: map[string][]string{
			"host":            {host},
			"portNumber":      {"3306"},
			"localPortNumber": {port},
		},
		DocumentName: aws.String("AWS-StartPortForwardingSessionToRemoteHost"),
	}

	output, err := c.svc.StartSession(ctx, input)
	if err != nil {
		return "", err
	}

	return *output.SessionId, nil
}

余談ですが、start-sessionの際のtargetにはFargateタスクを指定することができます。このときの指定方法は以下となります。

ecs:{ECSクラスタ名}_{ECSタスクID}_{ランタイムID}

ランタイムIDはDescribeTasksAPIから取得することができます。

{
...
   "tasks": [ 
      { 
         ...
         "availabilityZone": "string",
         "capacityProviderName": "string",
         "clusterArn": "string",
         "connectivity": "string",
         "connectivityAt": number,
         "containerInstanceArn": "string",
         "containers": [ 
            { 
               ...
               "runtimeId": "string", // これ
               "taskArn": "string"
            }
         ],
         "cpu": "string",
         "createdAt": number,
         "desiredStatus": "string",
        ...
      }
   ]
}

こうすることで、ローカルのポートに対するネットワークアクセスがEC2インスタンスの特定ポートにフォワードされるようになります。

Dockerからの接続

ローカルでの開発環境構築にDockerを用いている方は多いと思います。ローカルから先ほどのポートフォワードを使用するためには一度localhostにルーティングする必要がありますが、先ほどの図のように、各種マイクロサービスへのアクセスをALBでルーティングすることを考えると、ホストヘッダーはAWS環境の設定に合わせておく必要があります。

例)ALBのListener Ruleで「ホストヘッダーがhoge.private.netの場合にマイクロサービスAにルーティングする」という設定がある場合、ローカルからのアクセスでhostをlocalhostにしてしまうと、ポートフォワードでEC2インスタンスにアクセスするまではいいものの、Listener Ruleに適合できずマイクロサービスにアクセスできなくなってしまいます。

ここでは、Dockerコンテナ内の/etc/hostsを書き換えることを考えます。

Dockerコンテナではhost.docker.internalというDNSを用いてホストへの名前解決を行う仕組みがあり、

コンテナ内部からnslookupを実行することで、解決先のIPアドレスを確認することができます(このIPアドレスはローカル環境やDockerエンジンなどによって異なるようです)。

$ docker container exec -it container-name sh
/myContainer # nslookup host.docker.internal
Server:         127.0.0.xx
Address:        127.0.0.xx:yy

Non-authoritative answer:

Non-authoritative answer:
Name:   host.docker.internal
Address: 192.168.xxx.yyy

/etc/hostsを編集し、各種マイクロサービスに向けて利用したいhostをこのIPに解決させてやることで、ホストヘッダーを保ったままローカルへのフォワードを行うことができます。

/myContainer # cat /etc/hosts
127.0.0.1       localhost
()
192.168.xxx.yyy    hoge.private.net

docker-composeを利用して/etc/hostsを編集する方法は以下です。

docker-compose.yml
services:
  service:
    build:
        ...
    extra_hosts:
      - "hoge.private.net:192.168.xxx.yyy"

ポートフォワードを利用する都合上、エンドポイントにはポート番号を付与する必要はあります。

.envの変更例
- SERVICE_A_ENDPOINT=http://hoge.private.net
+ SERVICE_A_ENDPOINT=http://hoge.private.net:{ポートフォワードで使用するローカルのポート番号}

ここまでの手順を踏むことで、ローカルのDockerコンテナ→EC2インスタンス→Private Hosted Zone→ALB→マイクロサービスという接続を確立することができます!

まとめ

マイクロサービス間の通信にはPrivate Hosted Zoneを利用して安全な接続を確立させることができますが、ローカルから単純にアクセスすることができない問題がありました。
そしてこれを解決する手段として、AWS Systems Manager Session Managerを利用したポートフォワードとホスト名解決、Dockerを用いる場合の注意点に触れ、ローカルの開発環境からの接続方法について説明してきました。

これでローカルでの開発サイクルを爆速で回すことができますね!

今後皆さんに、快適なAWSライフが訪れることを祈って。

参考文献

5
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
5
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?