技術的な話題 に関する投稿を表示しています

WordPress にプラグインを使わずに JavaScript だけで目次機能をつけてみた

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

目次機能が出来るプラグインとかってあるけど、別に自分で作ってもいいよねって思ったので作った。そのうちモリモリっとテーマやプラグインの整備に力を入れていくので、そのあたりに介入されると困るので~~ってくらいの理由で作った次第。

これこれ!この目次! h2 タグを使うんだよ

2階層までなら対応するよ! h3 タグだよ

ここは h3 タグ

ここは h2 タグなので 1 階層目になる

ここは 2 階層。 h3 タグだからね

デモおわり

ここまで見出しだらけで見にくいのは仕方ない。

仕組み

  1. h2 タグと h3 タグを投稿箇所から探す
  2. それぞれのタグに ID をつける
  3. それぞれへのリンクを生成する
  4. 目次用の HTML を作る
  5. 一番最初の見出しの直前に HTML を挿入する

タイトルこそ WordPress って言ってるけど JavaScript が差し込めるブログサービス的なものなら、投稿箇所を探す部分を調整する必要はあるけど、どれでもできるんじゃないかな。
jQuery を使わなくていいように作ったので jQuery ないんだけど、みたいな環境でもいいと思う。いやそもそもそういう環境はまた違う問題っぽいけど…。

JavaScript

(function() {
	setTimeout(function() {
		var headingIndex = 1;
		Array.prototype.forEach.call(document.querySelectorAll('.post_content'), function(postContent) {
			if (postContent.innerText.length < 100) {
				return;
			}

			var headings = postContent.querySelectorAll('h2,h3');
			if (headings.length <= 0) {
				return;
			}

			var indexHtml = '
    '; var lastElement = 'H2'; Array.prototype.forEach.call(headings, function(e) { var id = 'heading-' + headingIndex; e.id = id; if (lastElement != e.tagName) { if (lastElement == 'H2') { indexHtml += '
      '; } else { indexHtml += '
    '; } lastElement = e.tagName; } indexHtml += '
  • ' + e.innerHTML + '
  • '; headingIndex++; }); indexHtml += '
'; var wrapper = document.createElement('div'); wrapper.classList.add('content-index'); wrapper.innerHTML = indexHtml; headings[0].parentNode.insertBefore(wrapper, headings[0]); }); if (location.hash.length > 1) { setTimeout(function() { var element = document.querySelector(location.hash); if (element != null) { window.scroll(0, element.offsetTop); } }, 10); } }, 10); })();

CSS

.post_content .content-index {
	border: 2px solid #eee;
	border-radius: 20px;
	padding: 20px;
	background-color: #fefefe;
}

.post_content .content-index:before {
	content: '目次';
	display: block;
	font-size: 24.5px;
	margin-bottom: 15px;
}

他のスタイルとのバッティング回避した部分を除くとこれだけ。シンプルに線を引くことと色味の調整くらい。

SEO的な観点とか

目次足すと SEO に効果あるのかわからんけど、ふつうに見ていて、長めの投稿だったら合ったほうが便利だよね、とは感じる。

一応 Fetch as Google をして Google Bot にこの目次が認識されていることは確認したので、まあいいんじゃないかな。

composer create-project は何が起こるのか

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

CakePHP 3 のクイックスタートを改めて見てたら composer create-project ... ってでてきて何だこいつは…ってなった話。

Quick Start Guide - 3.x

php composer.phar create-project --prefer-dist cakephp/app bookmarker

composer create-project がやること

要はドキュメントにあるので読めって話になる。

Command-line interface / Commands - Composer

You can use Composer to create new projects from an existing package. This is the equivalent of doing a git clone/svn checkout followed by a "composer install" of the vendors.

There are several applications for this:

1. You can deploy application packages.
2. You can check out any package and start developing on patches for example.
3. Projects with multiple developers can use this feature to bootstrap the initial application for development.

To create a new project using Composer you can use the "create-project" command. Pass it a package name, and the directory to create the project in. You can also provide a version as third argument, otherwise the latest version is used.

ざっくりと翻訳してみると、こんなことが書かれているようだ。

既にあるパッケージから新しいプロジェクトを作ります。それは git clone または svn checkout をして composer install を行うのと同等です。

1. アプリケーションパッケージをデプロイできる
2. パッケージをチェックアウトして開発をしていける
3. 新しい開発のため、ブートストラップな初期化を提供ができる

"create-project" コマンドによってそれは実行できる。パッケージ名、プロジェクトのディレクトリ名、そしてパッケージバージョンを指定できる。

CakePHP のドキュメントにあったコマンドを読み解く

php composer.phar create-project --prefer-dist cakephp/app bookmarker

まず指定されているパッケージを見てみる。

cakephp/app - Packagist

A skeleton for creating applications with CakePHP 3.x.

なるほど。つまり、空っぽの CakePHP アプリケーションがパッケージ登録されているので、それをもってくることで新規に空っぽの CakePHP アプリケーション作れるやん!ということだそうだ。

ところで --prefer-dist はなんだろう。 composer のドキュメントをもう一度見る。

--prefer-dist: Install packages from dist when available.

んーむ。 dist があればインストールするよって言ってるけどなんのことやら…? → install の項目にガッツリと書いてある。

--prefer-dist: Reverse of --prefer-source, Composer will install from dist if possible. This can speed up installs substantially on build servers and other use cases where you typically do not run updates of the vendors. It is also a way to circumvent problems with git if you do not have a proper setup.

--prefer-source の逆で、 dist からのインストールを優先する。これによってビルドサーバやアップデートをしないような環境では高速に動作する。あるいは git の設定をしてないときに問題を解決出来る。

--prefer-source の説明も合わせて読んだところ、どうやら composer は dist って呼ばれるところを基本的には利用するらしい。 source はバージョン管理リポジトリで、直接 git clone するようなものになっているので、設定をしてないと問題が起きたりするそう。 Github を直接見に行ってエラー、とかそういう感じなのかな?まあ、デフォルトでは dist なので --prefer-dist はあまり気にしなくても良さそう。

で。

3 つめの引数に書いてある bookmarker が作られるディレクトリだ。
カレントディレクトリに bookmarker というディレクトリが作られ、その中に cakephp/app パッケージの内容が展開されているはず。加えて composer install も行われているので bookmarker/vendor ディレクトリも作られている。

まとめ

要は git clone しまっせ、みたいなものだと思っておけばだいたい良さそうだ。

Dockerfile を Github で書いて Docker Hub へ自動で連携する

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

出来ると思うんだけど~~って言いながら、なかなかやってなかったのでいい加減やった。

Docker Hub にアカウント登録

Docker Hub

右上からサインアップ出来る。適当な ID とメールアドレス、パスワードを入力すると良い。メールで確認がやってくるので、リンクぽちー。

以上で終わり。

Github でリポジトリを作る

Github にリポジトリを作っていきます。

Github 上からファイルの追加が出来るので、手元の Dockerfile をアップする。当然ながらパブリックなリポジトリで扱うのであれば、社外秘です!みたいな内容を含まないように気をつけて。パスワードとか、脆弱性ある古いバージョン使ってるじゃんとか、鍵が載ってるよとか、うっかり事故気をつけようね。

というわけで出来ました。

sters/docker-php7-cakephp3-base: docker base image for php7 x cakephp3

Docker Hub で Automated Build の設定をする

Docker Hub 側で自動ビルドが出来ます(便利!)URL が提供されるので、正しいトークンを送るとビルドキューへ追加され、自動でビルド、イメージの提供がされ始めます。
というわけで連携を進めていきます。

この URL および トークンを Github の Webhook に追加します。

ちなみに Docker Hub に Webhook もありますが、これはビルドが終わったときにそのイベントを指定した URL に通知できるものです。でっかいイメージを扱いたい時とか、ビルドしたイメージがちゃんと使えるかテストしたいとか、そういうときに使えるんじゃないですかね。

Github で Webhook の設定

Docker Hub で取得できたトリガーの URL を Github の Webhook に設定します。

とりあえずは Push された時、でいいと思います。タグが付いたとき、にすると Docker Hub 側でタグごとのビルドがされていきます。バージョニングしていく上ではそうするほうがいいでしょうね~。

Docker Hub で確認

ビルドが動き始めました。しばらく待つと完了します。

何事もなければこれでイメージが利用できるようになります。

 $ docker run -it --rm sters/php7-cakephp3-base "/bin/sh"

// ここからゲスト側

/ # composer
Do not run Composer as root/super user! See https://getcomposer.org/root for details
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 1.5.2 2017-09-11 16:59:25

Usage:
  command [options] [arguments]

Options:
  -h, --help                     Display this help message
  -q, --quiet                    Do not output any message
  -V, --version                  Display this application version
      --ansi                     Force ANSI output
      --no-ansi                  Disable ANSI output
  -n, --no-interaction           Do not ask any interactive question
      --profile                  Display timing and memory usage information
      --no-plugins               Whether to disable plugins.
  -d, --working-dir=WORKING-DIR  If specified, use the given directory as working directory.
  -v|vv|vvv, --verbose           Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  about           Shows the short information about Composer.
  archive         Creates an archive of this composer package.
  browse          Opens the package's repository URL or homepage in your browser.
  clear-cache     Clears composer's internal package cache.
  clearcache      Clears composer's internal package cache.
  config          Sets config options.
  create-project  Creates new project from a package into given directory.
  depends         Shows which packages cause the given package to be installed.
  diagnose        Diagnoses the system to identify common errors.
  dump-autoload   Dumps the autoloader.
  dumpautoload    Dumps the autoloader.
  exec            Executes a vendored binary/script.
  global          Allows running commands in the global composer dir ($COMPOSER_HOME).
  help            Displays help for a command
  home            Opens the package's repository URL or homepage in your browser.
  info            Shows information about packages.
  init            Creates a basic composer.json file in current directory.
  install         Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.
  licenses        Shows information about licenses of dependencies.
  list            Lists commands
  outdated        Shows a list of installed packages that have updates available, including their latest version.
  prohibits       Shows which packages prevent the given package from being installed.
  remove          Removes a package from the require or require-dev.
  require         Adds required packages to your composer.json and installs them.
  run-script      Runs the scripts defined in composer.json.
  search          Searches for packages.
  self-update     Updates composer.phar to the latest version.
  selfupdate      Updates composer.phar to the latest version.
  show            Shows information about packages.
  status          Shows a list of locally modified packages.
  suggests        Shows package suggestions.
  update          Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
  upgrade         Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
  validate        Validates a composer.json and composer.lock.
  why             Shows which packages cause the given package to be installed.
  why-not         Shows which packages prevent the given package from being installed.

/ # php -v
PHP 7.1.10 (cli) (built: Sep 30 2017 01:01:44) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

/ # exit

おわり

基本的に Official なイメージを使うとおおよそ大丈夫だと思うけど、再ビルド必要です!とか拡張機能必要です!とか、そういう状況になったら都度やるのもアレなので、オレオレイメージを作ると便利ちゃん。

そういえば Docker Hub って Dcokerfile によって記述された Docker イメージを管理共有してくれているけど、 docker-compose.yml で記述されるコンテナ管理セットを共有する場ってあるのかな。ちょっと調べたけどクエリが悪いのか見つからず…。

Office365 の Excel で Ctrl + H を押しても「検索」開く(解決済み)

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

会社の PC で Office365 の Excel を使っているがここ最近で、置換のショートカットキーが効かなくなったような気がしている。

Ctrl + H を押しても置換ではなく、検索が開くのだ。まあ、置換タブを押せばいいだけなのだが。

で気になって調べた。

もしやショートカットキーが変わった?ということもあるので、公式のガイドを見る。

Windows 用 Excel キーボード ショートカットとファンクション キー - Excel

CTRL + H
[検索と置換] ダイアログ ボックスの [置換] タブを表示します。

間違いなく Ctrl + H だ。
特にアプリ追加したなどないし、再起動しても変わらない。はてさて。

日本語の情報が全く見つからなかったが、英語で同じような状況を検索すると同じ状況の困った!がヒット。

Excel Shortcut Keys ctrl+f and ctrl+h now gives the same function? - Microsoft Tech Community

I just want to know why would microsoft change the function of excel 2016 in pressing ctrl+h (Find and replace) and have it the same as pressing ctrl+f (Find fucntion)?? The recent update makes the shortcut keys the same. I mean it is really useful when you can a quickly press keys to find and replace. Instead they made it the same as find function only which really is really annoying and time consuming for excel advanced users.

公式コミュニティだ。そして全く同じ問題が起きている。
雑に訳すと「Excel 2016 を使ってるんだけど Ctrl + H の動きが Ctrl + F と一緒じゃね?」って言ってる。

ついている回答を挙げてみるとこんな感じ。

I updated couple of minutes ago on 1709.8528.2084, Ctrl+H works now in that build.
And I'm on Inseders Slow (now Monthly Channel if i remember correctly)

これまた雑に訳すと「アップデートしたら動くようになったよ!Insider の Slow チャンネルでは直っているからそのうちになおるかも?」みたいなことを言っている。ぼくは大きなダメージもないのでまったり待ち。

 

 

と思ったけど 10/06 の朝に更新をしてみたら治った。
更新は ファイル → アカウント を押して「 Office 更新プログラム」かアップデート確認・実行ができる。

ぼくの場合はこのタイミングで 1708.8431.2094 というビルド番号になり Ctrl + H が直ってた。

CentOS 6 系から CentOS 7 系にアップグレードしたかった

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

某所の CentOS サーバをいい加減に 7 系にしよう!と思い、アップグレード手順なんかを調べていたら結構大変そうなことになった記録。結論としてまだアップグレードは出来てない。大して yum 以外でモリモリ入れたのも無いし、およそ nginx と php が動けば良いだろうな、入れ直しが手っ取り早そうだ。

公式 Wiki にアップグレードツールの件が記載されている。
TipsAndTricks/CentOSUpgradeTool - CentOS Wiki

目につく部分に警告が書かれている。

DO NOT USE this tool. Warning: use of this tool is currently BROKEN as several system-critical packages are of a higher version number in CentOS 6.7 than they are in CentOS 7 so those do not get upgraded correctly. This renders yum and several other system tools non-functional.

要するに CentOS 6.7 以降のパッケージは CentOS 7 に入っているパッケージよりもバージョンが進んでいるものもあり、うまく動かないらしい。詰まる該当パッケージのバージョン下げれば良いんじゃないか、行けるっしょ、という気持ちでトライしてみる。
※良い子は真似しないでね!

// まずは現状の確認をする
# cat /etc/redhat-release
CentOS release 6.9 (Final)


// アップグレードツールのリポジトリを追加
# vi /etc/yum.repos.d/upg.repo

// 以下を記載

[upg]
name=CentOS-$releasever - Upgrade Tool
baseurl=http://dev.centos.org/centos/6/upg/x86_64/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6


// 必要なツールのインストール
# yum install redhat-upgrade-tool preupgrade-assistant-contents


// 可能なアップグレードを確認
# preupg --list
CentOS6_7


// アップグレード検証をする
# preupg -s CentOS6_7

Preupg tool doesn't do the actual upgrade.
Please ensure you have backed up your system and/or data in the event of a failed upgrade
 that would require a full re-install of the system from installation media.
Do you want to continue? y/n

// y を入力

Gathering logs used by preupgrade assistant:
All installed packages : 01/11 ...finished (time 00:01s)
All changed files      : 02/11 ...finished (time 04:51s)
Changed config files   : 03/11 ...finished (time 00:00s)
All users              : 04/11 ...finished (time 00:00s)
All groups             : 05/11 ...finished (time 00:00s)
Service statuses       : 06/11 ...finished (time 00:00s)
All installed files    : 07/11 ...finished (time 00:04s)
All local files        : 08/11 ...finished (time 00:40s)
All executable files   : 09/11 ...finished (time 00:07s)
RedHat signed packages : 10/11 ...finished (time 00:00s)
CentOS signed packages : 11/11 ...finished (time 00:00s)
Assessment of the system, running checks / SCE scripts:
001/096 ...done    (Configuration Files to Review)
002/096 ...done    (File Lists for Manual Migration)
003/096 ...done    (Bacula Backup Software)
004/096 ...done    (MySQL configuration)
005/096 ...done    (Migration of the MySQL data stack)
006/096 ...done    (Changes related to moving from MySQL to MariaDB)
007/096 ...done    (PostgreSQL upgrade content)
008/096 ...done    (GNOME Desktop Environment underwent several design modifications in CentOS 7 release)
009/096 ...done    (KDE Desktop Environment underwent several design modifications in CentOS 7 release)
010/096 ...done    (several graphic drivers not supported in CentOS 7)
011/096 ...done    (several input drivers not supported in CentOS 7)
012/096 ...done    (several kernel networking drivers not available in CentOS 7)
013/096 ...done    (several kernel storage drivers not available in CentOS 7)
014/096 ...done    (Names, Options and Output Format Changes in arptables)
015/096 ...done    (BIND9 running in a chroot environment check.)
016/096 ...done    (BIND9 configuration compatibility check)
017/096 ...done    (Move dhcpd/dhcprelay arguments from /etc/sysconfig/* to *.service files)
018/096 ...done    (DNSMASQ configuration compatibility check)
019/096 ...done    (Dovecot configuration compatibility check)
020/096 ...done    (Compatibility Between iptables and ip6tables)
021/096 ...done    (Net-SNMP check)
022/096 ...done    (Squid configuration compatibility check)
023/096 ...done    (Reusable Configuration Files)
024/096 ...done    (VCS repositories)
025/096 ...done    (Added and extended options for BIND9 configuration)
026/096 ...done    (Added options in DNSMASQ configuration)
027/096 ...done    (Packages not signed by CentOS)
028/096 ...done    (Obsoleted rpms)
029/096 ...done    (w3m not available in CentOS 7)
030/096 ...done    (report incompatibilities between CentOS 6 and 7 in qemu-guest-agent package)
031/096 ...done    (Removed options in coreutils binaries)
032/096 ...done    (Removed options in gawk binaries)
033/096 ...done    (Removed options in netstat binary)
034/096 ...done    (Removed options in quota tools)
035/096 ...done    (Removed rpms)
036/096 ...done    (Replaced rpms)
037/096 ...done    (GMP library incompatibilities)
038/096 ...done    (package downgrades)
039/096 ...done    (restore custom selinux configuration)
040/096 ...done    (General)
041/096 ...done    (samba shared directories selinux)
042/096 ...done    (CUPS Browsing/BrowsePoll configuration)
043/096 ...done    (CVS Package Split)
044/096 ...done    (FreeRADIUS Upgrade Verification)
045/096 ...done    (httpd configuration compatibility check)
046/096 ...done    (bind-dyndb-ldap)
047/096 ...done    (Identity Management Server compatibility check)
048/096 ...done    (IPA Server CA Verification)
049/096 ...done    (NTP configuration)
050/096 ...done    (Information on time-sync.target)
051/096 ...done    (OpenLDAP /etc/sysconfig and data compatibility)
052/096 ...done    (OpenSSH sshd_config migration content)
053/096 ...done    (OpenSSH sysconfig migration content)
054/096 ...done    (Configuration for quota_nld service)
055/096 ...done    (Disk quota netlink message daemon moved into quota-nld package)
056/096 ...done    (SSSD compatibility check)
057/096 ...done    (Luks encrypted partition)
058/096 ...done    (Clvmd and cmirrord daemon management.)
059/096 ...done    (State of LVM2 services.)
060/096 ...done    (device-mapper-multipath configuration compatibility check)
061/096 ...done    (Removal of scsi-target-utils)
062/096 ...done    (Configuration for warnquota tool)
063/096 ...done    (Disk quota tool warnquota moved into quota-warnquota package)
064/096 ...done    (Architecture Support)
065/096 ...done    (Binary rebuilds)
066/096 ...done    (Debuginfo packages)
067/096 ...done    (Cluster and High Availability)
068/096 ...done    (Quorum implementation)
069/096 ...done    (fix krb5kdc config file)
070/096 ...done    (File Systems, Partitions and Mounts Configuration Review)
071/096 ...done    (Read Only FHS directories)
072/096 ...done    (Sonamebumped libs)
073/096 ...done    (SonameKept Reusable Dynamic Libraries)
074/096 ...done    (Removed .so libs)
075/096 ...done    (In-place Upgrade Requirements for the /usr/ Directory)
076/096 ...done    (CA certificate bundles modified)
077/096 ...done    (Developer Tool Set packages)
078/096 ...done    (Hyper-V)
079/096 ...done    (Content for enabling and disabling services based on CentOS 6 system)
080/096 ...done    (Check for ethernet interface naming)
081/096 ...done    (User modification in /etc/rc.local and /etc/rc.d/rc.local)
082/096 ...done    (cgroups configuration compatibility check)
083/096 ...done    (Plugable authentication modules (PAM))
084/096 ...done    (Foreign Perl modules)
085/096 ...done    (Python 2.7.5)
086/096 ...done    (Ruby 2.0.0)
087/096 ...done    (SCL collections)
088/096 ...done    (System kickstart)
089/096 ...done    (YUM)
090/096 ...done    (Check for usage of dangerous range of UID/GIDs)
091/096 ...done    (Incorrect usage of reserved UID/GIDs)
092/096 ...done    (NIS ypbind config files back-up)
093/096 ...done    (NIS Makefile back-up)
094/096 ...done    (NIS server maps check)
095/096 ...done    (NIS server MAXUID and MAXGID limits check)
096/096 ...done    (NIS server config file back-up)
Assessment finished (time 06:32s)
I/O warning : failed to load external entity "/usr/share/openscap/xsl/security-guide.xsl"
compilation error: file /usr/share/preupgrade/xsl/preup.xsl line 40 element import
xsl:import : unable to load /usr/share/openscap/xsl/security-guide.xsl
I/O warning : failed to load external entity "/usr/share/openscap/xsl/oval-report.xsl"
compilation error: file /usr/share/preupgrade/xsl/preup.xsl line 41 element import
xsl:import : unable to load /usr/share/openscap/xsl/oval-report.xsl
I/O warning : failed to load external entity "/usr/share/openscap/xsl/sce-report.xsl"
compilation error: file /usr/share/preupgrade/xsl/preup.xsl line 42 element import
xsl:import : unable to load /usr/share/openscap/xsl/sce-report.xsl
OpenSCAP Error:: Could not parse XSLT file '/usr/share/preupgrade/xsl/preup.xsl' [oscapxml.c:416]
Unable to open file /root/preupgrade/result.html
Usage: preupg [options]

preupg: error: [Errno 2] No such file or directory: '/root/preupgrade/result.html'

やっぱりうごかない。調べると openscap のバージョンを下げることで先にすすめるらしい。
CentOS6.8 から CentOS7 へのアップグレード - demandosigno

// バージョン確認
# rpm -qa | grep openscap
openscap-1.2.13-2.el6.x86_64


// 古いものを入れる
# rpm -Uhv --oldpackage http://dev.centos.org/centos/6/upg/x86_64/Packages/openscap-1.0.8-1.0.1.el6.centos.x86_64.rpm
http://dev.centos.org/centos/6/upg/x86_64/Packages/openscap-1.0.8-1.0.1.el6.centos.x86_64.rpm を取得中
準備中...                ########################################### [100%]
   1:openscap               ########################################### [100%]


// 確認
# rpm -qa | grep openscap
openscap-1.0.8-1.0.1.el6.centos.x86_64


// 再度ツール実行
# preupg -s CentOS6_7

~~

Assessment finished (time 06:21s)

Result table with checks and their results for main contents:
---------------------------------------------------------------------------------------------------------------
|Bacula Backup Software                                                                    |notapplicable     |
|MySQL configuration                                                                       |notapplicable     |
|Migration of the MySQL data stack                                                         |notapplicable     |
|Changes related to moving from MySQL to MariaDB                                           |notapplicable     |
|PostgreSQL upgrade content                                                                |notapplicable     |
|GNOME Desktop Environment underwent several design modifications in CentOS 7 release      |notapplicable     |
|KDE Desktop Environment underwent several design modifications in CentOS 7 release        |notapplicable     |
|several graphic drivers not supported in CentOS 7                                         |notapplicable     |
|several input drivers not supported in CentOS 7                                           |notapplicable     |
|Names, Options and Output Format Changes in arptables                                     |notapplicable     |
|BIND9 running in a chroot environment check.                                              |notapplicable     |
|BIND9 configuration compatibility check                                                   |notapplicable     |
|Move dhcpd/dhcprelay arguments from /etc/sysconfig/* to *.service files                   |notapplicable     |
|DNSMASQ configuration compatibility check                                                 |notapplicable     |
|Dovecot configuration compatibility check                                                 |notapplicable     |
|Net-SNMP check                                                                            |notapplicable     |
|Squid configuration compatibility check                                                   |notapplicable     |
|Added and extended options for BIND9 configuration                                        |notapplicable     |
|Added options in DNSMASQ configuration                                                    |notapplicable     |
|w3m not available in CentOS 7                                                             |notapplicable     |
|report incompatibilities between CentOS 6 and 7 in qemu-guest-agent package               |notapplicable     |
|restore custom selinux configuration                                                      |notapplicable     |
|samba shared directories selinux                                                          |notapplicable     |
|CUPS Browsing/BrowsePoll configuration                                                    |notapplicable     |
|FreeRADIUS Upgrade Verification                                                           |notapplicable     |
|bind-dyndb-ldap                                                                           |notapplicable     |
|Identity Management Server compatibility check                                            |notapplicable     |
|IPA Server CA Verification                                                                |notapplicable     |
|OpenLDAP /etc/sysconfig and data compatibility                                            |notapplicable     |
|SSSD compatibility check                                                                  |notapplicable     |
|Clvmd and cmirrord daemon management.                                                     |notapplicable     |
|device-mapper-multipath configuration compatibility check                                 |notapplicable     |
|Removal of scsi-target-utils                                                              |notapplicable     |
|Quorum implementation                                                                     |notapplicable     |
|fix krb5kdc config file                                                                   |notapplicable     |
|cgroups configuration compatibility check                                                 |notapplicable     |
|System kickstart                                                                          |notapplicable     |
|NIS ypbind config files back-up                                                           |notapplicable     |
|NIS Makefile back-up                                                                      |notapplicable     |
|NIS server maps check                                                                     |notapplicable     |
|NIS server MAXUID and MAXGID limits check                                                 |notapplicable     |
|NIS server config file back-up                                                            |notapplicable     |
|several kernel networking drivers not available in CentOS 7                               |pass              |
|several kernel storage drivers not available in CentOS 7                                  |pass              |
|OpenSSH sshd_config migration content                                                     |pass              |
|Configuration for quota_nld service                                                       |pass              |
|Disk quota netlink message daemon moved into quota-nld package                            |pass              |
|Luks encrypted partition                                                                  |pass              |
|Configuration for warnquota tool                                                          |pass              |
|Architecture Support                                                                      |pass              |
|Debuginfo packages                                                                        |pass              |
|Cluster and High Availability                                                             |pass              |
|Read Only FHS directories                                                                 |pass              |
|In-place Upgrade Requirements for the /usr/ Directory                                     |pass              |
|Developer Tool Set packages                                                               |pass              |
|Hyper-V                                                                                   |pass              |
|Check for ethernet interface naming                                                       |pass              |
|Plugable authentication modules (PAM)                                                     |pass              |
|Ruby 2.0.0                                                                                |pass              |
|SCL collections                                                                           |pass              |
|Incorrect usage of reserved UID/GIDs                                                      |pass              |
|Compatibility Between iptables and ip6tables                                              |informational     |
|VCS repositories                                                                          |informational     |
|Removed options in coreutils binaries                                                     |informational     |
|Removed options in gawk binaries                                                          |informational     |
|Removed options in netstat binary                                                         |informational     |
|Removed options in quota tools                                                            |informational     |
|GMP library incompatibilities                                                             |informational     |
|CVS Package Split                                                                         |informational     |
|httpd configuration compatibility check                                                   |informational     |
|NTP configuration                                                                         |informational     |
|Information on time-sync.target                                                           |informational     |
|Disk quota tool warnquota moved into quota-warnquota package                              |informational     |
|File Systems, Partitions and Mounts Configuration Review                                  |informational     |
|Sonamebumped libs                                                                         |informational     |
|SonameKept Reusable Dynamic Libraries                                                     |informational     |
|Foreign Perl modules                                                                      |informational     |
|YUM                                                                                       |informational     |
|Reusable Configuration Files                                                              |fixed             |
|Replaced rpms                                                                             |fixed             |
|package downgrades                                                                        |fixed             |
|OpenSSH sysconfig migration content                                                       |fixed             |
|State of LVM2 services.                                                                   |fixed             |
|Configuration Files to Review                                                             |needs_inspection  |
|File Lists for Manual Migration                                                           |needs_inspection  |
|Obsoleted rpms                                                                            |needs_inspection  |
|Binary rebuilds                                                                           |needs_inspection  |
|CA certificate bundles modified                                                           |needs_inspection  |
|Python 2.7.5                                                                              |needs_inspection  |
|Check for usage of dangerous range of UID/GIDs                                            |needs_inspection  |
|Packages not signed by CentOS                                                             |needs_action      |
|Removed rpms                                                                              |needs_action      |
|General                                                                                   |needs_action      |
|Removed .so libs                                                                          |needs_action      |
|Content for enabling and disabling services based on CentOS 6 system                      |needs_action      |
|User modification in /etc/rc.local and /etc/rc.d/rc.local                                 |needs_action      |
---------------------------------------------------------------------------------------------------------------
Tarball with results is stored here /root/preupgrade-results/preupg_results-170926155342.tar.gz .
The latest assessment is stored in directory /root/preupgrade .
Summary information:
We found some potential in-place upgrade risks.
Read the file /root/preupgrade/result.html for more details.
Upload results to UI by command:
e.g. preupg -u http://127.0.0.1:8099/submit/ -r /root/preupgrade-results/preupg_results-*.tar.gz .

出てきた。結構いろいろみてくれるのね、しゅごい。

/root/preupgrade/result.html に詳細があるそうなので、ドキュメントルートに持っていって、ブラウザから確認すると次のような画面になった。

  • 半分くらいの項目はちゃんと確認しないと危ない気がする
  • がんばってアップグレードしてもとっちらかった状態になりそう
  • web サーバとして簡単に使っている程度なので、バックアップ全部とって入れ替えるもあり

というところから、入れ替えちゃったほうが早そうだなあ、という結論。

まずは docker だったりで CentOS 7 を準備して、稼働中のサービス、設定ファイルや鍵を移しつつ、いろいろ動作するか確認したのち、まるっと入れ替える、という流れになりそーう。

つづく。

Node.js から Headless Chrome を操作できる puppeteer を試す

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

いい感じに Javascript がゴリゴリ書かれていて、認証が必要なサイトを定期的にキャプチャを取る、という謎タスクが生まれ、ちょうど最近話題の Headless Chrome でも試してみるかな~~と思ったら puppeteer なるものがあるそうなので、これを使ってみる。

GoogleChrome/puppeteer: Headless Chrome Node API

GoogleChrome 配下にいるので Chrome 公式のパッケージだ。

何も気にせず動かしてみる

// puppeteer 導入
$ npm init
$ npm install puppeteer

ドキュメントにもあるサンプルソースを入れてみる。

// index.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'example.png'});

  await browser.close();
})();

https://example.com へとアクセスし、そのページをキャプチャして ./example.png に保存するソースだ。 await で簡単わかりやすい書き方になるのが素晴らしい。これを動かしてみる。

$ node index.js
(node:30285) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Failed to launch chrome!
/var/www/html/crawl/bitbucket_billing/node_modules/puppeteer/.local-chromium/linux-497674/chrome-linux/chrome: error while loading shared libraries: libXss.so.1: cannot open shared object file: No such file or directory


TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

(node:30285) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

かなしみ…。どうにも libXss.so.1 が開けずにエラーっぽい。パッケージを探してインストールし、再度動かしてみる。

$ yum provides */libXss.so.1
...

libXScrnSaver-1.2.2-6.1.el7.i686 : X.Org X11 libXss runtime library
Repo        : base
Matched from:
Filename    : /usr/lib/libXss.so.1

libXScrnSaver-1.2.2-6.1.el7.x86_64 : X.Org X11 libXss runtime library
Repo        : base
Matched from:
Filename    : /usr/lib64/libXss.so.1


$ sudo yum install libXScrnSaver
...

$ node index.js
(node:1345) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Failed to launch chrome!
/var/www/html/crawl/bitbucket_billing/node_modules/puppeteer/.local-chromium/linux-497674/chrome-linux/chrome: error while loading shared libraries: libgtk-3.so.0: cannot open shared object file: No such file or directory


TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

(node:1345) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

GTK+3 が要求される。都合によって CentOS6 なのだが GTK+3 は入れるのがめっちゃ困難だった記憶。とりあえず GTK+3 というか Chrome を入れる方法を調べたら必要なライブラリも揃うだろう、と判断して調べる。

Google Chrome 60 Released - Install on RHEL/CentOS 7/6 and Fedora 26-20

Yes, they’ve discontinued support for RHEL 6.X version as of Google Chrome and on other side, latest Firefox and Opera browsers run successfully on the same platforms.

CentOS6 では Chrome は動かない、ダメ。Richard Lloyd 氏がスクリプトを作っているそうだが、そのサイトでも、いつまでも古いものを使うな、って書いてあった。

Site has been shut down

You have two choices really (ignoring Chromium, which no-one seems to be keeping up-to-date for RHEL/CentOS 6):

1. Use Mozilla Firefox ESR via "yum install firefox" that is shipped with RHEL/CentOS 6. Unfortunately, this can be up to a year out of date w.r.t. features compared to the more "normal" Mozilla Firefox that you can download from Mozilla (but that "normal" Mozilla Firefox is now also using GTK+3 and doesn't work on RHEL/CentOS 6 either).

2. Move to RHEL/CentOS 7, perhaps first in a VM before upgrading on bare metal once the VM is to your satisfaction. The latest Google Chrome installs and runs out-of-the-box on RHEL/CentOS 7 (see below).

気を新たにdockerで動かす

まあ、一旦使ってみるなら docker でもいいか。

$ docker run -it -d -v $(pwd):/var/www/puppeteer_test centos:latest
Unable to find image 'centos:latest' locally
latest: Pulling from centos
6c5159923047: Pull complete
acec82331181: Pull complete
1c1b67b33c28: Pull complete
Digest: sha256:57c91a43765de2147fa666a128477bc6101d6fda660feaaa5fbb1a8b110c934f
Status: Downloaded newer image for centos:latest
2be8ec65e6e4db3d852d563b82cc4c90ca90f62b4aaa6304d6e00f06d6a4a9e7

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
2be8ec65e6e4        centos:latest       "/bin/bash"         16 seconds ago      Up 16 seconds                           romantic_sinoussi

$ docker exec -it 2be /bin/bash

# cd /var/www/puppeteer_test/
# ls
index.js  node_modules  package-lock.json  package.json

# node -v
bash: node: command not found

// nodeがいないので入れる
# curl -sL https://rpm.nodesource.com/setup_8.x | bash -
# yum install nodejs

# node index.js
(node:114) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Failed to launch chrome!
/var/www/puppeteer_test/node_modules/puppeteer/.local-chromium/linux-497674/chrome-linux/chrome: error while loading shared libraries: libpangocairo-1.0.so.0: cannot open shared object file: No such file or directory


TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

(node:114) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

// とりあえず yum provides で探しながら一個ずつ入れていく
// をまとめたものがこちら
# yum groupinstall "Development Tools"
# yum install pango libXScrnSaver libXcomposite libXcursor libXi libXtst cups-libs libXrandr GConf GConf2 alsa-lib atk gtk3

// 起動してみる
# node index.js
(node:11985) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Failed to launch chrome!
[0920/101508.987383:ERROR:zygote_host_impl_linux.cc(88)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.


TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

(node:11985) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

root で --no-sandbox なしは認められないらしいのでソースを変える。

// index.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({args: ['--no-sandbox']});
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'example.png'});

  await browser.close();
})();

再び実行。

# node index.js

# ls
example.png  index.js  node_modules  package-lock.json  package.json

キャプチャ取れたので、画像を確認。

勝ち。

というわけで puppeteer 動いた。CentOS6 で CentOS7 が動いてて~ってちょっとアレなので、バージョン上げてまた試……、じゃない、本来の目的だった認証通してキャプチャできるかをやらなくちゃ。続く。

Rx と Promise と。

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

Rx 云々を見かけるたびに Web でそんな出番ないような…?非同期やすとりーむや配列的なのをよろしくするやつ?と、もはや知ることを放棄していて。今頃もったいないなーと思ったので、改めてちょっと調べるの巻。

Rx について

ReactiveX - Intro

オブザーバーパターンを拡張したもので、シーケンスなデータやイベントをサポートするため、スレッドやノンブロックIOとかを抽象化して組み立てることができる。オブザーバーパターン = 監視する人と通知する人がいるよー、というイメージでとりあえずはよい。

Promise について

JavaScript Promiseの本

非同期処理を上手くパターン化して書くための、統一的なインターフェース。ここではオブザーバーという話はない。 コードを見ても、非同期を取りまわすためのいい感じのインターフェースである、というイメージ(個人の感想です)

Rx とPromise はどう使い分けるとよさそう?

いくつかサイトを眺めてみて。

JavaScriptの非同期処理には何を使うべきか - Qiita
State, Promise & Reactive プログラミング
What are the differences between observables and promises in JavaScript? - Stack Overflow

で、 Stackoverflow の回答が結構それっぽいのかなあと。

Simply put: A promise resolves to a single value asynchronously, an observable resolves to (or emits) multiple values asynchronously (over time).

ざっくり訳すと。

Observable (= Rx として) は多数の非同期を継続的に処理したい
Promise は単発の非同期処理をしたい

なので。

Rx は配列操作とか、画面操作とか、とかと相性がよい
Promise は単発になりがちのもの、外部呼出し(Ajaxとか)と相性がよい

このボタンおしたら Ajax して、待機状態にして、結果が来たらここを書き換えて、イベント設定して、… なんてものは Rx と Promise を組み合わせるといいのかもしれない。が、昨今のフロントエンドにおいては Flux なんかでライブラリの中でよろしくやっていることが多いので、あえて開発する人が関与していくことは少ない…?

どちらかというとアプリ系で Rx が盛り上がってる気がしていて、 Web フロントではそうでもないような…。どっちも知識薄くてわからんのだけど、言語が違っても画面とロジック、裏でデータがほいほい、みたいなことってどちらも一緒のことだと思うんだけど、その差はなんなんなん?

docker を使っていてポートを指定するときに IP アドレスを指定できる

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

開発用だったり検証用だったりで docker をぼちぼち使っているのですが、ゲストのポートをホストのポートへバインドするときに、IPアドレスを指定できます。

$ docker run -it -d -p 127.0.0.1:10080:80 myapp

どこで使うんだよって話ですが、セキュリティを考えるときには使うんじゃないっすかね、たぶん。

IPアドレスの指定がないと 0.0.0.0 で待ち受けるので、接続をどこからでも受け付けます。そのため、例えばホスト側で nginx 等を使って何かしらの認証が必要なリバースプロキシをしていたとしても、指定ポートでコンテナに直接アクセスができてしまいます。

nginx で不特定多数から見られないよう Basic 認証を設定していたのですが、見れるのかな?ってポート指定してアクセスしたら無事に見れてしまって、うふふってなってました。

$ docker run -it -d -p 10080:80 nginx
485e21a84ea180bc69db198bcdb7e417fb61d2747642059b8bd4435873aa1503

$ netstat -nao | grep 10080
tcp        0      0 :::10080                    :::*                        LISTEN      off (0.00/0/0)

$ docker rm -f 485
485

$ docker run -it -d -p 127.0.0.1:10080:80 nginx
94ea5a9d74578fc69777cf0dc3b7dc918ce2e4ec74fef52441521a8d163680d8

$ netstat -nao | grep 10080
tcp        0      0 127.0.0.1:10080             0.0.0.0:*                   LISTEN      off (0.00/0/0)

ドキュメントはここ
https://docs.docker.com/engine/reference/commandline/run/#publish-or-expose-port--p-expose

NGINX Unit なるものリリースされたらしいのでとりあえず PHP でも動かしてみる

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

NGINX Unit なるものがリリースされたらしい。

・NGINX 公式サイトの情報
NGINX Unit

・NGINX Unit 公式サイト
NGINX Unit

・見かけた記事
NGINXからアプリケーションサーバ「NGINX Unit」がオープンソースで登場。PHP、Go、Pythonに対応。Java、Node.jsにも対応予定 - Publickey

ドキュメントに書かれているインストール方法や設定を見てたらパッと出来そうだったので思いたってやったったの巻。

どうにも公式サイトのドキュメンテーションを見ると CentOS と Ubuntu 向けにはパッケージが適当されているっぽい。なのでコレを利用する。また nginx が既にいる環境でやると便利そうなので docker で立ち上げていく

// ホストにて
$ docker run --name nginx-unit -d -p 8080:80 nginx
$ docker exec -it nginx-unit bash

// 以降はコンテナ内、ゲストで作業

// 準備
# apt-get update
# apt-get install curl net-tools vim less

// キーの登録
# curl http://nginx.org/keys/nginx_signing.key > /tmp/nginx_signing.key
# apt-key add /tmp/nginx_signing.key

// /etc/apt/sources.list に追記
# vim /etc/apt/sources.list
deb http://nginx.org/packages/mainline/ubuntu/ xenial nginx
deb-src http://nginx.org/packages/mainline/ubuntu/ xenial nginx

// nginx-unit のインストール
# apt-get update
# apt-get install unit


// コンフィグ確認
# cat /etc/init.d/unit
CONFIG=/etc/unit/config

// /var/run/control.unit.sock が設定用のソケットファイルっぽい
    dumpconfig)
        curl -sS --unix-socket /var/run/control.unit.sock localhost >${CONFIG}.new



# vim /etc/unit/config
{
     "listeners": {
         "*:8300": {
             "application": "myapp"
         }
     },
     "applications": {
         "myapp": {
              "type": "php",
              "workers": 3,
              "root": "/var/www/html/",
              "index": "index.php"
         }
     }
}

// 起動
# service unitd start

// コンフィグの確認
# curl --unix-socket /var/run/control.unit.sock http://localhost/
{
        "listeners": {},
        "applications": {}
}

// コンフィグの変更してみる
# service unitd stop
# mv /etc/unit/config /etc/unit/myconfig.json
# service unitd start

# curl -X PUT -d @/etc/unit/myconfig.json --unix-socket /var/run/control.unit.sock http://localhost/
curl: (52) Empty reply from server

# curl --unix-socket /var/run/control.unit.sock http://localhost/

// レスポンスが返ってこない

// ログ確認
# less /var/log/unitd.log

// ... とくにエラーっぽいものなし。

パッケージのものだとうまく動かない???

ということでソースから入れる路線を試す。

// パッケージからのものを消す
# apt-get remove unit

// 準備
# apt-get install git build-essential php php-dev libphp-embed
# cd /tmp
# git clone https://github.com/nginx/unit
# cd unit

// ビルド
# ./configure --prefix=/usr/share/unit/
# ./configure php
# make all

// インストール
# make install
# ln -s /usr/share/unit/sbin/unitd /sbin/unitd

// 起動
# unitd

// 設定してみる
# vim /tmp/unit_config.json

{
     "listeners": {
         "*:8300": {
             "application": "myapp"
         }
     },
     "applications": {
         "myapp": {
              "type": "php",
              "workers": 3,
              "root": "/var/www/html/",
              "index": "index.php"
         }
     }
}


# curl -X PUT -d @/tmp/unit_config.json --unix-socket /usr/share/unit/control.unit.sock http://localhost/
{
        "success": "Reconfiguration done."
}

# curl --unix-socket /usr/share/unit/control.unit.sock http://localhost/
{
        "listeners": {
                "*:8300": {
                        "application": "myapp"
                }
        },

        "applications": {
                "myapp": {
                        "type": "php",
                        "workers": 3,
                        "root": "/var/www/html/",
                        "index": "index.php"
                }
        }
}

// 動作確認
# echo "<?php echo 'hello php';" > /var/www/html/index.php
# curl localhost:8300
hello php

// nginx からプロキシしてみる
# vim /etc/nginx/conf.d/default.conf

upstream unit_backend {
    server 127.0.0.1:8300;
}

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /var/www/html;
        index  index.html index.htm;
    }

    location ~ \.php$ {
        proxy_pass http://unit_backend;
        proxy_set_header Host $host;
    }
}

// 動作確認
# service nginx reload
Reloading nginx: nginx.

# curl localhost/index.php
hello php

出来ているようなので、もう少しちゃんと PHP を動かしてみる。

// phpモジュール追加
# apt-get install php-mbstring php-zip

// composer 導入
# curl https://getcomposer.org/installer > installer
# php installer
# rm installer
# chmod +x composer.phar

// Laravelプロジェクトインストール
# ./composer.phar create-project laravel/laravel app

// Laravel プロジェクトの色々設定
# chmod -R 777 /var/www/html/app/storage/
# php artisan key:generate

// nginx-uniti のルートディレクトリを変更
#  curl -X PUT -d '"/var/www/html/app/public/"' --unix-socket /usr/share/unit/control.unit.sock http://localhost/applications/myapp/root
{
        "success": "Reconfiguration done."
}

// 確認
# curl --unix-socket /usr/share/unit/control.unit.sock http://localhost/
{
        "listeners": {
                "*:8300": {
                        "application": "myapp"
                }
        },

        "applications": {
                "myapp": {
                        "type": "php",
                        "workers": 3,
                        "root": "/var/www/html/app/public/",
                        "index": "index.php"
                }
        }
}


// nginx のルートディレクトリを変更
# vim /etc/nginx/conf.d/defualt.conf

    location ~ / {
        proxy_pass http://unit_backend;
        proxy_set_header Host $host;
    }

# service nginx reload


// 動作確認
# curl localhost

// 何か出ていそうなのでブラウザで開く…!

イエーイ! NGINX Unit で Laravel アプリケーションが動いたぞ~~~空っぽだけど…。

ご覧とおり php-fpm は動いてなくて unit のワーカーが動いてるだけですねー、しゅごい。

# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  32648  3464 ?        Ss   00:50   0:00 nginx: master process nginx -g daemon off;
root         8  0.0  0.0  18224  2176 ?        Ss+  00:51   0:00 bash
root     26019  0.0  0.0  22196   852 ?        Ss   03:00   0:00 unit: main [unitd]
nobody   26021  0.0  0.0  32440   736 ?        S    03:00   0:00 unit: controller
nobody   26022  0.0  0.0 204620  1440 ?        Sl   03:00   0:00 unit: router
nobody   27293  0.0  0.7 269004 28232 ?        S    03:28   0:00 unit: "myapp" application
nginx    27330  0.0  0.0  33088  2152 ?        S    03:31   0:00 nginx: worker process
root     27331  1.2  0.0  18140  2064 ?        Ss   03:41   0:00 bash
root     27337  0.0  0.0  36572  1588 ?        R+   03:41   0:00 ps aux

現状では Go / PHP / Python に対応しているようで言語環境の準備と configure 時に言語指定すれば使えるようです。

手元にいい感じにお試せるアプリケーションがいないので細かい挙動の様子をみるなど出来ていないのですが、リリースニュース直後からはてぶが盛り上がったり github も盛り上がりを見せている(ように見える)ので、ちょっとしたバグなどなどあってももりもり直されていくんじゃないかなーという予感です。

そのうち Apache + mod_php / nginx + php-fpm / nginx + unit などでパフォーマンスも比べていきたいですねー!

Python で階層型クラスタリング

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

最近は Python をちょこまかといじることが増えてきたように感じています。というところで、クラスタリングのことを調べていたのですが、なんとなく k-means しておけばいいっしょ、くらいだったのですが、もっと奥深い色々が出てきて、クラスタリングわからないマンになりつつあります。そんなわけで、階層型クラスタリングを調べて、やってみたのでまとめておきます。

 

クラスタリングとは

クラスタリングとはデータを良い具合にまとめるという、教師なし学習の典型例です。何かしらの特徴量に沿って、データの流れや雰囲気をアルゴリズム的に見抜き、その様子でデータをまとめることができます。

クラスタリングとしてもっとも一般的なものは k-means でしょうか。k-means は非階層型クラスタリングと呼ばれています。具体的には以下のようなアルゴリズムでクラスタリングを行います。

  1. 事前に設定されるクラスタ数 K を元に、ランダム(※)にクラスタの中心を決定する
  2. 各要素から一番近いクラスタの位置を探し、それを各要素のクラスタとして設定する
  3. 各クラスタの重心を計算し、その重心位置をクラスタの中心として設定する
  4. 各要素から一番近いクラスタの位置を探し、それを各要素のクラスタとして設定する
  5. 各クラスタの重心を計算し、その重心位置をクラスタの中心として設定する
  6. ...これを重心の移動がしきい値を下回るまで、あるいは指定の回数を超えるまで実施する
  7. 最終的に、各要素から一番近いクラスタの位置が、その要素のクラスタとして決定される

(イメージ図@雑)

※ランダムにするか、程よい位置にするか(k-means++)などの初期位置決定の方法があります

後述する階層型との比較でも挙げますが、初期位置によってある程度クラスタの別れ方が分岐することも往々にして発生します。また、クラスタリング結果が毎回異なる可能性もあり、厳密な分類がほしい、なんとなく纏めるでは嫌、といった場合の要望をかなえることはできません。かといって階層型クラスタリングがこの要望を常に叶えられるわけでもないのですが...

今回取り上げる階層型との比較として後述しますが k-means では、事前にクラスタ数を設定する必要があるため、それが難しいような状況では大きな力を発揮することが難しいです。

ちなみにアルゴリズムとして x-means という、クラスタ数を自動で設定できるものもあります。K=2 として再帰的に k-means を実施し BIC と呼ばれるベイズ情報量規準が、設定したしきい値を下回るまで実行していくようなものになっています。詳しいことはぼくもあまりわからないので、その話が上がっている論文を見ると幸せになれると思います。

http://www.rd.dnc.ac.jp/~tunenori/xmeans.html

機械学習について触れていると、似たような「クラス分類」といったワードを見かけたこともあるかと思います。こちらはクラスタリングとは異なり、教師あり学習で「あるデータがどのクラスに属しているかを当てる」という全く別の問題になります。リンゴは果物、キャベツは野菜、というような、明確に正解があるようなデータを分類していくときには、クラス分類になります。

 

階層型クラスタリングとは

階層型クラスタリングは以下のようなアルゴリズムで処理されます。

  1. すべての要素に固有のクラスタを設定する
  2. クラスタを2つ取り上げていき、最も距離が近いもの(※)をくっつけて、新しいクラスタを作成する
  3. これを再帰的に実行し、最終的にクラスタが1つになるまで実施する

※「距離」の定義は設計次第です。ユークリッド距離、コサイン距離、など様々な計算方法があります。

※くっつけて出来上がったクラスタ間の距離計算は設計次第です。

  • クラスタが含む要素をそれぞれ比較し、最も近いものを距離とする(最近隣法)
  • クラスタが含む要素をそれぞれ比較し、最も遠いものを距離とする(最遠隣法)
  • クラスタが含む要素をそれぞれ比較し、平均的な距離とする(平均法)
  • クラスタが含む要素をそれぞれ比較し、それぞれの距離について分散を利用して程よい値を取る(ウォード法、よく使われているらしい)

(イメージ図@雑)

そうして出来上がったクラスタリング結果ですが、このままでは木構造のようになっており、データとして扱うにはどれがどういうクラスタに属しているのかを明確に決定しにくいです。実際にあるタスクで利用したときには、最大距離の7割を基準にして、クラスタを分割していました。状況によってはパラメータの調整する必要がありそうな予感がしています。

計算量と実際にかかる時間を考慮すると、既に説明した k-means や、この階層型クラスタリングが高速に処理でき、「それっぽい結果」が得られるので、解決したい問題にも寄ってくるとは思いますが、クラスタリングにおける選択肢として現実的なのかなあと思います。

 

階層型・非階層型(k-means)のそれぞれの使いどころ

階層型が活用できそうなところは以下のようなところではないでしょうか?

  • データセットのうち、それぞれの距離に何かしらの特徴がありそう
  • どれくらいのクラスタに分けたらいいかわからない

階層型の場合。クラスタ間の距離で並べ替えることも容易にできるはずなので、しきい値の設定が小さいせいでクラスタを分けすぎてしまっても、ある程度までは前後関係もそれっぽく追いついてきます。なので、出来上がった後に、人力でカバーすることも可能なのではないかなあと思います(それってどうなの感はありますが...)あるいはクラスタ数がいくつになるか想定できないような場合にも、全てのデータを一度まとめあげるので、自動で出てきた結果の様子を見つつ、手動でクラスタ分けしていくことができると思います。

一方で非階層型...もとい k-means の活用できそうなところは以下のようなところではないでしょうか?

  • データセットの雰囲気がわからないけど、いったん分割してみたい
  • 明確に K 個の分類をしたい

k-means では最初に K を決定しないといけないので、どうあがいても K 個のクラスタができあがります。この分ける数が重要になるような、クラス分類に近いようなものの場合であれば k-menas は非常に有効なのではないでしょうか。ただし、それぞれのクラスタ間で相関がないことや、中途半端な位置のデータはどちらとも言えないような結果になることは注意がいるかもしれません。

また、どんなデータを入れても、なんとなくまとめてくれるので、人力で確認したときも、「ああーこういう見方もあるのね、なるほど」という謎の納得感が得られやすいとも思います。

 

階層型クラスタリングを Python でやってみる

それでは Python で 階層型クラスタリングをやっていきます。ここでは sciki-learn に入っているアイリスデータセットを対象に pandas で読み込んで scipy.cluster.hierarchy を使っていきます。

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster
from sklearn.datasets import load_iris

# iris データセットの読み込み
iris = load_iris()
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
iris_labels = list(map(lambda t: iris.target_names[t], iris.target))

# 階層型クラスタリングの実施
# ウォード法 x ユークリッド距離
linkage_result = linkage(iris_df, method='ward', metric='euclidean')

# クラスタ分けするしきい値を決める
threshold = 0.7 * np.max(linkage_result[:, 2])

# 階層型クラスタリングの可視化
plt.figure(num=None, figsize=(16, 9), dpi=200, facecolor='w', edgecolor='k')
dendrogram(linkage_result, labels=iris_labels, color_threshold=threshold)
plt.show()

# クラスタリング結果の値を取得
clustered = fcluster(linkage_result, threshold, criterion='distance')

# クラスタリング結果を比較
print(clustered)
print(iris.target)

出力結果

様子も見ずデータを絞ったりせず全てぶっこんで、というのはちょっと微妙なところですが setosa だけキレイに分かれましたね。setosa と、それ以外とで距離に開きがあるので、setosa だけ非常に特徴的ってことがわかります。それで versicolor と virginica はぼちぼち似ている、なんてところですね。

 

 

というわけで、階層型クラスタリングの話でした。

余談にはなりますが、そんなこんなで出来たやつをいい感じにクラスタ分けしたよ~^^って渡したら、分岐したやつを画像じゃなくて csv とかで欲しいって言われたのでそれはまた。

1 2 3 4 5 6 7 8 9 10 11 12 13