ConoHaへのサーバー引っ越しレジュメ (DeleGate-Nginx/Postfix/Dovecot, Let’s Encrypt)

概要

作業概要は以下の通りだ

  • サーバーをDTI(@ServerMan)からConoHaに変更する
  • サーバーOSをCentOS (6.9)からArch Linuxに変更する
  • WebサーバーをDeleGateからNginxに変更する
  • WebコンテンツシステムをPureBuilder2からPureBuilder Simplyに変更する
  • Mimir YokohamaのWebコンテンツを別サーバーのWordPressから新サーバーのPureBuilder Simplyに変更する
  • Webメニューを完全JavaScriptフリーにする
  • Postfix/Dovecotを引っ越しする
  • DNSサーバーをConoHaとし、DNSの設定を変更する
  • 一部のドメインを廃止とする
  • 一部のドメインの役割を変更する
  • Let’s EncryptによるSSL証明書を取得し、HTTPSに対応する
  • 同証明書にメールも対応させる

なお、あまり詳細な解説はしていない。 Linuxやサーバーに関する基本的な事項に対して理解していないまま実施するのは危険であるため、 「コピペ仕様」にはしていない。 詳細に対してエラーが生じるなどの理由でより情報が欲しい場合はあるだろうが、 「言っていることが理解できない」のであれば、サーバー構築をするにはまだ危険なレベルにあると思ったほうが良いだろう。

承前: 開発

PureBuilder Simplyの開発

今回のために新しいコンテンツシステムであるPureBuilder Simplyを開発した。 この詳細は別に譲ろう。

テンプレートの開発

PureBuilder SimplyはPandocテンプレート+eRubyテンプレートというテンプレートを扱うことができる。 PureBuidler Simplyにテーマファイルがあるわけではないので自分で書く前提である。

今回は複数の表示デザインがあるのだが(現在のところは表示されるのはアーティクルモードとプロモーションモードの2つだけだが)これは全てPandocテンプレートとCSSで実現している。

もちろん、このようなテンプレートの開発はWeb屋の腕の見せ所だろう。

CSS

サイトはそれほど華やかなデザインではないが、技術的に劣っているわけではない。 その最たるものがCSSのみで書かれたハンバーガーメニューとドロワーだろう。

ポイントを簡単に言うと

  • ドロワーはfixedで上部に最初からある。display: noneで、高さは比較的新しい単位であるvhである
  • ハンバーガーメニューはtransitionを使っている
  • z-indexでメニューのほうが上になるようにする
  • 状態変遷は次のような方法でコントロールしている
    • 表示されないチェックボックスを用意し、操作対象をlabelで関連づける。これによりlabelで囲まれた要素をクリックするとチェックボックスがトグルする
    • CSSの+セレクタは同じ親要素を持つ次の兄弟要素に適用される
    • チェックボックスにはチェックが入っているときに適用される疑似要素:checkedがある
    • そこで、チェックボックス本体の直後に対象となるセクションコンテンツを置くことでチェック状態によって状態をトグルできる
    • ドロワーはfixedなのでどこに書いても構わないので、このセクションコンテンツの中におく。場所としてはメニューの位置に基づくのが良いだろう

見た目はWordPress時とあまり変わっていないが、性能は大幅に向上した。

速度

キャッシュの効かない初回アクセスで従来のWordPressページが5.57s、新しいページは約400msと10倍程度高速化した。

ConoHaサーバーのスタートアップ

立ち上げ

ConoHaのサインアップは10分もあれば可能で、即座にサーバーを使いはじめられる。

サーバー仕様は東京リージョンの1GBプランである。

512GBプランは安いが、性能が十分でなく、またマイグレーションができないため、1GBプランとしている。

この時点でサーバー選択が可能なので、Arch Linuxを選択する。 また、この時点である程度のセットアップが可能だ。 ここでSSH公開鍵の登録が可能である。

のようにして専用に生成しておく。 そしてconoharoot.rsa.pubを登録する。 名前からもわかるように、ここで登録する鍵はroot鍵である

また、開放するポートも選択できる。 ここで開放するポートはサーバーよりも上流でフィルタリングされる。 ここで透過していないポートに対するアクセスはサーバーに到達しない。

ただし、結果的にSSHポートの変更はサーバーに負担がかかるものになっている。 少なくともパスワードログインは禁止すべきだろう。

現時点ではSSHのみを通過させる。

初期設定

ConoHaのArch Linuxは標準インストールではなく、ある程度調整されている。 主にSSHログインが可能で、SSH公開鍵が登録され、パスワード認証不可となっている、といったことだ。 こうした内容は/etc/cloud/conoha-init/conoha-init.shによって確認可能だ。

複数のサーバーを接続する予定であれば変更すべき点は多いが、そうでなければ作業は平凡だ。

アップデート

まずはアップデートする。 20MB/sも出るため、超早い。

Zsh

私としてはZshがないと話にならないので入れておく。

これ移行はZshでの作業

一般ユーザーの追加

もちろん、一般ユーザーは登録されていない。

この時点で一旦再起動しないとパスワード設定ができないので、再起動しておく。

パスワードの設定

その上でvisudoを使って%wheelに対するsudoを許可する。

pacaurとyaourtのインストール

AURパッケージも扱う予定であるため、pacaurを入れておく。

# su - jrh
% gpg --recv-keys 487EACC08557AD082088DABA1EB2638FF56C0C53
% sudo pacman -S git

%git clone --depth=1 "https://aur.archlinux.org/cower.git"
%cd cower
%makepkg -si
%cd ..

%cower -d pacaur
%cd pacaur
%makepkg -si

これでAURからのパッケージインストールが可能になったので、Yaourtを入れておく。 作業が明確に自動化されていたり、システマチックに行えるのならYaourtはいらないかもしれないが、 まとめて検索するためには欲しい。

% pacaur -S yaourt

必要なソフトのインストール

ここでいう「必要なソフト」は私の取り扱い上の話だ。

% yaourt -S ruby openbsd-netcat rsync mercurial postfix dovecot nginx fcgiwrap certbot certbot-nginx vim

一般ユーザーでの鍵ログインの準備

サーバーで受け入れの準備をする

% mkdir .ssh

ローカルで鍵を生成しておく。

% ssh-keygen -f ~/.ssh/conoha.rsa

~/.ssh/configに設定する。 これは名前によるアクセスを可能にするためである。 (同一ホストに対する異なる鍵でのアクセスのため、鍵を指定せずに済むようにでもある) もちろん、読み替えること;

Host conoha-root
  User root
  Port 22
  HostName conoha.site.static.cnode.io
  IdentityFile ~/.ssh/conoharoot.rsa

Host conoha
  User jrh
  Port 22
  HostName conoha.site.static.cnode.io
  IdentityFile ~/.ssh/conoha.rsa

rootでは作業できるので、転送する。

% ssh conoha-root 'cat > /home/jrh/.ssh/authorized_keys' < ~/.ssh/conoha.rsa.pub

サーバー側でパーミッションの設定を行う

% sudo chown -R jrh:jrh .ssh
% chmod go-a -R .ssh
% chmod go-a .

そしてsshdリロード

% sudo systemctl reload sshd

DNS設定

ドメインを複数持っていない場合はいきなり完全移行できなかったりするので、 一時的にサブドメインを作るなどする必要がある。

ConoHaコントロールパネルのDNSからDNSの設定を行う。 (DNSの設定ってなんだ、という者はサーバーを立てるにはまだ早い。修行してくるべし)

これは本番サーバーのものを含む。 ただし、稼働中の本番サーバーのアドレスをこの時点で変更してはならない

そして、DNSサーバーをConoHaのものに変更する。

今回の場合従来はドメインサービス提供のDNSを使用していたため、同サービスのメニューから変更を行った。

WebとLet’s Encrypt

Nginxの立ち上げとテスト

従来がDeleGateで、Nginxにするため互換性は全くない。

Nginxは単純にstartすればアクセス可能な状態だが、移行のための準備をしていく。

まずは、Archの記述に合致するようにserver-avilableディレクトリを読むようにしたほうが良いだろう。

http {
    include       mime.types;
    include       /etc/nginx/servers-avilable/*;
    ...
}

これで/etc/nginx/server-avilableを作ればそこに配置したファイルを読むようになった。 この時点でrestartすると…

Dec 19 18:45:42 archlinux nginx[9339]: 2017/12/19 18:45:42 [warn] 9339#9339: could not build optimal types_hash, you should increase either types_hash_max_size: 1024 or types_hash_bucket_size: 64; ignoring types_hash_bucket_size

とのことなので増やす。

http {
    include       mime.types;
    include       /etc/nginx/servers-avilable/*;
    default_type  application/octet-stream;

    server_names_hash_bucket_size       128;
    types_hash_max_size                 4092;
    ...
}

配置するファイルはArchwikiに従ったもので大丈夫だ。

server {
  listen 80;
  listen [::]:80;
  server_name domain.tld;
  root /usr/share/nginx/html;
  location / {
    index index.htm index.html;
  }

  # ACME challenge
  location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /var/lib/letsencrypt;
  }
}

domain.tldは適切なドメインに置き換えること。

ここで、あまり書かれていない重要なこと。

rootというのはドキュメントルートで、locationの起点となる。 つまり、

location /foo {
  root /usr/share/nginx/html;
}

であるとき、/foo/bar.htmlにアクセスすると読まれるファイルは/usr/share/nginx/html/foo/bar.htmlであって、/usr/share/nginx/html/bar.htmlではない

そのような単純なマッピングを行いたい場合はrootではなくaliasを使う。

ACME challengeはhttp://domain.tld/.well-known/acme-challenge/randomnumberに対して行われるため、 rootで正しい。

だが、このディレクトリがないので作っておいたほうが良いようだった。

% sudo mkdir -p /var/lib/letsencrypt/.well-known/acme-challenge

あとはcertbotを使えば良いのだが…

% sudo certbot certonly --email webmaster@domain.tld --webroot --webroot-path /var/lib/letsencrypt/ -d domain.tld

その前にConoHaのサーバー設定でHTTPをあけておかなくてはならない。 そして、IPv4とIPv6両方をあけておくこと。 別々になっていることに気づかず、ACME Challengeのリクエストが到達せずに(/var/log/nginx/access.logを見ても記録されていない)随分とハマってしまった。

移行作業

各ドメインごとのドキュメントルートを作り、ファイルを配置、 さらに対応したドメインごとの設定ファイルを作る。

今回の場合、Mimir Yokohamaのページは既にPureBuilder Simplyによる静的ファイルへのビルドが完了しているし、 移行対象のものに関してもPureBuilder2で静的ファイルにビルドされているものなので、単純にファイルを配置するだけの簡単なお仕事である。

Aki SIE関連のアドレスはdiscontinuedなので、301を返す。

server {
        listen 80;
        listen [::]:80;
        server_name aki-sie.com akisie.com aki-sie.yokohama akisie.yokohama;

        return 301 http://www.mimir.yokohama/;
}

http://journal.reasonset.net/に関しては301でリダイレクトしていたので、これも反映する

server {
        listen 80;
        listen [::]:80;
        server_name journal.reasonset.net;
        return 301 http://chienomi.reasonset.net$request_uri;
}

NginxのLet’s encryptの対応

メールサーバーの移行まで完了した時点で作業を行うのだが、 certbotで必要なドメインをすべて署名してもらったら、1SSL対応化を行う。

設定例は以下

server {
  listen 80;
  listen [::]:80;
  listen 443 ssl http2;
  listen [::]:433 ssl http2;

  ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/domain.tld/chain.pem;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:50m;
  ssl_session_tickets off;
  ssl_prefer_server_ciphers on;
  add_header Strict-Transport-Security max-age=15768000;
  ssl_stapling on;
  ssl_stapling_verify on;

  server_name domain.tld;

  index index.html;
  location / {
    root /var/www/domain.tld/doc;
  }

  # ACME challenge
  location ^~ /.well-known/acme-challenge {
    root /var/lib/letsencrypt;
  }
}

メールサーバーの移行

機能させるために一旦古い証明書で作業しているため、Let’s Encryptの証明書が取れたら証明書ファイルを変更してreload/restartすること。

Postfix

Postfixに関してはCentOS 6とArch Linuxではファイル配置が異なり、 バージョンの違いから設定ファイルの違いも大きく互換性に乏しい。

そこで、CnetOSの設定ファイルを/etc/postfix.centとしてコピーする。 このほか、/etc/alias*も忘れずに移行する必要がある。 また、一旦古い証明書ファイルもコピーした。

設定ファイルの違いを見るため、diffを取る。

% diff /etc/postfix.cent/main.cf /etc/postfix/main.cf > main.diff
% diff /etc/postfix.cent/master.cf /etc/postfix/master.cf > master.diff

このdiffファイルをkwriteで2開く。 今回はNemoのsftp機能を使って開いた。

このdiffを元に手作業で変更箇所を反映していく。 主にssl, auth, virtual関連の変更を反映する必要があった。

virtual関連のファイルは/home/mailuserにあったため、これをコピーする。 ただし、これはvirtualメールボックスを含むため、移行完了までは継続的にアップデートする必要がある。 この前にvirtual用に設定していたmailuserを設定する必要もあった。

% sudo useradd --no-create-home --uid=20000 --shell /bin/nologin --system -U -M mailuser

だが、これだけではグループが適正な値にならない。 そこで、グループIDを変更した上で、ユーザーの所属する主グループIDも変更する。

% sudo vigr
% sudo vipw

Postfixをrestartして完了。

Dovecot

Dovecotに関しては設定ファイルをコピーしてそのまま使うことができた。

Dovecotの設定ファイルとして/home/mailを使っていたので、これをコピーする。

Dovecotをrestartして完了。

上流

DNSを変更してもDNSキャッシュの関係でまだ旧サーバーにもメールが届く。 ある程度はaliasを使って転送しても良いのだが、素直にPostfixで受け取りつつ、Maildirのファイルを同期していくのが良いだろう。

DeleGateを使っていたため、DeleGateのSMTP転送機能を使う方法もあるのだが、うまく動作しなかったのと、TLS接続を受け付けることができないため、諦めた。

セキュリティ関連

複雑な話になるので省略。

そこまで難しいことをしているわけではないが、 普通の対策と、らしい対策とで簡単には突破できないようにしている。

後処理(旧サーバー)

DovecotとDeleGateは停止してしまう。

Postfixは5日程度稼働したら停止してしまっていいだろう。3

その後、バックアップ(主にログや設定など)をとったらサーバー破棄である。

おまけ: ConoHaについて

DNS

ConoHaのDNSは高速である上に非常に設定しやすい。

これまで使用していたお名前.comのもの、XDomainのものは非常に設定しづらかったので、 これだけでもConoHaにする価値はあるレベルだ。

料金

コンピューティングリソースはDTIとあまり変わらないが、料金は約倍になった。

ただし、ConoHaの柔軟性やメリットを考えればそう悪くない。 両方持つと少々負担は大きいが4、一気に完全移行したためDTIを解約でき5、許容すべきコストアップだろう。

速度

速い。

「コンピューティングリソースは大きく変わらない」といったが、速度は明らかにあがった。 特にネットワークの高速化とストレージの高速化の恩恵は大きく、Webの応答性は明確に向上した。

柔軟性

DTI VPSは色々と柔軟性が足りず、困った。 特に、OSのバージョンをあげようにもサーバーがひとつしかないので、できない。 ConoHaなら

  • インスタンスが立てられるのでサーバー仕様変更もできる
  • OSが選べる。さらにカスタムイメージのアップロードも可能
  • サーバーのアップグレードが簡単にできる(512MBプランを除く)

ファイアウォール

上流でフィルタリングしてくれるのはとても嬉しい。 設定が楽になる、というのもあるが、サーバーの耐障害性が勝手にあがる上に、まあまあ重いiptablesでの処理量が減るため、パフォーマンスが改善する。 リソースが限られている中では非常に嬉しい。


  1. Let’s Encryptの証明書はSAN(Subject Alternative Name)に 対応したもので、fullchain.pemが全てのドメインに対応したものになる

  2. Kwrite/Kateにはdiffのハイライト機能があるからだ

  3. TTLは3600だったため、それに従うならば1時間待てば良いのだが…

  4. DTIが月額500円ほど(年間6000円程度), ConoHaが900円ほど(年間10800円程度)である

  5. といっても、私はDTI SIMの契約があるため、DTIの契約そのものが終了するわけではない

サーバーを使ったConoHaを使うと幸せになれるnの理由

はじめに

この記事はConoHa Advent Calenderとして書かれたものである。

Qiitaのみなさん、ConoHa愛好家のみなさん、はじめまして。 コンニチハ、ハルカデス。

Qiitaをよく見ている人ならこのブログをご覧頂いたこともあるかもしれない。 技術的な記事を期待しているかもしれないが、今回はカテゴリからしてもおわかりの通り、ビジネス的なお話である。

ConoHaについて

ConoHaはGMOグループが提供しているVPSサービスである。 以前はVPSではなくレンタルサーバーを名乗っていたような気がするが、最近はVPSだと言っている。

「三雲このは」というマスコットキャラクターで有名なサービスになる。

そのためにサービス自体については軽視されがちなのだが、サービスも特徴的である。

ホスティングサービス, VPS

まず、かなり難しい区分なのだが、web基準で考えると次のような段階がある。

  • 用意されているアプリを使うだけ
  • 用意されているスペースにファイルが配置できる
  • サーバーを操作できる
  • サーバーを構築できる

アプリを使うだけ、というのはブログやECなどの特定の機能を持ったアプリケーションによるサイト公開が可能になっているものだ。 主立ってはamebloやSeeSaaなど、ConoHaと同じGMOグループではGMOペパボのJUGEMや、GMOメディアのYaplogなどがある。

さらに、GMOペパボには「ホームページ作成サービス」系のGoopeもある。 Qiitaもこの形態のひとつであるといえる。

用意されているスペースにファイルが配置できるのは、かつてのGeocitiesが有名で、 これを有料のサービスで行うというのはかつてはひとつの頂点であったとも言える。 最近は減ったがかつては多彩なサービスがあった。 GMOペパボのLolipopはその中でも有名にして定番のものだろう。私も以前は使用していた。また、同社はHetemlという上位サービスも用意している。 GMOインターネットもドメインサービスのお名前.comの一環として提供していたりする。

残る2者はいわゆるroot権限がとれるタイプのものだ。 前者はサーバーの再インストールや、サーバーのOS選択などは基本的にできない。後者はサーバーのインストールや追加なども可能なものだ。

VPS

VPSという語に明確な定義というか、区別はないようだ。 VPSという語が登場した当初のことを考えると、基本的に「rootがとれる従量課金のサーバー」という意味合いであったように思われる。 実際、VPSと名乗るもので、従来のホスティングサービスとの違いが「従量制課金である」ということ以外にあまり明瞭なものがなかったのである。

VPSで当初よりAmazon AWSは非常に強力なサービスであったが、 通信料など外部的要因によって変動する事象によって従量課金となるため、軽い気持ちでサーバーを立てた人が月に数十万円の請求をされる事案も少なくなかった。

そのために、金銭的にかなり余裕があるのでない限りVPSは選択肢にはならなかったのが実情だ。

ConoHaは当初その中間のような存在だったと言える。 VPSのようにいくつものサーバーを立てることができるが、料金はサーバー台数*プランというシンプルなもので、過大な請求がかかることなく安心して使用することができた。

現在のConoHa

当初と比べConoHaは幾分VPSらしくなった。 例えば計算資源からは独立したストレージ容量や、時間従量制などだ。

時間従量制というのは、携帯電話料金のような上限つき従量制と近いものだが、 例えば512MBプランは月額630円で、1時間あたり1.0円。24時間*31日だと744円であるため、それよりも安く収まることになる。

普通は従来型ホスティングサービスが月額制なのだから、現代では珍しい純粋なユーザーメリットによる料金体系である。

このために一時的な用途やテストサーバーなどを含め気軽かつ柔軟にサーバーを用意できることがConoHaの大きな特長である。

ライバル

このようなサービスはあまり類をみないように思うが、最大のライバルはやはりさくらインターネットによる「さくらのVPS」だろう。

このサービスも月額制で複数インスタンスが利用可能だ。 人気はこちらのほうが高く、周囲にも利用者が多い。

クーポンをイベントで配布していた、という点でも類似である。 ただし、ConoHaはクーポン配布はやめてしまったようだ。

お客様のサーバーにConoHaを

私が代表を務めるMimir Yokohamaでは、サーバーを使用するサービスを提供する際には指定がない限りConoHaを使用している。

これは、私がConoHaからコミュニティ支援を受けているという事情もあるが、 別にこれは他サービスの利用を制約するものではない。 ConoHaを使用しているのは、それによってエンジニアとお客様が幸せになるためだ。

サーバーを伴うサービスの展開戦略

自社で使用している巨大なインスタンスにすべて格納する「共有サーバー」スタイルを採用している事業者も多いだろう。

実際のところ、Mimir Yokohamaのお客様のご要望は、サーバー一台用意するのはコスト的に見合わないタイプ(アクセスの少ないウェブサイトなど)と、 共有させるのは難しいタイプ(XMPPサーバーを立てたり、アプリケーションサーバー必要とするケース)が混在している。

ひとつの仮想ホストですべてを賄おうとするのは、管理面を考えてもあまりうれしいことはない。 パフォーマンス低下によってお客様にもストレスをかけてしまうかもしれないし、 セキュリティ的に分離したいケースがあることも普通に考えられるのだ。

つまり、多少アップグレードしてでも複数の環境をホスティングする共有タイプのサーバーを提供してコストを抑えるべきケースと、 性能は控えめでもサーバーを専有すべきケースがある。

これをroot権限があるタイプのレンタルサーバーで行うのは、費用がかさむ上に管理も大変だ。 以前そのようなサービスを提供していたこともあるが(Mimir Yokohamaができるより前の話だ)、正直労力に見合わないものであった。

ConoHaならば仕様の異なる複数のインスタンスを立てることができる。 事業の拡大とともにインスタンスをどんどん増やしていっても構わないのだ。

2GBプランであれば共有webサーバーにも十分耐える。 上手に構成すれば1GBプランでも十分だ。 (分散できるのであれば1GBプランのホストを2つ用意するほうが負荷は厳しくない)

負荷の少ない環境で専有サーバーが必要なケースは512MBプランのインスタンスを使うといいだろう。

性能面

私は個人サイトはDTIのサーバーで運営しているのだが(このブログはまた違う)、速度的にはConoHaにとてもかなわない。 最大の理由は「すべてSSDで提供している」というストレージバックボーンだろう。 Mimir YokohamaのWebプランは可能な限り静的ページとして提供する構造をしている。 そのため、ほとんどストレージ性能と帯域で速度が決まる。「高速なウェブページ」を売りにする以上、インフラが遅いことによる限界というのは悲しいものがある。

もちろん、湯水のようにお金を使えば大幅なパフォーマンスアップも可能なのだが、 「リソースが足りてさえいればコストを抑えても性能が確保できる」というのは非常に嬉しいポイントだ。 仕方なくサーバーを使われるお客様というのは、なるべくなら費用は抑えたいとしつつも、性能はストレスのないものだと信じているものだ。 あまり性能が劣っているとお客様が悲しまれてしまう。

ConoHaは最小の512MBプランを、ちゃんと「多くのリソースは使わない」というだけの理由で選ぶことができるのだ。

以前はConoHaよりもさくらのVPSのほうが安かったのだが、現在は最小プランではほぼ同等の内容でConoHaのほうが少し安い。 1GBプランはConoHaのほうが安いにもかかわらず、SSDの容量はConoHaのほうが大きい。 Mimir Yokohamaは一般のお客様が中心であるため、これらのプランが中心となる。

この「費用の小さいプランでも満足度が高い」ということは、サービスを提供する際の価格設定を抑えられるということにもなる。 サーバー側の金額が安いとあまり大きな利益を載せにくいという面はあるが、安い価格から提供可能なサービスは競争力を向上させるだろう。

イメージ保存機能

サーバーを「凍結したい」という場合や、「移行したい」「サーバーは廃止するがデータは損失したくない」というケースは意外と多い。 サービスを開始する際にサービスを終了することを考えたくない気持ちはわかるのだが、 良いサービスのためには「お客様が気持ちよく完了できること」は不可欠な要素なのだ。

イメージ保存機能はこうしたケースにおいて極めて便利だ。 イメージ保存機能の存在によってお客様に柔軟な終了戦略を提示することができる。

API

ConoHaにはAPIがある。 これはさくらのVPSに対するアドバンテージでもある。

APIを使用することでサーバーの管理を容易にすることができる。

多くのサーバーを展開する場合において手作業の量を減らせば価格設定も抑えることができ、 それは事業の強みとしても活かせるはずだ。

しかも、APIはシンプルなJSONであるため、書くのも簡単。 このような機能をスマートに活用することは、優れたサービスとして欠かせない作法であろう。

カスタムISOからのインストール

重要なのはISOイメージによるインストールが可能なことだ。

Mondo RescueによってシステムバックアップのISOイメージ化か可能であるため、ローカルな仮想環境で練った(アップデートも済ませた)サーバーイメージをテンプレートとして使用することで、スタートアップの時間と労力を減らすことができる。

これは納期を急がれるお客様のご要望にお応えできることに加えて、 コストの大幅な削減にもつながる。

無駄な手間をかけることで価格を釣り上げるような戦略はとるべきではない。 お客様が本当に必要とされているもののためにより多くの労力をかけるべきなのだ。

これは、VPSでなければ難しい(場合によってはVPSですらも難しい)Arch Linuxによるサーバーによって 少ないリソースでもパフォーマンスを発揮するという点でも活用されている。

海外リージョン

今は日本でも海外の顧客を相手にする企業は大変多い。

B2Bサービスを提供する場合、お客様は海外向けにサービスを提供される場合は多いのだ。 実のところ海外とのトランジットが割と少ない日本のサーバーというのは、海外から見るとあまりうれしくない。 どこかの果てにある得体のしれないサーバーであり、実際に応答が遅い。 これは我々が日本からベルギーやデンマークあたりのサーバーにアクセスすれば体験できる事象だ。

私もお客様が海外と商売をされているケースというのは「意外なほど多い」という印象を受けている。 このようなケースにおいてはやはりサーバーも日本ではなく適切な場所に配置したいところだ。

ConoHaにはアメリカとシンガポールという、「適切な場所」としやすい2箇所が用意されている。

このおかげでよりお客様にとってかゆいところに手が届くサービスを展開しやすくなっている。

提灯記事?いやいや…

なんといってもこの記事はConoHaのAdvent Calenderのために書いたものだし、 私がコミュニティ支援を受けているということもあり、PR記事疑惑は拭えないところだろう。

だが、そんなことはない。 なんといっても、特にこの記事作成にあたってConoHaから見返りを得ていないのだ!!!!! (もちろん、見返りをいただけるのなら大歓迎である!!!!!)

実際にMimir Yokohamaのサービスというのは、「ConoHaがある」ということが先にあった。 つまり、Mimir Yokohamaにラインナップされているサーバーを利用するタイプのサービスは、 いずれも私が「ConoHaがあるからこんなこともできる」という発想によって設定したものであり、 ConoHaがなければこれらのサービスは存在しなかった可能性が高い。 実際、ConoHaとの関係がなかった初期にはサーバーを使うサービスというものは非常に限定的であった。

もちろん、ConoHaがなくてさくらのVPSがあるという状況であれば、 いずれそのようなサービスを考えてさくらのVPSを使って展開していた可能性は高いが、 ConoHaを使うことによってお客様にメリットを提供しつつ、 それを事業の強みとして活用することができているのだ。

それどころか、一時期「ConoHaを活用できる」ということは、事業の価値において非常に大きな割合を占めていた。 そのようなVPSの利用があまり一般的でなく、圧倒的に低価格に、かつ柔軟にサービスを構成することが可能だったからだ。

Mimir Yokohamaはお客様の笑顔のために、ConoHaを利用しています。

新品コンピュータを初期状態に戻せるようにバックアップ

これは何

新品コンピュータを購入したときに、完全に元の状態に復元できるようにするものである。

バックアップ手段は色々とあるのだが、もっとも確実かつ完全な手段が「ディスクの完全なクローンを得る」という方法だ。

その方法について紹介しよう。

なお、これは「やや複雑な手段をとっても効率的に、確実に元に戻せる手段を構築したい」という人向けであり、 そのような場合は諦めるという人や、考えたり努力することを回避したいという人は対象としていない。

新品ならでは

新品のコンピュータのディスクは、ファイルをコピーしているわけではない。 データを流し込んで複製しているのだ。

一般的には、先頭からWindowsシステム用の領域が書き込まれ、そして末尾にバックアップ用の領域があるのが一般的である。 バックアップ用の領域はパーティションが切られ、その中に格納されていることもあるし、パーティションレイアウトの末尾の後ろに配置される場合もある。

このため、データが書かれているのは先頭と末尾のみで、中間は全くデータがなく、0が書かれている。

128GBのディスクをフルクローンした場合は当然ながら128GBのファイルが出来上がる。 だがしかし、連続する0のデータは圧縮すると極めて小さくなる。そのため、このようなディスクイメージは圧縮することによって非常にコンパクトにできるのだ。

これが使用していたディスクであればうまくいかない。 データが記録されていない領域も0で埋められているわけではなく、ごちゃごちゃに記録された状態になっているため、フルディスクイメージを圧縮したときに極端に小さくなることはない。 そのため、完全性や確実性が低下してでもなるべくコンパクトな形式でバックアップするのが一般的なアプローチになる。(partimageやntfscloneなどを使うということだ)

書き戻しに関して

次のようにして取得したイメージは

$ dd if=/dev/sda | xz -z -c > /mnt/sda.img.xz

次のようにして書き戻すことができるのだが

$ xz -d -c /mnt/sda.img.xz > /dev/sda

この場合、128GBの「元のディスクに」書き戻すことを前提している。

サイズが違う場合どうなるかというと、ディスクが大きければパーティションがディスク全域ではなく途中で終了し、途中にリカバリー用の領域が置かれた状態になる。 ディスクが小さい場合はパーティションがディスクのサイズよりも大きく設定されてしまう。 この修正はちょっと大変だ(Windows上では難しいかもしれない)。

だが、実際のところリカバリー領域は書き戻しによって復元できているため、なくなってしまっても構わない、と考えられる。 そして、パーティションテーブルはパーティションの内容より前にある。 よって「先頭からデータのある分だけを復元すれば良い」ということになる。

このため、復元したデータは全部書く必要はなく

$ xz -d -c /mnt/sda.img.xz | dd of=/dev/sda bs=1024M count=10

のようにすれば良い(1024MB=1GiBを10回なので10GiB書き込むことになる)。 内容としてはどのみち残りは0なのであり、書いてもかかなくても同じだ。 ディスクサイズが異なる場合はパーティションサイズのみ変更すれば良い。

バックアップの仕方

Linuxで起動する…ところまでは省略しよう。 UnetBootinなどの使用は勧めないが。

まずはpartedでディスクを確認しよう

$ sudo parted -l

これによって各ディスク名(/dev/sdaなど)を得ることができる。 ここではバックアップ対象のディスクはsdaであるとしよう。 異なる場合は読み替えること。

リムーバブルディスクにバックアップ

モダンなLinuxシステムではリムーバブルディスクを接続すれば認識され、ファイルマネージャが起動する。

ファイルマネージャ上でリムーバブルディスクを開き、そこで右クリックから端末(ターミナル)を開く、とする。 そして

$ dd if=/dev/sda bs=64M of=recover.img

のようにする。 もし、圧縮も同時に行うのであれば

$ dd if=/dev/sda bs=64M | xz -z -d > recover.img.xz

のようにする。時間がかかっても圧縮率を上げるのであれば

$ dd if=/dev/sda bs=64M | xz -z -d -e -9 > recover.img.xz

とすれば良い。

なお、NASなどのネットワークドライブ上に保存する場合も似た手順で行うことができる。

ネットワーク上のLinuxホストに対して行う

もう、説明がいるのかどうかも怪しいが、計算機母艦となるLinuxを運用している場合は 受け取る側のLinuxホストではnetcatをインストールしておく。

そしてnetcatで通信を待機する。 次の例ではBSD netcatを使用する。

$ nc -N -l 22500 > recover.img

受け取り側で圧縮する場合は次のようにする。

$ nc -N -l 22500 | xz -z -d -e -9 > recover.img.xz

送り側はncなどはない場合が多いだろうし、ライブブートではインストールも難しい。 しかし、bashにはTCP機能があるため、これを利用する。

$ dd if=/dev/sda bs=64M > /dev/tcp/192.168.1.128/22500

注意点は、bashでなければならないこと、そしてファイルに書けばよいわけではなくリダイレクト機能を使わなければいけないことだ。

LinuxでReakTek r8152/r8153 USB LANが頻繁に切断される

QtuoのUSB 3.1 Type-CのUSBアダプタを使っているのだが、これは

  • 2x USB 3.0 Type-A
  • SD card reader
  • microSD card reader
  • RJ-45 1000BASE-T Ethernet

という構成になっている。 これらはおおよそ正常に動作するのだが、イーサネットに関しては頻繁に切断されてしまう。

調べるとACPIのリセットで解決するという声もあるのだが、 それでは解決しなかった。

dmesgでエラーの内容としては、

usb_reset_and_verify_device Failed to disable LTM

と記録されている。

これはtlpをこのデバイスにおいて無効にすることで解決する。 `/etc/default/tlp“において

USB_BLACKLIST="0bda:8153"

とすることで解決する。

それで頻繁に切断されるという問題は解決するのだが、ある程度通信していると、

Tx status -71

というエラーによって切断されてしまう。

netdev listに この件が報告されている。

これは、RealTekが配布しているドライバーで解消されるような情報もあるのだが、 AURにあるドライバは2.08.0であり、最新版は2.09.0なのだが、 2.09.0でも

LINUX driver for kernel up to 4.8

ということであり、もはやまともにサポートされていないようだ。 (それでも、このDKMSモジュールをインストールすることで1.6GBのISOイメージのダウンロードとアップロードは問題なく行えた)

RealTekのNICはWi-Fiのほうも途中で通信不能になるようなバグがあり、 LinuxにとってはRealTekのNICはあまりよくないという印象だ。 (ただし、RT2870チップは問題なく動作する――むしろ非常に良好に動作する)

Wi-Fiの場合は機種を限定するものの避ける方法はあるが、 USBのネットワークアダプターは多くがRealTek製で、選択肢が非常に難しい。

動作を確認できているわけではないのだが、USBイーサネットアダプターとしては AX88772/AX88719というASIX製のチップを採用したものがあるようで、 これらはプロプライエタリドライバがあるほか、プロプライエタリドライバを(手動で)導入しなくても動作するディストリビューションもあるようだ。

Pandoc + TexLive (LuaTex)によるPDF生成と和文フォント

Pandocは非常に便利なツールだが、オプションがなかなか複雑で、特にPDFにする場合などは長くて毎回打つのはしんどい。 このことは初心者にとって使いにくい理由のひとつになっていると思い、簡単に扱うためのZshスクリプトを書いた

だが、この際に思わぬことに気づいた。

LuaTeX(TexLive)経由でPDF化する際に、ドキュメントクラスがarticlereportなら-V mainfontが効くのだが、ltjarticleltjsarticleでは効かない。 正確には、欧文にしか効かない。

追いかけると、LuaTeXはmainfontは欧文フォントの指定であり、和文フォントは独立してmainjfontを用いること、とある。 しかし、-V mainjfont=...は効かない。

検索するとmainfontを指定すれば良い、というページばかりヒットするのだが。 諸君は本当に試したのか。見聞きしたままコピペしてないか。

というわけで、Pandoc 日本ユーザー会の[藤原 惟さん(すかいゆきさん, @sky_y)](https://twitter.com/sky_y)にご報告させていただいた。

すると、一時間ほどでご回答いただけた。

-V CJKmainfont=...とすると和文フォントが指定できる、と。

-V mainfont=...で欧文フォント、-V CJKmainfont=...で和文フォントの指定。 早速スクリプトにも反映した。

今回の解決はすべて藤原惟さんのおかげである。 改めて、この場を借りて感謝申し上げたい。

さて、Pandocは非常に便利なツールなのだが、多様なフォーマットに変換する都合からか、微妙なバグが割と多い。 そのバグが変換先エンジンの問題なのか、Pandocの問題なのか切り分けにくいので毎度なかなか辛い。

現時点では--pdf-engine=lualatex環境下において

  • テーブルが右に突き抜けてしまうことがある (発生するテーブルであれば、書き方を変えても駄目)
  • ページを下に突き抜けてしまうことがある (脚注やページ番号にはかぶってページの外に出る。 表と画像を併用すると発生しやすい)

ということを把握している。 どちらもPandocよりはTexLiveが原因のような気がするが、発生原因が特定できず、現在のところ謎バグである。

また、

  • インラインコードの--がTeXのハイフンに変換される
  • コード中の"がTeXの引用符に変換される

という問題もあるのだけれど、これも冒頭で紹介したスクリプトにおいては発生せず、

pandoc -s --filter pandoc-crossref -f markdown -V geometry=a4paper -V geometry:margin=1in -V mainfont="Source Han Serif JP" -V monofont="VL Gothic" -V documentclass=ltjarticle --pdf-engine=lualatex --toc "$1" -o "${1:r}.pdf"

とすると発生する。CJKフォントを指定しても発生する。ドキュメントクラスをltjsarticleにしても発生する。 けれど、スクリプトが実行しているデフォルトの内容としては

pandoc -s -f markdown -V geometry=a4paper -V documentclass=ltjsarticle --pdf-engine=lualatex --toc -o test.pdf test.md

なのだけど発生しない。元のコマンドでフォント指定をやめると発生しなくなる。 どうも試していくとmonofontを指定するとフォントがなんであれ駄目っぽい。

AURにfcitx-mozc-neologd-utを上げました

fcitx-mozc-neologd-ut/mozc-neologd-utをメンテナンスされていた方がut2に移行したため、 これらのパッケージがAURからなくなっていた。

そこで、私がAURにputしておいた

これらに関してはupstreamの作者の方(utuhiro氏)がAnterogos使いであるため、 PKGBUILDが標準で用意されている。 私がアップロードしたものはこれを調整したものとなっている。

行った調整は主に次のようなものだ。

  • メンテナの表示を私に
  • pkgnamefcitx-mozc-neologd-utを先に
  • 本体アーカイブをインターネットから取得するように
  • 本体アーカイブのチェックサムの追加
  • makedependsqt5-baseを追加(mozcが要求する)

手法はおおよそ先人に近いもの(fc2 blog))だが、次のような点が違う

  • ダウンロードはupstreamから行っている
  • makedependsなどの調整を行った

久しぶりのPerlの使い心地は

Perl 5はもうすっかり廃れてしまった言語で、Perlが好きな私も最近はあまり長いプログラムは書かなくなっていた。

Perl自体が滅びたとか、恥ずかしいような風潮もあるのだけれども、LinuxerにとってはSedやAwkを発展させたさらなる便利ツールなので、Perlが活用できていないというのはコマンドライン活用が下手ということになるので、そっちのほうがだいぶ恥ずかしい話になる。

とはいえ、そういう日常的な作業としては長くても50行いかないような話であるため、プログラミングらしいプログラミングというのはPerlで行うことはごく少なくなってきている。 あえてPerlで書くことが好ましい状況というのは、$_をどれだけ多用するかという問題だし、そうなると長いコードを書くにはとても向かない状況が出来上がる。

そのため、「汚くてもいいのでインスタントに書く」ときだけPerlを使うようになるし、そもそもほとんどの場合ワンライナーで使うことになる。

こうなってくるとそれなりの長さのプログラムをPerlで書くという機会はめっきりなくなってしまい、50行を越えるプログラムを使うということは稀になってしまっていた。 50行に満たないプログラムなら、グローバル変数や雑な変数乱造は普通にあるし、サブルーチンやリファレンス、オブジェクトなどを使う機会はなかった。

だが、久しぶりに仕事で集中的にPerlを触った。 が、なかなか手ごわかった。

一番つらかったのは「デリファレンスの不明瞭さ」だ。

Rubyで言うと次のようなコード

result_list.each do |one_result|
fname = one_result[:fname]
one_result.each do |match|
linenum = match[:linenum_from] + 1
match[:lines].each do |line|
printf '%d %s', linenum, line
linenum += 1
end
end
end

Perlではこうなった。

foreach my $one_result (@{$result_list}) {
my $fname = ${$one_result}{fname};
foreach my $match (@{${$one_result}{result}}) {
my $linenum = ${$match}{linenum_from} + 1;
foreach my $line (@{${$match}{lines}}) {
FH->printf('%d %s', $linenum, $line);
$linenum++;
}
}
}

なにをどうデリファレンスするか、が複雑すぎて極めて混乱した。 単純にこれはリファレントを得たいということを実現できる仕組みになっていないのだ。 このあたりはもう少し美しい解法があってもよかったのではないかと思う。 Perl4との互換性から生まれた仕様という気もするが、このあたりの仕様がぐちゃぐちゃなPHPよりもひどい。

また、先のブログでも書いたWindows環境でのUnicodeファイル問題

open(FH, ">", "ภาษาไทยファイル名");

これが文字化けるのだ。Rubyだと問題ない。

File.open("ภาษาไทยファイル名", "w")

もっとも、RubyはMinGWで動作するし、PerlでもCygwinでは問題ないので、ここはPerlの問題とは違うかもしれない。 ただ、全体的にPerlが「アメリカ人の感覚だなぁ」と思うことはある。 優れていたはずの文字エンコーディングに対するサポートも、ちょっと甘い。

Perlの文字列操作は割と独特。 PerlはUCSを採用し、内部エンコーディングにUTF-8、内部改行文字にLFを採用している。 そして、「文字ベースで処理するにも関わらず、文字列としてバイナリがデフォルト」。

次の場合はいずれもバイナリ文字列となる。

my $str1 = "こんにちは世界";
my $str2 = <>;
open(FH, "<", "somefile");
my $str3 = <FH>;
close(FH);

次のようにすればいずれも内部文字列となる。

use utf8;
my $str1 = "こんにちは世界";
binmode(STDIN, ":utf8");
my $str2 = <>;
open(FH, "<:utf8", "somefile");
my $str3 = <FH>;
close(FH);
use open IN => ":utf8";
open(FH, "<", "somefile");
my $str4 = <FH>;
close(FH);

文字列は「バイナリ」か「内部文字列」かの2種類だ。 内部文字列はUTF-8+LFになっているので、そのまま出力すると元の文字エンコーディングに関わらずUTF-8で出力される。

open(FH, "<:encode(euc-jp):crlf", "somefile");
my $str = <FH>
close(FH)
print $str; # 入力はEUC-JPだったけれども、「内部文字列をそのまま出力」するとUTF-8になる

Perlは文字列操作を常に正規表現で行う。 固定文字列検索は次のような具合になる。

$str =~ /\Qこ.ん.に.ち.は\E/;

あるいは

my $search_q = quotemeta("こ.ん.に.ち.は");
$str =~ /$search_q/;

きちんと内部文字列にしておかないと次のようなコードは失敗する。

$str =~ tr/A-Z/A-Z/;

Perlでは「バイナリ」「内部文字列」と言っているが、その実ASCIIとUTF-8だったりする。 内部文字列フラグがない文字列は、ASCII前提の頭で処理されるのだ。

そして、困ったことにPerlでは内部文字列フラグつきの文字列をバイナリに戻す方法がない。 文字列を内部文字列化することはできる。

use Encode 'decode';
my $str = <>;
my $internal_str = decode("UTF-8", $str);

ただし、このとき必ず変換される。 すでにUTF-8であると言えば変換されないわけではなく、 UTF-8として不正な文字は?にされてしまう。

encodeした文字列はバイナリであるが、これも変換を伴う。なので、次のコードではASCIIに変換しようとして、いずれも変換できないために出力は??となる。

use Encode qw/encode decode/;
my $str = "世界";
my $utf = decode("UTF-8", $str);
my $asc = encode("ASCII", $utf);
print $asc;

内部文字列として処理したあとURIエンコーディングを行う必要があったため、怪しげな超絶技法を駆使したりした。

{
use bytes;
$qword =~ s/(\W)/"%".uc(unpack("H2",$1))/eg;
}

だが、UCSとして文字列を扱うための機能自体は備わっている、ということを感じる。 一応固定文字列検索をindexで行う方法もある。

use utf8;
binmode(STDIN, ":utf8");
while(<>) {
if (index($_, "こんにちは") >= 0) { print; }
}

substrは実用的ではないほど使いにくいため、s///を使わざるをえないが。 文字列操作は割と悪くない。Perlの得意分野だからか。 もっといまいちな文字列の取り扱いをする言語は多く、古いわりにはまっとうに動作する。

だが、文字列を問答無用で破壊的に変更してしまうのはちょっと辛いものがある。 例えば次のようなケースでは変更前の値を変更後に使用する必要があるためコピーが必要になる。

sub get_and_save($) {
my $qval = $_[0];
my $quri = $qval;
{
use bytes;
$quri =~ s/(\W)/"%".uc(unpack("H2",$1))/eg;
}
getstore("http://www.example.org/board?q=$quri", "${qval}.html"));
}

破壊的に変更したくない時は決して珍しくないので、Perlを微妙に感じる場合のひとつだ。

ちなみに、文字列の扱いに関してはRubyはCSIを採用するため「変わっている」。

文字列にはエンコーディングに関する情報を与えることができるが、 特にこの場合に変換はしない。

str.force_encoding("EUC-JP")

Ruby2.0からは文字列はdefault_externalの値のエンコーディングだとみなされる。ロケールに従うため、ここではja_JP.UTF-8を使用していることからUTF-8とみなされ、 変換しようとするとUTF-8からの変換が行われる。 次の例ではUTF-8からEUC-JPに変換している。

puts "こんにちは世界".encode("EUC-JP")

open時にPerlみたいな方法で指定することもできるようになっているが、 このときにPerlのように内部文字列の変換を行うわけではない。 次のコードではEUC-JPのファイルを読んでEUC-JPで出力する。

File.open("somefile", "r:euc-jp") {|f| puts f.gets }

EUC-JPと認識されているので、変換することはできる。

File.open("somefile", "r:euc-jp") {|f| puts f.gets.encode("UTF-8") }

あるいは内部エンコーディングを指定して自動で変換することもできる。

File.open("somefile", "r:euc-jp:utf-8") {|f| puts f.gets }

だが、Rubyは独特なので、Perlがモダンでないという話とは繋がっていない。 Rubyの文字エンコーディング関連は、日本人によるものだけに非常に楽だ。

Rubyと比べてという話にはなるのだけれど、JavaScriptやPHPとくらべても、彼らが当たり前に「よきに計らってくれる」ことを手動で面倒をみてあげないといけなかったりして、ちょっと不自由な感じがある。

文字列と数値をコンテキストで分けている、というのもちょっと微妙だ。 それだったらまだ型を指定させたほうがマシであるように感じる。 どうしても==eqの書き間違えなどは発生するし、割とデバッグもしづらい。

Perlへの批判は大部分がPerl4に起因するもので、「書き方次第」なのだが、統一感がないというか、ちょっとすっきりしない仕様というのはままある。 これはZshを使っていてbashに感じるものと似たもので、古いが故か洗練されていないというか、なにより直感に反する要素がしばしば入ってくるのがストレスだ。

まず、リファレンスを多様するのであれば他のプログラミング言語のほうが楽だ。 そして、デリファレンスをたくさん書くのであればオブジェクト指向で書くほうがずっとスマートだ。

Perlに求めていたのはそのあたりの改善だったのだが、それはPerl6によってではなく、RubyやPythonによって達成されてしまった。 だが、これらはすでに「Perlの良さ」はないため、Perlの新しいバージョンにはその改善を求めたかった。 実際にはPerl6は全く異なる言語であり、(なかなか良いように見えるが)望んだものとは少し違う。

依然としてUnixツールとしてのPerlは優れたものであり、これよりUnixツール的に優れた言語は登場していないが(Streemが実用レベルに達すればその状況は変わるかもしれない)、本格的なプログラミングにはいささか辛い部分も見え隠れする。 特に、「わかりづらくなりやすい」という点に関しては問題が大きく、さらに妙に面倒な状況が出る場合もある。

今、Perl5を使うにはいささかの割り切りが必要だ。 だが、他の大多数の言語よりも依然として良いのもまた事実。 プログラミング言語が業務的に語られることの多い昨今だが、優れた言語は優れた言語なのであるということを改めて実感させてくれる部分もあった。

Gtkなデスクトップ環境でGtkとQtのテーマと格闘する

Gtk環境でのQt

作業環境はManjaro XFceであり、実際の利用環境はCinnamonで、KDE Plasmaは入れていない。

KDE環境だとQtのstyle (QtだとthemeではなくてVisual Styleと呼ぶ。Windows風)は色々入っていたりするのだけども、こういうGtkデスクトップ環境だと古臭いの(ClearlooksとかMotif)とかしか入っていなかったりして、Qtアプリの見かけがだいぶよろしくない。

そこでまず、QtらしいStyleを導入する。 Qt4とQt5のStyleは分かれていて、Manjaro/Archだとkde4やkde5という名前で分けられているようだ。 とりあえず見た限りplasmaパッケージに含まれてはいるものの、KDE本体に依存するようなパッケージはない模様。

Qtのテーマ設定はQt4はqtconfig-qt4、Qt5はqt5ctで行う。

これでKDEとは関係ないけれどQtを使うアプリ(例えばEksterteraとかq4wineとか)の見た目がよくなり、 Ruby qtbindingsも快適に使うことができる。

そう、そもそも今回、ruby-qtbindingsを使ったら見た目がひどかったことからスタートしているのだ。

Gtk2テーマ

Gtk2は恐ろしく鬼門だ。 特に厄介なのが次のアプリ

  • Sylpheed / Claws Mail
  • Mikutter
  • Leafpad

なぜならば

  • Sylpheed / Calws Mailはテーマによっては起動しない。
  • Sylpheed / Claws Mail / Mikutterはスクロールバーがおかしいテーマが多い
  • MikutterはBreezeテーマなどタブの表示が変になるものも多い
  • スクロールバーずれ程度で問題がなく美しいOxygen-GtkはLeafpadはSEGVる

Leafpadを分離するのが良案か。

#!/bin/bash

GTK2_RC_FILES=/usr/share/themes/Adapta-Eta/gtk-2.0/gtkrc exec /usr/bin/leafpad

なお、Gtk3版のl3afpadを使うという手もある。

XFce4は.gtkrc-2.0や環境変数で使用するgtkrcを指定していると、全体的にそれに従う。 外観の設定に左右される範囲よりも大きいようだ。

Oxygen-gtkは良いのだけどなかなか問題がある

  • Gtk3アプリでは表示がおかしくなる可能性が高い
  • Xfce4ではアイコンが反映されなくなる
  • 部分的にUIフォントを頑なに無視する
  • もう開発も止まっているようで今後使えなくなるかも

安定して使えるGtk2テーマがなくなるのはなかなか痛い。見た目はよくはないが、XFceテーマシリーズが安定しているようだ。

Gtk3

Gtk3のほうはもっとシンプル。

Gtk2テーマを指定してXFceを起動すると、Gtk3テーマもそれに従おうとするので、明示する必要がある。

Gtk3も同一テーマで構わないものであれば良いのだが、oxygen-gtkは駄目だ。

if [[ $DESKTOP_SESSION == xfce ]]
then
  export GTK_THEME=Adapta-Eta
fi

しかし、これでもアプリケーションから開くときにGeditを指定するとOxygen-Gtkを使用してしまう。 同様のケースでGeditはfcitxが死んでいる状態で起動したりするので、コマンド名で指定して起動する必要がある。

Cinnamonではデスクトップ設定が反映されるので設定しないほうが良い。

GUIツールキットのお話

今回はRuby qtbindingsを使おうとしたところからスタートした。

RubyでのGUIはまるでスタート廃れたかのような状態にあり、まるでruby-gnome2が唯一の選択肢であるかのようである。 ただ、ruby-qtbindingsは開発は継続しているし、Windowsでも動作するようになっているようだ。

個人的にあまりGtkは好まない。 UIとして使いにくいというのもあるし、設定が複雑でうまく反映されないというような理由もある。 プログラミング上も、QtのほうがAPIは使いやすい。

UIとして優れているのはGtk2だと思う。

Gtk3はメニューが非常に少なく、わかりにくい。 また、ファイルダイアログが劣悪で、ファイルを開くときはまだ良いのだが、ディレクトリを開く場合は非常に辛い。 というのは、何もファイルを選択しない状態で入力を開始した場合のみキーボードからのパス入力を受け付けるが、 Enterを押した時点で「開く」動作をする。 そのため、ディレクトリを開く動作のときには最後まで入力するか、クリックするかしか選べない。

Qt4/Qt5のダイアログはファイル名としてディレクトリパスを入力することもできるが、やはりディレクトリを開く場合には一発で開かれてしまう。 さらに登録される場所がさらに限定的となり、Gtk3よりなお悪い。 なお、XFce4ではQtのファイルダイアログをGtk3のものに置き換えるようだ。

Gtk2のファイルダイアログではファイルリスト側にフォーカスされていればWindows同様にインクリメンタルサーチとなり、 ファイル名欄が常に存在しディレクトリに対して入力を行うとまずディレクトリに移動するという振る舞いとなっている。 これはおそらく最も適切である。 (だが、これも場合によるようで、Qt4/5のような振る舞いをする場合もある)

API的に、あるいは設計的に見ればGtk2からGtk3、Qt3からQt4というのは大幅に進化している。 実際に「よくなった」のだ。

だが、現状でGtk3、あるいはQt5の熟成度というのは全く足りていない。 Qt4からQt5はある程度の互換性を持った状態で進化している。 その進化はわからないでもない。コンピュータの進化に合わせて、あるいは周辺環境の進化に合わせてコンポーネントを作り直した。 その際にいくつかのいまひとつな設計を修正し、テーマデザインにフラットデザインを推奨した。

だが、そのような変更は必要だったのか疑問だ。 内部的な進化や、標準テーマデザインの変更に関しては互換性を維持しての改修でもよかったのではなかろうか。

期間的にGtk3やQt5は日が浅い。 Gtk3のコンセプトが不評ということもあるが、2011年にGtk2は開発終了しているにも関わらずGtk3への移行が進んでいないプロジェクトは多い(XFce4もそのひとつだろう)。 現状ではGtk2を採用するプロジェクトもGtk3への移行を模索する時期にきているが、Gtk3に移行できているプロジェクトはまだ少ない。 Qtに関しては各プロジェクトが苦労してQt3から移行を完了し、あるいは移行できなかったプロジェクトは廃止されているが、 Qt4を採用しつづけるプロジェクトはまだ少なくない。

この状態でさらに非互換性を持つGtk4やQt6へ移行する必要があるのだろうか。 いずれのツールキットもより熟成すべきであるように私には思える。

また、GUIプログラミングも以前は盛んでwxwidgetなどが流行したときもあったのだが、早々に廃れてしまった。 WXWidget自体は現在も開発が続けられているが、wxrubyは既に終了しており、rwxは十分に開発・テストされていないようだ。 RubyにおけるGUIプログラミングの選択肢は非常に乏しく、qtbindingsも将来的には不透明だ。

さらに、Gtkのその問題点から開発者が離れている。 これはGtkの問題点というよりも、Gnomeの方針に開発者がついてきていないという問題なのだが、 GnomeはGtk4においても方針を維持しており、今後もこの傾向は続くだろう。

ユーザー側にはあまり見えない状況ではあるが、実はLinuxのGUI周りはかなり不安定な状況にある。 個人的にはQtがより普及し、バインディングが安定していてくれると良いのだが…

Linux的ビデオのハードウェアエンコーディング

ネットで検索するとかなりいい加減な情報が溢れているので、 そこまで分かっているわけではないけれどまとめてみた。

「エンコード」

まずencodeとdecodeがわからない記事がいくつか出てくるのでそこから。

  • データを映像/音声にするのがデコード
  • 映像/音声をデータにするのがエンコード

エンコードは動画編集をしているのでなければ、基本的に動画変換のお話。

ハードウェア

ハードウェアでやる、というのはCPUに頼らず、ハードウェア側に搭載された専用のチップを使用してエンコーディングする話をしている。

これにより

  • CPUにかかる負荷が低減できる
  • 速度が向上する(かもしれない)

というメリットがある。

遅いGPUと速いCPUの組み合わせだと速度向上には必ずしもつながらないが。 だが、GPUは非常に速いので高速化する可能性は高い。

最近の盛り上がり

動画処理というか、変換というかではなくて、 「ゲームしながらゲームを録画するため」 だと思う。

ゲームはリソースをいっぱい近く使うので、録画するのは難しい。 処理落ちなどの原因になる。 そんなことがあったら多分ガチなゲーマーは許さない。

録画時はそのまま映像データを収録しているとあまりに巨大ファイルになる。 ゲームのプレイ時間は長い可能性が高いし、これはこれできつい。 というわけで、これをハードウェアにオフロードしてCPUや描画用グラフィックスに負担をかけることなく録画/変換して保存しようということだと思う。

ただ、IntelのQSVは恐らく趣旨が違う。 というのは、こちらは「CPUやメモリにも負担をかける」からだ。 VP9エンコードにもKaby Lakeにも対応していたりするので、もしかしたら動画編集を意識しているのかもしれない。

VA-APIとVDPAU

VA-APIはIntelが、VDPAUはNvidiaが作ったLinux用のハードウェアビデオアクセラレーション用のAPIだ。

それぞれサポートされているのだが、そのサポートに注意がいる。

  • libva-vdpau-driverはVA-APIをラップしてVDPAUに投げる
  • libvdpau-va-glはVDPAUをラップしてVA-APIに投げる

このため

  • NVIDIAはVA-APIは使えるけれど実際はVDPAUを使用する
  • IntelはVDPAUは使えるけれど実際はVA-APIを使用する

ちなみに、AMDのオープンソースドライバ(ATIあるいはamdgpu)はVA-APIドライバとしてlibva-mesa-driver(とmesa)と、libva-vdpau-driverの2種類の方法がある。 前者であればVA-APIを直接取り扱う。後者であればVDPAUに流す。

そして、重要な点

  • VDPAUはハードウェアデコード用
  • VA-APIはハードウェアエンコード/デコード用

つまり

  • NvidiaのカードはVA-APIをサポートしていないのでこの方法でのエンコードは無理
  • AMDのカードはlibva-mesa-driverを使わないとハードウェアエンコードできない

ハードウェア側機能

IntelにはQSV, NvidiaにはNVEnc, AMDにはVCEがある。

QSV (Quick Sync Video)

デコードだけじゃなかったんだぁ…と思ってしまったけれど、 実はIntelのビデオアクセラレーションは割とLinuxに手厚い。

QSVの利用はIntel Media Server Studioを要求されるけれども、色々と条件が厳しいし、フリーではない(無料ではあるけれど)。

性能的には劣るもののVA-APIから叩けるらしい。

VCE (Video Coding Engine)

デコード用のUVD(Unified Video Decoder)とセットのAMDの機能。

VA-API経由で叩ける。

NVEnc

Nvidiaのエンコード用ビデオアクセラレーション。 Pascal世代になってとっても速くなったらしい。

Maxwell Gen2でもHEVC 720p高画質で300fpsとか言っているのでGPUすごい。

QSVのほうが速いとか言っている人がいるけれども、多分そんな環境まずない。

画質が割と腐っているけれど、H265がH264とほぼ同じ速度で変換できるので、H265にすればだいぶよろしくなる。 H265エンコードはMaxwellからのサポート。

エンコーダと画質のお話

ハードウェアエンコードを行う場合、実際のデータ変換処理を行う「エンコーダ」としてハードウェアへのアダプタを指定する。

つまり、NVEncを使う場合、libx264の代わりにnvenc_h264を使うことになる。 なのでエンコーダオプション(-crfとか)も変わる。

結果的に変換のされ方が変わる。 同一のクオリティを要求しても同じデータにはならない。

そして、ハードウェアエンコードを行った場合の画質、あまりよくない。 H.264でq=30くらいまで落とすとものすごくわかりやすいことになる。 q=28でも厳しい。

画質が悪いのだからできれば「保存したい」よりも「消すのはちょっと…」くらいのモチベーションの動画で使いたいところなのだが、 そういう動画こそサイズを小さくしたい。 ところが、ハードウェアエンコーダでサイズを小さくすると画質が目立って悪い。 だいぶ悩ましい話だ。

ffmpegでエンコード

Linuxの動画はffmpegで変換するものでしょう?

というわけで、QSVとVCEはVA-APIに対応しているのでVA-APIで。

ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 -vf 'format=nv12,hwupload' -c:v h264_vaapi output.mp4

エンコードもハードウェアでやればより軽い。

ffmpeg -init_hw_device vaapi=foo:/dev/dri/renderD128 -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device foo -i input.mp4 -filter_hw_device foo -vf 'format=nv12|vaapi,hwupload' -c:v h264_vaapi output.mp4

-qオプションは指定できるみたい。

ffmpeg -init_hw_device vaapi=foo:/dev/dri/renderD128 -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_device foo -i input.mp4 -filter_hw_device foo -vf 'format=nv12|vaapi,hwupload' -c:v h264_vaapi -q 28 output.mp4

Nvidiaがひとり独自路線なのはちょっと気になるけれども、使いやすいのはnvencのほう。

ffmpeg -i input.mp4 -c:v nvenc_h264 -q 28 output.mp4

qの値など実際は探り探りやっていくしかない感じ。

あなたのビデオカード、眠ってませんか

GPUは基本的に積極的に働かせないと動かない子なので、 結構なリソースを眠らせている可能性は結構ある。

Linux環境だとCinnamonとKDE Plasmaがハードウェアアクセラレーションに対応しているため、 割と最近のIntel CPUやAMD APUを搭載しているコンピュータだとCPU的にはXFceやMATE、LXDEなんかよりよっぽど軽かったりもする。

そういうのがあればある程度は働いてくれるのだが、これもビデオカードの機能のごく一部を使っているだけ。 ちなみに、OpenGL用のアクセラレータを使っている。

この状態でもデコーダやエンコーダは遊んでいるので、せっかくのリソース活用してあげてください。

ハードウェアエンコーダの画質はちょっと微妙だけども。

ブログわけました + DeleGateのバグ

tech系のアクセスと、非tech系の記事のアクセスが交錯して、あまりよくないことになっていたので、分割することにした。

これに伴って両方ともアドレスを変更、現状、journal.reasonset.netはChienomiのほうに転送されるが、将来的にはJournal de Akiのほうに転送される予定。

Journal de AkiはおおよそデイリーPVが4000ほどで、3000がtech、1000が非techになる。 分割してからは700ほどまで低下している。 固定ページに飛ばしていたのだが、ここでアクセスが切れるケースが多かったので、5日の夕方にmovedを返すように変更した。

今回の変更では従来よりもプロキシの数がひとつ減っている。 というのは、DeleGateのバグにかかってしまったためだ。

MOUNT="/journal/* .../*"
MOUNT="/* .../* nvhost=journal.reasonset.net"
MOUNT="/* .../* nvhost=blog.reasonset.net"

という記述をしたところ、新しい転送先にjournalというアドレスが含まれていたためか、どうしても転送先に/journal/*相当の転送をしてしまうのだ。

そんなこともあったのだけれど、まぁ、Mimirを含めてブログは充実させていく予定なのでほどほどに期待しておいてほしい。 最近いそがしくてあまり書けていないけれど。