株式会社クリアコード > ククログ

ククログ

«前月 最新 翌月»
タグ:

東京Ruby会議11での発表「アプリケーションへのRubyインタープリターの組み込み」とOSS Gateワークショップ2016-05-28 #tkrk11 #oss_gate

5月28日に開催された東京Ruby会議11で「アプリケーションへのRubyインタープリターの組み込み」と題して、アプリケーションにRubyを組み込む実装について話しました。

関連リンク:

質疑応答の補足

内容は前述のスライドや発表動画を参照してください。ここでは発表後の質疑応答の内容について補足します。

質問:ruby_sysinit()は呼ばなくていいの?

ruby_sysinit()は呼ばなくていいの?」に対する当日の回答は「ruby_sysinit()の説明は省略した」だったのですが、どうして省略したかを補足します。

Rubyインタプリターを組み込んだアプリケーションの1つであるrubyコマンドの実装(main.c)を見るとruby_sysinit()を呼んでいます。そのため、Rubyインタプリターの初期化には必要なAPIにみえます。

しかし、ruby_sysinit()のコメントには次のように書いています。ざっくり言うとrubyコマンドを初期化するためのもので、Rubyインタプリターを組み込むときはこの関数を呼ぶんじゃなくて自分で初期化してね、と言っています。

Initializes the process for ruby(1).

This function assumes this process is ruby(1) and it has just started. Usually programs that embeds CRuby interpreter should not call this function, and should do their own initialization.

中身を見ると、コマンドライン引数と標準入出力を初期化しています。Windowsで動く場合はもっと初期化しています。

Rubyインタプリターを組み込んだアプリケーション例として紹介したmilter managerではruby_sysinit()を呼んでいません。コマンドライン引数は自前で処理しています。

ただ、ruby_sysinit()を呼ばないというのはmilter managerがWindowsをサポートしていないからできることです。Windows用の初期化をしているrb_w32_sysinit()をチラ見するとわかりますが、この関数がやっている処理のうち、自分に必要な分はどこかを判断してそれらを自前で実装することは難しいでしょう。(Windowsに詳しい人ならそうでもないかもしれません。)

そのため、Windowsでも動くアプリケーションにRubyインタプリターを組み込む場合はruby_sysinit()を呼ぶのがよいでしょう。(コメントでは呼ぶなと書いていますが。)

質問:共有ライブラリーの方にアプリケーションのメイン関数を渡せばアプリケーションでRUBY_INIT_STACKを呼ばなくていいんじゃない?

スライドで言うと以下のページの実装についての質問です。

以下のようにしてembedded_init()内でapplication_main()を呼ぶようにすればいいんじゃない?という話です。

int
main(void)
{
  embedded_ruby_module = dlopen();
  embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init");
  embedded_ruby_init(application_main);
}
void
embedded_init(main_func application_main)
{
  RUBY_INIT_STACK;
  /* ... */
  application_main();
}

これに対する回答は「RubyインタプリターとPythonインタプリターを一緒に組み込めないのでやりたいことを実現できない」でした。一緒に組み込む時のイメージは次の通りです。

void
main(void)
{
  embedded_ruby_module = dlopen();
  embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init");
  embedded_ruby_init(application_main);
  /* ↓はアプリケーションのメイン関数の前に実行しないといけないけど、
     ↑でメイン関数が実行されちゃう。 */

  embedded_python_module = dlopen();
  embedded_python_init = dlsym(embedded_python_module, "embedded_init");
  embedded_python_init(application_main);
}

それに対する別案が「他のインタプリターの初期化もやる関数を渡せば?」でした。こんなイメージです。

void
main(void)
{
  embedded_ruby_module = dlopen();
  embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init");
  embedded_python_module = dlopen();
  embedded_python_init = dlsym(embedded_python_module, "embedded_init");

  init_func init_functions[] = {
    embedded_python_init,
    application_main,
    NULL
  };
  embedded_ruby_init(run_init_functions, init_functions);
}

void
run_init_functions(init_func *init_functions)
{
  if (init_functions[0]) {
    init_functions[0]();
    run_init_functions(init_functions + 1);
  } else {
    application_main();
  }
}

質疑応答はここで時間切れでした。

たしかにこれで動きそうです。

会議後にはこんなアイディアもありました。

これはどういうことかというと、アプリケーション側ではRUBY_INIT_STACKに必要なデータを用意するだけにして、実際の呼び出しは別の共有ライブラリーの方に移す、という実装にすればいいんじゃない?ということです。

RUBY_INIT_STACKruby_init_stack()というアドレスを引数にとるAPIを呼び出しているだけなので、このアイディアも実現できます。こんなイメージです。

void
main(void)
{
  int address;

  embedded_ruby_module = dlopen();
  embedded_ruby_init = dlsym(embedded_ruby_module, "embedded_init");
  embedded_ruby_init(&address);

  application_main();
}
void
embedded_init(int *address)
{
  ruby_init_stack((VALUE *)address);
  /* ... */
}

milter managerでも実装できます。(アプリケーション本体に手を入れられないケースではこの方法の実装は難しいでしょう。)

「アプリケーションがRUBY_INIT_STACKを呼ばないといけない問題」は複数の方法で解決できますね!このような場で話をするといろんなアイディアを聞けて便利ですね!みなさんも積極的に実装の話をしてはいかがでしょうか。

RUBY_INIT_STACK問題が解決したので、複数言語のインタプリターを組み込んだケースのことを想像してみたところ、次はforkで問題にあたりそうです。Ruby以外の言語が複数のスレッドを作っている場合が問題になりそうです。

外部ライブラリーとGC

質疑応答では触れられませんでしたが、外部のライブラリーが使用しているメモリー量をRuby(mrubyもCRubyも)のGCシステムが知らないために適切なタイミングでGCが動かない問題についても補足します。

発表では「外部からRubyのGCシステムにメモリー使用量を通知する仕組みが必要だと思う」と話しました。

CRubyのTypedDataにはメモリー使用量を返すAPIがありますが、それは使えないはずです。それを使うと、GCシステム側が情報をpullする仕組みになるからです。pullする仕組みにすると、いつpullするの?pullした情報をどうやって管理するの?あたりが大変になりそうです。

通知する仕組みだとこれらの問題を解決できそうです。

ということで、gc-triggerという拡張ライブラリーを作ってみました。このライブラリーは他の拡張ライブラリーに使用メモリー通知用のAPIを提供します。他の拡張ライブラリーがこのAPIを使ってメモリー使用量を通知すると、適切なタイミングでGC.startを実行します。

本来はCで提供しているAPIを呼んだほうがよいですが、テスト用・説明用にRubyのAPIも用意したので、そっちを使って使い方を説明します。

ここでは、例としてrcairoを使います。rcairoは画像を扱うからです。画像を扱う拡張ライブラリーはRuby管理外のメモリー使用量が大きめなので、この問題に遭遇しやすいケースなのです。

次のコードは1000個画像オブジェクトを生成します。どの画像オブジェクトも参照されていないのですぐにGCで回収できます。

require "cairo"

width = 6000
height = 6000
1000.times do |i|
  Cairo::ImageSurface.new(:argb32, width, height)
end

gc-triggerでメモリー使用量の増減を通知するコードは次のようになります。

require "gc-trigger"
require "cairo"

def image_surface_finalizer(size)
  lambda do |id|
    GCTrigger.update_memory_usage(-size)
  end
end

def create_image_surface(width, height)
  size = 4 * width * height
  surface = Cairo::ImageSurface.new(:argb32, width, height)
  GCTrigger.update_memory_usage(size)
  ObjectSpace.define_finalizer(surface, image_surface_finalizer(size))
  surface
end

width = 6000
height = 6000
1000.times do |i|
  create_image_surface(width, height)
end

画像オブジェクトのサイズは、単純化して「1画素のサイズ(4バイト)×縦×横」で計算しています。

それぞれの場合でのメモリー使用量をグラフにすると次のようになります。従来の方はメモリー使用量が安定しませんが、gc-triggerを使った方はメモリー使用量が一定になります。

メモリー使用量

発表で話したアイディアを動くようにしてみました。これで通じるでしょうか。

OSS Gateワークショップ2016-05-28

発表でも紹介しましたが、OSS Gateワークショップ2016-05-28を東京Ruby会議11と同時開催しました。東京Ruby会議11でポスターを展示していたので、そこではじめて知ったという方も多かったのではないでしょうか。

OSS GateおよびOSS Gateワークショップを紹介していて痛感したことは「口頭での説明でないとちゃんと伝えられない」ということでした。たとえば、「私はすごくないのでメンターできないです。。。」に対して口頭では伝わるように回答できますが、資料では伝わらないです。これだとスケールしないので口頭での説明でなくても伝わる資料の作成が必要です。

ポスターを展示しての紹介は想像以上に効果があり、OSS Gateワークショップ2016-07-30の登録者が10人以上増えました。東京Ruby会議11実行委員会に協力してもらえて本当によかったです。ありがとうございました。

ちなみに、想像以上に登録者が増えたおかげでメンターが足りなそうです。OSS開発に参加したことがある人(技術力不問)でOSSの開発に参加する人が増えるといいなぁと思う人は「メンター」としてOSS Gateワークショップ2016-07-30に登録してください。すでに定員オーバーでキャンセル待ちでの登録になりますが、気にしないでください。キャンセル待ち扱いでも気にせずに当日来てください。入れます。

まとめ

5月28日に開催された東京Ruby会議11での発表とOSS Gateワークショップ2016-05-28について補足しました。

東京Ruby会議11の参加者アンケートはまだ回答を受け付けているので参加した方はぜひ回答してください。

2016-06-01

MySQLとPostgreSQLと日本語全文検索2:初心者向けMroonga・PGroonga情報 #mypgft

6月9日に「MySQLとPostgreSQLと日本語全文検索2」というイベントを開催しました。今回もDMM.comラボさんに会場を提供してもらいました。当日のツイートはMySQLとPostgreSQLと日本語全文検索2 - Togetterまとめにまとまっています。

2月9日に開催した1回目のイベントでは次の2つのことについて紹介しました。

  • MySQLでの日本語全文検索の歴史と実現方法
  • PostgreSQLでの日本語全文検索の歴史と実現方法

2回目となる今回は次のことについて紹介しました。

ここではMroonga(むるんが)とPGroonga(ぴーじーるんが)の1歩進んだ使い方について少し紹介します。

関連リンク:

今回の発表ではMroongaとPGroongaのオススメの使い方およびレプリケーションまわりについて紹介しました。

Mroongaのオススメの使い方

Mroongaは次のように使うのがオススメです。

  • できればストレージモードを使う
  • テーブル定義はデフォルトのパラメーターを使う(デフォルトでいい感じになるように調整してあります。)
  • 検索するときはIN BOOLEAN MODE*D+プラグマを使う

詳細はスライドに書いていますが、以下のようなSQLで使うということです。

CREATE TABLE items (
  title text,
  FULLTEXT INDEX (title)
  -- ↑をCOMMENTでカスタマイズできるが
  --   まずはデフォルトで使うのがオススメ
) ENGINE=Mroonga
  DEFAULT CHARSET=utf8mb4;

SELECT * FROM items
  WHERE MATCH (title)
        --        ↓ *D+プラグマを使ってデフォルトでANDにする
        AGAINST ('*D+ 激安 人気' IN BOOLEAN MODE);
        --                      ↑ ブーリアンモードを使う
        --                         Web検索エンジンのような使い勝手になる

また、ストレージモードを使うための工夫としてスレーブだけをMroongaにしてレプリケーションする構成とその設定方法も紹介しました。ストレージモードではトランザクションを使えませんが、この構成にするとトランザクションを使わなくてもよくなります。

PGroongaのオススメの使い方

PGroongaは次のように使うのがオススメです。

  • 主キーを用意する
  • インデックス定義はデフォルトで使う(デフォルトでいい感じになるように調整してあります。)
  • 検索はsearch_pathを設定した上で@@演算子を使う

詳細はスライドに書いていますが、以下のようなSQLで使うということです。

CREATE TABLE items (
  -- ↓主キーを用意する
  id integer PRIMARY KEY,
  title text,
);
CREATE INDEX pgroonga_items_index
  ON items
  --              ↓ インデックスに主キーを含める
  USING pgroonga (id, title);
  -- パラメーターは指定せずデフォルトで使う

ALTER DATABASE db1
  -- ↓ search_pathにpgroongaスキーマを入れ、pg_catalogスキーマよりも先にする
  SET search_path TO "$user",public,pgroonga,pg_catalog;

SELECT *,
       -- ↓検索スコアーを取得(このために主キーが必要)
       pgroonga.score(items) AS score
  FROM items
  --          ↓ @@演算子を使う
  WHERE title @@ "激安 人気"
  ORDER BY score DESC;

PGroongaのレプリケーションについても説明しました。

PGroongaはPostgreSQL標準のレプリケーション機能を使えませんが、pglogicalと組み合わせたレプリケーションは使えます。なお、PostgreSQL 9.6以降ではPostgreSQL標準のレプリケーション機能を使えるようにPGroongaの開発を進めています。

PostgreSQLでの日本語全文検索を実現する方法としてPGroongaに興味のある方は、PGroongaをWindows用のサーバーログ管理ソフトであるVVAULT AUDITでの利用事例「VVAULT AUDITにおけるPGROONGAの利用」も合わせて参照してください。

まとめ

6月9日に開催された「MySQLとPostgreSQLと日本語全文検索2」でのMroongaとPGroongaの発表内容について紹介しました。

MySQL・PostgreSQLで日本語全文検索したい場合はぜひこれらを検討してみてください。

タグ: Groonga
2016-06-09

Let’s Encryptをcloudapp.azure.comで使おうとするときのハマりどころ

はじめに

誰でもSSL/TLS証明書を無償で取得できることを標榜しているサービスとして、Let's Encryptがあります。2016年4月に正式サービスの提供が開始されたことから、実際に試してみた人も多いことでしょう。

今回は、Let's Encryptをcloudapp.azure.comで使おうとするときのハマりポイントを紹介します。

2016/06/17 Kazuhiro NISHIYAMAさんに記事内容の誤りをletsencrypt の Rate Limit についてというエントリで指摘していただきました。ありがとうございます。Rate Limitの回避方法の言及が不十分だった箇所、適用例外についての記述が誤っている箇所、ステージング環境を利用する場合のコマンドラインオプションがある旨の3点について記述をあらためました。

cloudapp.azure.comについて

cloudapp.azure.comはAzureで仮想マシンを立ちあげて、公開するときの既定のドメインです。 次のようなルールでDNSを設定できます。このうちロケーションは東日本リージョンであればjapaneastになります。

  • <指定の名前>.<ロケーション>.cloudapp.azure.com

cloudapp.azure.comでLet's Encrypt

お使いのディストリビューションにcertbotがあればそれを、ない場合や最新のcertbotが利用したければ、certbotのサイトからクライアントをダウンロードします。

$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto

あとは次のようにダウンロードしたスクリプトを実行するだけ、と思うかも知れません。

$ ./certbot-auto

Azure上とはいえ、Linuxディストリビューションを使う分には何も問題なさそうではありますが、ここにハマりポイントがあります。

実際に実行してみると、次のようなエラーに遭遇しました。(2016年6月時点)

There were too many requests of a given type :: Error creating new cert :: Too many certificates already issued for: azure.com
Please see the logfiles in /var/log/letsencrypt for more details.

エラーメッセージで検索してみると、このエラーに遭遇している人が他にもいることがわかりました。

この投稿からたどれるリンクには、Let's Encryptの制限に関するものがありました。

細かな制限はいろいろありますが、どうやら特定ドメインに対しては、週に20個のSSL/TLSサーバー証明書しか発行しないようです。そのため*.cloudapp.azure.comなどのように、皆がこぞってSSL/TLSサーバー証明書を取得しに行くような場合、見事に制限にひっかかってしまいます。

これを回避するには別途独自にドメインを取得したり、Dynamic DNSやIaaSなどを利用している場合はサービスプロバイダーに対応(後述)してもらったりしなければなりません。

2016/06/17訂正:DDNSやIaaSについての言及を追加。

適用例外はありますか?

*.cloudapp.azure.comで上記の制限がかかるとなると、大分厳しいように思えます。

申請すれば、制限を緩和してもらえるというような情報もありましたが、本当にそういったものはあるのでしょうか。

実際に疑問に思って質問しているケースがありました。

LE Tech AdvisorであるJacob氏の発言を読む限りでは、残念ながら個別に申請して例外扱いしてもらう方法はなさそうです。

ただし、後から教えてもらったのですが、DDNS Rate LimitedToo many certificates already issued for dynamic dns provider sub domainで言及されているように、次の条件を満たせば例外扱いされるようです。

ただし、上記の条件を満たすのはすぐにというわけにはいきません。

2016/06/17訂正:個別に申請する方法がない点、サービスプロバイダーに対応してもらう方法を追記。

更新や再取得の場合は?

ここまで厳しいのは、あくまで新規取得の場合です。 更新する場合や、再取得の場合は影響はないようです。

正式版じゃなくてもいいから試してみたい

正式なSSL/TLS証明書じゃなくても、certbotを試してみたい、というのであればステージング環境があります。

こちらは、ステージング環境だけあって、制限がゆるいです。*.cloudapp.azure.comでも問題なくSSL/TLSサーバー証明書を取得できます。

その場合には次のコマンドラインオプションを使います。

  --test-cert, --staging
                        Use the staging server to obtain test (invalid) certs;
                        equivalent to --server https://acme-
                        staging.api.letsencrypt.org/directory (default: False)

2016/06/17訂正:パッチをあてなくてもコマンドラインオプションでできるので、そちらを紹介するように訂正。

この方法で取得した場合、認証局が「Fake LE Intermediate X1」になります。 正式版の「Let's Encrypt Authority X3」ではありません。アクセスするにはブラウザのセキュリティ例外などの設定が必要です。

まとめ

今回は、Let's Encryptを*.cloudapp.azure.comの配下で取得しようとすると、制限にひっかかってしまうというハマりポイントの事例を紹介しました。 ただし、この情報は2016年6月時点のものなので今後改善されるかも知れません。

*1 実際にazurewebsites.netやazure-mobile.net、cloudapp.netは登録されています。

2016-06-16

OSS開発支援サービスを開始 #oss_gate

クリアコードは「OSS開発支援サービス」の提供を開始します。

「OSS開発支援サービス」について

「OSS開発支援サービス」はその名の通り「お客さんがOSSを開発すること・お客さんがOSSの開発に参加することを支援するサービス」です。以下は支援の仕方の一例です。実際にこのサービスを提供するときは、以下の例をそのまま全部提供するのではなく、お客さんの現状にあった適切な支援はなにかを考えるところからお客さんと一緒に取り組みます。

  • お客さんが開発しているOSSの開発にクリアコードが参加する
  • お客さんが使っているOSSの開発に、お客さんが参加することを、クリアコードが支援する
    • 開発に参加する方法をワークショップ形式で伝える
    • バグレポートをする前にレポートをレビューする
    • 機能提案レポートをする前にレポートをレビューする
    • pull request・パッチを送る前にコードをレビューする
  • お客さんが開発したソフトウェアを、お客さんがOSSにすることを、クリアコードが支援する
    • ソフトウェアの一部をOSSにする場合、どの部分をどのようにOSSにするとよいかに関して技術面・ビジネス面から相談にのる
    • OSSをメンテナンスすることに関する情報提供・技術的支援

企業としてOSSを開発すること・OSSの開発に参加することにはメリットがあります。たとえば次のようなメリットです。

  • 企業外の開発者とやりとりしたり、企業外の開発者が書いたコードを読むことで、企業内の開発者の技術力があがる
  • 使っているOSSの問題を手元で回避するよりも、OSS側で直した方がメンテナンスコストが低い
  • 企業の技術力をアピールする機会になる
  • 企業外の開発者に企業を知ってもらいやすくなる

もちろん、すべての企業にとってこのようなメリットがあるわけではありません。このようなメリットがある企業もあるということです。そして、メリットがある企業を支援するのがこの「OSS開発支援サービス」です。

企業としてOSSの開発に取り組みたいという場合はお問い合わせください。どのように取り組むのがよさそうか、どんな支援が適切そうかなどを相談するところから一緒に取り組んでいきましょう。

サービスの紹介は以上です。以下はどうして「OSS開発支援サービス」を提供することにしたかについての話になります。興味があれば続きもご覧ください。

背景

実はクリアコードはすでに何年も前から同様のサービスを提供しています。

例として「お客さんが開発しているOSSの開発にクリアコードが参加する」の事例をいくつか紹介します。

SennaGroongaの前身の全文検索エンジン)は未来検索ブラジルさんが開発しているOSSです。Sennaの開発に参加し始めたのは2008年です。

Hatoholミラクル・リナックスさんが開発しているOSSです。Hatoholの開発に参加し始めたのは2013年です。

FluentdTreasure Dataさんが開発しているOSSです。Fluentdの開発に参加し始めたのは2015年です。

今回「OSS開発支援サービス」という名前をつけてサービス提供を始めた理由は次の2つです。

  • 「このようなサービスを提供していること」をわかりやすくるため
  • OSS Gateをもっと広めるため

前者に関してはこのようにブログを書いたり、問い合わせフォームに「OSS開発支援について」という項目を追加したりしています。飲食店で外にメニューがあると入るかどうかを決めやすい、というような感じです。

後者に関しては少し詳しく説明する必要があります。

OSS Gateは「OSS開発に参加する人を増やす取り組み」です。現在はそのための1つの方法としてOSS Gate ワークショップだけを開催しています。

OSS Gateワークショップは「OSS開発に参加したことがない人」向けに作られたものです。実際、そのような人たちがOSS Gateワークショップに参加し、OSS開発に参加するきっかけになったと言っています。

OSS Gateは、趣味であろうと仕事であろうと用途や目的は関係なくOSS開発に参加する人を増やしたいという取り組みです。しかし、仕事でOSS開発に参加する人を増やす、という観点ではリーチしにくい部分があります。そこをクリアコードでフォローできないかと考えています。

たとえば、OSS Gateワークショップの開催日と開催人数です。

現在、OSS Gateワークショップは奇数月の最終土曜日に開催しています。休日なので企業としてOSS開発に参加したいという場合に利用することは難しいです。ワークショップの内容はだれでも自由に使えるライセンスで公開しているので、自分たちで開催することもできますが、まだOSS開発に参加している人がいない場合は難しいでしょう。

OSS Gateワークショップの参加人数は徐々に増えていて、次回の7月30日開催分にはキャンセル待ちがでています。企業としてOSS開発に参加したい場合、企業内の開発者が一緒にワークショップに参加したくなりますが、多くの人数が一気に同日のワークショップに参加することは難しい状況です。

OSS Gateワークショップを業務時間中にその企業向けに開催するサービスをクリアコードが提供することで、企業での開催を推進します。これによりOSS Gateでリーチしにくい部分をフォローします。

OSS GateはまだOSS Gateワークショップだけに注力している段階です。OSS Gateワークショップに参加した人が、その後のOSS開発に関して相談できるなにかを提供したいね、という話もでていますが、まだその実現には至っていません。(興味のある方はOSS Gateのチャットで相談しましょう!)

企業でOSS Gateワークショップをやった場合も「OSS開発に参加する最初の一歩を踏み出した後のフォロー」が必要になることがあります。その場合はやはり業務時間にフォローして欲しくなるのですが、OSS Gateではまだそこをフォローできません。OSS Gateでフォローできるようになっても、企業内で秘密の情報を交えながらフォローした方がいいケースはフォローできません。そのようなケースはクリアコードでフォローします。

先日、Speeeさん向けにOSS Gateワークショップを開催しました。詳細はSpeeeさんのレポートOSS Gateにあるレポートを参照してください。

このとき、「OSS Gateワークショップの次の一歩のサポートもあるとうれしい」というフィードバックをもらいました。おそらくSpeeeさん以外もそう思うでしょう。クリアコードは以前からそのあたりをフォローするサービスを提供しています。そのサービスでOSS開発に参加する人が増えることはクリアコードとしてもOSS Gateとしてもうれしいことです。必要な人たちにこのサービスが届くように「OSS開発支援サービス」としてわかりやすくしました。

長くなりましたが、「OSS開発支援サービス」という名前をつけてサービス提供を始めた理由である次の2つについて補足しました。

  • 「このようなサービスを提供していること」をわかりやすくるため
  • OSS Gateをもっと広めるため

まとめ

「OSS開発支援サービス」の提供を開始したことを宣言しました。

このサービスを利用したい、自分たちも同様のサービスを提供したい(クリアコードとしてもOSS GateとしてもOSSの開発に参加する人が増えることはうれしいことなので、それをやりたいという企業を歓迎します)など「OSS開発支援サービス」に興味のある方はぜひお問い合わせください。相談しましょう。

タグ: 会社
2016-06-27

«前月 最新 翌月»
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|
タグ: