20200708のLinuxに関する記事は10件です。

Flatpakアプリケーションで日本語入力ができない? 原因は、Fcitx。

私の今の環境は、Lubuntu 18.04である。

Flatpakは、GNU/Linux環境用のパッケージマネージャである。ソフトウェアをサンドボックス内で実行できることから、開発者は、個別のディストロ用に別々のパッケージを作る必要がないため、UbuntuやDebianで、aptを使ってインストールするより、最新版のソフトウェアを取得できる場合が多いのだ。

しかしながら、Flatpakを利用したソフトの多くで、日本語入力ができない現象に見舞われた。あとからというわけではなく、Flatpakアプリをインストールして、最初からである。

原因は、Fcitx

原因は、Fcitxであった。
どうやら、Fcitxの持つ仕組みと、Flatpakのサンドボックス特性が、相性が良くないらしい。

簡単な解決法 : IBusに切り替えよう。

Fcitxでダメなら、IBusではどうだろうか。IBusに切り替えたところ...なんと正常に使えるようになった。よって、FcitxからFcitxに乗り換える手順を説明する。

IBus (と、それ用のMozc)のインストール

sudo apt install ibus ibus-mozc

インプットメソッドの設定

まず、インプットメソットの設定を開く。端末を開き

im-config

すると、以下のようなウィンドウが開く
1.png
OKを押して進もう。すると
2.png
推奨はされていないが、とりあえず、Yesを選択。
3.png
こうなるので、ibusを選択してOK
4.png
最後に、こうなる。これで、基本設定は完了だ。適用するには、一旦ログアウトして、再度ログインすれば良い。

再ログインしたら

ログインすると、右下のバーに、キーボードのアイコンが現れているので、それを右クリック。そして、設定を開く。
設定画面になるので、「入力メソッド」タブに移動。「追加」を押して、一応、「日本語」キーボードを追加する。そして、英語キーボードを削除。

そして、とりあえず、テキストエディタでも開いて、入力しようとしてみよう。しかし...私の場合、英語配列のままだった。
この場合一旦ログアウトして、再度ログインすれば、ちゃんと日本語配列になっていた。半角/全角を押せば、日本語入力もできる。

最後に

これで、Flatpakアプリにおいても、日本語入力が可能になった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

conda環境へのパッケージインストールにおける注意点

概要

皆様,こんにちは.
私事で恐縮ですが,最近は忙しく論文執筆に追われておりました.
忙しい時に限って何かとトラブルが起こるものです.今回はそんなトラブル対処の備忘録です.

ようやくタスクもひと段落したので,記事としてまとめておきます.
備忘録,メモ的な内容ですので,お気軽に見ていただければと思います.

トラブル内容

パッケージがconda環境に入らなかった.以下詳細.

conda環境にパッケージを追加したい.なのでactivateして,

(base) source activate hogehoge
(hogehoge) conda install hogehoge

しかしcondaのレポジトリにないといわれる.

PackagesNotFoundError: The following packages are not available from current channels:

なので,しぶしぶpipでインストールすることに.

(hogehoge) sudo pip install hogehoge

しかしなぜかconda環境にパッケージが入らない.

解決法

スーパーユーザ権限で実行してたのが原因.
base環境にぶち込まれてしまう.
なので純粋に,

(hogehoge) pip install hogehoge

でよい.

スーパーユーザ権限を無心で乱用していたことに対する報いである.
大変しょうもないミスであるが,今後はきちんとコマンド一つにとり何を意味しているかを意識したい.

はやく実験しようと焦ると,linuxの基本やコードの記法もおろそかになってくるので,
日頃の自習をしっかりとして,いろんな状況でも対応できるようにしたい.

終わりです.ご覧いただきありがとうございました.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sedコマンド

sedコマンド

よくわかっていなかったので簡単にまとめた。

sedコマンドとは

「sed」は「Stream EDitor」の略で

sed -オプション スクリプトコマンド 入力ファイル

以上のコマンドで
指定したファイルをコマンドに従って処理し、標準出力へ出力します。

オプション Right align
-r スクリプトで(基本正規表現ではなく)拡張正規表現を使用する
-f スクリプトファイル 実行するコマンドとしてスクリプトファイルの内容を追加する
-e スクリプト スクリプトを追加する
-i 標準出力へ出力する代わりに、出力する内容で元のファイルを置き換える

ファイル内の「abc」を「ABC」に変更

sed s/adc/ABC ファイル名

オプションがないとき

最初の引数がsedスクリプトとして、残りの全ての引数は入力ファイルとして扱われる。

/etc/shells の中で、「usr」と書かれている場所を「USER」に置き換える

sed s/user/USER/ /etc/shells
    #スクリプト    #ファイル名

簡単にでしたがこんな感じで、
どうゆうものかは理解することができたので、使えるようにしていきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Arch Linuxインストール時の、Wifi接続に関する備忘録

この記事は

Arch Linuxをインストールする際にWifi接続を行う手順を、自分用に簡潔に記録しました。

インストールは基本的に、@TsutomuNakamuraさんの「Arch Linux インストール俺々式完全版」に従って行えば問題なく終わります。
このとき、インターネット接続が必須となるのですが、有線接続が使えなかった自分はWifiの設定にやや苦労したので、その際の解決法を記録しておきます。

結論から言うと

WPA/WPA2の場合はwpa_supplicantを使うと良いです。自分はwifi-menuが使えませんでした。

手順

全景

> ip link
> ip link set [interface] up
> iwlist [interface] scanning | grep "ESSID"
> wpa_supplicant -B -i [interface] -c <(wpa_passphrase [ESSID] [password])
> dhcpcd
> ping archlinux.org
  • [interface] : インターフェースの名前
  • [ESSID] : SSID名
  • [password] : パスワード

1. インターフェースの名前を確認

ip link

まずはインターフェースの名前を確認する。
Wifiのインターフェースとして、自分の場合は「wlan0」というような名前が表示された。

2. インターフェースの有効化(不要なことが多い?)

ip link [interface] up

1.で取得したインターフェースが無効になっている場合はこのコマンドで有効化する。インターフェースの有効・無効については、「Arch Linuxインストールメディア環境下でのWi-Fi接続(ワイヤレス設定)について | Unskilled?」の「有効・無効の見分け方」以下を参照。

3. SSIDを検索

iwlist [interface] scanning | grep "ESSID"

1.で取得したインターフェースの中から、接続したいWifiのSSIDを探す。見つからない場合は再度コマンドを叩くと表示されることがある他、インターフェースが間違っている可能性がある。

4. Wifiに接続

wpa_supplicant -B -i [interface] -c <(wpa_passphrase [ESSID] [password])

1.で取得したインターフェース名、3.で取得したSSID、Wifiのパスワードをそれぞれ入力して接続する。このコマンドはWPA/WPA2が対象のものだが、まあ大概の機器はWPA/WPA2だろうくらいのノリでこの備忘録を書いている。

5. ホスト情報の割り当て

dhcpcd

呪文。
IPアドレス等を取得して、各種設定を行う。
詳しくは「dhcpcd - システム管理コマンドの説明 - Linux コマンド集 一覧表」を参照。

6. 接続を確認

ping archlinux.org

適当なアドレスにpingを打って、接続が完了したことを確認する。
pingの先は適当。

まとめ

Arch Linuxって楽しいよね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kubernetesにおけるゼロダウンタイムデプロイメント

はじめに

新しいバージョンのアプリケーションをデプロイする時に、アプリケーションをコンテナで運用している場合は、コンテナを作り替える必要がある。デプロイ時に適切な手順を踏まないと、リクエストを正しく捌けずに、クライアントにエラーを返すことになる。これは本番環境で動いているアプリケーションに取ってはクリティカルな問題である。
Kubernetesは RollingUpdate をデフォルトで対応していため、デプロイは適切にManifestを設定をしていればダウンタイムなくデプロイしてくれる。新しいPodの生成からServiceへの追加はヘルスチェック(readnessProbe)を適切に設定するだけで良い。しかし、古いPodを停止する時には、色々考慮する問題が出てくる。適切に設定を行わないと、まだアプリケーションがリクエストを処理中にもかかわらず、Podを停止してしまうということが起こり得る。今回はこの問題を解決するために考察したことをまとめた。

KubernetesのPodを停止するまでの挙動

Kubernetesの挙動は以下のようになっている。

スクリーンショット 2020-07-08 午後4.17.28.png

Kubernetesでは「 preStop 処理 + SIGTERM 処理」と、「ServiceからのPodの除外処理」が非同期で行われる。コンテナは SIGTERM のシグナルが送られた場合に、適切に処理中のリクエストを捌き切ってから終了するようにする必要がある。そうでなければ、 SIGKILL シグナルがコンテナに送られて強制終了してしまう。したがって、適切な terminationGracePeriodSeconds を設定する必要がる。
また、 Service は、該当のコンテナに新しいリクエストを送らないように切り離すが、コンテナが処理を行っている途中である場合は、その接続を切断しないようにしなければいけない。

Service (LoadBalancer)

LoadBalancerがコンテナに新しいリクエストを送らないようにするとともに、現在行っている処理の接続を切断しないようにするために、Connection Drainingの設定をする必要がある。

例えば、EKSでは、 Connection Draining の有効化や、タイムアウトの設定を以下のように設定する。

manifest
apiVersion: v1
kind: Service
metadata:
    name: test
    annotations:
        service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "10"
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "10"
spec:
...

アプリケーションのGraceful Shutdown

コンテナが安全に終了するためには、それぞれのコンテナがGraceful Shutdownをサポートしている必要がある。

例えば、 Gunicorn はGraceful Shutdownを対応している。適切に graceful_timeout の値を設定すれば問題ない。

https://docs.gunicorn.org/en/stable/settings.html

また、アプリケーションコンテナの前に Nginx のようなリバースプロキシを立てるのはよくある構成だが、その場合、 Nginx も同様に対応していないと、 Nginx が先に終了し、クライアントに 504(Gateway Timeout)が返ってしまう。
Nginx 自体はGraceful Shutdownに対応しているが、Graceful Shutdownを行うためのシグナルが、 SIGTERM ではなく、 SIGQUIT である。

TERM, INT   fast shutdown
QUIT    graceful shutdown

http://nginx.org/en/docs/control.html

そこで、終了時のシグナルを SIGTERM から SIGQUIT に変更してやる必要がある。

Dockerfile
FROM nginx:<version>

...

STOPSIGNAL SIGQUIT

https://github.com/nginxinc/docker-nginx/issues/377

また、タイムアウトは worker_shutdown_timeout の値を設定する。

http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout

コンテナ実行時の複数コマンド実行

SIGTERM シグナルはコンテナ内の PID 1 のプロセスに送られる。コンテナ起動時に複数コマンドを実行したい場合は、Startup Scriptを使うことはあるが、通常のスクリプトを用いると、そのスクリプトが PID 1 を持つことになり、アプリケーションにシグナルが送られない。
そこで、 exec を用いて、スクリプトのプロセスをアプリケーションのプロセスに変更することで対処できる。

Dockerfile
...

ENTRYPOINT ["./startup.sh"]
CMD ["runserver"]
startup.sh
#!/bin/sh
set -e

# コマンド

exec "$@"

PID 1問題

LinuxにおけるPID 1は通常 init である。 init は全てのプロセスの親であるため、このプロセスが殺されると、システムがダウンしてしまうため、特別扱いされている。具体的には、明示的にハンドラを設定していないシグナルは無視される。

NAME
        kill - send signal to a process

NOTES
       The only signals that can be sent to process ID 1, the init process,
       are those for which init has explicitly installed signal handlers.
       This is done to assure the system is not brought down accidentally.

https://man7.org/linux/man-pages/man2/kill.2.html

したがって、アプリケーションサーバが明示的に SIGTERM のハンドラを設定していない場合は、プロセスが SIGTERM を無視してしまい、終了処理が適切に行われない。 Node.js でこれが起きるらしい。

この問題の回避方法は「明示的にシグナルをハンドリングする」または、「アプリケーションのプロセスをPID 1以外で立ち上げる」の2つが考えられる。前者はそれぞれで対応すれば良い。前者が難しい場合は、後者を使う必要がある。

Kubernetes 1.17以上の場合は、ShareProcessNamespace を使ってPID 1を回避できる。

manifest
apiVersion: v1
kind: Pod
metadata:
    name: test
spec:
    shareProcessNamespace: true
...

Kubernetes以外、またはKubernetesのバージョンが低い場合は、 tiniのような軽量initと呼ばれるライブラリを使えば解決できる。

Dockerfile
...

ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

CMD ["runserver"]

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kubernetes(Docker)におけるゼロダウンタイムデプロイメント

はじめに

新しいバージョンのアプリケーションをデプロイする時に、アプリケーションをコンテナで運用している場合は、コンテナを作り替える必要がある。デプロイ時に適切な手順を踏まないと、リクエストを正しく捌けずに、クライアントにエラーを返すことになる。これは本番環境で動いているアプリケーションに取ってはクリティカルな問題である。
Kubernetesは RollingUpdate デフォルトで対応しているため、基本的にはKubebernetesに任せておけばうまく行く。しかし、古いPodを停止する時に、正しく設定を行わないと、まだアプリケーションがリクエストを処理中にもかかわらず、Podを停止してしまう。今回はこの問題を解決するために考察したことをまとめた。

KubernetesのPodを停止するまでの挙動

Kubernetesの挙動は以下のようになっている。

スクリーンショット 2020-07-08 午後4.17.28.png

Kubernetesでは「 preStop 処理 + SIGTERM 処理」と、「ServiceからのPodの除外処理」が非同期で行われる。コンテナは SIGTERM のシグナルが送られた場合に、適切に処理中のリクエストを捌き切ってから終了するようにする必要がある。そうでなければ、 SIGKILL シグナルがコンテナに送られて強制終了してしまう。したがって、適切な terminationGracePeriodSeconds を設定する必要がる。
また、 Service は、該当のコンテナに新しいリクエストを送らないように切り離すが、コンテナが処理を行っている途中である場合は、その接続を切断しないようにしなければいけない。

Service (LoadBalancer)

LoadBalancerがコンテナに新しいリクエストを送らないようにするとともに、現在行っている処理の接続を切断しないようにするために、Connection Drainingの設定をする必要がある。

例えば、EKSでは、 Connection Draining の有効化や、タイムアウトの設定を以下のように設定する。

manifest
apiVersion: v1
kind: Service
metadata:
    name: test
    annotations:
        service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "10"
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "10"
spec:
...

アプリケーションのGraceful Shutdown

コンテナが安全に終了するためには、それぞれのコンテナがGraceful Shutdownをサポートしている必要がある。

例えば、 Gunicorn はGraceful Shutdownを対応している。適切に graceful_timeout の値を設定すれば問題ない。

https://docs.gunicorn.org/en/stable/settings.html

また、アプリケーションコンテナの前に Nginx のようなリバースプロキシを立てるのはよくある構成だが、その場合、 Nginx も同様に対応していないと、 Nginx が先に終了し、クライアントに 504(Gateway Timeout)が返ってしまう。
Nginx 自体はGraceful Shutdownに対応しているが、Graceful Shutdownを行うためのシグナルが、 SIGTERM ではなく、 SIGQUIT である。

TERM, INT   fast shutdown
QUIT    graceful shutdown

http://nginx.org/en/docs/control.html

そこで、終了時のシグナルを SIGTERM から SIGQUIT に変更してやる必要がある。

Dockerfile
FROM nginx:<version>

...

STOPSIGNAL SIGQUIT

https://github.com/nginxinc/docker-nginx/issues/377

また、タイムアウトは worker_shutdown_timeout の値を設定する。

http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout

コンテナ実行時の複数コマンド実行

SIGTERM シグナルはコンテナ内の PID 1 のプロセスに送られる。コンテナ起動時に複数コマンドを実行したい場合は、Startup Scriptを使うことはあるが、通常のスクリプトを用いると、そのスクリプトが PID 1 を持つことになり、アプリケーションにシグナルが送られない。
そこで、 exec を用いて、スクリプトのプロセスをアプリケーションのプロセスに変更することで対処できる。

Dockerfile
...

ENTRYPOINT ["./startup.sh"]
CMD ["runserver"]
startup.sh
#!/bin/sh
set -e

# コマンド

exec "$@"

PID 1問題

LinuxにおけるPID 1は通常 init である。 init は全てのプロセスの親であるため、このプロセスが殺されると、システムがダウンしてしまうため、特別扱いされている。具体的には、明示的にハンドラを設定していないシグナルは無視される。

NAME
        kill - send signal to a process

NOTES
       The only signals that can be sent to process ID 1, the init process,
       are those for which init has explicitly installed signal handlers.
       This is done to assure the system is not brought down accidentally.

https://man7.org/linux/man-pages/man2/kill.2.html

したがって、アプリケーションサーバが明示的に SIGTERM のハンドラを設定していない場合は、プロセスが SIGTERM を無視してしまい、終了処理が適切に行われない。 Node.js でこれが起きるらしい。

この問題の回避方法は「明示的にシグナルをハンドリングする」または、「アプリケーションのプロセスをPID 1以外で立ち上げる」の2つが考えられる。前者はそれぞれで対応すれば良い。前者が難しい場合は、後者を使う必要がある。

Kubernetes 1.17以上の場合は、ShareProcessNamespace を使ってPID 1を回避できる。

manifest
apiVersion: v1
kind: Pod
metadata:
    name: test
spec:
    shareProcessNamespace: true
...

Kubernetes以外、またはKubernetesのバージョンが低い場合は、 tiniのような軽量initと呼ばれるライブラリを使えば解決できる。

Dockerfile
...

ENTRYPOINT ["/tini", "--"]
CMD ["runserver"]

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linuxでいろいろ調査

はじめに

調査の目的はいろいろですが、調査に利用した内容の備忘録です。

コマンドを使ってみる

環境変数を使ってみる

  • LD_DEBUG

解析してみる

GNU GLOBAL

タグ作成

gtags -v

HTML化

htags -sanF

OpenGrok

Comparison with Similar Tools

gcc

  • --export-dynamic
  • --version-script
  • --start-group
  • --end-group
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コマンド備忘録

Linux

curl

サーバから、もしくはサーバへデータ転送を行うコマンド.
curlを使ってPOSTやDELETEを行ったり、cookieからSessionを読み込むことができる.

curl <option> <url>

参考
curl コマンド 使い方メモ
curlコマンドでapiを叩く

touch

ファイルの最終更新日を変更するコマンド.
「touch ファイル名」で実行した時間がファイルの最終更新日になる.

touch <option> <file>

tree

今いるディレクトリ以下のディレクトリ構造を表示するコマンド

tree

pwd

今いるディレクトリまでのPathを表示するコマンド

pwd

windows

windowsのコマンド

dir

カレントディレクトリの中身を参照する

dir

call

バッチファイルからバッチファイルを起動するコマンド.

※バッチファイルとは
→バッチ処理を行うファイル(コマンドをまとめて一括処理してくれるファイル).

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「WebShellQuickScanner」Linuxサーバの指定フォルダーのPHPファイル中に、WebShellと高リスクファイルを速いて探し出すのクイックチェックツール,そして、検査結果をTelegramにプッシュする。

「WebShellQuickScanner」とは

「WebShellQuickScanner」は、Linuxサーバの指定フォルダーのPHPファイル中に、WebShellと高リスクファイルを速いて探し出すのクイックチェックツール,そして、検査結果をTelegramにプッシュする。

「Telegram」はTelegram Messenger LLPが開発するインスタントメッセージシステムである。詳細はこちら(Telegram - ウィキペディア)をクリックしてください。

この記事は、日本語の使用ドキュメントとして発表されます。

「WebShellQuickScanner」プロジェクトのリポジトリ

Github: DeepSkyFire/WebShellQuickScanner

「WebShellQuickScanner」について

Linuxサーバの指定フォルダーのPHPファイル中に、WebShellと高リスクファイルを速いて探し出すのクイックチェックツール,そして、検査結果をTelegramにプッシュする。

しかし、検出されたファイルは削除されません。だから、これらのファイルをどうするかはあなた自分自身を決断するです。

「WebShellQuickScanner」にクイックスタート

  • 「WebShellQuickScanner」のダウンロード

    wget --no-check-certificate https://raw.githubusercontent.com/DeepSkyFire/WebShellQuickScanner/master/src/WebShellScanner.sh && chmod +x WebShellScanner.sh
    
  • クイックチェックをスタート

    bash WebShellScanner.sh -p /data/www-data(ターゲットフォルダーを指定する)
    

「WebShellQuickScanner」の使用方法の詳細

  • 必要の依存関係

もし、検査結果をTelegramにプッシュしての機能を利用するためには、OSにcURLをインストールするは必要があります。

  • 検査結果をTelegramにプッシュする

この機能を利用するためには、まず、Telegramで@BotFatherに新しいBOTを申し込みします。そして、Telegramで@userinfobotを使って、自分のアカウントのCHAT_IDを確認してください。

注意! 現在、Telegramでは、メッセージに送信できる最大文字数を制限するようになりました。最大文字数は4096文字。もし検査結果の文字数が4096文字を超えると送信失敗となります。その以後、私は新しいプッシュ方法を更新する予定です。*

  • ホスト名(Host Name)について

ホスト名を自分で指定することができます。ホスト名を指定しない場合、スクリプトは自動的にOSからデフォルトのホスト名を読み込みます。

  • ログファイルの保存について

ログファイルをサーバー中に保存したい場合は、以下の設定のような設定しましょう:

bash WebShellScanner.sh -p /data/www-data -l /home/wwwwlogs

注意! ログ保存フォルダーのアドレスの末尾に「/」記号を書かないでください。

  • フルデモ

もし、あなたは「/data/www-data」フォルダーを検査し、ログファイルを「/home/wwwlogs」フォルダーに保存し、そして、「MyServer1」のホスト名を指定した、検査結果をTelegramにプッシュするの場合は、以下の設定にような設定しましょう:

    bash WebShellScanner.sh -p /data/www-data -t TELEGRAM_BOT_TOKEN -c TELEGRAM_CHAT_ID -n MyServer1 -l /home/wwwwlogs
  • ヘルプメッセージを表示する

    bash WebShellScanner.sh -h
    
  • 定期点検

Linux OSからcrontab -eを使って、スキャンタスクを設定します。

例:

    15 4 * * * "/root"/WebShellScanner.sh -p "/data/www-data" -t "TELEGRAM_BOT_TOKEN" -c "TELEGRAM_CHAT_ID" -n "MyServer1" -l "/home/wwwlogs" > /dev/null

上記の例では、1日1回、午前4時15分にスキャンタスクが実行される。

パラメータ(変数)の説明

WebShellScanner.sh [-h] [-p <ターゲットフォルダー>] [-t <TELEGRAM BOT TOKEN>] [-c <TELEGRAM CHAT ID>] [-n <ホスト名>] [-l <ログファイルの保存フォルダー>]

使用可能なパラメータ:

-h ヘルプメッセージを表示する. オプションパラメータ(変数).

-p <ターゲットフォルダー> 検査したいなフォルダー.

-t <TELEGRAM BOT TOKEN> Telegram Bot Token. オプションパラメータ(変数).

-c <TELEGRAM CHAT ID> Telegram Chat id. オプションパラメータ(変数).

-n <HostName> カスタムホスト名. オプションパラメータ(変数).

-l <ログファイルの保存フォルダー> ログファイルの保存フォルダー(ログ保存フォルダーのアドレスの末尾に「/」記号を書かないでください). オプションパラメータ(変数).

オープンソースライセンス

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OVSで実現するERSPANデスティネーションの構築(冗長化もあるよ)/2.6MB GIF動画アリ

概要

本ドキュメントは、公式ドキュメント上の実装例と冗長化のアイデアについて実現性を検証した結果を備忘録的に残したものとなります。

ERSPAN
+---------------+                               +--------------------+
| ERSPAN SOURCE +--------[L3/IP Network]------->+ ERSPAN DESTINATION |
+---------------+                               +--------------------+

某社のスイッチにてERSPANというパケットミラーリングのプロトコルを使用することとなった。ERPSANではミラーしたパケットをトンネリングプロトコルで包み、ネットワークを超えて転送する。

このときERSPANを終端しキャプチャ装置にパケットを転送する仕組みが何らか必要となる。

パケットキャプチャソリューションの一部や、一部のルータ等においては、ERSPANを終端する機能を有するが、いずれも高価である。

そこでOSS仮想スイッチである、Open vSwitchを用いてERPSANを終端するための実装を検討した。また実装に当たっては商用での稼働も検討し、冗長化についての検討も行った。

数秒単位での冗長化切替を実現できる、Open vSwitchでの実装が可能であることを確認した結果をここに記す。

はじめに

ERSPANとは

ERSPANはスイッチでキャプチャしたパケットをネットワークを超えて転送するためのCisco社が提唱・実装しているプロトコルである(Internet draftにもなっている1)。
ERSPANではスイッチでミラーしたパケット(Ethernetフレーム)をトンネリングプロトコルであるGREを用いてカプセリング化し、トンネルエンドポイントに指定されたリモートのIPアドレスに向けてミラーされたパケット(Ethernetフレーム)を運ぶ。

この時、GREヘッダと、ミラーされたErhternetフレームの間にERSPANヘッダを差し込む、結果プロトコルスタックとしては以下のようになる。

+---------------+
| Some IP       |
|      Playload |
+---------------+
| IP(Inner)     |
+---------------+
|Ethernet(Inner)|
+---------------+
| ERSPAN        |
+---------------+
| GRE           |
+---------------+
| IP(Outer)     |
+---------------+
|Ethernet(Outer)|
+---------------+
| Physical      |
+---------------+

ERSPANにはいくつかバージョンが存在するが、ここではその説明は割愛する。

ERSPANデスティネーションについて

上述の通り、ERSPANにおいてはGREによりパケットを転送する。 ERSPANのパケットはGREヘッダが付けられたIPパケットとして、ただ単純ににFIBテーブルに従い転送し、IPネットワークの中では通常のIPパケットとして処理される。

デスティネーションにおいてはパケットキャプチャ装置等に向けてこのGREおよびERSPANヘッダを取り除いたパケットを転送する。

今回このERSPANデスティネーションをOSSを用いて実装する。

Open vSwitchのERSPAN対応

Open vSwitch(以下OVS)では ver 2.10 以上 かつ Linux kernel 4.18以上で ERPSPAN プロトコルに対応している2。そのため、今回はOVSを用いた実装について検討する。

環境について

実際の設定例に先立ち、検証に使用したハードウェアやソフトウェアの情報と最終的にFixした環境を紹介する。

ハードウェア/VMおよび使用したソフトウェアの情報

OVSを用いる部分については2020年現在、OSのディストリビューションに付属しているOVSが2.13.0と比較的新しいリリースのUbuntu 20.04を用いている。

  • Hypervisor(以後ホストOVSと呼称)

    • CPU: AMD Ryzen 5 3600 (6コア 12スレッド)
    • Memory: 16GB
    • OS: Ubuntu 20.04 LTS
      • Linux kernel 5.4.0
    • OVS: 2.13.0 (DPDK datapath)
      • 別の検証目的でDPDKのOVSをテストしていた環境のためホストOVSはDPDK版を使用
  • OVS VM(2 VMs/ 以後ゲストOVSと呼称)

    • CPU: 2 Cores
    • Memory: 1 GB
    • OS: Ubuntu 20.04 LTS
      • Linux kernel 5.4.0
    • OVS: 2.13.0
    • keepalaived: 2.0.19
  • Traffic VM(2 VMs)

    • CPU: 2 Cores
    • Memory: 1 GB

VM配置およびそれぞれのVMの役割

少々いびつな構成ではあるが、ハイパーバイザにKVMを用いて構築した仮想マシン上のゲストOVSと物理マシンにインストールされたホストOVSを組み合わせて検証環境を構築した。

ハイパーバイザにインストールされたホストOVSがキャプチャ元スイッチ(ERSPANソース)を疑似し、VM上に構築されたゲストOVSがERSPANデスティネーションを疑似する。

Traffic VMはホストOVSによって接続されている。このTraffic VM間でPingによる通信を発生させ、その通信をホストOVS側の設定においてミラーリングし、ゲストOVSにERSPANを用いて転送する。ゲストOVSではホストOVSから送られてきたERSPANのパケットを解き、ゲストOVS内でパケットを所定のポートへミラーする。

ゲストOVSは冗長構成を検討するために2台用意し、アクティブ・スタンバイ構成でのHA構成を取る。

一つのハイパーバイザ上で構築
  (Guest OVS)       (Guest OVS)
+--------------+  +--------------+
|ERSPAN Dest 1 |  |ERSPAN Dest 2 |
+--------------+  +--------------+  +--------------+  +--------------+
| Open vSwitch |  | Open vSwitch |  | Traffic VM 1 |  | Traffic VM 2 |
+--------------+  +--------------+  +--------------+  +--------------+
| Ubuntu 20.04 |  | Ubuntu 20.04 |  | Linux        |  | Linux        |
+--------------+  +--------------+  +--------------+  +--------------+
|Guest Machine |  |Guest Machine |  |Guest Machine |  |Guest Machine |
+-------+------+  +-------+------+  +-------+------+  +-------+------+
        |(vhost user)     |(vhost user)     |(vhost user)     |(vhost user)
+-------+-----------------+-----------------+-----------------+------+
| KVM / Open vSwitch 2.13.0 with DPDK datapath                       |  (Host OVS)
+--------------------------------------------------------------------+
| Ubuntu 20.04 LTS / Kernel 5.4.0                                    |
+--------------------------------------------------------------------+
| Host Machine AMD Ryzen 5 3600 / 16GB RAM                           |
+--------------------------------------------------------------------+

ホストOVS上のネットワーク構成

ホストOVSとゲストOVSの、それぞれの間はL2で同一セグメントで接続する。

また、Traffic VM間でホストOVSを経由したPingを発生させ、ホストOVSを通過したPingパケットをERSPANでミラーリングし、ゲストOVSに転送する構成で試験を行う。

ERSPANデスティネーションとなるゲストOVSはVLAN 5に所属し、キャプチャ対象となるトラフィックを生成するTraffic VMはVLAN 3に所属する。

ERSPANのトンネルポートの送信元IPアドレスを設定するためにVLAN 5にはInternal Portを生成し、IPアドレスを割り当てる。また、ERSPANのトンネルポート自体はホストOVSのVLAN 10に作成している(理由は後述)。

ERSPANデスティネーションとなるゲストOVS同士はVRRPによってVIPを共有する。
ホストOVSはこのVIPに対してのERSPANソースセッションを作成する。

ホストOVS内のブリッジ/VLAN構成について(最終形)
              VRRP                                             Ping
       |-----------------|                              |---------------->|
+--------------+  +--------------+               +--------------+  +--------------+
|ERSPAN Dest 1 |  |ERSPAN Dest 2 |               | Traffic VM 0 |  | Traffic VM 1 |
+------+-------+  +------+-------+               +------+-------+  +------+-------+
       | .2(VIP:.10)     | .3(VIP:.10)                  |                 |
+------+-----------------+--+----+  +----+----+  +------+-----------------+-------+
VLAN 5:192.168.50.0/24      |.1          |VLAN 10     V       VLAN 3:192.168.0.0/24
br-int             +--------+----+  +----+----+       |       br-int
                   |Internal Port|  |ERPSAN SP|<------+
                   +-------------+  +---------+  Mirroring Setting from VLAN 3 to ERSPAN SP
        [ERSPAN source session from .1 to .10]

SP: Source-port
DP: Destination-port

ゲストOVS上のネットワーク構成

ゲストOVS内では1つのブリッジを作成し、vNICが所属するVLAN 4と、ミラーされたパケットを流すためのポートが所属する VLAN 2 、 ERSPANデスティネーションに設定されている VLAN 10の三つを用意した。

このOVSのブリッジに対してERSPANデスティネーションセッションを作成し、ERSPANデスティネーショポートからモニターポート向けへのミラー設定を投入する3

ERSPANデスティネーションVM内のOVSのブリッジ/VLAN構成について(最終形)
                                      Mirroring setting from DP to MP
                                      +----------------+ 
[ERSPAN dest session from .10 to .1]  |                V
    +-------------+              +----+----+     +------------+
    |Internal Port|              |ERSPAN DP|     |Monitor Port|
    +------+------+              +----+----+     +-----+------+
           | .2(VIP:.10)              |                |
+--+-------+------+              +----+----+     +-----+------+
   |     VLAN 4:192.168.50.0/24  VLAN 10         VLAN 2
+--+--+  br0                     br0             br0
|vNIC |
+-----+


DP: Destination-port
MP: Monitor-port

設定例の記載にあたって

本ドキュメントはERSPANの設定についてのナレッジを残すことを目的としているため、OVSのインストールやKVMでのVMの作成、OVSとVMの接続などはアウトオブスコープとし、ここではホストOVSのブリッジの作成、ポートの作成、VMの作成、VLANの設定等は終わっている前提で進めます。
必要に応じて、公式ドキュメントや、各種チュートリアル等を参照してください。

ゲストOVS/ERSPANデスティネーションの設定

前提条件

ERSPANデスティネーションVM内のOVSのブリッジ/VLAN構成について(初期状態)
(Empty)
  • OVSのインストールは済んでいる
  • ERSPANパケットを受信するVMのvNICはens4としてOSに認識されている
  • ens4とは別にOSにリモートログインでき、かつインターネットアクセスできるvNICが付与されている(この記載例ではens3がそのvNICとなっており、linux bridgeからDHCPでIPアドレスを振られてます)
  • 設定についてはほぼ重複するため、二つあるゲストOVSのうち、片側のVMのみ記載しています。適宜読み替えてもう一方のVMにも設定ください。

ブリッジの追加とブリッジへのvNICの追加

# ovs-vsctl add-br br0 #ブリッジに追加
# ovs-vsctl add-port br0 ens4 tag=4 #ブリッジへの受信ポートの追加
# ovs-vsctl set port br0 tag=4 #内部ポートをVLAN 4所属に変更
# ip a add 192.168.50.2/24 dev br0 #VMによりIPアドレスを変更
# ip link set up br0
# ip link set up ens4

またbr0へ付与したIPアドレスの永続化と、ens4の有効化のためにnetplanにより設定を永続化する。

/etc/netplan/60-erspan-dest.yaml
network:
    ethernets:
        ens4:
            dhcp4: no
            dhcp6: no
        br0:
            dhcp4: no
            dhcp6: no
            addresses:
            - 192.168.50.2/24 #VMによりIPアドレスを変更
    version: 2

keepalivedによるVRRPの形成

VRRPによる冗長化を組むためにkeepalivedをインストールし、設定する。

# apt-get install keepalived 

ホストOVSとL2接続している関係からか、ホストOVS側でのトンネルエンドポイントに対するARPテーブルがGarpで消えない事象が発生したため、vMAC方式を使用している。L3接続の場合は不要だと思いますが、どこかでARPテーブルをほじられるのことを考えるとvMACにしておいたほうが安全かもしれません。

/etc/keepalived/keepalived.conf
global_defs {
    router_id 192.168.50.2 #VMによりIPアドレスを変更
    vrrp_skip_check_adv_addr
    vrrp_garp_interval 1
    vrrp_garp_master_refresh  10
    vrrp_gna_interval 10
}

vrrp_instance VI_1 {
    state BACKUP
    accept
    interface br0
    virtual_router_id 51
    use_vmac vrrp51
    advert_int 1
    priority 100
    virtual_ipaddress {
        192.168.50.10/24
    }
}

設定を反映。

# systemctl resatrt keepalived

2VMとも設定し、うまく行けばVRRPが組めているので確認する。

VRRPでActiveとなているVMでのipコマンドでの確認結果
$ ip a show dev vrrp51
12: vrrp51@br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:00:5e:00:01:33 brd ff:ff:ff:ff:ff:ff
    inet 192.168.50.10/24 scope global vrrp51
       valid_lft forever preferred_lft forever
VRRPでStandbyとなているVMでのipコマンドでの確認結果
$ ip a show dev vrrp51
12: vrrp51@br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:00:5e:00:01:33 brd ff:ff:ff:ff:ff:ff

ERSPANデスティネーションの設定

ERSPANのデスティネーションセッションの設定を入れます。

# ovs-vsctl add-port br0 erspan_dp tag=10 -- \
            set Interface erspan_dp type=erspan \
                options:key=1 options:remote_ip=192.168.50.1 \
                options:erspan_ver=2 options:erspan_dir=1 \
                options:erspan_hwid=4  

ここで作成したトンネルポートをVLANタグを4でも2でもなく、10としているのはerspan_dpが所属するVLANを隔離するためとなる。
VLANを隔離しない(たとえばVLAN 4を設定した)場合、キャプチャされたパケットがERSPANポートから飛び出し、ens4などを経由して外部に流出してしまう。
さらに、;逆にVLAN 4内で流通するブロードキャストやマルチキャストパケット(たとえば、ARPリクエストやDHCPリクエストや、今回内部で使用しているVRRPのアドバタイズなど)がERSPANポートに流入する。その結果今回のERSPAN送信元である、ホストOVS向けにそれらのパケットが流出してしまう。

そのため、ここではVLANを隔離する目的でERSPANポートである erspan_dpをそのほかのVLANとは違うVLANに設定している。

もし他に良い手があればご教授ください…。

ERSPANセッションのミラールールの設定

ERSPANデスティネーションセッションでカプセル化を解かれたパケットをモニターポートへ吐き出す設定を追加する。

# ovs-vsctl add-port br0 mport tag=2 -- set Interface mport type=internal # モニターポートを追加
# ovs-vsctl --\
           --id=@srcp get port erspan_dp --\
           --id=@dstp get port mport --\
           --id=@m create mirror name=m0 output-port=@dstp select-src-port=@srcp --\
           set bridge br0 mirrors=@m #トンネルポートからモニターポートへのミラー設定を追加
# ip link set up mport

必要に応じてモニターポートの永続化設定をnetplanに投入しておく。

/etc/netplan/60-erspan-dest.yaml
--- a/60-erspan-dest.yaml 2020-07-06 17:03:20.839471511 +0000
+++ b/60-erspan-dest.yaml 2020-07-06 17:02:03.370304770 +0000
@@ -8,5 +8,8 @@
             dhcp6: no
             addresses:
             - 192.168.50.2/24
+        mport:
+            dhcp4: no
+            dhcp6: no
     version: 2

以上で、デスティネーション側の設定は終了です。

ホストOVS/ERSPANソースの設定

前提条件

ホストOVS内のブリッジ/VLAN構成について(初期状態)
+--------------+  +--------------+             +--------------+  +--------------+
|ERSPAN Dest 1 |  |ERSPAN Dest 2 |             | Traffic VM 1 |  | Traffic VM 2 |
+------+-------+  +------+-------+             +------+-------+  +------+-------+
       |                 |                            |                 |
+------+-----------------+-------+             +------+-----------------+-------+
VLAN 5                                         VLAN 3
br-int                                         br-int
  • ホストOVSはインストール済み
  • ゲストVM類はOVSに接続済み
  • OVSのブリッジが作成済みでかつ各VM向けポートにはTag VLANの設定が投入済み

この状態からERSPANに必要な設定を投入する。

ゲストOVSとホストOVS間のVLAN 5でのネットワーク導通の確保

まず、VLAN 5のブリッジに対してERSPANのソースIPアドレスとなるためのIPアドレスを付与する。
ソースIPを付与するポートの名前はvlan5とし、Internal Portで作成し、IPアドレスの付与、リンクアップを実施する。

# ovs-vsctl add-port br-int vlan5 tag=5 -- set Interface vlan5 type=internal
# ip a add 192.168.50.1/24 dev vlan5
# ip link set up vlan5

この状態で問題なく疎通できていればホスト側からVIPに対してPingが飛ぶはずです。

$ ping 192.168.50.10 -c 3
PING 192.168.50.10 (192.168.50.10) 56(84) バイトのデータ
64 バイト応答 送信元 192.168.50.10: icmp_seq=1 ttl=64 時間=1.55ミリ秒
64 バイト応答 送信元 192.168.50.10: icmp_seq=2 ttl=64 時間=0.196ミリ秒
64 バイト応答 送信元 192.168.50.10: icmp_seq=3 ttl=64 時間=0.215ミリ秒

--- 192.168.50.10 ping 統計 ---
送信パケット数 3, 受信パケット数 3, パケット損失 0%, 時間 2009ミリ秒
rtt 最小/平均/最大/mdev = 0.196/0.654/1.553/0.635ミリ秒
$ ip nei show 192.168.50.10
192.168.50.10 dev vlan5 lladdr 00:00:5e:00:01:33 DELAY

netplanで上記設定したVLAN 5へのIPアドレスを永続化する。

/etc/netplan/60-network-for-ovs.yam
network:
  ethernets:
    vlan5:
      dhcp4: no
      dhcp6: no
      addresses:
      - 192.168.50.1/24
  version: 2

ERSPANソースセッションの作成

次に、ERSPANソースセッションを作成する。

# ovs-vsctl add-port br-int erspan_sp tag=10 -- \
            set In-terface erspan_sp type=erspan \
            options:key=1 options:remote_ip=192.168.50.10 \
            options:erspan_ver=2 options:erspan_dir=1 \
            options:erspan_hwid=4

ミラリング設定を追加

最後にVLAN 3からerspan_spに向けたキャプチャの設定を追加する4

ovs-vsctl --\
          --id=@p get port erspan_sp --\
          --id=@m create mirror name=m0 select-all=true select-vlan=3 output-port=@p --\
          set bridge br-int mirrors=@m

これでキャプチャの設定が完了し、VLAN 3上のトラフィックがすべてERSPANを通じてデスティネーションに届けられているはずです。

試験

実際にゲストOVSにて、ホストOVSで流れるパケットがキャプチャできていることおよび、KVMでゲストOVSのVMを強制シャットダウンし、ゲストOVSでキャプチャセッションが切り替わっていることを確認する。

動画

今回無駄にGIF動画で実際にキャプチャができている様子、切り替わる様子をまとめた。

  • 1番目と2番目の窓が、ERSPANデスティネーションVM1台目、2台目それぞれのモニターポートでtcpdumpの実行結果
  • 3番目の窓ががLAN 3上でPingを打っているVMのpingの実行結果
  • 一番下がホストOVSでの各種操作(状態確認、VMの停止等)

初期状態おいては上から2つ目の窓である、ゲストOVS2においてキャプチャされている。
この上阿智でゲストOVS2で障害を擬似的に発生させ、ゲストOVS1にスイッチオーバされることを確認する。

試験状況(2.6MB GIFアニメ)

MAC学習の確認

ホストOVSでのMAC学習の状況を確認。現在のVIPはポート6の先にいることがわかる。

キャプチャされたパケットの確認

上から3つ目の窓がVLAN 3上でPingを打っているサーバであり、こちらに出ているシーケンス番号と上段2番目の窓のActive系ゲストOVSのERSPANデスティネーションサーバでキャプチャされた結果を比較している。

障害の疑似・デスティネーションサーバの障害

現在Active系であるになっているゲストOVSのVMをvirsh destroyで殺し、最上段のStandby系ゲストOVSにERSPANのパケットが吸いこまれ、キャプチャを継続できていることが確認できる。

パケットドロップはPingのカウントにして4パケット、約4~5秒程度の切り替わり時間となりました

最後にMAC学習の確認

ホストOVSでのMAC学習の状況を確認し、VIPはポート5の先に移動したことがわかる。

まとめ

OVSを用いることでERSPANのデスティネーションセッションを構築でき、パケットキャプチャを実現できることがわかった。また、Keepalivedと組み合わせることで、ERSPANセションを冗長化することも確認できた。

今回の構成においては負荷試験は実施していませんので、どれぐらいのHWスペックでどれぐらいの処理性能を出すことができるかは今後の課題となる。

以上


  1. https://tools.ietf.org/html/draft-foschiano-erspan-03 

  2. http://docs.openvswitch.org/en/latest/faq/releases/  

  3. 実環境においてはこの モニターポートが物理NICになる、もしくはこの先でキャプチャプログラムがリッスンする形となる。 

  4. このキャプチャ設定はVLAN単位でキャプチャする場合の例です。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む