/
カテゴリー

はじめに  

作問者のabeです. 実はこの問題は史実に基づいて作られた問題です.

回答を見ていると私と同じ罠に引っかかったチームが居て嬉しかったです(?).

問題文  

部長さんへ

新米エンジニアの捕鯨太郎です。 プロジェクトでWEBサーバを構築するとのことなので、 docker-composeを使用してWEBサーバを構築しました。 無事に起動し、WEBページも確認できたのですが、 再起動するとアクセスできなくなってしまいました。

自動的に再起動するように記述してあるので不可解です。 しかも、トラブルシュートしているといつの間にか アクセスできるようになります。自分には原因がわかりません。

お知恵を拝借したいのですが、 お時間をいただけないでしょうか。

前提条件  

  • ~/web-server/docker-compose.ymlがあり編集可能
  • docker ps -aなどで確認するとコンテナが起動している

初期状態  

踏み台から $ curl 192.168.17.1 をしても応答がない

終了状態  

再起動しても踏み台から $ curl -I 192.168.17.1 をするとステータスコード200のレスポンスが返ってくる

答え  

まずは初期状態を確認しましょう.

~ curl 192.168.17.1
curl: (7) Failed to connect to 192.168.17.1 port 80: Connection refused

WEBページにアクセスできないのが確認できます. 次にコンテナの様子をみてみましょう.

[email protected]:~$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                  PORTS                NAMES
4e7db3e7cc97        nginx:latest        "/docker-entrypoint.…"   3 minutes ago       Up Less than a second   0.0.0.0:80->80/tcp   web-server_nginx_1

あれあれ,起動しているな…

もう一度確認すると… いつの間にか復活してる!!!

~ curl 192.168.17.1 -I
HTTP/1.1 200 OK
Server: nginx/1.19.6
Date: Sun, 07 Mar 2021 03:33:11 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 15 Dec 2020 13:59:38 GMT
Connection: keep-alive
ETag: "5fd8c14a-264"
Accept-Ranges: bytes

確認作業を行っているうちに復活してしまいました.

実はdockerコマンドを叩くとデーモンが起動するという罠があります.先ほどのdocker ps -aを見てみるとUp Less than a secondとあり,起動したてなのがわかります.

以下のコマンドで確認するとデーモンは起動していますが,enableになっていない事がわかります.

[email protected]:~$ systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; disabled; vendor preset: enabled)
     Active: active (running) since Sun 2021-03-07 12:32:53 JST; 9min ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 2176 (dockerd)
      Tasks: 17
     Memory: 114.7M
     CGroup: /system.slice/docker.service
             ├─2176 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
             └─2362 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.18.0.2 -container-port 80
             

[email protected]:~$ systemctl is-enabled docker
disabled

そう,enableのし忘れですね.以下のコマンドで解決です.

systemctl enable docker

採点基準  

dockerサービスをenableできたか.

おわりに  

前提条件に書いた2点,docker-compose.ymlが編集可能である,docker ps -aすると起動している, に引っかかってくれたチームがいて嬉しかったです.

冷静に確認されると一瞬で解決してしまう問題なので簡単枠で出題しました.

 /
カテゴリー

はじめに  

作問者のabeです. この問題は昔バックアップパーティションに救われた経験から作りました.

概要  

ベテランエンジニアの虎魂くんはWEBサーバの運用を行っていました. ある日,記憶から消えかけていたような昔のプロジェクトのお客さんから電話がありました.

どうやらWEBサーバにアクセスするとエラーページが表示されるようです. 原因の理由と解決方法を回答してください.

前提条件  

  • WEBページのデータのバックアップはない.
  • WEBページの公開パスなどの変更は可能.

初期状態  

手元のPCから $ curl 192.168.11.1 をしてもエラーコードが帰ってくる.

終了状態  

  • 障害前に表示されていたページが表示されること.
  • 手元のPCから $ curl 192.168.11.1 をするとステータスコード200のレスポンスが返ってくる.

採点基準  

  • /dev/sdbのパーティションが壊れていることに気が付く 30%
  • 障害前のWEBページが確認できる 70%

答え  

まずは終了状態の確認を行います.400番台なのでエラーが発生しています.

[email protected]:~$ curl localhost
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>

一応Nginxが起動しているか確認します.起動しています.

[email protected]:~$ systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2021-03-07 13:31:36 JST; 27min ago
       Docs: man:nginx(8)
   Main PID: 585 (nginx)
      Tasks: 2 (limit: 1168)
     Memory: 11.5M
     CGroup: /system.slice/nginx.service
             ├─585 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             └─586 nginx: worker process

Warning: some journal files were not opened due to insufficient permissions.

/var/log/nginx/error.logを確認すると以下のエラーが出力されています.

2021/03/07 13:55:27 [error] 586#586: *1 directory index of "/mnt/data/" is forbidden, client: 127.0.0.1, server: _, request: "GET / HTTP/1.1", host: "localhost"

ls /mnt/data/で確認するとディレクトリが空なのがわかります. ここでパスを見てみるとmntである事がわかります. mntディレクトリはファイルシステムをマウントする用途に使われるので/etc/fstabを確認します.

以下のようなマウントポイントが設定されてました.

/dev/sdb1 /mnt/data ext4 defaults 0 0

対象のデバイスを確認します.

sudo gdisk /dev/sdb 

するとエラーメッセージが出力されます.抜粋した以下を確認するとメインのパーティションテーブルが壊れている事がわかります. しかし,バックアップは生きているようなので復旧していきます.

Main header: ERROR
Backup header: OK
Main partition table: ERROR
Backup partition table: OK

復旧するので1番を選択

Found invalid MBR and corrupt GPT. What do you want to do? (Using the
GPT MAY permit recovery of GPT data.)
 1 - Use current GPT
 2 - Create blank GPT

Your answer: 1

リカバリモードに入り,バックアップから復元し書き込みます.

Command (? for help): r

Recovery/transformation command (? for help): c
Warning! This will probably do weird things if you've converted an MBR to
GPT form and haven't yet saved the GPT! Proceed? (Y/N): y

Recovery/transformation command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/sdb.
The operation has completed successfully.

マウントしてみます.

sudo mount -a

確認すると正常に復旧できた事がわかります.

[email protected]:~$ cat /mnt/data/index.html 
<!DOCTYPE html>
<html lang="ja">
        <head>
                <meta charset="utf-8" />
                <link rel="stylesheet"  href="style.css">
                <title>タイトル</title>
                <script
                          src="https://code.jquery.com/jquery-3.3.1.min.js"
                            integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
                              crossorigin="anonymous"></script>
        </head>
        <body>
                <main>
                        <p>ICTSC WEB!!!</p>
                </main>
        <script>
                $(function(){
                });
        </script>
        </body>
</html>

おわりに  

この問題は物理ディスクを2つ使うため,自分たちで仮想基盤を運用する本戦で出そうと思ってました. 150点問題としては多くのチームに解いていただけたので嬉しかったです.

gdiskではなくtestdiskを用いて回答していたチームもいました. どちらを使用しても終了条件を満たせるので満点にしています.

来年から社会人なので,最後の作問でしたが楽しかったです.

 /
カテゴリー

問題文

概要

k8sクラスターにcert-managerをインストールしhttp01認証で証明書を発行するissuerを設定した。
その上でhostのnginxのpodを展開するDeploymentと、それを公開するService、Ingressを記述した/home/user/manifests/web.yamlを作成しapplyした。
しかし、なぜか証明書が発行されない。その理由と解決方法を報告してほしい。

トポロジを以下の図で示す。

前提条件

  • cert-managerで取得する証明書は自己署名証明書を使用している
    • hostcert-managerなど必要なものにはルートCA証明書をインストール済み
  • hostuserユーザーでkubectlコマンドが利用できる
  • host/home/user/manifestsに今回applyしたmanifestが保存されている
    • cert-manager.yaml ingress-controller.yaml root-ca.yaml変更しないこと
    • web.yamlの再展開(kubectl delete, kubectl applyなど)可
  • acme-serverには、追加でDNSサーバーが稼働しており以下のレコードが設定されている
        IN      NS      ictsc.test.
        IN      A       192.168.20.125

ca      IN      A       192.168.20.125
www     IN      A       192.168.20.1
  • lbにはkeepalivedがインストールされている。
    • 変更しないこと

初期状態

  • hostcurl -L www.ictsc.testを実行したとき以下のようなエラーになる
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
  • hostkubectl get certを実行したときwww-ictsc-test-tlsのREADYがFalseになっている

終了状態

  • hostcurl -L www.ictsc.testを実行したときにnginxの初期ページが表示される
    • 証明書が設定されている
  • hostkubectl get certを実行したときにwww-ictsc-test-tlsのREADYがTrueになっている

接続情報

lbhostのみSSHログインできる。

VM名ホスト名ユーザパスワード
lb192.168.20.1userictsc2020
host192.168.20.20userictsc2020
acme-server192.168.20.125
k8s-master192.168.20.129
k8s-worker1192.168.20.130
k8s-worker2192.168.20.131

解説

原因

cert-managerはhttp01認証を行うときにacme challengeが行えるかセルフチェックを行います。その際、www.ictsc.testつまり192.168.20.1宛てにGETリクエストを行いますが、このリクエストの戻りのパケットがnginxのPODから直接cert-managerのPODに送られてしまいます。
その時の送信元アドレスが、リクエストの送信時の宛先と異なるためセルフチェックのリクエストは失敗してしまい証明書の取得が行われませんでした。

以下のようにセルフチェックが失敗していることを確認できます。

[email protected]:~$ kubectl describe challenges.acme.cert-manager.io
Name:         www-ictsc-test-tls-dhksj-696946275-1037365539
Namespace:    default
Labels:       <none&gt;
Annotations:  <none&gt;
API Version:  acme.cert-manager.io/v1
Kind:         Challenge
...
省略
...
Status:
  Presented:   true
  Processing:  true
  Reason:      Waiting for HTTP-01 challenge propagation: failed to perform self check GET request 'http://www.ictsc.test/.well-known/acme-challenge/c4mi9je6PJ87H9ulTtAPOu1xSjXEyDo9': Get "http://www.ictsc.test/.well-known/acme-challenge/c4mi9je6PJ87H9ulTtAPOu1xSjXEyDo9": dial tcp 192.168.20.1:80: connect: connection timed out
  State:       pending
...
省略
...

解決策

解決策としては以下のことが考えられます。

  • LBにhaproxyやnginxなどのリバースプロキシを用いる
  • LBにヘアピンNATの設定を行う

今回は、keepalivedを変更できないのでヘアピンNATの設定を行います。

/etc/sysctl.confに以下の設定を追記し更新します。

$ sudo vim /etc/sysctl.conf
net.ipv4.vs.conntrack=1 # 最終行に追記

$ sudo sysctl -p

iptablesで内部からの通信をMASQUERADEする設定を行い永続化します。

$ sudo iptables -t nat -A POSTROUTING -s 192.168.20.128/25 -m ipvs --vaddr 192.168.20.1/32 -j MASQUERADE
$ sudo netfilter-persistent save

/home/user/manifests/web.yamlをapplyし直すとkubectl get certでREADYがTrueになることを確認できます。
またcurl -L www.ictsc.testで正常にnginxのページが表示されます。

[email protected]:~/manifests$ kubectl delete -f web.yaml
deployment.apps "web" deleted
service "web" deleted
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions "a-web" deleted

[email protected]:~/manifests$ kubectl apply -f web.yaml
deployment.apps/web created
service/web created
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/a-web created

[email protected]:~/manifests$ kubectl get cert
NAME                 READY   SECRET               AGE
www-ictsc-test-tls   True    www-ictsc-test-tls   35s

[email protected]:~/manifests$ curl -L www.ictsc.test
<!DOCTYPE html&gt;
<html&gt;
<head&gt;
<title&gt;Welcome to nginx!</title&gt;
<style&gt;
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style&gt;
</head&gt;
<body&gt;
<h1&gt;Welcome to nginx!</h1&gt;
<p&gt;If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p&gt;

<p&gt;For online documentation and support please refer to
<a href="http://nginx.org/"&gt;nginx.org</a&gt;.<br/&gt;
Commercial support is available at
<a href="http://nginx.com/"&gt;nginx.com</a&gt;.</p&gt;

<p&gt;<em&gt;Thank you for using nginx.</em&gt;</p&gt;
</body&gt;
</html&gt;

採点基準

  • 理由が説明できている 20%
  • 解決方法が記述されておりその解決方法で解決が可能 80%

さいごに

問題を解いていただきありがとうございました。

また上記のような想定解法での解答はなく、以下のような解答を頂きました。DNATの解法は、想定はしていたのですがCoreDNSは完全に想定しなかったので、確かにそういう方法もあるのかと思いました。(self checkとは?という感じではありますが、問題の終了条件を満たせるので満点としました。)

  • CoreDNSのレコードを変更しingress controllerのPODのIPアドレスを名前解決するようにする
  • lbでk8sのいずれかのノードにDNATをする
 /
カテゴリー

概要

インカレ技術サークル ICTSC に入ったあなたは、Kubernetesクラスタチームに配属されました。
チームの先輩から、Kubernetes上にチーム紹介のWebサイトをつくるよう頼まれました。
Kubernetesのマニフェストは先輩に渡されたものから変更しないで欲しいそうです。
しかし、クラスタに適用したところ、Webサイト用のPodがPendingになってしまいます。
卒研の進捗が思わしくない先輩は教員の課題から手が離せないため、頑張ってあなたが解決してください。
解決後は、チーム名の書かれたWebサイトが見えるようにしておいてください。
問題が解決できればサークル内で共有したいため、原因と解決方法を報告してください。
先輩から渡され、k8sに適用したマニフェストは/home/user/manifestに保存されています。

前提条件

  • /home/user/manifestにあるファイルの内容を変更してはいけない

初期状態

  • NginxのDeploymentがPendingになっている。

終了状態

  • pendingとなっていたDeploymentが正常に稼働している。
  • 正常化したDeploymentによって稼働するNginxで、解答するチームの名前が書かれたWebサイトが確認できるようになっている。
  • 開始時と同じマニフェストのみが適用されている。

接続情報

VM名ホスト名
kzz-k8s-master192.168.12.1
kzz-k8s-node1192.168.12.2
kzz-k8s-node1192.168.12.3
kzz-k8s-node1192.168.12.4

解説

/home/user/manifestには、Flannel 、MetalLB、Rook、NginxのDeploymentが書かれたtest-nginx.yamlなどのマニフェストが保存されています。
test-nginx.yamlはPVC 、そのPVCを/usr/share/nginx/htmlにマウントするNginx のDeployment 及び、Nginx を公開するためのLoadBalancer Serviceを定義します。
問題環境では、問題名にあるNginx だけでなくPVC もPendingとなっています。
PendingになっているPVCをkubectl describe pvc cephfs-pvc で確認すると、failed to provision volume with StorageClassとエラーが出ています。

このKubernetesクラスタではRookを利用し、CephをPersistent Volumeとして使っています。
このことから、Rookの設定の異常等を予想し、Rookのエラーを確認する必要があります。
そのため、/home/user/manifestにあるRookのマニフェスト(cluster.yaml)では、無効化されているcrash-collectorを有効化し適用します。
立ち上がったcrash-collectorをkubectl describe で確認すると、Unable to attach or mount volumesとなっています。
Rookがマウントしてそうな場所を探すと、 cluster.yamlの33行目にdataDirHostPath: /var/lib/rookなる行があります。
そして、この直前の31行目のコメントに、

  # Important: if you reinstall the cluster, make sure you delete this directory from each host or else the mons will fail to start on the new cluster.

と書かれています。

原因としては、
Rookを削除する際にコンフィグ情報やログデータなどが保存されているdataDirHostPathを削除しなかったことです。
Rookを適用後、気まぐれに削除をした環境で、再度Rookを適用した場合に発生する現象です。
解決方法としては、RookのDocumentに書かれた通りにクラスターの清掃を行うだけです。
はじめに、適用されているRookを削除します。

kubectl delete -f filesystem.yaml 
kubectl delete -f storageclass.yaml
kubectl delete -f toolbox.yaml
kubectl delete -f cluster.yaml
kubectl delete -f operator.yaml
kubectl delete -f common.yaml

今回のマニフェストでdataDirHostPathはデフォルトの/var/lib/rookとなっています。
全てのKubernetes Nodeの/var/lib/rookを削除します。
最後に、Rookを適用すると正常に稼働することが確認できます。
ただし、本問題の条件としてマニフェストを変更しないことが挙げられているので、crash-collectorを無効にしておく必要があります。

kubectl apply -f common.yaml
kubectl apply -f operator.yaml
kubectl apply -f cluster.yaml
kubectl apply -f toolbox.yaml
kubectl apply -f storageclass.yaml
kubectl apply -f filesystem.yaml 

ただ、チーム名の書かれたWebサイトは用意されていないので、kubectl exec -it ”Pod名” -- bashをしてNginxのpodに入ります。
そのShellで、 echo -e "<a>”チーム名”</a>" > /usr/share/nginx/html/index.html を行えば終了です。

今回はrook1.2を利用していたので手動で削除しましたが、1.3から自動で消してくれるCleanup Policyなるものがあるらしいです。

採点基準

  • 原因(/var/lib/rookがRook作成時に残っていたので、前のクラスタの認証情報に邪魔されてクラスタが作成できない)が述べられている。 40%
  • 各ノードの/var/lib/rookを削除し、kubectl -n rook-ceph exec -it $(kubectl -n rook-ceph get pod -l "app=rook-ceph-tools" -o jsonpath='{.items[0].metadata.name}') ceph status にてHealthが HEALTH_OKになっている 20%
  • pvcが作成され、PVCを利用したNginxのPodが展開されている。 30%
  • 踏み台から各チーム名が記載されたWebサイトが確認できる。 10%

反省

採点基準を分けたものの、解決していないとどれも満たせないことに採点中気づきました。

 /
カテゴリー

概要

BGPの検証ネットワークを構築してみたところ、PC1<–>PC2間でpingが飛ばないという現象が起きました。
ただしR3実環境ではとある組織の所有物なので設定の変更はできないものとし、R3は何かしらのダイナミックのルーティングプロトコルが動作しているときいている。
PC1<–>PC2間の間で通信ができないので原因を突き止めてほしい。

前提条件

  • static routesを書かないで
  • R3にはsshできません

初期状態

手元のPCから $ ping 192.168.19.30 をしても応答がない。

終了状態

PC1からPC2までpingが正しく飛ぶ。

解説

想定解

この問題はMTUの不一致でOSPFのネイバーがR2<–>R3間で確立できておらずEBGPのピアが張れないため、PC1<–>PC2間で通信ができないという問題でした。
この問題はR3が触れないためR3のR2側のインターフェースのMTUを割り出す必要があります。
割り出しにはPCからのpingを使います。ping -s MTU値 -M do IPアドレスを使えばIPアドレスまでの経路で指定したMTU値でpingが通れるかどうかを調べることができます。
PC1でping -s MTU値 -M do 192.168.19.26を実行し、もし経路上のMTUより指定したMTU値が大きい場合は以下のように経路上のMTUの上限を通知してくれます。

[email protected]:~$ ping -s 1450 -M do 192.168.19.26
PING 192.168.19.26 (192.168.19.26) 1450(1478) bytes of data.
ping: local error: message too long, mtu=1350
ping: local error: message too long, mtu=1350
ping: local error: message too long, mtu=1350
^C
--- 192.168.19.26 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2034ms

ここで注意すべきはpingの宛先IPアドレスでPC1からpingが通らないと思って192.168.19.26ではなく192.168.19.25に上記のオプションでpingを飛ばすと192.168.19.25のインターフェースのMTU値は見ることができません。このオプションのpingのパケットが出ていくインターフェースのMTU値しかわからないので192.168.19.26まで飛ばしてパケットを192.168.19.25から出す必要があります。

なお上記オプションでMTU値を変えて複数回pingをする場合、linuxがMTU値をキャッシュするためpingをする前にsudo ip route flush cacheを実行してキャッシュを削除する必要があります。

R3のMTU値が解ればR2のR3側のインターフェースに同じMTU値を設定すればOSPFのネイバーが確立できるのでEBGPのネイバーも張ることができ、PC1<–>PC2間でpingが通るようになります。

その他解法

実際の本選参加者は上記のようなまどろっこしい解法は行わずR2のVyOS上でtcpdumpを行いOSPFのパケットの中身を見てMTU値を割り出していました。おそらくそちらのほうがメジャーなやり方ですが、tcpdumpを使えない環境等でpingでMTU値を割り出す方法も知っていると便利かと思います。

採点基準

この問題は50点問題なので部分点などは与えずPC1<–>PC2間で疎通が取れるようになる解答のみに点数を与えています。