こんにちは、EC基盤本部SRE部ZOZOSREチームの石川です。 普段はZOZOTOWNのオンプレミスとクラウドの構築・運用に携わっています。
ZOZOTOWNには長い歴史がありますが、その中核を成すWebアプリケーションのアーキテクチャは、サービス開始当初から現在に至るまで大きく変わらず稼働しています。
一方で、インフラは少しずつ変わっています。高負荷となるセールやイベント時のスケールアウトするために、またハードウェアのライフサイクルに合わせる形で、物理サーバ → 仮想基盤 → クラウドと徐々に技術が変遷しています。
本記事では、クラウドへのスケールアウトを加速させるために、オンプレミスで稼働中のWebサーバをAmazon EC2(以下、EC2という)で動作させるまでの取り組みを紹介します。
スケールアウトに向けたZOZOTOWNの課題
ZOZOTOWNのWebサーバは、Windows Serverで稼働しています。
アプリケーションの大部分はClassicASPで成り立っており、これを短期間でモダンなプログラムにリプレースすることは困難を極めます。
ZOZOTOWNでは、現在リプレースプロジェクトが進んでいますが、アプリケーションの改修を待たずにスケールアウトしていく仕組みを模索していました。
VMware Cloud on AWS
一昨年よりオンプレミスからのクラウドへのスケールアウトとしてVMware Cloud on AWSを利用しています。
直接的なサーバ構成を意識せずにクラウドを活用していく一歩としての選択です。
L2延伸させることでAWS上にオンプレミスを疑似的に拡張でき、従来の負荷対策よりも柔軟な対応をすることが可能になりました。
より詳細な情報は下記に記載されていますので興味のある方はそちらを参照してください。
しかし、いくつかの課題が残っていました。
L2延伸部分やホストの構成をする必要があり、オンプレミスでの拡張からはかなり短縮になったとはいえ、まだまだ構築開始からサービス投入までの時間がかかります。 また、VMを稼働させるESXのホストの負荷状況を管理する必要もあります。
そこで、サービス投入までの時間の更なる短縮と、管理負荷を軽減するためにEC2を活用することにしました。
EC2にはAWS CloudFormationなど、インスタンスの設定をテンプレートで管理できるサービスもあり、より迅速にスケールできそうです。
EC2を活用するために
現在運用しているVMware Cloud on AWSと比較して異なる点は「L2延伸をしない」というところにあります。
通信自体はAWS Direct Connectを利用しての専用線ですが、今までの内部通信が別のセグメントを通じての通信となります。
そのためSplunkを利用して既存のWebサーバの通信先を洗い出し、Firewallの許可やルーティング設定を入れることでVMware Cloud on AWSと同様に通信できるようにしました。
Splunkについては、過去にも記事をまとめていますので、興味がある方はそちらを参照してください。
techblog.zozo.com techblog.zozo.com
EC2 Windows Serverの自動構築
テンプレートの初期化
サーバがWindows Serverという都合上、SID(識別子)が重複してしまいます。そのため、初期化した状態でAMIにしておく必要があります。
初期化にはAmazon EC2が提供するEC2Configを利用し、Sysprepや管理者パスワードの初期化を実施します。
EC2Configにはユーザーデータの実行という機能があり、設定していると起動する度に指定したPowerShellを実行してくれます。
ユーザーデータのスクリプトはbase64でエンコード/デコードされます。
cat スクリプト名 | base64
なお、ユーザスクリプトの要注意ポイントが、マルチバイト文字の取り扱いです。
日本語があるとbase64デコードの際に文字化けしてうまく動作しないため、コメントやイーサネット名での絞り込みには注意してください。
ドメインの参加有無や設定する順序が重要なパラメータはユーザーデータのスクリプトで、それ以外の設定はAMIを再作成して対応します。
ユーザデータの実行による初期設定
前述したEC2Configのユーザーデータの実行にてPowerShellを実行して、Windows Serverの初期設定を実施していきます。
起動する度に実行されることがポイントで、設定状況を見つつ次に進むよう、スクリプトを組んでいます。
プログラムの主なフローを下記に記載します。
Windows Serverの初期設定は、ホスト名の変更やドメイン参加など再起動を伴う処理が多くあります。
インスタンスの開始と共にスクリプトが実行され、設定していきますが今どこまでが完了しているかわかりません。
そのため各工程ごとにタグを作成・変更することにより、初期設定の進行状況を確認できるよう工夫しました。
$InstanceID = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/instance-id aws ec2 create-tags --resources $InstanceID --tags $Tags
EC2上の情報を取得、設定するのに必要なAWSコマンドをOS上で実行するには、事前にAWSのセッション情報を取得することが必要です。
aws sts get-session-token --duration-seconds xxxx
AWS CLIは暗黙的にSTSを呼び出しますが、この時にOS上の時刻(JST)がUTC判定されてしまい、取得したセッション情報が即Expireしてしまうことがありました。
そのためスクリプトの最初で、JSTへの変更と時刻同期を行った後、明示的にSTSを呼び出しています。
net start w32time Start-Sleep -s 10 w32tm /resync /rediscover w32tm /query /status
またドメインの参加に必要なユーザ、パスワードなどは全てAWS Secrets Managerへ保管し、スクリプトで取り出して利用します。
$EncryptKeyFromSecretsManager = aws secretsmanager get-secret-value --secret-id TEAMNAME/KEYNAME --query 'SecretString' --output text $EncryptKeyArray = $EncryptKeyFromSecretsManager.Split(",")
一例としてドメイン参加部分のスクリプトを紹介します。
}elseif ((Get-WmiObject Win32_ComputerSystem).Domain -eq "WORKGROUP") { Echo "---Set hostname has done.---" Echo "---WORKGROUP---" Echo "---join Domain---" $domain = aws ec2 describe-instances --instance-ids $InstanceID --query 'Reservations[0].Instances[0].{InitialSetting:Tags[?Key==`domain`].Value|[0]}' --output text if ($domain -eq "join"){ Echo "---join Domain---" (中略) $credential = New-Object System.Management.Automation.PsCredential "USERNAME", "PASSWORD" Add-Computer -DomainName DOMAINNAME -Credential $credential #Domain joined.setting has done,create domain-status Tag. aws ec2 create-tags --resources $InstanceID --tags "Key=domain-status,Value=joined" Stop-transcript Restart-computer
ユーザーデータとしてこれらのPowerShellスクリプトが実行され、自動で初期設定が完了します。
タグハンドリング
タグは初期設定の状況確認のみならず、別用途でも利用します。
予め対象のタグになって欲しい状態を登録しておき、サーバを再起動することでそれに応じたアクションをとるような仕様になっています。
例えば対象のマシンをドメインから外したいとします。
その場合はWebコンソールから、domainタグの値を "join" → "unjoin"にして再起動をします。そうするとサーバはドメインを抜けた状態で起動します。
以下は、スクリプトでの実装例です。通常の初期設定ルートの他に、タグによる分岐を実装しています。
Echo "---Domain Leave---" $domain = aws ec2 describe-instances --instance-ids $InstanceID --query 'Reservations[0].Instances[0].{InitialSetting:Tags[?Key==`domain`].Value|[0]}' --output text if ($domain -eq "unjoin"){ Echo "---Domain Leave---" (中略) $credential = New-Object System.Management.Automation.PsCredential "USERNAME", "PASSWORD" Remove-Computer -Credential $credential -Force #Domain leaved.setting has done,create domain-status Tag. aws ec2 create-tags --resources $InstanceID --tags "Key=domain-status,Value=unjoined" Stop-transcript Restart-computer
アップデートなどで不要になったサーバを終了させる前に、便利な機能です。
CIOps化
PoC初期段階ではWebコンソールにて構築をしていました。
現在は前述したユーザーデータのスクリプトも含め、AWS CloudFormationにて管理しています。
このCloudFormationテンプレートはGitHub上で管理されます。
ユーザーデータもテンプレートに記載します。
Resources: (中略) # -------------------------------------------------------------# # WindowsServerLaunchTemplate # -------------------------------------------------------------# WindowsServerLaunchTemplate: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateName: !Sub "${Env}-${InstanceBaseName}-launch-template" LaunchTemplateData: (中略) UserData: Fn::Base64: !Sub - | <powershell> (ユーザーデータのPowerShellスクリプト) </powershell> <persist>true</persist>
スケールアウトする際は、別途作成したツールを用いて、必要なだけのAWS::EC2::Instance
リソースをテンプレートに追加します。
EC2インスタンスの設定はLaunch Templateで管理されていますが、負荷分散などのため、このLaunch Templateが複数存在します。
インスタンスとLaunch Templateの紐付けを簡略化するためにツールを用意しました。
生成されたテンプレートをPushし、MasterブランチへマージするとAWS CloudFormation変更セットの作成・実行され、インスタンス作成から数分で自動構築が完了します。
まとめ
本記事ではZOZOTOWNのWebサーバをEC2上にWindows Serverで自動構築するために、検討したことについて紹介しました。
今まではオンプレミス中心の構築・運用でしたが、これを機にクラウドへと領域を広げて新しい経験や知識を得ることができたと感じます。
今後はAuto Scalingの導入やサービス投入までの自動化を進めていく予定です。
最後に
ZOZOテクノロジーズでは、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください!