Web系から業務系まで、さまざまなシステムがクラウドで構築されるようになりましたが、システムの基盤がオンプレミスからクラウドに移行しただけで、トラブルや障害がなくなるわけではありません。今回は、Amazon Web Services(以下、AWS)上で構築されたシステムに関するトラブルシューティングについて、実例を交えて解説します。
トラシュー事例(初級編)
本番環境に突然ログインできなくなった!
AWSでシステムを運用している場合、サーバ内部のメンテナンスはSSHやRDPといった技術を使ってリモートログインしてから行うようになり、オンプレミス環境での電源OFF/ONなどの物理的な作業はAPIやAWS Management Consoleから操作するようになりました。
初級編のトラブルは図1 のようにWebサーバ、アプリケーションサーバ(App) 、データベースサーバ(DB)をAmazon EC2(以下、EC2)を使って構築したシステムで発生したものを想定します。ある日、サーバのメンテナンスを目的としてメンテナンス拠点からログインしようとしましたが、ログインできませんでした。監視サーバは異常を検知しておらず、サービスにも影響は出ていないようですが、メンテナンスができなくなったので急遽原因の調査をすることになりました。
図1 初級編の想定システム
原因調査と対策例
トラブル対応時にまずやるべきことは、表示されているエラー、ログ、監視サーバの監視状況などから現状を把握することです。今回のケースはサーバにログインすることができない状況ですが、リモートからログインを試みて失敗したときのエラー内容からも解決の糸口をつかむことができます。またサーバ内のログをサーバ外部に転送している場合は、転送されたログの内容からも手がかりをつかめます。今回の場合、SSHでログインを試みると次のようなエラーが出力されていました。
ssh: connect to host web.example.com port 22: No route to host
発生しているトラブルの内容から、次のような仮説を立てることができると思います。
① 名前解決はできているが、メンテナス拠点からEC2インスタンスの22番ポートにアクセスができない
② メンテナス拠点、監視拠点からともにサービスにアクセスできるので、EC2インスタンスがダウンしている可能性は低い
③ メンテナンス拠点からサービスを利用できるため、メンテナス拠点からEC2インスタンスへのルーティング設定に問題がある可能性は低い
④ 監視拠点からはサービスの利用、SSHアクセスともにできていることから、SSHサーバのプロセスがダウンしている可能性は低い
しかし、情報に誤りがあって間違った仮説を立てている可能性もあります。そのため、このような仮説が間違っていないことを順序立てて確認することも大切です。
トラブル発生時の仮説検証や問題切り分けの進め方としては、クラウド環境でも低いレイヤから行ったほうがわかりやすいように思います。
オンプレミス環境での物理的障害に近いものからチェックを始めます。EC2インスタンスも仮想サーバを稼働させている物理ホストの影響を受けてダウンする場合もありますが、そのような場合は監視サーバからもアクセスができなくなり障害を検知するはずです。EC2インスタンスが正常に稼働しているか確認するためには次のような情報をAPIやManagement Consoleでチェックすると判断がつくかと思います。
Instance Stateがrunning状態にあること
System status ckecksがpassed(0)状態であること
また、EC2インスタンスのパブリックIPを固定化する機能であるElastic IPの状態もチェックしてみてください。誤ってこれを取り外してしまうとアクセスができなくなりますが、そのような状態であった場合、監視サーバからもアクセスが失敗するはずですので原因となっている可能性は低いです。
EC2インスタンスがダウンしていないことが確認できたら、次の確認に移ります。EC2インスタンスはAmazon VPC(以下、VPC)と呼ばれるユーザ専用の仮想ネットワーク空間上で稼働しています。VPCの中ではユーザが好きなようにルーティング設定ができるようになっており、VPCから直接インターネットにアクセスする場合はインターネットゲートウェイへ正しくルーティングされている必要があります。ルーティング設定が誤っている場合はメンテナンス拠点からのSSH以外のプロトコルも通信に失敗するため、この点についても問題となっている可能性は低いですが、低レイヤの確認事項としてチェックしましょう。
設定ミスによって思わぬトラブルにつながる
ここまでで物理的、ネットワーク的には問題がないことが再確認できましたので、さらに上のレイヤをチェックしましょう。レイヤ3、レイヤ4のアクセス制御としてEC2にはSecurity Groupという機能があります[1] 。これはアクセス元、アクセス先のIPアドレスや、プロトコル、ポート番号による制御を可能にします。Security Groupについてチェックしたいポイントは次の2点となります。
EC2インスタンスに正しいSecurity Groupが割り当てられているか
割り当てられているSecurity Groupにメンテナンス拠点からのアクセスが許可されているか
この段階で「メンテナンス拠点のアクセス許可ルールを誤って削除してしまったためトラブルへとつながった」ことがわかりました。
IAMによる権限設定とAWS Config、Cloud Trailによる追跡
AWSにはIAMという権限管理のサービスもあります。Security Groupのルール設定は適切な権限を持つ人にのみ付与して、それ以外の人は参照のみ可能というように制限することができます。この機能を活用することで、適切な権限を持つ管理者だけがSecurity Groupのルールを削除可能、ということを実現できます。
また、AWS Configを利用することでAWSリソースの変更を追跡をすることや、CloudTrailを利用することでAWSリソースの操作などをログとして保管することも可能となります。トラブルが発生したときに原因を追う手がかりにもなりますので、これらはぜひ有効にしたい機能です。
AWSに限らずクラウドサービスではAPIが提供されているものが数多くあります。筆者が担当しているサービスではAWS SDK for RubyとRSpecを使ってAWSアカウントのあるべき状態をテストコードに定義して、AWSリソースに意図しない変化が発生した場合にはテストが失敗するようになっています[2] 。このような工夫をすることでトラブルの早期発見と防止につなげられると思います。
トラシュー事例(上級編)
参照系のデータベースアクセスが負荷分散されない
問題発覚と環境説明
AWSのサービスの中にはEC2のようなサービスのほかに、ロードバランサのサービスであるElastic Load Balancing(以下、ELB) 、リレーショナルデータベースのサービスであるAmazon RDS(以下、RDS)などがあります。これらのサービスはフルマネージド型のサービスとなっており、可用性が高く、アクセスが増えてくると自動的にスケールする機能(ELB)や、1台のホストに障害があった場合に自動的に切り替わるといった機能(RDS)があります。これらの機能はたいへん便利なのですが、その特性をきちんと理解していないと思わぬトラブルに遭遇する場合があります。
次のトラブルは図2 のような構成のシステムで発生しました。Web/アプリケーションサーバからデータベースの参照は、HAProxyを経由して2台のRDSリードレプリカに負荷分散されています。しかしあるときを境に、このRDSへの負荷分散がある特定のRDSに対して負荷分散されないという状態になりました。参照系の負荷分散に失敗していることで参照クエリが1台のホストに集中してしまい、負荷が高まっているようでした。
図2 上級編の想定システム
監視サーバは負荷分散されていないRDSの障害は検知しておらず、現時点ではサービスにも大きな影響は出ておりませんでしたが、影響が出る前に対応をすることになりました。
原因調査と対策例
初級編のトラブルではメンテナンス拠点からサーバへの接続に関する問題でしたが、システム間の接続に問題があった場合も同じようなステップで対応します。
今回の場合はHAProxy経由でRDSにアクセスしている構成となるため、HAProxyが出力するログ(図3 )やMySQLクライアントでHAProxyを経由してアクセスしてみた結果(図4 )を見てみましょう。
図3 HAProxyが出力するログ
Feb 6 16:58:45 localhost haproxy[10512]: Server mysql/read1 is DOWN, reason: Layer4 connection problem, info: "Connection refused", check duration: 2ms. 1 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
図4 MySQLクライアントでHAProxyを経由してアクセス(失敗)
$ mysql -u root -p -h 127.0.0.1 -P 3306
Enter password:
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0
HAProxyのログは、バックエンドのRDSへの接続に失敗していることを示しています。HAProxyがRDSへの接続に失敗しているため、そのホストへ参照クエリを分散しないような状態となっていると推測できます。MySQLクライアントを使って、HAProxy経由でRDSに接続しようとしてもやはり失敗します。
今回の場合も監視サーバからは障害を検知していませんが、RDSに何かしらの障害がおきているのでしょうか? 初級編の場合は低いレイヤから確認して問題の切り分けをしましたが、今回の場合はその前に1つステップをはさみます。前述したとおり、アプリケーションはHAProxyを経由してRDSに接続をしていますので、できるだけ構成をシンプルな状態にするのです。今回の場合、HAProxyを経由することなく直接RDSに接続すると、監視サーバからアクセスができるのと同じようにRDSにアクセスができました。
ここまでの内容をまとめると、HAProxyを経由した場合だけRDSとの接続に失敗しているようですが、具体的な理由まではわかっていません。このような場面での切り分け例として、tcpdumpコマンドなどで通信をキャプチャすることが考えられます。
図5 の例はWebサーバ内でtcpdumpコマンドを実行し、3306ポートへの通信をキャプチャした例となります。この中で、ip-10-0-1-76.ap-northeast-1.compute.internal.mysql
というのはRDSにローカル通信した場合の接続先であり、そこに通信しようとしていることがわかります。ここで表示されている接続先と、RDSのエンドポイントを名前解決した結果を比較してみましょう。
図5 tcpdumpコマンドを実行し、3306ポートへの通信をキャプチャ
$ sudo tcpdump dst port 3306
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
15:04:59.745944 IP ip-10-0-0-21.ap-northeast-1.compute.internal.34921 > ip-10-0-1-76.ap-northeast-1.compute.internal.mysql: Flags [S], seq 188805037, win 17922, options [mss 8961,sackOK,TS val 265836392 ecr 0,nop,wscale 7], length 0
エンドポイントを名前解決した結果(図6 )が、HAProxyが接続しようとしていたものと異なる結果となりました。HAProxyが特定のリードレプリカに接続ができていない問題は、これが原因で間違いないようです。HAProxyは起動時に設定ファイルで指定された接続先を名前解決して、結果をキャッシュするようになっています。RDSは稼働するホストに障害が発生した場合にホストの自動交換機能で、ハードウェアが自動的に交換されますが、そのとき内部的にローカルIPアドレスが変更されてしまうのです。
図6 RDSの接続先をdigコマンドで名前解決
$ dig rds-slave02.a1b2c3d4f5g6.ap-northeast-1.rds.amazonaws.com any
; <<>> DiG 9.9.4-RedHat-9.9.4-14.el7_0.1 <<>> rds-slave02.a1b2c3d4f5g6.ap-northeast-1.rds.amazonaws.com.com any
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 413
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;rds-slave02.a1b2c3d4f5g6.ap-northeast-1.rds.amazonaws.com. IN ANY
;; ANSWER SECTION:
rds-slave02.a1b2c3d4f5g6.ap-northeast-1.rds.amazonaws.com. 5 IN A 10.0.1.32
;; Query time: 4 msec
;; SERVER: 10.0.0.2#53(10.0.0.2)
;; WHEN: 月 2月 09 16:12:13 JST 2015
;; MSG SIZE rcvd: 107
このようなミスマッチで通信に影響が出る場合もありますので、クラウドや使用しているアプリケーションの特徴を理解して設計する必要があります。
今回の構成ではRDS側のローカルIPアドレスが変更される可能性が考慮されておりませんでしたが、RDSのローカルIPアドレスが変更されるようなイベントが発生した場合に、HAProxyを再起動して名前解決をしなおすようなしくみが必要となります。
[クラウド編]ではクラウド環境におけるトラブル対応について、AWSを例にして解説しました。IaaS系のクラウドサービスはさまざまありますが、サービスごとにそれぞれ特徴が違ってきますので、それらを理解して使いこなす必要があります。また、クラウドではバックアップから新しいサーバをすばやく用意することもできます。こういった特徴を活かして、新しいサーバを使ってサービスの復旧を優先するという考え方も大切になります。
トラブルシューティングをする場合の基本的な方針はオンプレミスでもクラウドでもあまり大きな差はありませんので、しっかりと基礎技術を身につけることが大切になると思います。
第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
第2特集
「知りたい」「 使いたい」「 発信したい」をかなえる
OSSソースコードリーディングのススメ
特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)