Home > unix

unix Archive

Docker ストレージドライバによる RHEL/CentOS 対応について

この記事の所要時間: 130

Shin x blog Advent Calendar 2013 の 8 日目です。

docker

これまで Docker を RHEL/CentOS で動かす際に懸案だったのは、AUFS への対応でした。RHEL/CentOS 6.x のカーネルは AUFS へ対応していないので、Docker を動かすには、AUFS 対応のカーネルを入れる必要がありました。

Docker 0.7 では、この対応としてストレージドライバという機構が採用されました。

ここでは、ストレージドライバによる RHEL/CentOS 対応について見てみます。

CentOS 6.5 で利用されているストレージドライバ

Docker 0.7 で採用されたストレージドライバは、Docker コンテナが利用するファイルシステムを選択する機構です。これにより、AUFS 以外のファイルシステムを利用ことが可能になっています。0.7 では、aufs / devicemapper / vfs の 3 つのドライバーが存在します。

この中で、RHEL/CentOS では devicemapper というドライバが利用されています。

どのドライバが利用されているかを確認するのには、docker info コマンドを実行します。

CentOS 6.5 で docker info コマンドで確認すると下記のようになります。Driver: の箇所が devicemapper と表示されており、これがドライバとして利用されているのが分かります。

[vagrant@localhost ~]$ sudo docker info
Containers: 47
Images: 24
Driver: devicemapper // <----
  Pool Name: docker-253:0-1835642-pool
  Data file: /var/lib/docker/devicemapper/devicemapper/data
  Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
  Data Space Used: 1790.1 Mb
  Data Space Total: 102400.0 Mb
  Metadata Space Used: 4.7 Mb
  Metadata Space Total: 2048.0 Mb

ちなみに ubuntu 上で docker info を実行すると、こちらは aufs と表示されます。

vagrant@precise64:~$ sudo docker info
Containers: 2
Images: 1
Driver: aufs
 Root Dir: /var/lib/docker/aufs
  Dirs: 5
  WARNING: No swap limit support

AUFS 以外のストレージドライバ採用の経緯

AUFS 以外のストレージドライバとして何を利用するかの経緯については、community.redhat.com にエントリがあります。

Adventures in Dockerland | Open Source Community

まず、AUFS について。AUFS は、現在の Ubuntu カーネルでは非推奨となっていて、いずれは削除されるようです。

このため、RHEL/CentOS 対応を行うかどうかに関わらず、AUFS 以外のストレージドライバへの対応が必要だったように思います。

そして、AUFS に変わるコピーオンライトなストレージとして、下記の 4 つが検討されていました。

  • overlayfs
  • btrfs
  • lvm snapshots
  • lvm thin provisioning

結果的には、最後の lvm thin provisioning が採用されており、devicemapper ドライバとして実装されています。こうして RHEL/CentOS でも Docker が利用できるようになりました。

件のエントリでは、それぞれの方法についての考察なども書かれていますので、気になる方は読んで見てください。

CentOS 6.4 で Docker を動かす

Docker 0.7 で対応した lvm thin provisioning は、RHEL/CentOS 6.4 からサポートされているので、CentOS 6.4 環境でも Docker が動きそうです。

そこで、Vagrant 上の CentOS 6.4 でも試してみました。インストール方法などは前エントリのままです。

$ cat /etc/redhat-release
CentOS release 6.4 (Final)

$ sudo docker run -i -t centos /bin/bash
bash-4.1#

$ sudo docker info
Containers: 0
Images: 0
Driver: devicemapper
  Pool Name: docker-253:0-262375-pool
  Data file: /var/lib/docker/devicemapper/devicemapper/data
  Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
  Data Space Used: 291.5 Mb
  Data Space Total: 102400.0 Mb
  Metadata Space Used: 0.7 Mb
  Metadata Space Total: 2048.0 Mb

ちゃんと動いてますね。docker info コマンドでは、Driver は、ちゃんと devicemapper になっています。

CentOS 6.3 で Docker を動かない?

では逆に動かないところも見てみたいので、次は CentOS 6.3 でも試してみました。

適当な Box ファイルが手元に無かったので、AWS Marketplace にある CentOS 6.3 の AMI を使いました。

# cat /etc/redhat-release
CentOS release 6.3 (Final)

# docker run -i -t centos /bin/bash
bash-4.1#

# docker info
Containers: 9
Images: 2
Driver: devicemapper
  Pool Name: docker-202:64-9417-pool
  Data file: /var/lib/docker/devicemapper/devicemapper/data
  Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
  Data Space Used: 858.7 Mb
  Data Space Total: 102400.0 Mb
  Metadata Space Used: 1.7 Mb
  Metadata Space Total: 2048.0 Mb

あれ?ちゃんと動いていますね。Driver も devicemapper になっています。コンテナ内で yum など使っていくつかパッケージをインストールしたり、docker commit でイメージを作ったりしましたが、正常に動いているようでした。

調べてみると、lvm thin provisioning は正式採用されたのは 6.4 ですが、6.3 の段階で Technology Preview として実装されていたようです。なるほど。

LVM の Thin Provisioning 型 LV とスナップショット利用

6.2 なら、Driver: vfs となり、違った挙動が見れるのかもしれません。

利用するストレージドライバの決定

Docker が環境に応じてどのようにストレージドライバを決定しているかを見てみました。

利用するストレージドライバの決定は、graphdriver/driver.go にある New メソッドにて行われます。下記が該当ソースです。

まず、環境変数やオプションによるドライバ指定を処理します。すでにドライバが指定されていれば、そのドライバを利用します。

その後に priority 変数の順に利用するドライバを決定します。それぞれのドライバが利用できるかチェックして、利用できるならば、それを使います。priority は、下記のように定義されているので、aufs / devicemapper / vfs の順でチェックが行われます。

このように通常、ストレージドライバは自動で決定されます。

一方、環境変数や -s オプションを使うことで利用するドライバを指定することも可能です。

下記では、docker コマンドに -s オプションに vfs を指定して、docker デーモンを起動しています。docker info を見ると vfs がドライバとして利用されていることが分かります。

$ sudo service docker stop # docker を一旦停止
$ sudo docker -d -s vfs # docker コマンドから、docker デーモンを起動

$ sudo docker info
Containers: 0
Images: 0
Driver: vfs

さいごに

Docker 0.7 でサポートされたストレージドライバによる RHEL/CentOS サポートについて見てみました。

コピーオンライトなストレージは、Docker における重要な技術なので、今後もより良い環境を求めて、他のファイルシステムへの対応が進みそうです。

ここで見たようにストレージドライバに関しては RHEL/CentOS 6.4 環境でも動いているので、6.5 環境がまだ無い人は試してみて下さい。

参考

  • コメント (Close): 0
  • トラックバック (Close): 0

殺伐とした黒い画面にカラフルなキャラがお出迎え

  • 2013-03-25 (月)
  • unix
この記事の所要時間: 27

一部では「黒い画面」と恐れられているターミナルですが、こんなキャラがお出迎えしてくれると気分も変わるのでは。

terminal_login_message

ターミナルを開いたり、SSH でログインした時にキャラを表示させる方法です。

/etc/motd にメッセージを書くとログインした時に記述したメッセージを表示することができます。レンタルサーバやクラウドサービスのサーバにログインすると表示されるメッセージも /etc/motd に記載されています。

例えば、さくらの VPS であれば、下記のように記載されています。

% cat /etc/motd

SAKURA Internet [Virtual Private Server SERVICE]

AmazonLinux では以下のようになっています。

$ cat /etc/motd

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2012.09-release-notes/

ここにキャラを表示するエスケープシーケンスを記述すると、ログインした時に表示することができます。

valllog さんのエントリに記載のあったキャラクターのデータをお借りして、データをアップしました。

このデータをダウンロードして、/etc/motd に書き込めば ok です。

$ curl -O https://gist.github.com/shin1x1/5230392/raw/82993c6c1d982447e7e99def4f58fc14fc234665/batz.txt
$ sudo -s
# cat batz.txt >> /etc/motd

再ログインすると勇敢な戦士が登場します。

terminal_login_message_batz

削除する時は /etc/motd を編集して、該当部分を削除するだけです。

$ sudo vim /etc/motd

素敵な黒い画面ライフを!

  • コメント (Close): 0
  • トラックバック (Close): 0

Vagrant で自分の PC に「作って、壊して、元に戻せる」サーバを作る

この記事の所要時間: 157

Vagrant 便利ですね。Web システム構築ではサーバ構築、設定を何度となく繰り返すので、こういった「作って、壊して、元に戻せる」環境が自分の PC にあるというのはとても重宝します。

vagrant

ここでは Vagrant1.0 を使って、Mac OS X 上に CentOS サーバを構築します。また触ってみて便利だった機能もいくつかご紹介します。

Vagrant とは

きちんとして説明は本家サイトを見るのが良いのですが、端的にいうと PC にインストールできる仮想PCを便利に使うツールです。
1.0 では仮想PCとして VirtualBox に対応しています。(1.1 からは VMWare や EC2 などにも対応しています。)

1. VirtualBox のインストール

まず VirtualBox をインストールします。VirtualBox は下記サイトから無料でダウンロードすることができます。

https://www.virtualbox.org/wiki/Downloads

2. Vagrant1.0 のインストール

次に Vagrant をインストールします。最新版は1.1なのですが、まだリリースされたばかりで、後述する便利なプラグインなどが対応していないので、ここでは1.0をインストールします。

Vagrant1.0は公式サイトからパッケージをダウンロードするか、 gem でインストールします。

Vagrant – Downloads

gem でインストールする場合は以下になります。(追記: gem でインストールした場合、sahara プラグインが動作しない場合があるので、こだわりが無ければ上記パッケージでのインストールをお勧めします。)

$ sudo gem install vagrant

インストールができたら vagrant コマンドが使えるか確認しておきます。

$ vagrant -v
Vagrant version 1.0.7

3. box ファイルのインストール

Vagrant で仮想サーバを作成するには box ファイルというイメージファイルが必要になります。box ファイルは自分で作成することもできるのですが、主要な Linux については公開されているファイルがあるので、これをインストールします。

Vagrantbox.es では各OSごとのboxファイルダウンロードURLが一覧として公開されています。CentOS もいくつか種類があるので、その中から CentOS6.4_x86_64 の box ファイル(CentOS 6.4 x86_64 Minimal (VirtualBox Guest Additions 4.2.8, Chef 11.4.0, Puppet 3.1.0))をインストールします。

下記が box ファイルのインストール例です。

vagrant box add までがコマンドで、centos64_64 の部分が box ファイルの名称です。これは任意に付けることができるので、自分が分かりやすい名前を付けます。その後に box ファイルの URL を指定します。

$ vagrant box add centos64_64 http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130309.box
[vagrant] Downloading with Vagrant::Downloaders::HTTP...
[vagrant] Downloading box: http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130309.box
[vagrant] Extracting box...
[vagrant] Verifying box...
[vagrant] Cleaning up downloaded box...

インストールが完了したら、vagrant box list コマンドで確認します。

% vagrant box list
centos64_64

4. Vagrantfile の作成

次に vagrant の設定ファイルである Vagrantfile を作成します。作成する仮想サーバの設定はこのファイルに記述していきます。

Vagrantfile の作成するには vagrant init コマンドを使います。

% mkdir vagrant
% cd vagrant
% vagrant init
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
% ls
Vagrantfile

では Vagrantfile を編集してみましょう。
デフォルトのサンプルの設定がコメントで書かていますが、一旦削除して必要な設定だけ記述しています。

config.vm.box にはベースとなる box 名を指定します。ここでは先程ダウンロードした centos64_64 という box 名を指定しています。
これだけで仮想サーバを起動できるのですが、動作が分かりやすいように config.vm.boot_mode に :gui を指定しています。

% vim Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant::Config.run do |config|
  config.vm.box = "centos64_64"
  config.vm.boot_mode = :gui
end

5. 仮想サーバを起動

では実際に仮想サーバを起動してみましょう。

仮想サーバを起動するには、vagrant up コマンドを使います。

% vagrant up
[default] Importing base box 'centos64_64'...
[default] Matching MAC address for NAT networking...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] Mounting shared folders...
[default] -- v-root: /vagrant

コマンドを実行すると VirtualBox が起動します。

virtualbox

起動が完了すると VirtualBox のウィンドウにログインプロンプトが表示されます。
ウィンドウをクリックして、login に vagrant 、password に vagrant を入力するとログインすることができます。

virtualbox_login

Virtualbox ウィンドウから抜ける時は、ウィンドウ右下に書いてあるとおり、左のコマンドキーを1回押します。(Mac OS X の場合)

virtualbox_exit

vagrant ssh コマンドを使うと、ssh で仮想サーバにログインすることができます。

% vagrant ssh
Last login: Thu Mar 21 06:46:13 2013
Welcome to your Vagrant-built virtual machine.
[vagrant@localhost ~]$

vagrant status コマンドを使うと、現在の仮想サーバの状態が確認できます。下記では、起動中なので ruuning と表示されています。

% vagrant status
Current VM states:

default                     running

6. 仮想サーバを停止

仮想サーバを停止する場合、VirtualBox ウィンドウでログインした状態で、/sbin/shutdown などを実行する方法と vagrant halt コマンドで仮想サーバを停止する方法があります。

下記では vagrant halt コマンドで仮想サーバを停止しています。

% vagrant halt

vagrant status コマンドを実行すると停止していることが分かります。

% vagrant status
Current VM states:

web1                     poweroff

7. 仮想サーバを削除

仮想サーバを削除する場合、vagrant destroy コマンドを使います。

% vagrant destory
Are you sure you want to destroy the 'default' VM? [Y/N] y
[default] Destroying VM and associated drives...

vagrant status コマンドを実行すると削除されていることが分かります。

% vagrant status
Current VM states:

default                     not created

8. 仮想サーバでhttpdサーバを構築

では仮想サーバにhttpdをインストールしてみましょう。

まず Vgrantfile を編集して、仮想サーバの設定を追加します。
Mac 上ですでに各種デーモンが起動しているため、ポートの競合などを避けるために仮想サーバにIPアドレス、ホスト名を付与しています。また、VirtualBox のウィンドウを隠すために config.vm.boot_mode をコメントアウトしています。

% vim Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant::Config.run do |config|
  config.vm.box = "centos64_64"
  #config.vm.boot_mode = :gui

  config.vm.define :web do |web|
    web.vm.host_name = "web"
    web.vm.network :hostonly, "192.168.33.10"
  end
end

では仮想サーバを起動しましょう。

% vagrand up

SSH で仮想サーバにログインします。vagrant ssh コマンドでも可能ですし、IP アドレスを指定して、ssh コマンドでもログインできます。

% vagrand ssh
% ssh vagrant@192.168.33.10
※パスワードは vagrant

次に httpd をインストールして、起動します。

[vagrant@web ~]$ sudo yum -y install httpd
(snip)
[vagrant@web ~]$ sudo /sbin/service httpd start

この仮想サーバでは iptables が設定されていて、外部からの通信を遮断してしまいます。VirtualBox を実行している PC から通信できるように全てのフィルタリングルールを削除しておきます。

[vagrant@web ~]$ sudo /sbin/iptables -F
[vagrant@web ~]$ sudo /etc/init.d/iptables save

ブラウザから仮想サーバの httpd にアクセスしてみましょう。http://192.168.33.10/ にアクセスするとおなじみのテストページが表示されます。

browser

9. sahara プラグインで、作って、壊して、元に戻せる環境を構築

Vagrant にはプラグインで機能を拡張することができます。いくつかあるプラグインの中から、個人的にこれがあるから Vagrant が使いたくなるというプラグインをご紹介しましょう。

それは sahara プラグインです。sahara プラグインを使うと、データベースのトランザクションにおける rollback のように、サーバへ反映した作業をある時点まで簡単に戻すことができます。

では sahara プラグインをインストールして、気軽に元に戻せる機能を試してみましょう。

9–1. sahara プラグインをインストール

sahara プラグインをインストールするいは、vagrant gem install コマンドを使います。

% vagrant gem install sahara
Fetching: Platform-0.4.0.gem (100%)
Fetching: open4-1.3.0.gem (100%)
Fetching: popen4-0.1.2.gem (100%)
Fetching: thor-0.17.0.gem (100%)
Fetching: sahara-0.0.13.gem (100%)
Successfully installed Platform-0.4.0
Successfully installed open4-1.3.0
Successfully installed popen4-0.1.2
Successfully installed thor-0.17.0
Successfully installed sahara-0.0.13
5 gems installed
Installing ri documentation for Platform-0.4.0...
Installing ri documentation for open4-1.3.0...
Installing ri documentation for popen4-0.1.2...
Installing ri documentation for thor-0.17.0...
Installing ri documentation for sahara-0.0.13...
Installing RDoc documentation for Platform-0.4.0...
Installing RDoc documentation for open4-1.3.0...
Installing RDoc documentation for popen4-0.1.2...
Installing RDoc documentation for thor-0.17.0...
Installing RDoc documentation for sahara-0.0.13...

インストールが完了すると、vagrant で利用できるサブコマンドに sandbox が追加されます。

% vagrant
[/Users/shin/vagrant]% vagrant
(snip)
Available subcommands:
     box
     destroy
     gem
     halt
     init
     package
     provision
     reload
     resume
     sandbox  <---- 追加
     ssh
     ssh-config
     status
     suspend
     up

For help on any individual command run `vagrant COMMAND -h`

9–2. sahara プラグインを試す

実際に sahara プラグインを試してみましょう。

まず vagrant sandbox on コマンドで、snapshot mode を開始します。これは RDBMS の BEGIN にあたり、rollback した場合にこの時点まで仮想サーバを戻すことができます。

% vagrant sandbox on
[web] - Enabling sandbox
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%

vagrant sandbox status コマンドを実行すると snapshot mode 中であることが分かります。

% vagrant sandbox status
[web] - snapshot mode is on

仮想サーバにログインして、php をインストールします。php コマンドが実行できることが確認できたらログアウトします。

% vagrant ssh
Last login: Thu Mar 21 07:10:35 2013 from 192.168.33.1
Welcome to your Vagrant-built virtual machine.
[vagrant@web ~]$ sudo yum -y install php
[vagrant@web ~]$ php -v
PHP 5.3.3 (cli) (built: Feb 22 2013 02:51:11)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
[vagrant@web ~]$ exit

php のインストールを取り消すために、vagrant sandbox rollback コマンドで vagrant sandbox on を実行した時点に戻します。

% vagrant sandbox rollback
[web] - powering off machine
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
[web] - roll back machine
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
[web] - starting the machine again

これは仮想サーバが元に戻りました。再度 vagrant ssh でログインしてみると、さきほどインストールした php が存在しません。

[vagrant@web ~]$ php
-bash: php: コマンドが見つかりません

rollback を実行した後も snapshot mode が有効なままとなっています。(これは DBMS とは異なります)

% vagrant sandbox status
[web] - snapshot mode is on

では、つづけて、sudo rm -rf /bin を実行してみましょう。(必ず仮想サーバで実行して下さい!)

[vagrant@web ~]$ sudo rm -rf /bin
(snip)
[vagrant@web ~]$ ls
-bash: /bin/ls: No such file or directory

こりゃ大変!でも大丈夫。サーバをログアウトして、vagrant sandbox rollback を実行すれば元通り。

% vagrant sandbox rollback
[web] - powering off machine
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
[web] - roll back machine
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
[web] - starting the machine again

% vagrant ssh
[vagrant@web ~]$ ls

snapshot mode を終了する場合は vagrant sandbox off コマンドを実行します。vagrant sandbox off コマンドを実行すると現在の状態をそのまま保存して、snapshot mode が終了します。

% vagrant sandbox off
[web] - switching sandbox off
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%

vagrant sandbox status で確認すると snapshot mode が終了していることが分かります。

% vagrant sandbox status
[web] - snapshot mode is off

10. 複数の仮想サーバを起動

Web システムでは複数台のサーバで構成することが多いのですが、その検証として複数の仮想サーバを起動してみましょう。

Vagrantfile で、config.vm.define を追加して、それぞれの仮想サーバの設定を記述します。以下の設定では、web サーバ 2 台と db サーバ 1 台を起動しています。
config.vm.define の第一引数が仮想サーバの名前となります。db サーバでは、db.vm.cusomise でメモリを 1GB に変更しています。(デフォルトは 512 MB)

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant::Config.run do |config|
  config.vm.box = "centos64_64"
  #config.vm.boot_mode = :gui

  config.vm.define :web1 do |web1|
    web1.vm.host_name = "web1"
    web1.vm.network :hostonly, "192.168.33.10"
  end
  config.vm.define :web2 do |web2|
    web2.vm.host_name = "web2"
    web2.vm.network :hostonly, "192.168.33.11"
  end
  config.vm.define :db do |db|
    db.vm.host_name = "db"
    db.vm.network :hostonly, "192.168.33.12"
    db.vm.customize ["modifyvm", :id, "--memory", 1024]
  end
end

vagrant up コマンドを実行すると 3 台が順番に起動します。

% vagrant up
[web1] ...
[web2] ...
[db] ...

% vagrant status
Current VM states:

web1                     running
web2                     running
db                       running

...

それぞれのコマンドは、仮想サーバ名を指定すると、対象の仮想サーバに対して実行できます。下記では db サーバを起動して、ssh ログインしています。

% vagrant up db
% vagrant ssh db

仮想サーバ間の通信は IP アドレスで可能なので、複数サーバ構成を構築することができます。

11. 仮想サーバのエクスポート

構築した仮想サーバを box ファイルにエクスポートすることができます。エクスポートした box ファイルは別の PC でインストールすることができるので、同じ開発環境をチームで共有したり、ベースとなるサーバ環境を用意しておいてプロジェクト毎に差分を変更していくなどが可能となります。

仮想サーバを box ファイルへエクスポートするには、vagrant package コマンドを実行します。

% vagrant package
[web1] Attempting graceful shutdown of VM...
[web1] Clearing any previously set forwarded ports...
[web1] Creating temporary directory for export...
[web1] Exporting VM...
[web1] Compressing package to: /Users/shin/vagrant/package.box

vagrant package コマンドが完了すると、package.box という box ファイルが作成されています。

% ls package.box
package.box

このファイルを vagrant box add コマンドでインストールすれば、この仮想サーバをベースに新たな仮想サーバを作成することができます。

まとめ

Vagrant は Chef や puppet などで自動構築する機能があるので、これらと組み合わせることでより真価を発揮します。
ただ、それらを使わずとも、手軽に触れるサーバ、壊せるサーバ、開発環境としても十二分に便利なツールです。

業務はもちろんですが、ハンズオンなどで参加者に同じ環境を配布したい時にも使えそうなので、活用していきたいですね。

参考

Q. OS X, ruby, gem のバージョン(追記)

ykitade
現行のsaharaはパッチを当てないと使えなくね?OSXとrubyのバージョンが知りたい。
http://b.hatena.ne.jp/ykitade/20130323#bookmark-137571070

OS X は「10.8.3」です。

Vagrant は公式サイトの dmg ファイルからインストールしています。これなら ruby や gem をパッケージに内包しているので問題無く動作しました。バージョンは以下です。

% /Applications/Vagrant/embedded/bin/ruby -v
ruby 1.9.3p327 (2012-11-10 revision 37606) [universal.x86_64-darwin12.2.1]
% /Applications/Vagrant/embedded/bin/gem -v
1.8.23
  • コメント (Close): 0
  • トラックバック (Close): 0

CentOS 6.x で ipv6 を無効化する

  • 2013-03-18 (月)
  • unix
この記事の所要時間: 41

CentOS 6.x で ipv6 を無効化する方法です。

Google で検索すると色々な方法が出てきますが、本家 wiki.centos.org に方法が書いてありました。

5. How do I disable IPv6?

ipv6 モジュールは無効化しない

ipv6 モジュールを無効化すると SELinux など別の箇所で問題が出るようなので、これはやらない方が良いようです。

/etc/sysctl.conf で設定

/etc/sysctl.conf に以下の設定を追加します。追加した後は再起動もしくは /sbin/sysctl -p で設定を反映させます。

# ipv6 disable
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
$ sudo /sbin/sysctl -p

/sbin/ifconfig で確認

設定が反映した後に /sbin/ifconfig -a を見ると ipv6 の情報が表示されていないことが分かります。

設定前は inet6 の行があります。

$ /sbin/ifconfig lo
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

設定後は inet6 の行が表示されません。

$ /sbin/ifconfig lo
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

ただ設定後も netstat an で見ると ipv6 で Listen しているポートがあったりするのが不思議だったりしますが。

$ netstat -an -A inet6
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State
tcp        0      0 :::111                      :::*                        LISTEN
tcp        0      0 :::22                       :::*                        LISTEN
tcp        0      0 :::50717                    :::*                        LISTEN
udp        0      0 :::855                      :::*
udp        0      0 :::111                      :::*
udp        0      0 :::38581                    :::*
  • コメント (Close): 0
  • トラックバック (Close): 0

nginx で Too many open files エラーに対処する

この記事の所要時間: 754

nginx では1プロセスで多くのアクセスを捌くので、アクセス数が増えるとToo many open filesエラーが発生することがあります。
ここでは対処法と調べた内容を残しておきます。

1. fs.file-max の確認

まず fs.file-max の値を確認しておきます。fs.file-max は、システム全体でのファイルディスクリプタの上限数となっており、この値以上のファイルディスクリプタは確保することができません。
現在設定されている値は以下で確認できます。

$ cat /proc/sys/fs/file-max
167488

通常は上記の値で問題無いと思いますが、もしこの値が不足しているようなら設定値を更新します。

$ sudo -s
# echo 320000 > /proc/sys/fs/file-max
# cat /proc/sys/fs/file-max
320000

再起動時に有効となるように /etc/sysctl.conf に記述しておきます。

$ sudo vim /etc/sysctl.conf
fs.file-max=320000

2. worker_rlimit_nofile の指定

次はプロセス毎のファイルディスクリプタ上限数を増やします。
nginx ではプロセス毎のファイルディスクリプタ上限数を指定する設定項目 worker_rlimit_nofile が用意されているので、これを記述します。設定する値は worker_connections の3〜4倍くらいが良いでしょう。

$ sudo vim /etc/nginx/nginx.conf

worker_rlimit_nofile  4096;

(snip)

events {
      worker_connections  1024;
}

設定変更後は nginx をリスタートしておきます。

$ sudo /etc/init.d/nginx restart

設定値が反映されているか確認しておきます。ワーカープロセスのプロセスID で limits を確認します。下記では「Max open files」の行がこれに当たるのですが、Soft Limit と Hard Limit 双方とも worker_rlimit_nofile で指定した値が設定されていることが分かります。

$ ps ax | grep nginx | grep worker
17064 ?        S      0:00 nginx: worker process                   

$ cat /proc/17604/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            10485760             unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             8191                 8191                 processes 
Max open files            4096                 4096                 files     
Max locked memory         32768                32768                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       8191                 8191                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0   

プロセス毎のファイルディスクリプタ上限数を設定する方法として、ulimit コマンドや /etc/security/limits.conf による記述などがあるのですが、これらは環境によってはデーモン起動では適用されなかったり、起動ファイルに記述する必要があるなど注意が必要です。

nginx ではワーカープロセス初期化処理にて worker_rlimit_nofile で指定した値をsetrlimit(2) で設定するので、ulimit などで指定された値は上書きされます。(ソフトリミット、ハードリミット両方)

参照

おまけ

調べている中で色々と試したのでメモ。
もし同じようなことを試すなら、壊しても問題無い環境で行ないましょう。(私は AWS で EC2 インスタンス(AmazonLinux)を起動して試しました。) 

fs.file-max を 0 にする。

$ sudo -s
# echo 0 > /proc/sys/fs/file-max
# cat /proc/sys/fs/file-max
0
  • 元のシェルは動作している
# ls -a
. ..
  • 一般ユーザに戻るとだめ
# exit
$ ls -la
-bash: start_pipeline: pgrp pipe: Too many open files in system
-bash: /bin/ls: Too many open files in system
  • SSH でログインできない

ローカルから別コネクションでSSHでログインしようとしたのですが、ダメでした。

% ssh ec2-user@xxx.xxx.xxx.xxx
ssh_exchange_identification: Connection closed by remote host

結局、Management Console からインスタンスを stop & start することに。。。

  • /var/log/messages にログが記録される。

再起動後に /var/log/messages を見ると下記のようなログが記録されていました。

Feb 11 13:36:51 ip-10-121-3-15 kernel: [  221.357976] VFS: file-max limit 0 reached

nginx で worker_rlimit_nofile が適用される箇所

nginx が worker_rlimit_nofile で指定した値をカーネルに設定する箇所をソースコードから探してみました。(nginx1.2.7)
src/os/unix/ngx_process_cycle.c の861行目付近でこの処理が行われています。root でマスタープロセスを実行している場合は、この箇所ではまだ root ユーザで動作しているので、ソフトリミット、ハードリミット共に worker_rlimit_nofile で指定した値が設定されています。

  • コメント (Close): 0
  • トラックバック (Close): 0

Postfix + Cyrus SASL で SMTP AUTH を設定した

  • 2012-10-29 (月)
  • unix
この記事の所要時間: 79

CentOS5.8 で Postfix + Cyrus SASL で SMTP AUTH を設定したのでメモです。

0. 環境確認

Postfix はあらかじめインストールされていました。

$ sudo rpm -qa | grep postfix
postfix-2.3.3-2.3.el5_6

Postfix でサポートしている認証方法を確認します。。今回は cyrus を使うので ok。POPとアカウントを共有するなら dovecot を使います。

$ /usr/sbin/postconf -a
cyrus
dovecot

Cyrus SASL パッケージは以下がインストールされています。

$ sudo rpm -qa | grep cyrus-sasl
cyrus-sasl-lib-2.1.22-7.el5_8.1
cyrus-sasl-2.1.22-7.el5_8.1

1. Cyrus SASL インストール

Cyrus SASL パッケージはインストールされていましたが、個別の認証処理を担うパッケージがインストールされていなかったのでインストールしておきます。

$ sudo sudo yum -y install cyrus-sasl-md5

PLAIN 認証を使うなら以下もインストールします。

$ sudo sudo yum -y install cyrus-sasl-plain

2. Cyrus SASL パスワード設定

今回は Unix ユーザではなく、Cyrus SASLパスワードファイルで認証するので、saslpasswd2 で、アカウントを追加します。
-c でアカウント追加、-u でドメイン(example.com)、そしてユーザID(user)を指定します。SMTPクライアントからは、ここで入力したユーザIDとパスワードで認証します。
なお -u で指定したドメインは realm として Postfix の設定と揃える必要があります。/etc/postfix/main.cf で $mydomain に指定しているもので良いでしょう。

$ sudo /usr/sbin/saslpasswd2 -c -u example.com user
[パスワードを入力]

パスワードファイルを postfix ユーザが参照できるようにしておきます。

$ sudo chown postfix /etc/sasldb2

3. Cyrus SASL 設定

Cyrus SASL での認証方法を /usr/lib/sasl2/smtpd.conf に設定しておきます。
今回は saslauthd ではなく、Cyrus SASL パスワードファイルを利用するので、pwcheck_method を auxprop に変更します。

$ sudo vim /usr/lib/sasl2/smtpd.conf
#pwcheck_method: saslauthd
pwcheck_method: auxprop

4. Postfix 設定

最後に Postfix で SASL 認証を有効にします。設定は /etc/postfix/main.cf に追加します。

$ sudo vim /etc/postfix/main.cf
# SMTP AUTH
smtpd_sasl_auth_enable=yes
smtpd_sasl_local_domain=$mydomain
smtpd_recipient_restrictions=
  permit_mynetworks,
  permit_sasl_authenticated,
  reject_unauth_destination
smtpd_sasl_security_options=noanonymous,noplaintext

各パラメータの意味は以下になります。

  • smtpd_sasl_auth_enable=yes

    SMTP サーバ で SASL 認証を有効にします。

  • smtpd_sasl_local_domain=$mydomain

    SASL 認証 realm を指定します。saslpasswd2 の -u で指定したものと同じ値にします。この値が一致しないと認証できません。

  • smtpd_recipient_restrictions

    SMTP サーバが RCPT TO コマンドで適用するアクセス制限。指定した制限が有効になります。
    デフォルトでは permit_mynetworks と reject_unauth_destination が有効になっているので、SASL 認証に成功した要求を許可するために permit_sasl_authenticated を追加しています。

  • smtpd_sasl_security_options=noanonymous,noplaintext

    SASLセキュリティオプションを設定します。noanonymous では匿名認証を許可しません。noplaintext では平文パスワードによる認証(PLAIN認証)を許可しません。

    (Outlook Express では平文パスワードのみサポートしてますので、クライアントとして利用する場合は noplaintext を削除する必要があります。平文パスワードを利用する場合は TLS 対応を設定して通信経路を暗号化した方が良いでしょう。)

5. Postfix を再起動

設定を有効にするために Postfix を再起動します。

$ sudo /etc/init.d/postfix restart

6. 動作を確認する

telnet で 25 port にアクセスして、SMTP AUTH が有効になっているか確認します。

$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 example.com ESMTP Postfix

EHLO a を入力します。

EHLO a
250-example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH DIGEST-MD5 CRAM-MD5
250-ENHANCEDSTATUSCODES
250 DSN

AUTH 行にて認証方式が表示されます。これで SMTP AUTH が有効になっていることが確認できました。

あとは SMTP クライアントでSMTP AUTH関連の設定をして送信ができるかテストします。

7. エラーメッセージ

設定して動作確認していた際に上手く動作せずに /var/log/mail.log に記録されたエラーです。参考まで。

Cyrus SASL パッケージ不足

warning: xsasl_cyrus_server_get_mechanism_list: no applicable SASL mechanisms
fatal: no SASL authentication mechanisms

cyrus-sasl-md5 や cyrus-sasl-plain がインストールされていなかったために発生しました。yum でインストールして解決です。

SASL パスワードファイルの権限不足

warning: SASL authentication problem: unable to open Berkeley db /etc/sasldb2: Permission denied

SASL パスワードファイル( /etc/sasldb2 )に postfix ユーザの読み込み権限が無かったために発生しました。chown で postfix ユーザを所有者にて解決しました。

参考

http://www.postfix-jp.info/trans–2.3/jhtml/SASL_README.html

  • コメント (Close): 0
  • トラックバック (Close): 0

PHPで認証して、mod_xsendfileでファイルを出力する

この記事の所要時間: 545

ApacheでX-Sendfileが利用できるmod_xsendfileをPHPと連携して使ってみました。

PHPで認証してから、許可したユーザのみにファイルを出力する、という処理を実装する場合、ファイルはdocument_root外に配置しておいて、readfile()やfpassthru()でファイルを出力するという手法を良く使います。

この方法でも問題無い場合が多いのですが、容量の大きいファイルを出力する際は思ったようなスピードが出ない時があります。

そのような時はmod_xsendfileを使って、ファイル出力の部分をApacheに任せてしまう方法が有効です。

ここでは2010/11/12時点の最新版であるmod_xsendfile 0.12を対象としています。またインストール環境はRHEL、CentOSを想定しています。

mod_xsendfileのインストール

mod_xsendfileはApache2/2.2で動作するモジュールです。

インストールにあたって、apxsコマンドが必要となるので、もしインストールしていない場合はyum等でインストールします。(apxsは、RPMならhttpd-develパッケージに含まれています)

% sudo yum install httpd-devel

次にmod_xsendfileをダウンロードします。mod_xsendfileは、mod_xsendfile.cという1ファイルだけなので、それをダウンロードしてインストールします。

ブラウザからダウンロードする際は問題無いのですが、wgetでダウンロードしようとすると証明書エラーが発生します。–no-check-certificateオプションを付けるとこのエラーを避けることができます。

% wget --no-check-certificate https://tn123.org/mod_xsendfile/mod_xsendfile.c

apxsでmod_xsendfileをインストールします。

% sudo /usr/sbin/apxs -cia mod_xsendfile.c

インストールと共にhttpd.confにLoadModuleが追加されます。

[/etc/httpd/conf/httpd.conf]

LoadModule xsendfile_module   /usr/lib/httpd/modules/mod_xsendfile.so

mod_xsendfileを設定する

mod_xsendfileを利用するために設定を行います。

httpd.confでXSendFilePathを指定する

まず、XSendFilePathで出力を許可するパスを指定します。mod_xsenfileではdocument_root外のファイルを出力することができるので、誤って意図しないファイルの出力を避けるために出力対象のパスを指定する必要があります。

例えば、出力するファイルは「/path/to/images」以下にあるなら以下のように指定します。

[/etc/httpd/conf/httpd.conf]

XSendFilePath /path/to/images

XSendFilePathは、VirtualHostやDirecortyディレクティブでも指定が可能です。Directoryディレクティブでは実行するPHPファイルのパスを指定できます。例えば、CakePHPで指定するなら以下のようになります。(出力ファイルがapp/data以下にあるとします。)

[/etc/httpd/conf/httpd.conf]


  XSendFilePath /path/to/app/data

XSendFileをonにする

次にXSendFileをonに設定します。これによりX-Sendfileが有効となります。

/etc/httpd/conf*な設定ファイルでも記載できるのですが、アプリ側ファイルの方が都合が良いので、.htaccessに設定を書いておきます。

[/path/to/app/webroot/.htaccess]


    XSendFile on

これで設定は完了です。あとはPHPから出力するファイルを指定します。

XSendFileAllowAboveは削除

なおmod_xsendfile0.9ではXSendFileAllowAboveという設定項目があったのですが、これは0.10で削除されたようです。記載しているとエラーになるのでご注意を。

PHPから出力するファイルを指定

mod_xsendfileと使って、PHPからファイルを出力するにはX-Sendfileヘッダで出力ファイルパスを指定します。

下記のようにreadfile()を記載していた箇所(下から2行目)を、X-Sendfileを出力するheader()に書き換えます。

これでmod_xsendfileからファイルが出力されます。

ちょっと気になったこと

X-Sendfileヘッダは外部に出力される?

X-Sendfileヘッダには出力ファイルのパスを指定するので、外部に出力されるのはあまり好ましいことではありません。mod_xsendfileの処理が有効になっていれば、このヘッダは除去され外部には出力されないようになっています。

0バイトのファイルが出力される

X-Sendfileヘッダで指定したファイルパスが、XSendFilePathで指定したパスにマッチしないと0バイトになるようです。必要であれば、XSendFilePathにファイルパスを追加しましょう。

404が発生する

X-Sendfileヘッダで指定したファイルが存在しない場合は404になります。

PHP以外でも使える?

X-Sendfileヘッダをレスポンスヘッダに出力できれば良いので、PHP以外でも利用できるようです。

Apacheで使えるX-Sendfile

数年前にこんなエントリも書いたりしていたのですが、たしか当時はApacheでの実装は無かったと思います。

なんとなくそのままのイメージでいたので、Lighttpd(nginxにも似た機能があったような。X-Accel-Redirectかな。)いいなあと思ってたら、実は結構前からApacheでもできるようになったんですね。

この認証ロジックはPHP、出力はmod_xsendfileと、それぞれ得意な分野に分けるのは、Unixっぽくて好きです。

sendmail .forwardで起動するコマンドのパスを変えたら、smrshも変更する

  • 2010-01-13 (水)
  • unix
この記事の所要時間: 242

何回やってもハマるから書いとく。

.forwardやsmrshについての解説はここでは飛ばします。ようは受信メールをトリガーに何か処理をするためのものです。(携帯の空メール連携なんかは良くやりますね。)

大事なところはここ。

プログラム名の前にあるパス名は全て取り除かれるため、 “/usr/bin/vacation”, “/home/server/mydir/bin/vacation”, “vacation” などは全て “/usr/libexec/sm.bin/vacation” と解釈されます。

Manpage of SMRSH

.forwardで起動するコマンドのパスだけを書き換えてもファイル名しか見てないので意味がありません。

元の設定

例えば、これまで起動していたコマンドが[/path/to/old/command]とします。

.forwardは以下な感じ。

|/path/to/old/command

/etc/smrshにはシンボリックリンクが張られています。

# ln -s /path/to/old/command /etc/smrsh/command

起動コマンドパスの変更

何らかの事情でcommandコマンドのパスが変わって、[/path/to/new/command]になったとします。

当然ながら.forwardの起動コマンドパスを書き換えます。

|/path/to/new/command

これでokと思って、メールとの連携をするとなぜか[/path/to/old/command]が実行されます。

おかしいと思って/var/log/maillogを見ると、ちゃんと[/path/to/new/command]が実行されているログが残っています。

Jan 12 18:22:30 user sendmail[12152]: xxxxxxxxxxxxxxxxx: to=|/path/to/new/command ...

[/path/to/new/command]が実行されるはずで、ログにも記録されているのに、なぜか[/path/to/old/command]が実行される。なんで?なんで?となるわけです。

実はsmrshでは冒頭の引用のとおり.forwardに書いてある起動コマンドのパスなんか見ちゃあいないんですね。見ているのはファイル名だけで、結局は/etc/smrshにある同名のコマンドを実行するだけだと。

つまりこの場合は/etc/smrsh/にあるシンボリックリンクを書き換えないとダメなんですね。

# rm /etc/smrsh/command
# ln -s /path/to/new/command /etc/smrsh/command

これで[/path/to/new/command]が新たに起動するようになります。

あまり無いことなんで

はまります。たまにしかやらないので今回もはまりました。。。

ここに書いて次こそ忘れないように。

rootで rm -rf /* したら

この記事の所要時間: 212

身の毛もよだつコマンドですね。

rm_rf

もう5年以上前の話。

お客様の本番サーバでこれをやってしまいました。そのシステムはWeb+DBなシステムでLAPPを1サーバで構成していました。つまりDBもそこに含まれていたわけです。

そもそもやりたかったのは、とあるディレクトリ内のファイルを削除するだけでした。

# rm -rf *

とすれば良かったのですが、なぜか丁寧にやろうというか何というかこう入力した「つもり」だったんですね。

# rm -rf ./*

で、実際に実行されたのが以下。

# rm -rf /*

実行して数秒しても返ってこないので、アレ?と思って、焦って CTRL+C しても後のカーニバル。

そこから先はイマイチ覚えてないけど、[cd /][ls -la /usr]とかしても「No such file or directory」「command not found」やらが出て、とにかく異常事態。

とにかく心配なのはDB。バックアップはあったけど、人が取る運用でかつ定期的にキチンと取れていなかった(この運用設計も大きな問題だけど)。ただ唯一の救いは紙ベースの伝票がある業務だったので、最悪それを全部打ち込めば復旧可能ではありました。

お客様にはとにかくお詫びをして、何千枚の伝票を入力する覚悟を決めて、RAID1の片肺を持ち帰って復旧を試みました。

データ自体は残っているはずなので何とかなるはずとは思いつつ、色々試行錯誤した結果、DBクラスタのあるディレクトリは別パーティションを切っていたので、無事にマウントすることができました!

あとはOSとアプリケーションを再セットアップして、DBクラスタをコピーして何とか事なきを得ました。

あれから root での rm は怖くなっていて、今でも指さし確認をするように一呼吸置いてから実行するようにしています。皆さんも気を付けましょう。

他にやってしまった人達

@teilnehmen: げ、、うちまちがえて、/usr/local以下をrmした。。(汗

失敗話は盛り上がります。このエントリを書くきっかけになりました:-D

@cocoitiban: @shin1x1 chmod -R 777 . / やったことあります。スペースのやろうが・・・。

スペースorz

@elf: rm -rf /&* ならやったことあります(英語キーボード)

お、何と入力を間違えたのでしょ。試しにlsでやってみると、ぞっとしました。。。

vodafone.ne.jpアドレスのメールサーバが変わってた

この記事の所要時間: 240

この話は一般的なMTA経由でメールを送る場合は問題ありません。専用メール配信システム等でキャリアのメールサーバに直接配信する時のみ関係ある話です:-D

vodafoneメールアドレスのメールサーバが気がついたら変わってました。

まず各地域アドレスのMXレコードですが、以前は各地域毎にMXが異なっていたのですが、全てmx.k.vodafone.ne.jpに統一されています。

これ自体は以前から変わっていたような気もするのですが、地域毎のメールサーバが生きていたため、実は各々のメールサーバにメールを送ることが可能でした。

しかしここ数ヶ月の間で、[rnsq].vodafone.ne.jpの地域メールサーバ自体が存在しなくなったようで、それらのアドレスに送るには、MXレコードで指定されているmx.k.vodafone.ne.jpを利用するしかなくなりました。

各地域のアドレスを調べてみると以下のようになっていました。

地域 ドメイン MX 地域MX
北海道地区 d.vodafone.ne.jp mx.k.vodafone.ne.jp ok
東北・新潟地区 h.vodafone.ne.jp mx.k.vodafone.ne.jp ok
関東・甲信地区 t.vodafone.ne.jp mx.k.vodafone.ne.jp ok
東海地区 c.vodafone.ne.jp mx.k.vodafone.ne.jp ok
関西地区 k.vodafone.ne.jp mx.k.vodafone.ne.jp ok
北陸地区 r.vodafone.ne.jp mx.k.vodafone.ne.jp
中国地区 n.vodafone.ne.jp mx.k.vodafone.ne.jp
四国地区 s.vodafone.ne.jp mx.k.vodafone.ne.jp
九州・沖縄地区 q.vodafone.ne.jp mx.k.vodafone.ne.jp

今のところ[dhtc]については地域メールサーバが生きていますが、MX自体は[k]になっているので将来無くなってしまうかもしれません。

通常のMTAのようにMXレコードを引いて配信先メールサーバを決定する場合は問題無いのですが、メールサーバを決め打ちしている場合は対応が必要となるので要注意です。

いっそvodafone.ne.jpアドレスがsoftbank.ne.jpに移行してくれると助かるのですが:-P

[2009/02/19追記]

どうやら全ての地域メールサーバ(kを除く)が使用できなくなったようです。vodafone.ne.jpアドレスは素直にMXであるmx.k.vodafone.ne.jpに送信するようにしましょう:-D

ホーム > unix

検索
フィード
メタ情報

Return to page top