/

問題文

あるデベロッパーから「dev環境と間違えて、prod環境のMySQLで直接truncate table sbtest3;を実行してsbtest3テーブルのデータを削除してしまいました、復旧をお願いします!!」と緊急の連絡を受けました。
このクエリの実行後、すぐに間違いに気づいたデベロッパーはサービスをメンテナンス状態にし、アプリケーションサーバを停止したそうです。

問1(q01)

truncate table sbtest3; というクエリが実行された日時をyymmdd HH:MM:SSのフォーマットで報告してください。
また、どのようにこの日時を特定したかを説明してください。

問2(q02)

truncate tableが実行される直前の状態(truncate tableが実行される1つ前のクエリが実行された状態)にデータを復旧し、復旧後checksum table sysbench.sbtest3;の結果を報告してください。
また、データの復旧に必要な手順を説明してください。

情報

状況

  • このMySQLは毎日定時にsysbench databaseのバックアップを取得していて(コンテスト問題の作成上truncate table文が実行された日まで)、偶然truncate文が実行される(数分)前にこの日のバックアップが完了していた
  • バックアップは以下のコマンドで取得されている
  • mysqldump --opt --single-transaction --master-data=2 --default-character-set=utf8mb4 --databases sysbench > /root/backup/backup.dump
  • mysql -u root -p < /root/backup/backup.dumpでバックアップが取得された時点に復旧できる
  • adminユーザからsudo suすることでrootユーザから操作してください

問題サーバー

  • IPアドレス: 192.168.0.1
    ユーザー: admin
    パスワード: USerPw@19
  • DBユーザー: root
  • DBパスワード: root

ゴール

  • 問1, truncate table sbtest3; というクエリが実行された日時を特定し、その特定方法を説明していること
  • 問2, MySQLのデータが復旧し、その手順の説明とchecksum table sysbench.sbtest3;の結果が報告されていること
  • 問1に正解すると部分点が与えられます。

問題解説

「本番環境で間違ってtruncate table ...を実行してしまったから、その直前の状態までデータ復旧してほしい!!」
というデータベースの復旧問題でした。

あまり想像して楽しくなる状況ではありませんが、ソフトウェア開発をする上でデータベースのデータの復旧方法を知っておいても損はないと思います。
そこで、MySQLを例にバックアップと更新ログを利用して任意のタイミングまでデータを復旧する問題を出題しました。
DBMSごとの際はありますが、基本的にデータベースの復旧方法(特にロールフォーワードリカバリ)の概念は同じですので、今後使うDBMSでこういった状況に対応できるかを考えてみてください。

具体的な解法を説明する前にMySQLにおけるリカバリ方法、バイナリログ、バックアップについて説明します。

ポイント・イン・タイム・リカバリ (PITR)

データベースのバックアップを定期的に取得するだけでは、その時点のデータを復元することしかできないため、バックアップ以降の更新データを失うことになります。
そこで、データの永続化を保証する多くのデータベースでは、バックアップとは別にデータの更新情報を保持しておき、バックアップとそれ以降のデータ更新を復元することで任意の時点のデータをリカバリできるようにしています。このようなリカバリ方法をポイントインタイムリカバリ(PITR)と言います。

バックアップ以降のデータ更新情報をどのように保持するかはDBMSによって異なりますが、MySQLでは後述するバイナリログを利用することで取得できます。

バイナリログ

バイナリログにはMySQL serverで実行された更新がイベントという形で出力されています。
イベントには直接実行されたクエリが出力されているわけではなく、行ごとの変更分が特定のフォーマットにしたがって出力されています。
この他にもバイナリログそのもののバージョンやサーバの設定などのメタ情報も含まれており、これを利用してリカバリやレプリケーションを行うことができます。
truncate table ...を含むDDLはSTATEMENTが直接書かれていたり、推奨されませんが、DMLを直接STATEMENTに出力したりも出来ます。詳しくはバイナリログのドキュメントを参照してください

バイナリログの中身はmysqlbinlogコマンドを使うことで確認でき、より詳細に確認したい場合は--base64-output=DECODE-ROWS -vvなどのオプションをつけることで、実際に実行されたSQL文とは異なりますが、SQL文として復元されたものを確認できます。ただしクエリはコメントアウトされているので、このオプションをつけた結果をリカバリに使うことは出来ません。

バイナリログから特定の期間の更新を取得するには時間で指定する方法とバイナリログ中のオフセットを利用する方法がありますが、時間単位での指定は秒単位でしかできないため、リカバリにはオフセットを用いて指定する --start-position,--stop-positionを利用することをおすすめします。(秒間1クエリしか実行していない、なんてサービスはほとんどないはず)

バックアップ

MySQLにおけるバックアップ方法はいくつもあり実際は要件に合わせて選択しますが、今回はMySQL標準の論理バックアップクライアントのmysqldumpを利用してバックアップしました。
オプションの説明は省略しますが、問題環境では以下のコマンドでsysbench databaseの完全バックアップが取得されていました。

mysqldump --opt --single-transaction --master-data=2 --default-character-set=utf8mb4 --databases sysbench > /root/backup/backup.dump

--master-data=2が指定されていることから、バックアップを行ったホストに対してレプリケーションを繋ぐ際のコマンド(CHANGE MASTER...)がコメントアウトされて出力されます。この出力を見ることで、バックアップ以降の更新がバイナリログのどこから始まっているかをバイナリログのオフセットとして確認できます。

復旧手順

それでは実際に今回の問題環境でPITRを実施してデータを復旧する方法を説明します。

PITRの項で説明したように、まずはバックアップをリストアし、バックアップ以降でtruncate table sbtest3;の実行直前までの更新をバイナリログから抽出して適用することで復旧します。

mysqldumpによるダンプファイルの22行目に以下の出力があることからバックアップ以降の更新はbinlog.000018の34626719byte目以降ということがわかります。

-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000018', MASTER_LOG_POS=34626719;

次に、バイナリログからmysqlbinlogコマンドを使ってtruncate table...を実行を表すイベントのオフセットを取得します。
問題環境ではデータの更新が記録されているのはbinlog.000018だけなので、このファイルについてだけ考えれば良いです。

$ mysqlbinlog --no-defaults --base64-output=DECODE-ROWS -vv --start-position=34626719  binlog.000018 | grep -B 10 truncate
/*!80001 SET @@session.original_commit_timestamp=1574235055802260*//*!*/;
/*!80014 SET @@session.original_server_version=80018*//*!*/;
/*!80014 SET @@session.immediate_server_version=80018*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 54452036
#191120 16:30:55 server id 1  end_log_pos 54452141 CRC32 0x7e67e1d2     Query   thread_id=11    exec_time=0 error_code=0    Xid = 189179
use `sysbench`/*!*/;
SET TIMESTAMP=1574235055/*!*/;
/*!\C utf8mb4 *//*!*/;
SET @@session.character_set_client=255,@@session.collation_connection=255,@@session.collation_server=255/*!*/;
truncate table sbtest3

この出力の# at {数字}を見ることでtruncate文を実行したのが54452036 byte目から始まるイベントとわかります。
また、次の行をみると191120 16:30:55に実行されたことがわかります (問1)

問1としてあえて秒単位でしかない実行時刻を解答してもらったのは、実際にこういった問題が起こった場合には似たようなクエリがアプリケーションの正常な処理として実行されていたり、実は何回も間違った後だったりするため確認作業を必要とすることが多いからです。
辛いですね。。。

ここまででわかったbinlogの状況を図1に整理してみます。

スクリーンショット 2019-12-08 15.10.27.png
図1 binlogの状況

バックアップを適用することで②の直前の状態にまでデータが復旧されるため、②から③までの更新情報を適用する必要があります。
①から②の部分も適用したチームも見られましたが、この部分の更新分はバックアップに含まれているので、重複した更新を適用してしまうことになります。

最後にここまでで取得したオフセットを--start-position--stop-positionで指定して抽出し、適用することでリカバリが完了します(問2)

mysqlbinlog --no-defaults --start-position=34626719 --stop-position=54452036 binlog.000018 | mysql -u root -p
# または
mysqlbinlog --no-defaults --start-position=34626719 --stop-position=54452036 binlog.000018 > recover.sql
mysql -u root -p < recover.sql

採点基準

  • 問1: 30%
  • 問2: 70%

問2の採点にはリカバリ後に実行してもらったchecksum table ...の結果を利用するつもりでしたが、問題環境ではバイナリログからの抽出位置を正しく指定しなくてもchecksumが同じ値になってしまうことがありました。
そのため解答中のリカバリ方法の説明で、バックアップ以降、truncate table文まで(図1の②~③部分)の更新分を抽出、適用していることを確認して正解としました。

 /

問題文

あなたはローカルネットワーク上にwebサーバを構築し、IPv6アドレスを使用してwebページに接続できるようにセットアップをしています。
webページへはhttp://nginx.icttoracon.netでアクセスできるようにしたいです。

nginxのホストには、すでにnginxのパッケージをインストール済みです。
CSR1000Vとnginxのホストには固定でIPv6アドレスを割り当てました。
クライアント(VNC Server)にIPv6アドレスが自動設定されるように、CSR1000VにはSLAACの設定を行いました。

しかし、クライアント(VNC Server)のブラウザからhttp://nginx.icttoracon.netにアクセスしてもWelcome to Nginxのページを表示させることができません。
このトラブルを解決し、Welcome to Nginxのページを表示させてください。

クライアントが増えても自動でアクセスできるよう、設定変更はCSR1000Vとnginxホストのみとしてください。
DNSサーバはCSR1000Vを使用します。
各ノードにはssh/telnet用にIPv4アドレスが設定されていますので必要に応じて使用してください。
予選終了後に実環境で採点されるので、スコアサーバでの解答は不要です。

file

接続情報

HostProtocolIPv4 addressUser/Pass
CSR1000Vtelnet192.168.0.1admin/admin
nginxssh192.168.1.2admin/admin

ゴール

VNCサーバのブラウザからhttp://nginx.icttoracon.netでWelcome to Nginxのサイトが表示されること
上記のアクセスがIPv6で行われていること
(恒久的な設定でなくても構わない)

問題解説

本問題には3つの原因があります。
順を追って調べてみましょう。

疎通性確認

まずはクライアントマシンからnginxホストまでIPv6で疎通性があるか確認してみます。
簡単な確認ではありますが、トラブル原因のレイヤをある程度限定できます。

ubuntu@ICTSC-VNC:~$ ping fc01::2 -c 4
PING fc01::2(fc01::2) 56 data bytes
64 bytes from fc01::2: icmp_seq=1 ttl=63 time=0.887 ms
64 bytes from fc01::2: icmp_seq=2 ttl=63 time=0.607 ms
64 bytes from fc01::2: icmp_seq=3 ttl=63 time=0.802 ms
64 bytes from fc01::2: icmp_seq=4 ttl=63 time=0.699 ms

--- fc01::2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3040ms
rtt min/avg/max/mdev = 0.607/0.748/0.887/0.110 ms

コマンドの結果から本問題は初期状態でIPv6の疎通性があることが確認できます。

名前解決

名前解決ができるかどうか試してみましょう。
ドメイン名からIPv6アドレスを取得するにはAAAAレコードを参照します。
例としてAAAAレコードを取得するコマンドを以下に示します。

ubuntu@ICTSC-VNC:~$ dig nginx.icttoracon.net AAAA

; <<>> DiG 9.11.3-1ubuntu1.11-Ubuntu <<>> nginx.icttoracon.net AAAA
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 40684
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;nginx.icttoracon.net.        IN  AAAA

;; Query time: 89 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Wed Dec 04 22:46:46 JST 2019
;; MSG SIZE  rcvd: 49
ubuntu@ICTSC-VNC:~$

AAAAレコードは取得できていません。
問題文でDNSサーバはCSRとされていますが、クライアントはどこを参照しているのでしょうか。

ubuntu@ICTSC-VNC:~$ cat /etc/resolv.conf | grep -v "^#"

nameserver 127.0.0.53
options edns0
search localdomain
ubuntu@ICTSC-VNC:~$ cat /run/systemd/resolve/resolv.conf | grep -v "^#"

nameserver 133.242.0.3
nameserver 133.242.0.4
search localdomain
ubuntu@ICTSC-VNC:~$ 

IPv4でDNSサーバを受け取っているようですが、CSRのIPアドレスではありません。
1つ目の原因はDNSサーバ(CSR)が参照できていないことです。

問題文からクライアントの設定変更ではなくルータの設定変更で対応する方針であることがわかります。
クライアントの設定とCSRの設定を確認すると、クライアントのIPv6アドレスはRAを用いた自動設定であることがわかります。
ただしDNSサーバのアドレスが配布されていません。
RAでIPv6アドレスが設定されている場合は、以下の2つの方法でDNSサーバを配布することができます。

  • ステートレスDHCPv6
  • RAを用いたDNS配布(RFC8106)

例としてRAのみでDNSの配布を行います。
IOS-XEのコマンドリファレンスを参照すると、以下の設定でDNSの配布が行えそうです。

csr1000v#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
csr1000v(config)#int gi 1
csr1000v(config-if)#ipv6 nd ra dns server fc00::1

クライアントで確認してみます。

ubuntu@ICTSC-VNC:~$ cat /run/systemd/resolve/resolv.conf | grep -v "^#"

nameserver 133.242.0.3
nameserver 133.242.0.4
nameserver fc00::1
search localdomain
ubuntu@ICTSC-VNC:~$ dig nginx.icttoracon.net AAAA

; <<>> DiG 9.11.3-1ubuntu1.11-Ubuntu <<>> nginx.icttoracon.net AAAA
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35863
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;nginx.icttoracon.net.        IN  AAAA

;; ANSWER SECTION:
nginx.icttoracon.net.    10  IN  AAAA    fc01::2

;; Query time: 12 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Wed Dec 04 22:51:49 JST 2019
;; MSG SIZE  rcvd: 77

ubuntu@ICTSC-VNC:~$

DNSサーバとしてfc00::1が設定され、AAAAレコードが正しく参照できています。

nginx設定

IPv6の疎通性があり、名前解決も行えているので一旦クライアントの作業を終え、nginxホストを確認してみます。
まずは80ポートの使用状況を確認してみます。

[admin@nginx ~]$ sudo lsof -i:80
COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   1421  root    6u  IPv4   9815      0t0  TCP *:http (LISTEN)
nginx   1422 nginx    6u  IPv4   9815      0t0  TCP *:http (LISTEN)

typeを見るとIPv4となっており、nginxがIPv6アドレスで待ち受けていないことがわかります。
2つ目の原因はnginxはIPv6アドレスで待ち受けていないことです。
nginxがIPv6アドレスで待ち受けるよう、設定を変更します。

server {
    listen       80;
+   listen       [::]:80;
    server_name  localhost;

--- snip ---
[admin@nginx ~]$ sudo nginx -s reload
[admin@nginx ~]$ sudo nginx -s reload
[admin@nginx ~]$ sudo lsof -i:80
COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   1421  root    6u  IPv4   9815      0t0  TCP *:http (LISTEN)
nginx   1421  root   10u  IPv6  10932      0t0  TCP *:http (LISTEN)
nginx   1540 nginx    6u  IPv4   9815      0t0  TCP *:http (LISTEN)
nginx   1540 nginx   10u  IPv6  10932      0t0  TCP *:http (LISTEN)

フィルタリング設定

nginxがIPv6で待ち受ける状態となりました。
しかしまだクライアントからアクセスができません。
nginxホストのディストリビューションを確認してみます。

[admin@nginx ~]$ ls /etc | grep release
centos-release
redhat-release
system-release
system-release-cpe
[admin@nginx ~]$ cat /etc/centos-release 
CentOS release 6.10 (Final)
[admin@nginx ~]$

CentOS6.10であるため、フィルタリングはiptablesで行っていると予想されます。
iptablesのルールを確認してみましょう。

[admin@nginx ~]$ sudo iptables -L INPUT
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED 
ACCEPT     icmp --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ssh 
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:http 
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited 

一見すると問題が無いように見えますが、クライアントはWelcome to Nginxのページにアクセスできません。
それもそのはず、iptablesはIPv4のフィルタリング設定だからです。
実は初期状態からIPv4で80ポートは許可されており、クライアントはIPv4を用いてWelcome to Nginxのページを表示させることはできてました。

IPv6のフィルタリングはip6tableで行います。
ip6tableのルールを確認すると、80ポートのアクセスを許可しているルールが無いことがわかります。

[admin@nginx ~]$ sudo ip6tables -L INPUT
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all      anywhere             anywhere            state RELATED,ESTABLISHED 
ACCEPT     ipv6-icmp    anywhere             anywhere            
ACCEPT     all      anywhere             anywhere            
ACCEPT     udp      anywhere             fe80::/64           state NEW udp dpt:dhcpv6-client 
ACCEPT     tcp      anywhere             anywhere            state NEW tcp dpt:ssh 
REJECT     all      anywhere             anywhere            reject-with icmp6-adm-prohibited 

3つ目の原因はIPv6の80ポートが拒否されていることです。
問題文には恒久的な設定ではなくて構わないとしか記載されていないので、80ポートを許可する方法か、プロセスを停止する方法があります。
問題としてはどちらで行ってもいいですが、望ましいのは80ポートを許可する方法です。
ip6tablesで80ポートを許可します。

[admin@nginx ~]$ sudo ip6tables -I INPUT 6 -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
[admin@nginx ~]$ sudo ip6tables -I INPUT 6 -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
[admin@nginx ~]$ sudo ip6tables -L INPUT
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all      anywhere             anywhere            state RELATED,ESTABLISHED 
ACCEPT     ipv6-icmp    anywhere             anywhere            
ACCEPT     all      anywhere             anywhere            
ACCEPT     udp      anywhere             fe80::/64           state NEW udp dpt:dhcpv6-client 
ACCEPT     tcp      anywhere             anywhere            state NEW tcp dpt:ssh 
ACCEPT     tcp      anywhere             anywhere            state NEW tcp dpt:http 
REJECT     all      anywhere             anywhere            reject-with icmp6-adm-prohibited 

クライアントでアクセスしてみると、Welcome to Nginxのページが表示されます。

 /

問1 経路問題 その1

問題文

下記の図で、vmx2からvmx1のge-0/0/2へpingが飛ばない。 logを見て、考えられる原因について最もふさわしいものを選べ。なお、aclなどfilteringの設定はなく、vmx2の設定は適切であるとする。また、本問においてvmx2のgwはxrv1であるとする。

file
file
  • A: xrv1の物理インタフェースがdownしている
  • B: vmx1からの戻りの経路がない
  • C: vmx1とxrv1の間のvlan設定が間違っている
  • D: ospfのAD値設定が間違っている

解説

Bが正解。vmx1からの戻りの経路がないため。
pingの送信元インタフェースであるvmx2の192.168.1.4/24について、vmx1のshow routeには情報がないことが読める。
pingが通るためには、end-end間に存在する各機器において、往路、復路とも経路を保持している必要がある。往路の経路があるからpingが通るはず、と勘違いしてトラブルシューティングを長引かせてしまった経験をしたことがある人は、すぐに気づく問題である。

  • A:誤り。show ip routeでconnectedもospf経路交換も見えるため、IFは生きていると考えられる。
  • C:誤り。vmx1が10.0.1.20/30の経路をOSPFで受け取っていること等から、疎通性は正常であると判断できる。
  • D:誤り。AD値の設定は正常に見える。

問2 経路問題 その2

下記の図で、vmx2からvmx1のge-0/0/2へpingが飛ばない。logを見て、考えられる原因について最もふさわしいものを選べ。なお、aclなどfilteringの設定はなく、vmx2の設定は適切であるとする。また、本問においてvmx2のgwはxrv1であるとする。

file
  • A: xrv1のstatic routeの設定が原因となっている
  • B: vmx1のルーティングにかかわるプロセスがdownしている
  • C: vmx1において、xrv1とは反対側の上流に経路が向いていることが予想される
  • D: vmx1とxrv1の間のvlan設定が間違っている

解説

Aが正解。xrv1に/32のstatic routeが記載されており、10.0.1.5への経路がxrv2の方に向いているため。
10.0.1.4/30のエントリが見えるが、そのすぐ下に/32があり、ロンゲストマッチの原則から10.0.1.5/32への通信はxrv2の方へ向く。
本問は簡単な例だが、このようにいつの間にか経路が意図しない方に向いていることや、それを原因とする故障はたまに発生する。

  • B:誤り。show ip routeの結果を見る限り、考えにくい。
  • C:誤り。そうなっているかもしれないが、そもそもvmx1まで通信が明らかに到達しない。
  • D:誤り。10.0.1.4/30がdirectly connectedである点、10.0.0.1/32などをOSPFで受けとっている点から、疎通性は正常であると判断できる。

問3 経路情報の伝搬

IPルーティングに関する説明で正しいものを選べ。

  • A: 経路集約を行うことでルータ間での経路交換のメッセージ量やメモリの消費量を低減させることができる。
  • B: BGPは多量の経路を扱うプロトコルであるため、経路情報を可逆圧縮したNLRIと呼ばれるメッセージを用いて経路交換を行う。これは経路集約と呼ばれ、異なるメーカ間での相互接続性を確保するためにLZMAアルゴリズムが用いられている。
  • C: RIPやOSPFでは類似したアドレスの情報を多量に交換するため、各ルータの交換する経路情報はZIP形式で圧縮されている。
  • D: eBGPの設定されているルータはフルルートを保持しており、インターネット上の全ネットワークに接続性を持つこのため、eBGPルータは他のBGPルータに対して0.0.0.0/0の経路のみを配信する。

解説

Aが正しい回答です。経路集約を行うことで交換する経路数が減少するため、メッセージの量や、ルータの保持する経路を減らすことができます。
B,Cは誤りです。OSPFやBGPでは可逆圧縮を用いた経路の交換は行われません。
Dは誤りです。フルルートやそれに相当する経路を持つルータで0.0.0.0/0を生成して他のルータに広告することはよくありますが、iBGPルータあるいはRRルータとの間ではBGPで学習したすべての経路を交換します。

問4 ドキュメント記載に最適なアドレスは?

あなたは研究室のネットワークを管理している。あなたは後輩のために”他の1台のホストへの多量のIPv4 pingの送り方”という例を研究室の運用ドキュメントに記載することにした。このような場合に以下の中で【最も適した】アドレスはどれか?

ping -f <ipv4アドレスの宛先>

条件:後輩たちははNWに馴染みがないため、コマンド例をそのままコピーアンドペーストしてしまうことがある。この際に実際に存在するホストに対してパケットが送信されてしまい迷惑がかかることがないようにしたい。

  • A: 192.0.2.100
  • B: 0.0.0.0
  • C: 255.255.255.255
  • D 172.32.0.1
  • E 10.255.255.255
  • F 192.0.3.100
  • G 127.0.0.1

解説

正解A: ドキュメント向けアドレスであり適切です。[RFC5737] IPv4 Address Blocks Reserved for Documentationに記載があります。この中では、以下のようなアドレスがドキュメント向けに予約されています。

192.0.2.0/24 (TEST-NET-1)
198.51.100.0/24 (TEST-NET-2)
203.0.113.0/24 (TEST-NET-3)

他の選択肢は以下の通り誤りとなります。
B, C, Eはホストを指定するには不適切なアドレスです。D,FはGlobalアドレスであり、例示としては考えられますが、今回の条件と照らし合わせると不適切です。GはloopbackのIPアドレス空間なので他のホストではありません。RFC5735: Special Use IPv4 Addressesに記載があります。

問5 NAT

IPv4環境におけるNATの説明のうち正しいものを選べ

  • A: NAPTを利用することで1つのグローバルアドレスしか所持しない環境でも複数のノードがインターネット上のノードと同時に通信できる。しかし、DNSはプロトコル上、送り元、送信先のポートが53でなければならないため、同時にDNSサーバと通信できるノードは1台のみである。
  • B: NATによりHTTPSを用いてインターネット上のWebサーバと通信をすることが可能である。しかし、クライアント(送信元)のIPアドレスが変換されていると、クライアントはサーバ証明書の確認を行うことはできない。
  • C: NATはプライベートIPアドレスをグローバルIPアドレスに変換する技術であるため、宛先をプライベートIPアドレスに変換することはできない。
  • D: NATはプライベートIPアドレスをグローバルIPアドレスに変換する技術であるため、グローバルIPアドレスをグローバルIPアドレスに変換することはできない。
  • E: 他の選択肢はすべて誤り。

解説

この問題は出題が不適切であるため、C, D, Eを正答としています。
当初はEのみが正当である(C,Dが不適切である)としていました。これは、Linuxおよびいくつかの主要なルータのNAT機能においてプライベートアドレスアドレス空間であるかの判定は行われておらず、グローバルアドレス同士あるいはプライベートアドレス同士のNATが記載できるためです。しかし、NATに関するStandards TrackやInformationalの主要なRFCでは、Privateアドレス空間とExternalアドレス空間(グローバルアドレス空間)を前提として論じられているため、C, D も正答としました。

Aの前半は正しいが後半は誤りです。DNSの通信において、Query送出側のポートが53である制限はありません。B前半は正しいですが後半は誤りです。NAT処理を行ってもHTTPSのサーバの証明に影響はありません。

問6 IPv6

IPv6に関する説明のうち正しいものを選べ。

  • A IPv6ではMACアドレスからユニークに生成されるEUI-64アドレスをホスト部に含むアドレスが広く使われる。このため、IPv6を用いた通信ではMACアドレスを含まないEthernetヘッダが用いられている。
  • B IPv6ではIPv4の上に成り立つネットワークであるため、ホストや途中のルータではIPv4が有効になっていなければならない。
  • C 同一のネットワークに所属するIPv6ホストは送り元と宛て先でペイロードの交換を行う前にNLRIメッセージを交換し、到達性の確認してからペイロードの転送を行う。
  • D IPv4ではEthernet上のL2アドレスの解決にはARPプロトコルが用いられる。IPv6ではARPは用いられず、OSPFv3を用いてL2アドレスを相互に交換しMACアドレスを解決してフレームの転送が行われる。
  • E: 他の選択肢はすべて誤り。

解説

Eが正解です。

他の選択肢が不適切な理由は以下の通りです。
AはIPv6の通信においてもEthernetの通信にMACアドレスが不要になるわけではありません。前半部分については正しいですが、EthernetフレームにはMACアドレスが従来通り必要です。
Bについて、 IPv6はIPv4上のネットワークに構築されるわけではありません。IPv4で接続されたネットワークは不要です。
Cについて、同一ネットワークの通信に選択肢の動作は不要です。NLRIはBGPにおいて用いられる経路情報です。
Dは誤りです。IPv6においてEthernetアドレスの解決にはNeighbor Discoveryの仕組みが用いられます。

問7 OSPF その1

※本問題中の「図:Routing-K1」は他の問題に含まれる「図:Routing-K1」と同様です。

以下のpingの結果として適切なものはどれか。理由が述べられているものについては最も適切な理由なものを選べ。

R1#ping 192.168.0.2
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.0.2, timeout is 2 seconds:
(略)
  • 1: 成功する。
  • 2: 失敗する。R1とR2のMTUサイズが異なるのでPath MTU Discoveryが失敗しIPパケットが破棄される。
  • 3: 失敗する。R1とR2のMTUサイズが異なりOSPFのMTU mismatchによりOSPFのネイバー関係が確立できないため
  • 4: 失敗する。R1のOSPFはconnected経路をredistributeしていないため、R2はR1に付与されているアドレスに到達できない。

解説

続く4問は同じ構成となるため、状態を説明します。

  • このネットワークはR1, R2間のOSPFがMTU mismatchでOSPFのneighbor関係を確立されていない以外は正常な設定です。
  • OSPFのnetworkコマンドのワイルドカードマスクが/32で書かれているものと/24で書かれているものが存在しますが、今回の設問範囲では影響がありません。

回答に際してポイントとなるのは以下の点でした。

  • OSPFがR1-R2間でneighborを確立できていないこと
  • R1,R2間のI/FでOSPFが確立されていない場合でも、図の状態ではOSPF経路に載るため、R2からR3に対してこの経路は広告されること

以上を元に以下解説していきます。

1が正解。R1,R2間ではOSPFが確立されていないものの、Connectedな経路です。I/FのMTUサイズが意図的に変更されているが、例では明らかに小さいサイズのパケットです。
2,3,4はconnectedの通信には関係がありません。

問8 OSPF その2

※本問題中の「図:Routing-K1」は他の問題に含まれる「図:Routing-K1」と同様です。
以下のpingの結果として適切なものはどれか。理由が述べられているものについては最も適切な理由なものを選べ。

R1#ping 192.168.255.2 source 192.168.255.1
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.255.2, timeout is 2 seconds:
Packet sent with a source address of 192.168.255.1
(略)
  • 1: 成功する。
  • 2: 失敗する。R1とR2のMTUサイズが異なるのでPath MTU Discoveryが失敗しIPパケットが破棄される。
  • 3: 失敗する。R1とR2のMTUサイズが異なりOSPFのMTU mismatchによりOSPFのネイバー関係が確立できず、R1は対象アドレスの経路情報を持たない。
  • 4: 失敗する。R1のLoopback0のサブネットマスクは/32であるが、OSPF設定でnetworkコマンドのワイルドカードマスクが/24であるため、Loopback0のアドレスは広告されない。
  • 5: 失敗する。R1のOSPF設定でpassive-interface Loopback0が設定されているため、Loopback0を送り元アドレスとするIPパケットは送出できない。

解説

3が正解。OSPFのMTU mistmatchによりR1-R2間のOSPFの状態はEXSTARTのままで確立できていません。このため、R1はR2のLo0に関する経路を持たず、pingは失敗します。

他の選択肢は以下の点が誤り。
1は上記により誤りです。
2について、IPのPath MTU Discoveryと今回の結果には関係はありません。
4について、該当のnetworkコマンドはLoopback0を含む/24のアドレスを持つインタフェースでOSPFを有効にすることを示します。このため、マスク長が異なるためLoopback0のアドレスが配信されないわけではありません。
5についてpassive-interface Loopback0はOSPFに関する設定です。これをsourceとするIPパケットの送出を禁止するわけではありません。

問9 OSPF その3

※本問題中の「図:Routing-K1」は他の問題に含まれる「図:Routing-K1」と同様です。

以下のpingの結果として適切なものはどれか。理由が述べられているものについては最も適切な理由なものを選べ。

R3#ping 192.168.0.2 source 192.168.255.3
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.0.2, timeout is 2 seconds:
Packet sent with a source address of 192.168.255.3
  • 1: 成功する。
  • 2: 失敗する。R1とR2のMTUサイズが異なりOSPFのMTU mismatchによりOSPFのネイバー関係が確立できず、R2はR3に対してこのOSPF確立が失敗したconnected経路の情報を広告しない。
  • 3: 失敗する。R3のLoopback0のネットマスクは/32であるが、OSPF設定でnetworkコマンドのマスクアドレスが/24であるため、Loopback0のアドレスはR2に広告されず、R2は応答できない。
  • 4: 失敗する。R1とR2のMTUサイズが異なりOSPFのMTU mismatchによりOSPFのネイバー関係が確立できていないため、R2とR3の間のOSPF関係を確立できない。

解説

1の通り成功します。R2-R3間のOSPFは正常であり、関係する経路情報はR2,R3に伝搬されているためです。

2はR2においてあるインタフェースのOSPF確立がMTU mismatch起因で失敗しても、そのインタフェースがOSPFに参加していればそのインタフェースにかかわる情報はConnected経路としてR3に広告されます。
3について該当のnetworkコマンドはLoopback0を含む/24のアドレスを持つインタフェースでOSPFを有効にすることを示めします。このため、マスク長が異なるためLoopback0のアドレスが配信されないわけではありません。
4についてR2あるインタフェースのOSPF確立がMTU mismatch起因で失敗しても他のインタフェースでOSPFの確立ができないわけではありません。

問10 OSPF その4

※本問題中の「図:Routing-K1」は他の問題に含まれる「図:Routing-K1」と同様です。

以下のpingの結果として適切なものはどれか。理由が述べられているものについては最も適切な理由なものを選べ。

R3#ping 192.168.0.1 source 192.168.255.3
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.0.1, timeout is 2 seconds:
Packet sent with a source address of 192.168.255.3
  • 1: 成功する。
  • 2: 失敗する。R1とR2のMTUサイズが異なりOSPFのMTU mismatchによりOSPFのネイバー関係が確立できていないため、R2はR3がOSPF経由で学習した経路宛のパケットを破棄する。
  • 3: 失敗する。R1とR2のMTUサイズが異なりOSPFのMTU mismatchによりOSPFのネイバー関係が確立できていないため、R1はR3のLoopback0の経路を持たない。
  • 4: 失敗する。R1とR2のMTUサイズが異なりOSPFのMTU mismatchによりOSPFのネイバー関係が確立できていないため、R3はR1向けの該当経路を持たない。

解説

3が正解です。R3からのパケットはR1に到達しますが、R1がR3の経路を持たないため、pingは失敗します。R2は192.168.0.0/24をOSPF経路として持ち、R3に広告できます。このため、R3は192.168.0.1宛のパケットをR2に送出できます。R2はR1にパケットをルーティングをしますが、到着したR1では192.168.255.3への経路を(R1はR2とOSPFが確立できていないため)知らないため、パケットを破棄します。

他の選択肢は以下の点が誤りです。
1は以上の通りpingできないため誤りとなります。
2についてはR2はネイバー確立が失敗したインタフェースへのパケット転送を破棄するわけではありません。
4についてはR2のconnected経路をR3は受け取っているため、R3からのパケットはR1に到達できます。このため、この理由は適切でありません。

 /

問題文

あなたはサーバホスティング会社に勤めているシステムエンジニアです。
顧客より、下記のとおり依頼をいただきました。

前任者退職に伴って確認しようと思いサーバ内を確認していたところ、
間違えてFWサーバとwebサーバを再起動してしまいました。
再起動前はwebページに正常にアクセスできていたのですが、アクセスできなくなってしまったため、
いい感じに直していただきたいです。

しかし、セキュリティを考えていた設定とかもあると思うので、外部アクセスなどへはそのまま利用してほしいです。
あと、監視サーバからのURL監視も通るようにしてほしいです。こちらは特に制限をかける必要はありません。
でも、サーバの担当者が出張中で認証情報がわからないので、監視サーバへはログインできないです。
また、再発しないように設定いただけると嬉しいです。

変更した設定などはコマンドレベルでご報告いただけますようお願いします。

対応してほしいところ

上記依頼に則った上で、下記を満たすように修正してください。

  • VNCから以下のwebページへHTTPアクセスが正常に通るようにしてください。
    • 対象URL: http://www.qwe.example/
    • monitorから正常に監視ができるようにしてください。
    • FWサーバ 2222番ポートへの接続で、webへssh接続できるようにしてください。
  • 構成
  • アクセス可能
    • VNC(踏み台、クライアントPCと想定してください)
    • firewall01 (192.168.0.100/10.111.100.1 admin:USerPw@19)
    • web01 (10.111.100.10 admin:USerPw@19 ※FWサーバよりアクセス可能)
  • アクセス不可
    • monitor(192.168.0.200)
      • ※ 例示用URLのため、VNCのhostsに記載して名前解決しています
      • ※ 192.168.128.0/17 のIP帯からwebサーバへのランダムアクセスを行っています。
      • 問題環境の都合上ではありますが、一般userのアクセスと考えて対応してください。

構成図

( Internet )
      |
+-----+-----+      +-----+-----+
|    VNC    |      |  Monitor  |         
+-----+-----+      +-----+-----+
      |                  |
------+------------------+------- 192.168.0.0/16
      |
+-----+-----+
|    FW     |                
+-----+-----+
      |
------+-------------------------- 10.111.100.0/24
      |
+-----+-----+
|    WEB    |                
+-----+-----+

問題解説

本問題の要件としては下記の通りです。

  • VNC – NAT(firewall) – web の通信が通らないため直す
  • Monitor(監視サーバ)からの通信を、正常に通す
  • 外部アクセス(※記載の 192.168.128.0/17)のwebアクセスを可能にした状態で、既存の攻撃対策ルールを適用する

問題文より、firewall および webを再起動してしまった影響と読み解けるので、両サーバの設定を確認します。
今回問題が発生していた箇所は両サーバのfirewall設定が永続化しておらず、
再起動したためロールバックしてしまった、というシナリオでした。不]具合が発生していた箇所を記載します。

iptables NAT設定 (FWサーバ)

入っているNAT設定は WEBサーバ(10.111.100.10)に対する SSH(22) と HTTP(80)のDNAT設定です。
DNATルールにおかしい箇所があります。

誤っている箇所

  • -A PREROUTING -s 192.168.0.100/32 -p tcp -m tcp –dport 2222 -j DNAT –to-destination 10.111.10.10:22
  • -A PREROUTING -s 192.168.0.100/32 -p tcp -m tcp –dport 80 -j DNAT –to-destination 10.111.10.10:80

通信元ポートが22/80だったアドレスを”10.111.10.10″に変換となってしまっています。(第3オクテットが違います)
下記の例の様に直すと、正しいDNAT設定となります。\
また、前任者がsaveした際にできたであろう、 /etc/sysconfig/iptables.save をリストアする事によっても正しいDNAT設定に直す事も可能です。~
※後述のFORWARDルールについてはこちらをリストアしても直っていません。

解答例

  • -A PREROUTING -s 192.168.0.100/32 -p tcp -m tcp –dport 2222 -j DNAT –to-destination 10.111.100.10:22
  • -A PREROUTING -s 192.168.0.100/32 -p tcp -m tcp –dport 80 -j DNAT –to-destination 10.111.100.10:80

これでVNCからWEBサーバに対するNATでのSSH接続が可能となります。

iptables FORWARDルール (FWサーバ)

iptablesでの攻撃対策ルールについてです。
FWサーバの各ルールは基本的に各接続許可/拒否をログ出力するように設定されています。
FWサーバ(192.168.0.100)の/var/log/messagesを見てみると下記のような出力が確認できます。

Nov 16 06:21:07 firewall01 kernel: iptables : FORWARD_ALLOW IN=eth0 OUT=eth1 SRC=192.168.0.200 DST=192.168.100.10 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=64121 DF PROTO=TCP SPT=42576 DPT=80 WINDOW=29200 RES=0x00 SYN URGP=0 
Nov 16 06:21:08 firewall01 kernel: iptables : FORWARD_ALLOW IN=eth0 OUT=eth1 SRC=192.168.0.200 DST=192.168.100.10 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=64122 DF PROTO=TCP SPT=42576 DPT=80 WINDOW=29200 RES=0x00 SYN URGP=0 
Nov 16 06:21:10 firewall01 kernel: iptables : FORWARD_ALLOW IN=eth0 OUT=eth1 SRC=192.168.0.200 DST=192.168.100.10 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=64123 DF PROTO=TCP SPT=42576 DPT=80 WINDOW=29200 RES=0x00 SYN URGP=0 
Nov 16 06:21:11 firewall01 kernel: SYN_FLOOD IN=eth0 OUT=eth1 SRC=192.168.0.200 DST=192.168.100.10 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=15639 DF PROTO=TCP SPT=42636 DPT=80 WINDOW=29200 RES=0x00 SYN URGP=0 

監視サーバ(192.168.0.200)からのアクセスがどうやらSYN_FLOODとして判定され拒否されていそうです。
FORWARDルールを確認してみます。

-A FORWARD -s 192.168.0.0/16 -p tcp -m state --state NEW -m tcp --dport 22 -j FORWARD_ALLOW
-A FORWARD -s 192.168.0.0/16 -d 10.111.100.10/32 -p tcp -m tcp --dport 80 -j SYN_CHECK
-A FORWARD -s 192.168.0.200/32 -d 10.111.100.10/32 -p tcp -m state --state NEW -m tcp --dport 80 -j FORWARD_ALLOW 
-A FORWARD ! -s 192.168.0.200/32 -d 10.111.100.10/32 -p tcp -m state --state NEW -m tcp --dport 80 -j FORWARD_ALLOW 
-A FORWARD -s 192.168.0.0/16 -d 10.111.100.10/32 -p tcp -m state --state NEW -m tcp --dport 22 -j FORWARD_ALLOW 
-A FORWARD -s 192.168.0.0/16 -d 10.111.100.10/32 -p tcp -m tcp --dport 22 -j ACCEPT 
-A FORWARD -d 10.111.100.10/32 -p tcp -m tcp --dport 443 -j FORWARD_ALLOW 
-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 

ここで気にするべき点は、iptablesの上から順にルールを読む動作です。
WEBサーバのhttpに対するアクセスをした場合、一番最初に読むFORWARDルールは

-A FORWARD -s 192.168.0.0/16 -d 10.111.100.10/32 -p tcp -m tcp –dport 80 -j SYN_CHECK

となります。

上記FORWARDルールの順だと、依頼されている

あと、監視サーバからのURL監視も通るようにしてほしいです。こちらは特に制限をかける必要はありません

にマッチせず、WEBサーバのHTTPに対するFORWARD通信はすべて下記のSYN_CHECKルールに適用されてしまいます。
SYN_CHECKルールは15秒間に5回アクセスが発生した場合一時的にアクセスを拒否する。という動作をします。
※問題用として拒否制限はかなり厳しくしてあります。

-A SYN_CHECK -p tcp -m tcp -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A SYN_CHECK -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m recent --rcheck --seconds 20 --name SYN_ATTACK --rsource -j REJECT --reject-with icmp-port-unreachable 
-A SYN_CHECK -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m recent --rcheck --seconds 15 --hitcount 5 --name SYN_sorce --rsource -j SYN_FLOOD 
-A SYN_CHECK -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m recent --set --name SYN_sorce --rsource 
-A SYN_CHECK -p tcp -m tcp -j RETURN 
-A SYN_FLOOD -m recent --set --name SYN_ATTACK --rsource -j LOG --log-prefix "iptables : SYN_FLOOD " 
-A SYN_FLOOD -j REJECT --reject-with icmp-port-unreachable 

その為、監視サーバ(192.168.0.200)のFORWARDは真っ先に許可してあげる必要があります。
下記の様に順番を入れ替えます。

-A FORWARD -s 192.168.0.0/16 -p tcp -m state --state NEW -m tcp --dport 22 -j FORWARD_ALLOW 
-A FORWARD -s 192.168.0.200/32 -d 10.111.100.10/32 -p tcp -m state --state NEW -m tcp --dport 80 -j FORWARD_ALLOW ★このルールをSYN_CHECKルールより上に入れる。
-A FORWARD -s 192.168.0.0/16 -d 10.111.100.10/32 -p tcp -m tcp --dport 80 -j SYN_CHECK 
-A FORWARD ! -s 192.168.0.200/32 -d 10.111.100.10/32 -p tcp -m state --state NEW -m tcp --dport 80 -j FORWARD_ALLOW 
-A FORWARD -s 192.168.0.0/16 -d 10.111.100.10/32 -p tcp -m state --state NEW -m tcp --dport 22 -j FORWARD_ALLOW 
-A FORWARD -s 192.168.0.0/16 -d 10.111.100.10/32 -p tcp -m tcp --dport 22 -j ACCEPT 
-A FORWARD -d 10.111.100.10/32 -p tcp -m tcp --dport 443 -j FORWARD_ALLOW 
-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 

これでFWサーバの問題点はすべて解決しました。

firewalld (webサーバ)

WEBサーバでの問題点です。
問題文の通り、依頼主はFWサーバとWEBサーバを再起動してしまっています。
その為、webサーバ側にも何か起こっている可能性があります。
今回は、firewalldの許可リストからhttpが外れている事象でした。

# systemctl status firewalld.service
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since 土 2019-12-07 14:28:58 JST; 1 day 4h ago
     Docs: man:firewalld(1)
 Main PID: 569 (firewalld)
   CGroup: /system.slice/firewalld.service
           └─569 /usr/bin/python2 -Es /usr/sbin/firewalld --nofork --nopid

firewalldは上がっているので、ルールを確認します。

# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources: 
  services: dhcpv6-client ssh
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

httpのアクセスが許可されていなさそうです。

その為下記のコマンドで永続許可します。
※80番ポート許可でもOKです。

# firewall-cmd --zone=public --add-service=http --permanent
# firewall-cmd --reload

これですべての問題点が解決できた事になります。

 /

問題文

職場でkubernetesを用いてRedmineを導入することになった。
上司が構築作業をしていたが、どうもうまくRedmineが起動しないらしい。

部下のあなたがk8sが得意ということだったので、構築作業の続きをすることになった。
kubernetesが動作するサーバーにRedmine用のManifestが適用されているが、どうも正常起動していないようだ。

原因を究明を行い、Redmineを使える状態にして解決方法を報告してください。

問題のゴール

  • VNCクライアントのブラウザからRedmineが閲覧できること。
    http://192.168.0.100:30000
  • Redmineのデータがコンテナ再起動時にも保持されていること。

情報

  • Server:
  • k8smaster1:
    • ip: 192.168.0.100
    • userid: root
    • password: USerPw@19
  • container-registry:
    • ip: 192.168.0.101
    • 備考: 操作不可
  • Redmine_Manifest:
    • path: “/root/ictsc_problem_manifests/*.yaml”
  • Redmineログイン情報
    • userid: ictsc
    • password: USerPw@19

制限事項

  • Redmineは指定のManifest(Redmine_Manifest)でデプロイしてください。
  • Redmine_Manifestは変更出来ません。
  • Redmine_Manifest内のコンテナイメージはcontainer-registryから取得してください。
  • マニフェストの再適用, OSの再起動の操作は可能です。
  • 誤操作等で競技続行不可の場合は出題時環境への復元のみ承ります。

Kubernetes上にRedmineサービスを稼働させる問題です。
出題時にはRedmineを構成するRedmine-Pod, MariaDB-PodがPendingとなっており、利用不可の状態です。
コンテナが稼働しない原因を突き止め対処することでRedmineサービスを稼働させることができます。

問題解決のために以下の原因を解決する必要があります。

  1. masterへpodのデプロイに関するtaintsの削除
  2. コンテナランタイムcri-oにinsecure-registryの設定を追加
  3. MariaDBのPersistentVolumeのディレクトリ権限(Permission)を修正

問題解説

Kubernetes上にRedmineサービスを稼働させる問題です。
出題時にはRedmineを構成するRedmine-Pod, MariaDB-PodがPendingとなっており、利用不可の状態です。
コンテナが稼働しない原因を突き止め対処することでRedmineサービスを稼働させることができます。

masterへpodのデプロイに関するtaintsの削除

kubectl get podでコンテナの状態を見ます。

[root@k8smaster1 ~]# kubectl get pod
NAME                                  READY   STATUS    RESTARTS   AGE
my-release-mariadb-0                  0/1     Pending   0          9d
my-release-redmine-859cf77958-n95j5   0/1     Pending   0          9d

redmineとmariadbがpendingになっています。
kubectl describe pod <pod名> で各Podの状態を確認すると以下のイベントが確認できます。

Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  9d (x5 over 9d)     default-scheduler  0/1 nodes are available: 1 node(s) had taints that the pod didn't tolerate.

nodeのtaintsをpodが許容できないということなので、nodeのtaintskubectl describe nodesで確認します。

[root@k8smaster1 ~]# kubectl describe nodes
Name:               k8smaster1
Roles:              master
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=k8smaster1
                    kubernetes.io/os=linux
                    node-role.kubernetes.io/master=
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /var/run/crio/crio.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Sat, 23 Nov 2019 19:58:55 +0900
Taints:             node-role.kubernetes.io/master:NoSchedule

Taintsがnode-role.kubernetes.io/master:NoScheduleのため、Labelsにmasterが指定されているこのノードにはPodをScheduleすることができません。
今回はシングルノードのMasterノードでデプロイさせたいので、このtaintsを削除します。

[root@k8smaster1 ~]# kubectl taint nodes k8smaster1 node-role.kubernetes.io/master:NoSchedule-
node/k8smaster1 untainted

これでPodがノードにScheduleされるようになりました。

コンテナランタイムcri-oにinsecure-registryの設定を追加

再度kubectl get podで確認すると以下となりImagePullBackOffとなっています。

[root@k8smaster1 ~]# kubectl get pod
NAME                                  READY   STATUS             RESTARTS   AGE
my-release-mariadb-0                  0/1     ImagePullBackOff   0          9d
my-release-redmine-859cf77958-n95j5   0/1     ImagePullBackOff   0          9d

kubectl desctibe pod <Pod名>でEventを確認すると以下のエラーが確認できます。

Failed to pull image "private-registry.local/bitnami/mariadb:10.3.20-debian-9-r0": rpc error: code = Unknown desc = pinging docker registry returned: Get https://private-registry.local/v2/: dial tcp 192.168.0.101:443: connect: no route to host

コンテナイメージのpullの際に192.168.0.101:443への接続ができていません。
情報で示されているように192.168.0.101はContainer-Registryです。

/etc/hosts192.168.0.101 private-registry.localの記載があり、正しいことがわかります。

試しにDockerでImageのpullに失敗したコンテナイメージを手動でdocker pull private-registry.local/bitnami/mariadb:10.3.20-debian-9-r0してみます。
この操作は正常にpullすることができ、docker imagesでも正しくコンテナイメージがあることが確認できました。

[root@k8smaster1 ~]# docker images
REPOSITORY                               TAG                   IMAGE ID            CREATED             SIZE
private-registry.local/bitnami/mariadb   10.3.20-debian-9-r0   36300b3aaaa0        3 weeks ago         289 MB

ここで再度k8s上のPodの状態を確認しますが、エラーに変化はありません。

[root@k8smaster1 ~]# kubectl get pod
NAME                                  READY   STATUS             RESTARTS   AGE
my-release-mariadb-0                  0/1     ImagePullBackOff   0          9d
my-release-redmine-859cf77958-n95j5   0/1     ImagePullBackOff   0          9d

さらに詳細にみるために全namespaceを対象にget podします。

[root@k8smaster1 ~]# kubectl get pod --all-namespaces
NAMESPACE     NAME                                  READY   STATUS             RESTARTS   AGE
default       my-release-mariadb-0                  0/1     ImagePullBackOff   0          9d
default       my-release-redmine-859cf77958-n95j5   0/1     ImagePullBackOff   0          9d
kube-system   coredns-74c9d4d795-hj9tn              1/1     Running            3          9d
kube-system   coredns-74c9d4d795-l7949              0/1     Pending            0          9d
kube-system   dns-autoscaler-7d95989447-crv67       1/1     Running            3          9d
kube-system   kube-apiserver-k8smaster1             1/1     Running            3          9d
kube-system   kube-controller-manager-k8smaster1    1/1     Running            3          9d
kube-system   kube-proxy-mm8ld                      1/1     Running            3          9d
kube-system   kube-scheduler-k8smaster1             1/1     Running            3          9d
kube-system   nodelocaldns-wgq47                    1/1     Running            3          9d
kube-system   weave-net-z97jl                       2/2     Running            6          9d

RunningのPodがあるのに、docker imagesでは手動でpullしたイメージしかありませんでした。

何かおかしいですね。Dockerが使われていないのかもしれません。

k8sのコンテナランライムを確認するためにkubeletのユニットファイルを確認します。
ファイル/etc/systemd/system/kubelet.serviceに記載の環境変数ファイル/etc/kubernetes/kubelet.envには以下の記載があります。

KUBELET_ARGS="--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf \
--config=/etc/kubernetes/kubelet-config.yaml \
--kubeconfig=/etc/kubernetes/kubelet.conf \
--pod-infra-container-image=gcr.io/google_containers/pause-amd64:3.1 \
--container-runtime=remote \
--container-runtime-endpoint=/var/run/crio/crio.sock \
--runtime-cgroups=/systemd/system.slice \
 --node-labels=  "

container-runtimecrioが指定されていることがわかります。
しっかりとサービスが動いていますね。

[root@k8smaster1 ~]# systemctl status crio
● crio.service - Open Container Initiative Daemon
   Loaded: loaded (/usr/lib/systemd/system/crio.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2019-12-03 00:39:10 JST; 49min ago
     Docs: https://github.com/kubernetes-sigs/cri-o
 Main PID: 1076 (crio)
   CGroup: /system.slice/crio.service
           └─1076 /usr/bin/crio

crioはCRIなのでcrictlによる操作が可能です。
crictl imagesの結果ではrunningだったPodのコンテナイメージがありました。

[root@k8smaster1 ~]# crictl images
IMAGE                                              TAG                 IMAGE ID            SIZE
docker.io/coredns/coredns                          1.6.0               680bc53e5985f       42.3MB
docker.io/weaveworks/weave-kube                    2.4.0               86ff1a48ce14d       134MB
docker.io/weaveworks/weave-npc                     2.4.0               647ad6d59818d       49.5MB
gcr.io/google-containers/kube-apiserver            v1.15.3             5eb2d3fc7a44e       208MB
gcr.io/google-containers/kube-controller-manager   v1.15.3             e77c31de55475       160MB
gcr.io/google-containers/kube-proxy                v1.15.3             232b5c7931462       84.3MB
gcr.io/google-containers/kube-scheduler            v1.15.3             703f9c69a5d57       82.7MB
k8s.gcr.io/cluster-proportional-autoscaler-amd64   1.6.0               dfe4432cd2e2b       48.9MB
k8s.gcr.io/k8s-dns-node-cache                      1.15.4              3f8330c31e7d5       64.3MB
k8s.gcr.io/pause                                   3.1                 da86e6ba6ca19       747kB
gcr.io/google-containers/pause                     3.1                 da86e6ba6ca19       747kB

k8sのコンテナランタイムとしてcri-oが動作していたので、80ポートでアクセスさせるために、cri-oのprivate-resigtryへのinsecure-registryを追加しましょう。

214  # insecure_registries is used to skip TLS verification when pulling images.
215  insecure_registries = [
216    "10.233.0.0/24",
217    "private-registry.local"
218  ]

MariaDBのPersistentVolumeのディレクトリ権限(Permission)を修正

crioの設定を反映させた後、再度k8sのpodを確認します。

[root@k8smaster1 ~]# kubectl get pod
NAME                                  READY   STATUS    RESTARTS   AGE
my-release-mariadb-0                  0/1     Error     5          9d
my-release-redmine-859cf77958-n95j5   0/1     Running   1          9d

mariadbがエラーになっています。
コンテナの立ち上げは行われているので、コンテナのlogを確認します。

[root@k8smaster1 ~]# kubectl logs  my-release-mariadb-0
 16:43:21.98
 16:43:21.98 Welcome to the Bitnami mariadb container
 16:43:21.98 Subscribe to project updates by watching https://github.com/bitnami/bitnami-docker-mariadb
 16:43:21.98 Submit issues and feature requests at https://github.com/bitnami/bitnami-docker-mariadb/issues
 16:43:21.98 Send us your feedback at containers@bitnami.com
 16:43:21.99
 16:43:21.99 INFO  ==> ** Starting MariaDB setup **
 16:43:22.04 INFO  ==> Validating settings in MYSQL_*/MARIADB_* env vars
 16:43:22.04 INFO  ==> Initializing mariadb database
mkdir: cannot create directory '/bitnami/mariadb/data': Permission denied

'/bitnami/mariadb/data': Permission denied ディレクトリの権限不足のようですね。

/root/ictsc_problem_manifestsにあるk8sManifestを読み解くと、/var/opt/pv{1,2}にPersistentVolumeがあることがわかります。
kubectl get pvの結果よりmariaDBに対応するPathにchmod で権限を付与します。

[root@k8smaster1 ictsc_problem_manifests]# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                               STORAGECLASS   REASON   AGE
pv0001   20Gi       RWO            Recycle          Bound    default/data-my-release-mariadb-0                           9d
pv0002   20Gi       RWO            Recycle          Bound    default/my-release-redmine                                  9d

[root@k8smaster1 ]# chmod -R 777 /var/opt/pv1/

再度k8sのPodの状況を確認すると正常にRedmineのPodが稼働していることが確認できました。

[root@k8smaster1 opt]# kubectl get pod
NAME                                  READY   STATUS             RESTARTS   AGE
my-release-mariadb-0                  1/1     Running            9          9d
my-release-redmine-859cf77958-n95j5   1/1     Running            5          9d

VNCのブラウザからRedmineのページが閲覧できれば完了です。