CA MOBILE エンジニアブログ

株式会社シーエー・モバイルのエンジニアブログです

Rails gem を作ろう 1 - Rails プラグインを作る -

はじめまして、メディア事業部のフォンと申します。今回は Railsプラグインを作る方法を紹介致します。

サービスを開発するとき Rails だけで全部出来ますよね?なぜプラグインまで作らなければならないのでしょうか?

その答えはDRYです。

Don't Repeat Yourself(DRY)「同じことを繰り返さない」という開発パターンを実現するために Rails プラグインという仕組みが大きく役に立ちます。

例えば、大抵のシステムでユーザーテーブルを必要とすると思います。開発対象のサービスはたった一つだけなら問題無いですが、もしチーム内で2つ以上のサービスを開発するようになったら、そこでユーザーテーブルの実装の繰り返しが始まります。それを避けるために Railsプラグインシステムが実現されてあります。

今回は例として(つまらないですが)HelloJapanプラグインを作ります。 注意事項が一つあります。今回作るプラグインは普通の Ruby gem ではなく、Railsプラグインです。間違わないように気をつけてください。 じゃ…早速やってみましょう。

初期化コマンドは下記の通り。

rails plugin new HelloJapan

すると…

      create
      create  README.rdoc
      create  Rakefile
      create  hello_japan.gemspec
      create  MIT-LICENSE
      create  .gitignore
      create  Gemfile
      create  lib/hello_japan.rb
      create  lib/tasks/hello_japan_tasks.rake
      create  lib/hello_japan/version.rb
      create  test/test_helper.rb
      create  test/hello_japan_test.rb
      append  Rakefile
  vendor_app  test/dummy
         run  bundle install

が実行されbundle installの前まで準備してくれます。

では今回の一番重要なファイルを弄ってみましょう。 hello_japan.gemspecファイルですね。 中身をみると…

$:.push File.expand_path("../lib", __FILE__)

# Maintain your gem's version:
require "hello_japan/version"

# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
  s.name        = "hello_japan"
  s.version     = HelloJapan::VERSION
  s.authors     = ["Phone Phone"]
  s.email       = ["phonephone@example.com"]
  s.homepage    = "TODO"
  s.summary     = "TODO: Summary of HelloJapan."
  s.description = "TODO: Description of HelloJapan."
  s.license     = "MIT"

  s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
  s.test_files = Dir["test/**/*"]

  s.add_dependency "rails", "~> 4.2.3"

  s.add_development_dependency "sqlite3"
end

まず、TODOの部分を全部直して、Ruby バージョンを指定します。

#coding: utf-8
$:.push File.expand_path("../lib", __FILE__)

# Maintain your gem's version:
require "hello_japan/version"

# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
  s.name        = "hello_japan"
  s.version     = HelloJapan::VERSION
  s.authors     = ["Phone Phone"]
  s.email       = ["phonephone@example.com"]
  s.homepage    = "http://www.hello-japan.com"
  s.summary     = "Stringクラスにhello_japanヘルパーメソードを追加します。"
  s.description = "NilClassにもhello_japanを追加して文字列がnilになってしまった場合サービスが落ちないようにします。"
  s.license     = "MIT"

  s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
  s.test_files = Dir["test/**/*"]

  s.add_dependency "rails", "~> 4.2.3"
  s.required_ruby_version "~> 2.2.2"

  s.add_development_dependency "sqlite3"
end

あらかじめ test/hello_japan_test.rb の中に欲しい機能をお願いしておきます。

#coding: utf-8
require 'test_helper'

class HelloJapanTest < ActiveSupport::TestCase
  test "truth" do
    assert_kind_of Module, HelloJapan
  end

  test "String has hello_japan method" do
    assert_equal "Hello JapanはHello Japanになります", ":hj:はHello Japanになります".hello_japan
  end

  test "Nil has hello_japan method" do
    assert_nil nil.hello_japan
  end
end

それでrake testを実行してみると、もちろん失敗してしまいます。直しましょう。

vim lib/hello_japan/core_ext.rbで新しいファイルを開いて、下記のコードを書きます。

String.class_eval do
  def hello_japan
    gsub(/:hj:/,"Hello Japan")
  end
end

NilClass.class_eval do
  def hello_japan
    nil
  end
end

もう一度rake testをすると、まだエラーが発生します。 なぜなら、そのcore_ext.rbファイルが自動でrequireされてないからです。 lib/hello_japan.rbの中に

require "hello_japan/core_ext"

を入れて下さい。 今度はrake testが通りました。

# Running:

...

Finished in 0.003512s, 854.1497 runs/s, 854.1497 assertions/s.

念のため、コンソールで確かめてみましょう。 Rails プラグインはテストの為、ダミーアプリが作られますので、

cd test/dummy

rails console

に入ります。そして、コンソールで下記を打つと

> "Good morning. :hj:.".hello_japan

結果が

 => "Good morning. Hello Japan."

になります。 nilも問題無いです。

> nil.hello_japan
 => nil

今回はここで終了致します。

次の機会があったら、ActiveRecordを拡張する方法を紹介します。

幸せな一日を…

iOS 9 でアプリ内のローカライズがうまくいかなくなりました。

こんにちは。ono です。

当社では iOS 9 がリリースされたことで、以前 Xcode 6 で開発してリリースしていたアプリにおいてローカライズの言語を間違える事象が発生しました。
今回はその事象と対策についてまとめます。

起きていた事象

iPhone の言語設定が日本語のユーザなのにアプリ上では英語にローカライズされてしまう部分がある。

ローカライズを間違えたコード

既存のソースコードを確認してみると、この部分が誤作動していました

NSArray *languages = [NSLocale preferredLanguages];  
NSString *currentLanguage = [languages objectAtIndex:0];  
if ([currentLanguage isEqualToString:@"ja"] == YES) {  
  // 日本語  
}  
else{  
  // 日本語以外  
}  

ローカライズ間違いの原因

[NSLocale preferredLanguages] の返り値が iOS 9 では ja のみではなく、
ja-jp など地域設定が追加されたため、"ja" と一致で判定すると失敗するようです。

この事象iOS シミュレータの言語設定、地域設定を変更することでも再現できます。

対策

[currentLanguage hasPrefix:@"ja-"] の条件を追加することで jaの地域設定に対応することができました。

if ([currentLanguage isEqualToString:@"ja"] == YES || [currentLanguage hasPrefix:@"ja-"])

参考

iOS9で日本語のLocalizable.stringが適用されない - Qiita

次回は Xcode 6 で開発したアプリのソースコードXcode 7 に移行した時のお話をします。

【Rails5新機能紹介】Ruby Version Supportについて

こんにちは、シーエー・モバイルの新卒エンジニア Miaoです。

今回は、Rails5について話したいと思います。

Rails 5は2015年の秋に正式的公開する予定です。
が、まだ出てないですね。笑 来春あたりになりそうですね。

 

 Rails5の目玉

すでに色々な記事にも書いてありますが、下記はRails5の目玉です

  • Rails5 のRubyバージョンサポート
  • ActionCable
  • Turbolinks 3.0
  • Rails API
  • Others

上記の内容は一本ずつシリーズにして詳しく紹介していきたいと思います。
今回は「Rails5のRubyバージョンサポート」について詳しく紹介していきたいと思います。

 

 Ruby Version Support

Rails5はRuby2.2.1以上のバージョンしかサポートしません。

f:id:miaodi2002:20151027204716p:plain

 

Rails5の新規機能の前に、ruby 2.2の新機能・変更点をいくつかおさらいをしたいと思います。

 

Ruby2.2の新機能・変更点

・Kernel#itself

いわゆる恒等関数と呼ばれる、自身 (self) をそのまま返すメソッド Kernel#itself が追加されました。

Ruby では恒等関数の導入案は以前からありましたが、名前がなかなか決まらなかったという経緯があります。今回は itself という名前に落ち着いたようで、ようやく導入されました。

Kernel#itself は以下のような動きをします。

 

具体的な利用例ですが、以下のように配列の値自身で group_by したい場合に綺麗に書くことが出来ます。

 

Kernelモジュールに馴染みのない人の為に補足しておくと

Kernelモジュールは、グローバル関数(組み込み関数)として使えるメソッドを集めたものです。ObjectクラスはKernelをインクルードしているので、Kernelのメソッドはプログラムのどこからでもレシーバなしで利用できます。  (From AmiWiki)

 

・Binding

 Binding#local_variables
以前から定義済ローカル変数を一覧するKernel#local_variablesメソッドは存在していましたが、Binding#local_variablesはprivateでした。

それがpublicに利用可能となった変更です。

Binding#reciverは、そのbindingオブジェクトが呼び出されたもともとのレシーバ自身を返すメソッドです。(良く利用されるので新しいメソッドとして追加されました)。

Bindingをもっと知りたい方はこちらも参考にしてみてください:
http://qiita.com/lnznt/items/4c3e1050dafd59193808

 

・Enumerable

1. Enumerable#slice_after

Enumerableモジュールに配列処理用のメソッドslice_afterを追加しました。

特定の条件を満たす要素の直後でスライスするメソッドです。

配列処理をより便利になりましたね!

 

2. Enumerable#slice_when

同時にslice_whenメソッドも追加しました。

要素を前から順にブロックで評価し、その結果によって要素をグループ化した要素を持つEnumerator を返します。

1回sortしないと行けないかんじですが、Enumerable内での各要素出現数をカウントする時は使えそうです。

 

 以上

他にもかなり便利なメソッドが追加されていますので興味がある人はリファレンスを熟読されてはいかがでしょうか!

次回は「ActionCable」について紹介したいと思います。

お楽しみにしてください。

参考

1.Rails 5.0 needs ruby 2.2.1 or higher by jvanbaarsen · Pull Request #19257 · rails/rails · GitHub

2.» Ruby 2.2.0 preview1: ついに来ました!恒等関数 Kernel#itself などなど TECHSCORE BLOG

3. New Methods in Ruby 2.2

 

wrkでhttpd、nginx、H2Oを比べてみた

インフラシステムグループ deraxです。

今話題になってるH2Oについてhttpd、nginxとベンチマークにて比べてみました。

客観判断が出来るようチューニングは行わずインストールも出来るだけyumで実施しました。

●環境

CPU:Xeon X3363 @ 2.83GHz、 2Core
MEM:4GB
の仮想サーバ

●ベンチ条件

wrkでの比較で2スレッド、100接続、10秒にてベンチ。
3つともチューニングはしていません。

3つはポート別で起動するようにしましたが、
ベンチ中は一つしか動作させていません。

リクエストページは同一でテキストページ。

●感想

H2O > nginx > httpd の順番でH2Oが一番Transfer/secの数値が良かったです。 H2O早すぎる・・・

H2O、nginxの2つはtimeout が出てたのが気になります。
*単発でベンチを取得していますので数回実行すると結果は変わる・・・かもしれません。

httpdと比較して6倍程度速かったですので本番での稼動が実現したらメリット教授の期待感が持てます。

  • httpd 2.2.15@yumインストール:8080ポート
    Transfer/sec: 0.97MB

  • nginx 1.8.0@yumインストール:8081ポート
    Transfer/sec: 3.88MB

  • H2O 1.5.1@ソースインストール:8082ポート
    Transfer/sec: 5.69MB

今回H2Oはパッケージ利用しましたが、ソースからインストールする場合、
perlについてIPv6サポートしている最新のバージョンを利用しなくてはいけないので
若干手数が増えそうです。

また、ApacheやNginxはデフォルト設定ファイル(conf)があるのですが、
H2OはConfファイル自作する必要があります。

H2Oマニュアルページにサンプルあります。こんな感じです。

細かく設定出来そうですし、動作確認・検証を進めて また本ブログに記載したいと思います。

ベンチマーク詳細
  • httpd 2.2.15@yumインストール:8080ポート
/usr/local/wrk -t 2 -c 100 -d 10 http://[テストサーバIPアドレス]:8080/
Running 10s test @ http://[テストサーバIPアドレス]:8080/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 29.60ms 42.20ms 406.50ms 91.42%
Req/Sec 2.02k 1.30k 5.55k 63.39%
38175 requests in 10.11s, 9.83MB read
Requests/sec: 3776.17
Transfer/sec: 0.97MB
  • nginx 1.8.0@yumインストール:8081ポート
/usr/local/wrk -t 2 -c 100 -d 10 http://[テストサーバIPアドレス]:8081/
Running 10s test @ http://[テストサーバIPアドレス]:8081/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 78.98ms 149.99ms 1.97s 85.84%
Req/Sec 9.82k 6.50k 24.82k 67.23%
177276 requests in 10.33s, 40.06MB read
Socket errors: connect 0, read 0, write 0, timeout 43
Requests/sec: 17160.45
Transfer/sec: 3.88MB
  • H2O 1.5.1@ソースインストール:8082ポート
/usr/local/wrk -t 2 -c 100 -d 10 http://[テストサーバIPアドレス]:8082/
Running 10s test @ http://[テストサーバIPアドレス]:8082/
2 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 68.87ms 266.95ms 1.75s 93.92%
Req/Sec 16.18k 5.04k 19.14k 83.23%
250189 requests in 10.10s, 57.50MB read
Socket errors: connect 0, read 0, write 0, timeout 48
Requests/sec: 24775.05
Transfer/sec: 5.69MB

にわっちのインフラ奮闘記vol5-ログの可視化

本ブログでは初となります。

-10kg以上ダイエットに成功したインフラG にわっち(ヽ´ω`)です。

 

ログの可視化が話題になってからだいぶ立ちますが、
弊社のコミュニケーションサービスのアクセスログを可視化してみました。

f:id:niwattitti:20151015103617p:plain

環境とミドルウェアのバージョン

  • centOS :6.4
  • Elasticsearch :1.4.1
  • Kibana :3.1.2
  • logstash :1.4
  • fluentd(td-agent) :0.10.55

できること・特徴

  • 時系列ごとのアクセス数がわかる

ピークの時間帯が分かりやすい

f:id:niwattitti:20151014103434j:plain

  • レスポンスコードの比率がわかる

クリックすることで対象のもの(例えば404)のものだけリスト化することも可能です。

f:id:niwattitti:20151014105345j:plain

  • ログの詳細情報表示

 表示させたいフィールドにチェックを入れると表の項目として表示できる

f:id:niwattitti:20151015102746p:plain

  • n秒以上のスロークエリを出せる

response_time:>500,000,000ns(0.5s)

範囲指定も可 res_time:[200000 TO 400000]

f:id:niwattitti:20151014105637j:plain

  • geo情報について

logstashデフォルトでgeoipが取れる。
fluentdはデフォルトでgeoipがとれないのでプラグインを入れる必要あり
kibana3はIPアドレスかアクセス元を地図表示できますが、
日本地図が非対応なのは残念です。

f:id:niwattitti:20151014105710j:plain

Elasticsearchのプラグインを利用することで、
容易にクラスタ状況やnodeの把握ができる。

f:id:niwattitti:20151015103644p:plain

利用したプラグイン

  • elasticsearch-head

上記したとおり。

  • fluent-plugin-elasticsearch

fluentでElasticsearchを利用するときに使う。

  • fluent-plugin-geoip

IPアドレスから経度緯度を取得するために利用する。

  • fluent-plugin-record-reformer

タグの情報をレコードの内容に変換する事ができる。
host名をレコードに入れ込むのに使用する。

※他にも監視対象が増えた時にfluent-plugin-forestを使えば
タグを動的に扱う事ができるので設定ファイルの肥大化を抑える事ができる。

 

構築してみた感想・ログを可視化したメリット

  • 見たいときに見たい情報を素早く見みることができる。
  • レスポンスタイムが悪いURLが分かる。
  • HTTPのステータスコードと時間帯による割合の確認ができる。
  • 海外からのアクセスが可視化されるので不正アクセスが疑わしい国を判断できる。
  • 新規リリース・デプロイ時に障害ポイントが素早く分かる。
  • fluentdとlogstashを併用するときはformatの項目名を統一する必要あり。

 

おわりに

社内でこんなログみたい・あれしたい・これしたい・設定見せて・インストール手順教えて等の要望に応えつつ、複数のサービスログを可視化していきたいと思いますヽ(=´▽`=)ノ

Lsyncd でお手軽サーバ間ファイル同期

こんにちは。社内ではなんでもよろしく系エンジニアの ono です。
今回は Live Syncing Daemon ( Lsyncd ) を利用したお手軽なサーバ間のファイル同期について解説します。

どういうときに使う?

例えば外部から不定期に FTP で画像ファイルがアップロードされるサーバがあって、
そのファイルを開発サーバや web サーバなどと同期したい場合は、 lsyncd によってどのサーバにも同じファイルが生存している状態にすることができます。
今回は CentOS での利用方法を解説していきます。

プログラムのインストール

xinetd

yum install xinetd

lsyncd

もし rsync がなければインストール   yum install rsync

lsyncd インストール yum install libxml2-devel
yum install lsyncd

今回解説する lsyncd は version 2.1.5 です

lsyncd -version
Version: 2.1.5

設定ファイルの編集

マスターとなるサーバ
/etc/lsyncd.conf

settings {
    logfile = "/var/log/lsyncd/lsyncd.log",
    statusFile = "/var/log/lsyncd/lsyncd-status.log",
    statusInterval = 20
}

sync {
    default.rsync,
    delay = 0,
    source="/home/ono", -- 同期元
    delete=true,  -- rsync --delete するかどうか
    target="#{送り先のサーバ}:/home/ono/", -- 同期先
    rsync = {
      binary="/usr/bin/rsync",
      archive="true",
      compress="true",
    }
}
起動

sudo service lsyncd start

これでマスターとなるサーバの /home/ono が同期したいサーバの /home/ono と同期されるようになりました。
とても簡単ですね。

参考 :

CentOS6.5でlsyncdを動かす - Qiita

Lsyncd(Live Syncing Daemon)で複数サーバをリアルタイム同期する方法! | 株式会社LIG

CumulusLinux導入~Vol1.L2SW~

はてなブログでは初投稿になります、、、ので改めて自己紹介を。

わたくし、シーエー・モバイル基盤(インフラ)部門で主にネットワーク周りと時々マネジメントに勤しんでますpi★shiと申します。

弊社のインフラシステムグループは要件定義・設計構築から保守運用・キッティング等までひと通り自前で行っています。(ほぼオンプレミス環境)

マネージャーとしてはメンバーの更なるスキル底上げと自己の可動域を増やしていくと共に、従来の手法に囚われない柔軟な基盤環境の構築を推し進めております。

今回は"ネットワーク仮想化"の取り組みについて記載したいと思います。

巷ではすでにSDN/NFV周りの技術が出回ってきておりますが、今回はホワイトボックススイッチでの仮想化について検証してみました。

その上に載せるOSとしてLinuxベースのCumulusLinuxというソフトウェアを利用します。

中身はDebianです。devパッケージも使えます。現時点ではサーバベースのソフトウェア(httpdとか)はあまりパッケージとして入ってませんが、ソースからビルドしたらApacheだってMySQLだって起動しちゃいます。

●導入環境


社内開発環境でOSが仮想化されているサーバインスタンス群のエッジスイッチをホワイトボックススイッチ+CumulusLinuxにリプレースしました。

f:id:cam-engineer:20150901153338j:plain

 

●インストール


CumulusLinuxはONIEというブートローダでダウンロード/インストールします。
ONIE=Open Network Install Environment

ローカルにWebサーバ立ててドキュメントルートにCumulusLinuxのbinファイル設置。

# ONIE:onie-nos-install http://[webサーバのIP]/CumulusLinux-2.5.X-powerpc.bin 

 

●初期設定


ホワイトボックススイッチにシリアルコンソールで接続するときはbaud rateを”115200”に合わせないと文字化けします。

本家サイトにも注意書きされてます。
”Typically, switches will ship from the manufacturer with a mating DB9 serial cable. Switches with ONIE are always set to a 115200 baud rate.”

・初期ID/PWはコチラ
 ID:cumulus
 PW:CumulusLinux!

・Cumulus用ユーザー追加+sudo権限追加

$ sudo usermod -m camcum
$ sudo passwd XXXX #PW設定
$ sudo usermod -a -G sudo camcum


デフォルトでrootユーザーも存在してるので以降はrootユーザーで作業。

$ sudo su -
#

 

●管理ポート設定&IP固定化


CumulusLinuxはDebianなのでDHCPがデフォルト。固定する場合は/etc/network/interfases ファイルを修正。管理ポートはeth0になります。

# vi /etc/network/interfases
~~~
# The loopback network interface
auto lo
iface lo inet loopback

#The primary network interface
auto eth0
iface eth0
  address X.X.X.X/24
  gateway X.X.X.X
~~~

# ifdown eth0
# ifup eth0

 

●時刻設定

# cat /etc/timezone
Etc/UTC

# dpkg-reconfigure tzdata
⇒6. Asia 75. Tokyo を設定
 
# cat /etc/timezone
Asia/Tokyo

 

●アップデート

# apt-get -y update
# apt-get -y upgrade


※Cumulusのデフォルトレポジトリにはaptitudeはないらしい。。

 

●ライセンス投入


ライセンス入れないとCumulusのキモであるネットワークコマンド"switchd"が動きません。ちなみにライセンスに制限はなく1つのライセンスを複数のスイッチで動かそうと思えば動かせます。本番運用の際は1台につき1ライセンスです、、よね。

事前に入手したライセンスをtxtにしてhomeディレクトリ配下に設置して読み込み

# cl-license -i /home/cumulus/my_license_file.txt

ライセンス確認

# cl-license
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

num_licenses=1g
expires=XXXX #2016-XX-XX
email=XXX@camobile.com
lic_id=XXXX
NFR=0
need_eula=0
account_name=C.A. Mobile
-----BEGIN PGP SIGNATURE-----
XXXXX
XXXXX
XXXXX
XXXXX
-----END PGP SIGNATURE-----

 

●swtich設定


単純にL2SWとして動作するように設定しました。こんなイメージ。

f:id:cam-engineer:20150903114454j:plain

まず仮想ポート(swp)の設定を実施し、認識させてあげます。

・Uplinkport設定
上位側のアップリンクポートを2ポートアサインしてLAG(Link AGggregation)設定実施。

# vi /etc/network/interfases
~~~
auto swp47
iface swp47

auto swp48
iface swp48

auto bond0
iface bond0
 bond-slaves swp47 swp48
 bond-mode 802.3ad
 bond-miimon 100    #リンク監視間隔(ms)デフォルト0
 bond-min-links 1    #LAGポートの必要最低限ポート数。デフォルト0
~~~

※上位側のネットワーク機器も同様にLAG設定してあげること。

設定値の反映を確認

# ifquery --check -a

⇒ステータスが[pass]ならOK

interface読み込み

# ifreload -a

ステータス確認# cat /proc/net/bonding/bond0

# cat /proc/net/bonding/bond0
~~~
Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)

Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer2 (0)
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

802.3ad info
LACP rate: slow
Min links: 0
Aggregator selection policy (ad_select): stable
~略~
Slave Interface: swp48
MII Status: up
Speed: 1000 Mbps
Duplex: full
Link Failure Count: 1
 ~略~
Slave Interface: swp47
MII Status: up
Speed: 1000 Mbps
Duplex: full
 ~略~
 
~~~

Ethernet設定
サーバ接続用ポートとして4ポートをアサインします。VLAN10、20、30を作成し、swp1~3をそれぞれUntagged、swp4をtaggedにそれぞれ設定。

# vi /etc/network/interfases
下記追記
~~~
auto swp1
iface swp1

auto swp2
iface swp2

auto swp3
iface swp3

auto swp4
iface swp4

auto vlan10
iface vlan10
 bridge-ports swp1 swp4.10

auto vlan20
iface vlan20
 bridge-ports swp2 swp4.20

auto vlan30
iface vlan30
 bridge-ports swp3 swp4.20
~~~

 # ifquery --check -a #反映確認

# ifquery --check -a #反映確認
# ifreload -a    #interface読み込み

ステータス確認

# brctl show
bridge name     bridge id               STP enabled     interfaces
vlan10          XXX               yes             swp1
                                                        swp4.10
vlan20          XXX               yes             swp2
                                                        swp4.20
vlan30          XXX               yes             swp3
                                                        swp4.30

# mstpctl showport vlan10
*  swp1  8.001 down 8.000.74:E6:E2:F6:6A:81 8.000.74:E6:E2:F6:6A:81 0.000 Disa
*  swp4.10 8.002 down 8.000.74:E6:E2:F6:6A:81 8.000.74:E6:E2:F6:6A:81 0.000 Disa

# mstpctl showport vlan20
*  swp2  8.001 down 8.000.74:E6:E2:F6:6A:82 8.000.74:E6:E2:F6:6A:82 0.000 Disa
*  swp4.20 8.002 down 8.000.74:E6:E2:F6:6A:82 8.000.74:E6:E2:F6:6A:82 0.000 Disa

# mstpctl showport vlan30
*  swp3  8.001 down 8.000.74:E6:E2:F6:6A:83 8.000.74:E6:E2:F6:6A:83 0.000 Disa
*  swp4.30 8.002 down 8.000.74:E6:E2:F6:6A:83 8.000.74:E6:E2:F6:6A:83 0.000 Disa
 
# mstpctl showbridge

vlan10 CIST info
  enabled         yes
  bridge id       XXXX

~略~
vlan20 CIST info
  enabled         yes
  bridge id       XXXX

~略~

vlan30 CIST info
  enabled         yes
  bridge id       XXXX

~略~

これで最低限の機能をもったL2SWとして動作します。

次回はルータとしての利用(OSPF/BGP)や自動構成ツール(Ansible)との連携など記載していければと思います。