2016年04月20日

ラズパイで作るネットワークエミュレータ(後編)

はてなブックマークに登録

前編で紹介したネットワークエミュレータ「PackDrop」のプロトタイプですが、雑に作ったわりに評判が良く予想外に需要が出てきてしまったので、ちゃんと業務での利用に耐えられる「量産型」を製作しました。

前回の記事がややウケで気を良くしています。@pandax381です。

機能追加

プロトタイプを作成してから暫くのあいだ試験運用してみたのですが、実際に使った方々からいつくか要望を受けました。

  • パケットをバーストロスさせるためのスイッチが欲しい
    一定時間、連続的にパケットが消失するバーストロスを再現するときに「パケロスのボリュームを100%まで回してまた戻す」という作業が面倒なので、専用のスイッチを設けて欲しい。

  • 稼働状況がわかるようにLEDをつけて欲しい
    試験用のパラメータを設定したまま元に戻すのを忘れてしまい「あれ、なんか今日ネット遅くね?」という事案が多発したので、遅延・パケロスが設定されている場合には一目でわかるようにLEDで警告して欲しい。

  • 特定のVLANだけを対象にしたい
    タグVLANを利用している場合に、特定のVLAN-IDのパケットだけをネットワークエミュレーションの対象にし、それ以外のパケットは素通しさせたい。

作り始めた時にはそこまで考えが及んでいなかったので、やっぱり他の人に触ってもらうのって大事ですね。どれもそんなに手間を掛けずに対応できそうなので、量産型の製作に先立って要望された機能を追加してみます。

工作パート

追加工作で必要なパーツは以下の通りです。例によってどれも秋月で調達できるものですが、スライドスイッチはマルツの方が種類が豊富だったのでそちらで購入しました。また、プロトタイプでラズパイ本体にマウントしたブレッドボードにはもうスペースがないので、量産型の製作までは同じブレッドボードをもう1つ後付けしてしのぐことにします。

000

相変わらず回路図が書けないクソザコなので配線図的なものを載せておきます。こういった図がちゃちゃっと作れてしまう「fritzing」めっちゃ便利ですね。LEDとスライドスイッチを、それぞれGPIOの17番と22番に接続します。LEDには330Ωの抵抗を噛ませてあります。スライドスイッチはオンの時にGPIOとGNDがショートするように配置します。

001

コード修正

制御プログラム packdrop.c のコードを修正します。まず、プログラムの冒頭でGPIOの初期化処理を追加します。wiringPiSetupGpio() を呼び出した後、pinMode() でGPIOピンの入出力モードを設定します。LEDが繋がるピンは出力(OUTPUT)に、スライドスイッチが繋がるピンは入力(INPUT)に設定します。加えて、スライドスイッチの繋がるピンは pullUpDnControl() でプルアップ(PUD_UP)に設定します。

ラズパイはプルアップ抵抗とプルダウン抵抗を内蔵していて、各 GPIO ピンに対してプルアップ/プルダウンをソフトウェア制御できます。今回のように入力ピンを扱う際には、そのピンをプルアップして使えば抵抗を減らせて回路がシンプルになります(と何処かに書いてあったのでそうしてみました)。

プルアップの設定をした入力ピンは、何もつながっていなければ「1(HIGH)」になり、回路が繋がると(電流はそちらに流れるため)「0(LOW)」になります。今回のスライドスイッチの繋ぎ方だと「オン=0」「オフ=1」になりますが、とっても紛らわしいので「SW_ON」として値を定義して使うようにしています。

メインループの中で、digitalRead() を呼び出してスライドスイッチの状態を読み取り、オンであれば burst_mode() を呼び出します。burst_mode() でやっていることは、パケロスを100%に設定してLCDの表示を切り替えてLEDを点灯させているだけです。LEDを点灯させるには digitalWrite() で「1」を書き込めば良く「0」を書き込めば消灯します。その後は digitalRead() でスイッチの状態を読み続けて、オフの状態になったらメインループに戻って通常動作に復帰するようになっています。

通常動作での変更点は、遅延またはパケロスが設定されている場合にはLEDを点灯させるようにしただけです。

/*
 * packdrop.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
#include <wiringPiSPI.h>
#include <mcp3002.h>

#define APP_NAME     "PackDrop"
#define APP_VERSION  "v1.0"

#define SPI_CHANNEL  (0)
#define PINBASE      (100)
#define VOLUME_DELAY (PINBASE)
#define VOLUME_LOSS  (PINBASE+1)
#define SW_ON        (0)
#define PIN_LED      (17)
#define PIN_SW       (22)

char **devices;
int devnum;

static void
tc_set (const char *dev, int delay, int loss) {
    char cmd[128];

    snprintf(cmd, sizeof(cmd), "tc qdisc change dev %s root netem delay %dms loss %d%%", dev, delay, loss);
    system(cmd);
}

static void
tc_init (const char *dev) {
    char cmd[128];

    snprintf(cmd, sizeof(cmd), "tc qdisc del dev %s root >/dev/null 2>&1", dev);
    system(cmd);
    snprintf(cmd, sizeof(cmd), "tc qdisc add dev %s root netem delay 0ms loss 0%%", dev);
    system(cmd);
}

static void
lcd_write_core (const char *upper, const char *lower) {
    char cmd[128];

    system("i2cset -y 1 0x3e 0 0x80 b");
    snprintf(cmd, sizeof(cmd), "i2cset -y 1 0x3e 0x40 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x i",
        upper[0], upper[1], upper[2], upper[3], upper[4], upper[5], upper[6], upper[7]);
    system(cmd);

    system("i2cset -y 1 0x3e 0 0xc0 b");
    snprintf(cmd, sizeof(cmd), "i2cset -y 1 0x3e 0x40 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x i",
        lower[0], lower[1], lower[2], lower[3], lower[4], lower[5], lower[6], lower[7]);
    system(cmd);
}

static void
lcd_write (int delay, int loss) {
    char upper[9], lower[9];

    snprintf(upper, sizeof(upper), "  %3d ms", delay);
    snprintf(lower, sizeof(lower), "  %3d %%", loss);
    lcd_write_core(upper, lower);
}

static void
lcd_init (void) {
    char name[9], ver[9];

    system("i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x78 0x5f 0x6a i");
    system("i2cset -y 1 0x3e 0 0x0c 0x01 i");
    system("i2cset -y 1 0x3e 0 0x06 i");
    snprintf(name, sizeof(name), "%8s", APP_NAME);
    snprintf(ver, sizeof(ver), "%8s", APP_VERSION);
    lcd_write_core(name, ver);
}

static void
burst_mode (void) {
    int n, v;

    for (n = 0; n < devnum; n++) {
        tc_set(devices[n], 0, 100);
    }
    lcd_write_core(" burst  ", "   mode ");
    digitalWrite(PIN_LED, 1);
    while (1) {
        v  = digitalRead(PIN_SW);
        if (v != SW_ON) {
            break;
        }
        sleep(1);
    }
}

int
main (int argc, char *argv[]){
    int n, v, b, d, l;
    int delay = 0, loss = 0;

    lcd_init();
    if (wiringPiSetupGpio() == -1) {
        printf("wiringPiSetupGpio: failed\n");
        return -1;
    }
    pinMode(PIN_LED, OUTPUT);
    pinMode(PIN_SW, INPUT);
    pullUpDnControl(PIN_SW, PUD_UP);
    if (mcp3002Setup(PINBASE, SPI_CHANNEL) < 0) {
        printf("mpc3002Setup: failed\n");
        return -1;
    }
    devices = argv + 1;
    devnum  = argc - 1;
    for (n = 0; n < devnum; n++) {
        tc_init(devices[n]);
    }
    while (1) {
        v = digitalRead(PIN_SW);
        b = (v == SW_ON) ? 1 : 0;
        if (b) {
            burst_mode();
        }
        v = analogRead(VOLUME_DELAY);
        d = ++v / 2;
        v = analogRead(VOLUME_LOSS);
        l = ((float)++v / 1024) * 100;
        if (d != delay || l != loss || b) {
            delay = d;
            loss  = l;
            for (n = 0; n < devnum; n++) {
                tc_set(devices[n], delay, loss);
            }
            lcd_write(delay, loss);
            digitalWrite(PIN_LED, (delay || loss) ? 1 : 0);
        }
        sleep(1);
    }
    return 0;
}

以前と同じように -lwiringPi オプションをつけてコンパイルし、バイナリを生成しておきます。

# gcc -W -Wall -o packdrop packdrop.c -lwiringPi

自動起動のための設定

ethtool が必要になるので、事前に apt でインストールしておきます。

# apt-get install ethtool

毎回、手動でブリッジを作成しなくて済むように /etc/network/interfaces ファイルにブリッジの記述します。念のため、NICのオフロード機能やブリッジのマルチキャストスヌーピング機能を無効にする設定も記述しています。

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto eth1
iface eth1 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto br0
iface br0 inet static
    address 0.0.0.0
    bridge_ports eth0 eth1
    bridge_maxwait 0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null
    up echo 0 > /sys/devices/virtual/net/$IFACE/bridge/multicast_snooping

PackDropにはIPアドレスは割り当てないので、dhcpクライアントが自動で起動しないようにします。

# systemctl disable dhcpcd.service
# systemctl disable avahi-daemon.service

続いて、/etc/rc.local に packdrop コマンドを自動起動するための処理を記述します。少し手間を掛けて、sysfs経由でブリッジに登録されているインタフェースの一覧を取得し、packdrop コマンドの引数として渡すようにしています。これにより、interfaces ファイル側でブリッジに登録するインタフェースを変更した場合に、その変更が自動的に反映されるようになります。また、ブリッジそのものやインタフェースが見つからない場合には、LCDにエラーメッセージを表示するようにしています。

#!/bin/bash -e

packdrop=/root/bin/packdrop
br=br0

function getbrif() {
    echo `ls -A "/sys/devices/virtual/net/$1/brif/" 2> /dev/null`
}

ifaces=`getbrif $br`
if [ -n "$ifaces" ]; then
    $packdrop $ifaces >/dev/null 2>&1 &
else
    i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x78 0x5f 0x6a i
    i2cset -y 1 0x3e 0 0x0c 0x01 i
    i2cset -y 1 0x3e 0 0x06 i
    i2cset -y 1 0x3e 0 0x80 b
    i2cset -y 1 0x3e 0x40 0x45 0x72 0x72 0x6f 0x72 0x20 0x20 0x20 i
    i2cset -y 1 0x3e 0 0xc0 b
    i2cset -y 1 0x3e 0x40 0x20 0x45 0x42 0x52 0x4e 0x46 0x4e 0x44 i
fi

exit 0

動作確認

自動起動の設定を済ませたら、PackDropを再起動させて動作確認をします。

まず、ボリュームの値がゼロで、スライドスイッチもオフの状態です。この状態では遅延もパケロスも発生しないのでLEDは点灯していません。

002

遅延またはパケロスのボリュームを回すとLEDが点灯し、両方の値をゼロに戻せば消灯します。

003

スライドスイッチをオンにすると、LCDの表示が「Burst Mode」に変わりLEDも点灯します。このモードではボリュームの値に関係なく、全てのパケットが捨てられて一切の通信ができなくなります。スライドスイッチをオフにすると通常のモードへ戻り、ボリュームの値に従って動作します。

004

VLAN対応について

PackDropを通過するパケットの中から特定の VLAN ID のパケットのみを制御したい場合には、/etc/network/interfaces ファイルの内容を以下のように変更することで対応できます。ここでは、例として eth0 と eth1 に対して ID「100」の VLAN インタフェースを定義し、それを br0 に登録しています。ただ、この状態だと VLAN ID が 100 以外のパケットが破棄されてしまうので、新たなブリッジ「br1」を定義し、そこへ eth0 と eth1 を登録します。/etc/rc.local の処理で、packdrop には br0 に登録されているインタフェースのみが渡されるようにしたので、br1 に登録されているインタフェースは対象外、つまりパケットがそのまま素通しされることになります。

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto eth0.100
iface eth0.100 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto eth1
iface eth1 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto eth1.100
iface eth1.100 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto br0
iface br0 inet static
    address 0.0.0.0
    bridge_ports eth0.100 eth1.100
    bridge_maxwait 0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null
    up echo 0 > /sys/devices/virtual/net/$IFACE/bridge/multicast_snooping
    
auto br1
iface br1 inet static
    address 0.0.0.0
    bridge_ports eth0 eth1
    bridge_maxwait 0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null
    up echo 0 > /sys/devices/virtual/net/$IFACE/bridge/multicast_snooping

PackDropとVLANスイッチをうまく組み合わせることで、PackDropにイーサネットポートを増設しない構成も作れます。

例えば、以下のような設定をしたVLANスイッチの port1 にPackDropを接続して、port3 と port4 のあいだで遅延とパケロスを挿入する、といったことができます。

(余談ですが NETGEAR のこのVLANスイッチ、コンパクトで使いやすいし激安なので1つ持っておくといいですよ)

011

上記の構成で動作させる場合、interfaces ファイルの内容は以下のようになります。

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto eth0.100
iface eth0.100 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto eth0.200
iface eth0.200 inet static
    address 0.0.0.0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null

auto br0
iface br0 inet static
    address 0.0.0.0
    bridge_ports eth0.100 eth0.200
    bridge_maxwait 0
    up ethtool -K $IFACE tso off ufo off gso off gro off lro off 2>/dev/null
    up echo 0 > /sys/devices/virtual/net/$IFACE/bridge/multicast_snooping

量産型の製作

前置きが長くなってしまいましたが、ようやく量産型の製作に取り掛かります。プロトタイプの最大の欠点は、なんと言っても物理的にものすごく脆弱なことです。ブレッドボードにジャンプワイヤを生やしているだけなので、ちょっと指を引っ掛けただけでも断線の危険性がありますし、輸送したらバラバラになっているかもしれません...

ここでも同僚から「プリント基板設計しよう」「P版ドットコム」「中国に発注すれば安いよ」などと煽られましたが、ラズパイ本体にぴったりサイズのユニバーサル基板が各社から発売されているらしいので、それを使って量産型を作ることにしました。

部品一覧

今回はサンハヤト製のラズパイ用ユニバーサル基板を使うことにしました。こちら、表記は「B+用」となっていますがラズパイ2はB+と同じ形状なので全く問題なく使えます。あとは、プロトタイプで使っていたボリューム(可変抵抗)は、そのまま基板に実装できないタイプだったので、基板に実装できるタイプのものを改めて調達しました。

005

また、Eleduinoのケース付属のネジ(20mm)だと長さが足りなくてユニバーサル基板を固定できませんでした。このユニバーサル基板を固定するには少なくとも25mmのネジが必要です。通販で手ごろに入手できるところがなかったので、秋葉原にあるネジ専門店「西川電子部品」へ買いに行ってきました。付属ネジはM2.5ですが、M2.6で大丈夫です。

  • M2.6 25mm ネジ x 4
  • M2.6 ナット x 8
  • M2.6 ワッシャー x 12
006

製作

いままでブレッドボードで作っていたものをユニバーサル基板へ実装し直します。部品点数も少ないので、少し考えて配置すれば問題なく配線できると思います。配置と配線経路が決まったら、後はひたすら半田付けするだけです。

以下、僕の作例です。半田付けの初心者すぎてスズメッキ線の存在とか知らなくてブレッドボード用のジャンプワイヤだけで作ってあります。あと、このユニバーサル基板はスルーホールになっていて、パーツを間違って半田付けしてしまうと取り外すのにものすごく苦労します。何度も失敗してパーツを壊したり悲しい思いをしたので、それを教訓にしてパーツ部分にはICソケットとピンソケットを取り付けるようにしました。あと、左上に三本生えているピンヘッダはシリアル接続(UART)用です。

007

裏面はこんな感じです。電子工作に慣れている方ならもっとシンプルで見栄えのいいものが作れると思いますが「初心者が作るとこんな感じのものが出来上がるのか」と生温かい目で見てもらえると有難いです。。

008

ソケットにパーツを取り付けたら完成です。プロトタイプと比べてだいぶスッキリしましたね!

009

動作確認

量産型のユニバーサル基板をラズパイに取り付けて動作確認します。まず、プロトタイプに取り付けていたブレッドボードやボリュームを取り外して、ネジを25mmのものに交換します。ブレッドボードの両面テープは強力ですが、ゆっくりは剥がせばキレイに剥がれます。ユニバーサル基板を取り付ける前に、一旦ナットでケースを固定します。この際、スペーサーとしてワッシャーを3枚ずつ噛ませると丁度よくなります。

012

量産型のユニバーサル基板をピンヘッダに差し込みながら取り付けてナットで固定します。USBイーサネットアダプタは、プロトタイプの時と同じように側面に両面テープで固定しておきます。最後に電源を入れて、全ての機能が正常に動作することを確認します。

010

ファイルシステムの保護(電プチ対策)

こういったネットワーク機器は、シャットダウン処理などせず、なにも考えずに電源を切れたほうが使いやすいです(というより、そうでないと使いづらいです)。Raspbianは、素の状態だとSDカード上のファイルシステムを読み書き可能な状態でマウントしています。これで何が問題になるかというと、SDカード上のファイルシステムに常時書き込みが発生していると、SDカードの寿命を早めてしまう可能性があります。最近はどうかわかりませんが、一昔前だと何ヶ月かで壊れてしまうようなことが普通にありました。また、書き込み中に電源が落ちてファイルシステムを破壊してしまうことも考えられます。そういったことを防ぐために、SDカード上ファイルシステムを保護してあげる必要があります。ファイルシステムを保護するために fsprotect や overlayroot などのツールもありますが、ここでは initramfs と overlayfs を用いて自前でファイルシステムの保護を行います。

initramfs の作成

まず、busybox を使って initramfs の種を作成します。

# apt-get install busybox-static
# mkdir -p initramfs/{bin,dev,lib,proc,sys}
# /bin/busybox --install -s initramfs/bin
# cp /bin/busybox initramfs/bin

initramfs 内で必要になるカーネルモジュールをコピーします。最後のdepmodを実行し忘れるとカーネルがモジュールを認識できないので注意してください。

# mkdir -p initramfs/lib/modules/`uname -r`
# cp /lib/modules/`uname -r`/modules.{builtin,order} initramfs/lib/modules/`uname -r`
# mkdir -p initramfs/lib/modules/`uname -r`/kernel/fs/overlayfs
# cp -a /lib/modules/`uname -r`/kernel/fs/overlayfs/overlay.ko initramfs/lib/modules/`uname -r`/kernel/fs/overlayfs
# depmod -a --basedir initramfs

initramfs では /init スクリプトが自動実行されるので、initramfs ディレクトリ直下に、init という名前のシェルスクリプトを作成します。/dev/mmcblk0p2 を lower に、tmpfs を upper に指定して overlayfs をマウントしています。これだけで、SDカードのパーティションを Read Only でマウントしつつ、ルートファイルシステムは Read Write なシステムが構築できます。更にシステム起動後に発生したファイルシステム上の変更は電源断で消えてなくなるので、こういった装置にピッタリです。

また、コンフィグ投入の仕組みとして、FATパーティション(/dev/mmcblk0p1)に interfaces.txt という名前のファイルを置いておくと、それを /etc/network/interfaces にコピーしてから起動するようにしています。VLANの設定をしたくなった場合には、この仕組みを利用してPackDropの動作を変更できます。

#!/bin/sh

export PATH=/bin
export TZ=JST-9

mount -t devtmpfs devtmpfs /dev
mount -t proc proc /proc
mount -t sysfs sysfs /sys

echo "4 4 1 7" > /proc/sys/kernel/printk

modprobe overlay

mkdir /overlay /sysroot
mkdir /overlay/ro /overlay/rw
mount -t ext4 -o ro /dev/mmcblk0p2 /overlay/ro
mount -t tmpfs -o size=512M tmpfs /overlay/rw
mkdir /overlay/rw/root /overlay/rw/work
mount -t overlay -o lowerdir=/overlay/ro,upperdir=/overlay/rw/root,workdir=/overlay/rw/work overlay /sysroot
mount -t vfat -o ro /dev/mmcblk0p1 /sysroot/boot
mkdir /sysroot/overlay
mkdir /sysroot/overlay/ro /sysroot/overlay/rw
mount --move /overlay/ro /sysroot/overlay/ro
mount --move /overlay/rw /sysroot/overlay/rw

echo -n > /sysroot/etc/fstab
cp -f /sysroot/boot/interfaces.txt /sysroot/etc/network/interfaces 2>/dev/null

umount /sys
umount /proc
umount /dev

exec switch_root -c /dev/console /sysroot /sbin/init

作成したスクリプトに実行権限を付与します。

# chmod +x initramfs/init

initrafms ディレクトリの中身を cpio でアーカイブしてイメージファイルを作成します。initramfs のイメージファイルは、ブートローダが読み込める場所に配置する必要があるので /boot に出力します。

# cd initramfs
# find . | cpio -o -H newc | gzip -c > /boot/initramfs.img

ラズパイ本体の設定

作成した initramfs をロードしてブートするために、/boot/config.txt に以下の行を追記します。

initramfs initramfs.img

スワップファイルも無効にしておきます。

# dphys-swapfile swapoff
# dphys-swapfile uninstall
# update-rc.d dphys-swapfile disable

動作確認

上記の設定が済んだ状態で再起動させると、ファイルシステムが保護された状態で起動してきます。SDカードのパーティションは Read Only でマウントされているため、動作中に突然電源を切られてしまっても大丈夫です。

起動時のログを載せておきますので、うまくいかない場合には照らし合わせてみてください。 https://gist.github.com/pandax381/06508bd5e226f5aefd4997bba093cfde

また、以前の状態でファイルシステムを保護しない状態に戻したくなった場合には、以下のようにして /boot/config.txt に記述した initramfs の行をコメントアウトして再起動するだけで元どおりになります。

# mount -o rw,remount /boot
# sed -ie "s/^initramfs/#initramfs/" /boot/config.txt
# reboot

おわりに

電子工作初心者がラズパイで遊んでみたというだけの記事になってしまったような気がしなくもないですが、楽しかったです。そして、ソフトだけじゃなくハードもある程度できると幅が広がるなぁと当たり前のことを改めて実感しました。とりあえず、半田付けは初心者から少しだけレベルアップしたので、引き続き電子工作を楽しみながらハードにも強くなれるよう励みます。

あと、実際に社内で稼働しているバージョンはもう少し進化していて、ファームウェアの更新をしやすくするために rootfs の中身を squashfs でイメージ化したものをFATパーティションに置いて、それを使うようにしていたりします。このあたりのネタも面白いので、また個別に記事を書こうと思います。

klab_gijutsu2 at 19:02│Comments(0)TrackBack(0)

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント
「最新トラックバック」は提供を終了しました。