オープンソースこねこね

Webプログラミングなどについてあれこれ。

SSHラッパーコマンドEsshのv1.0.0をリリースしました

Esshは、zsh補完やLuaスクリプトによる動的なコンフィグレーションなど、便利機能を実装した、sshのラッパーコマンドです。

github.com

ドキュメントをまとめたWebサイトも作成しました。

https://essh.sitespread.net/

上記Webサイトのトップにasciinemaで撮ったターミナルオペレーションのデモを乗せましたので、そちらを見ていただければ、どういったものか大体のことはわかると思います。

開発の経緯

2015/11に以下の記事を書き、Zsshというコマンドを作成しました。

kohkimakimoto.hatenablog.com

その後、CentOSリポジトリzsshというパッケージが存在していたため、コマンド名をzsshからesshに変え、自分の利用にあわせて、適宜、機能の追加や変更を続けていました。そして、自分が使う上で必要十分な機能がそろって、大きな変更もなくなってきたので、この度これをv1.0.0としてリリースすることにしました。なお2015/11時点のものとは仕様が大きく様変わりしており、以下に改めて、機能について説明します。

機能

EsshはGoで書かれたシングルバイナリのCLIツールで多機能sshコマンドとして利用できます。実装的には内部でsshコマンドを実行するラッパーコマンドになっています。標準のsshコマンドでは使えない以下のような機能が利用できます。

  • Luaによる設定ファイル。これによりSSH接続サーバの設定(ssh_config)をより動的に構築することができる。
  • サーバ接続、切断時に発火するフックコマンドを設定できる。
  • サーバにタグをつけられ、一覧をターミナルに出力できる。
  • ZshBash用の強力な補完を組み込みで提供。
  • カレントディレクトリごとに設定ファイルの切り替え。
  • Capistranoのような複数サーバに並行してコマンドを実行できるタスクランナー。
  • 設定用のLuaコードはモジュールとしてGitリポジトリ上で共有できる。

一通りの機能に触れるためのチュートリアルを用意したので、利用の際はここから始めるとよいかと思います。

https://essh.sitespread.net/intro/ja/index.html

利用例

上記のチュートリアルには書いていないが、私個人が使っている機能などを中心にいくつか紹介します。

ログイン時にターミナルの色を変える

接続、切断時のフックコマンドhooks_before_connecthooks_after_disconnectでターミナルの色を変えます。私は本番環境につなぐときは赤にしています。

-- ~/.essh/config.lua

-- 事前にmacのターミナル設定でRedとBlackというプロファイルを作っておく
local red_screen_command    = "osascript -e 'tell application \"Terminal\" to set current settings of first window to settings set \"Red\"'"
local black_screen_command  = "osascript -e 'tell application \"Terminal\" to set current settings of first window to settings set \"Black\"'"

host "webserver-01" {
    HostName = "192.168.56.32",
    Port = "22",
    User = "kohkimakimoto",
    description = "web server-01",
    hooks_before_connect = { red_screen_command },
    hooks_after_disconnect = { black_screen_command },
    tags = {
        "web",
    },
}

host "webserver-02" {
    HostName = "192.168.56.33",
    Port = "22",
    User = "kohkimakimoto",
    description = "web server-02",
    hooks_before_connect = { red_screen_command },
    hooks_after_disconnect = { black_screen_command },
    tags = {
        "web",
    },
}

f:id:kohkimakimoto:20170410083157g:plain

アプリケーションのデプロイ

リモートサーバにコマンドを実行します。以下はただのサンプルです。実環境ではもっと複雑なスクリプトを実行させています。

host "webserver-01" {
    HostName = "192.168.56.32",
    Port = "22",
    User = "kohkimakimoto",
    description = "web server-01",
    tags = {
        "web",
    },
}

host "webserver-02" {
    HostName = "192.168.56.33",
    Port = "22",
    User = "kohkimakimoto",
    description = "web server-02",
    tags = {
        "web",
    },
}

task "deploy" {
    backend = "remote",
    targets = {"web"},
    prefix = true,
    parallel = true,
    script = [=[
        echo "deploy app to $ESSH_HOSTNAME"
        
        # your deploy commands...

        echo "Done."
    ]=],
}

f:id:kohkimakimoto:20170410104414g:plain

以上です。日々のSSHライフのお供に、どうぞお使いください。m( )m

ReactNativeのiOSアプリをオフラインの実機で動かす

※2017-04-10現在のメモ。

先に結論。以下の#if !(TARGET_IPHONE_SIMULATOR)で囲った部分のコードをios/AppName/AppDelegate.mに仕込むといいです。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

#if !(TARGET_IPHONE_SIMULATOR)
  jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#else
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
#endif

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"AppName"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

経緯

ReactNativeでiOSアプリを作ってみてます。SwiftやObjective-Cで作ったアプリのように、開発中のアプリをUSBケーブルにつないだiPhoneにインストールして、動作確認してみようとしたら、うまくアプリが立ち上がらない。ドキュメントやGithubのissuesをみるとiPhoneが同じwifiにいないと動作しないらしいです。

「react native offline」とかで検索するとstackoverflowやgithubのissuesで、対処法が書いてあるが、今ひとつうまくいかない。というか、ReactNativeの更新が速いせいか、いまでは通用しない古い情報がまじっていてよくわからないです。で、Objective-Cは以前触ったこともあってソースが読めるので、直接ソースを追ってみました。それでわかったこととして

  • 開発中ReactNativeは開発マシン上にhttpサーバを起動し、アプリはこのhttpサーバからJavaScriptコードを取得する。
  • この仕組みのおかげで、開発中再ビルドなしで、変更を反映させられる。実機の場合も同様だが、オフラインだとhttpサーバに到達できず、エラーになる。
  • ビルド時、リリースビルド用にJavaScriptを一つのファイルにまとめてアプリにバンドルするスクリプトが、XCodeのrun scriptで定義されている。
  • リリースビルドの場合のみ、httpサーバからではなくファイルからJavaScriptを読むようにマクロで制御されている。

というわけで冒頭に記載したようにios/AppName/AppDelegate.m内のコードを

#if !(TARGET_IPHONE_SIMULATOR)
  jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif

のようにして「シミュレータ以外のとき」という条件で、JavaScriptのロケーションを設定している変数を、リリースビルドのときと同様に、バンドルされたJavaScriptファイルを読みに行くよう上書きすればOKです。この場合当然、実機上で動かすと、再ビルドなしでコードの変更を反映させることはできなくなるので、それが必要なときは都度この部分を削除やコメントアウトするなりして、対応すればいいかな、と。今のところは。

OmnibusでPHPアプリケーションをRPMにパッケージングする

要約: Omnibusを使うとApachePHPをバンドルしたオールインワンなPHPアプリケーションのRPMパッケージを作れます。

最近CLIツールやAPIサーバなどはGoで書くようになって、PHPをさわる頻度が減ってきているのですが、それでもPHPの実行環境が不必要になることはなく、その個人的な一つの理由にAdminerがあります。これは2012年に一度ブログにも書いたのですが、PHPのシングルファイルをDocumentRootにおくだけで動作する、データベース用のWebUIです。

Adminer - PHPの1ファイルを置くだけで簡単に使えるDB管理ツール - オープンソースこねこね

現在でもメンテは続いているようで、今ではElasticsearchやMongoDBなどにも対応しているそうな。個人的にもずっと使い続けています。

ただ、前述のようにGoの適用範囲が広がってきたので、新しく立ち上げたサーバとかだと、Adminerを使う以外にApachePHPが必要ない環境もあります。後々の運用管理のことも考えてサーバ構築にはプロビジョニングツールを使っていることもあり、これ一つのために、ApachePHPを入れてhttpd.confとphp.iniを書き換えるレシピ書いて。。。てなことがやや面倒くさいです。

Adminerの機能単体でぱっとインストール、アンインストールできればいいかもしれない、と考えたので、omnibusを使ってApachePHPをバンドルしたオールインワンなPHPアプリケーションのRPMを作成してみました。omnibusについては以前ブログに書いたのでそちらを参照してください(CentOS用にSupervisor3.3.0のRPMパッケージを作成した - オープンソースこねこね )。今回の成果物は以下

github.com

Githubのリリースページに生成したRPMをあげたので

$ sudo yum install https://github.com/kohkimakimoto/omnibus-adminer-server/releases/download/v0.3.0/adminer-server-0.3.0-1.el7.x86_64.rpm

でインストールできます。これだけで/opt/adminer-serverApachePHPランタイムを含めたアプリケーションがインストールされます。これらはもちろんシステムワイドにあるApachePHPとは干渉しません。あとは、

$ sudo systemctl start adminer-server

とすればApacheが14200ポートで起動しますのでブラウザでhttp://localhost:14200/adminer.phpにアクセスすると

f:id:kohkimakimoto:20170329115005p:plain

と画面がでます。

補足

  • サービス設定は現状systemdのみの対応なので、このRPMはCentOS7限定です。initスクリプトを書けばCentOS6などにも対応できますが、自分が使わないので書いてません。
  • 設定ファイルは/etc/adminer-server、adminer.phpなどのPHPファイルは/var/lib/adminer-server以下に配置してあります。
  • 今回はやってないがMySQLなどDBもバンドルすれば、いわゆるLAMPアプリケーションがパッケージ配布可能になるんじゃないかと。

あとがき

こんなことしなくても、今はDocker使えばいいかも、とも思いますが、Systemdにサービスを登録するところまでyum installlでできるのでミドルウェア的なものを扱うのなら、RPMも便利ですよと。。。

テストやビルドスクリプトをDockerコンテナで実行させるための便利ツールを作った

ある程度複雑なプログラムだと、ローカル環境やCIサーバ環境など、どこでも動くようにテストを書くのが難しくなったりします。また、昨今のWebアプリケーションだと、デプロイ前に何らかのビルドプロセスが必要になることがほとんどで、依存物をインストールしたり、Webpackを動かしたり、Goのビルドを走らせたりします。こういうタスクもnodeやらGoに依存していて、複数の環境で差異なく動かすことを考えると、いろいろツライものがあります。

そこで、こういったテストやビルド処理はシェルスクリプトを書いて、Dockerコンテナ内で動かすことによって環境に対しての依存を解消していました。 たとえばGo言語の場合以下のようなテスト実行スクリプトtest_run.shを書いておきます。

#!/usr/bin/env bash
set -eu

go test $GOTEST_FLAGS $(go list ./... | grep -v vendor)

そしてこれをDockerコンテナで実行するためのスクリプトとして以下のようなtest.shを書きます。

#!/usr/bin/env bash
set -eu

DOCKER_IMAGE=${DOCKER_IMAGE:-'kohkimakimoto/golang:centos7'}
GOTEST_FLAGS=${GOTEST_FLAGS:--cover -timeout=360s}

repo_dir=$(cd $(dirname $0); pwd)

docker run \
  --env GOTEST_FLAGS="${GOTEST_FLAGS}" \
  -v $repo_dir:/build/src/github.com/username/repo \
  -w /build/src/github.com/username/repo \
  --rm \
  ${DOCKER_IMAGE} \
  bash ./test_run.sh

goコマンドなど、テストに必要な環境は全てDockerイメージとして用意しておきます。あとはリポジトリのルートにいる状態で./test.shを実行すれば、テストが実行されます。TravisCIのようなCIサーバはDockerに対応しているので、同様に./test.shを実行するだけで、ローカル環境と同じようにテストが実行できます。テストコード側で環境の違いを意識する必要はなくなります。

ところで、しばらくこの方式でやっていたところ、以下のような課題が浮き彫りになってきました。

  • 新しいプロジェクトごとにシェルスクリプトをコピペ、修正して使いまわしている。
  • これらのスクリプトはだいたい似たようなコードになりがちだが、利用するDockerイメージやdocker runのオプションなどが微妙にちがう。
  • テストやビルドなど個々のタスクごとに「実際のタスクを実行するスクリプト」と「それをDockerコンテナで実行するスクリプト」の2つのファイルを作る必要があり、数が増えるとファイルがごちゃごちゃしてきた。

ベタに書いたシェルスクリプトによる実装なので、プロジェクトが増えると、共通化などができずコードが冗長になりがちでした。そこで、大雑把に処理を整理してみると

  • カレントディレクトリをDockerコンテナの特定の場所にマウントする
  • --rmオプションをつけて、コンテナ停止後自動でコンテナを削除するようにする
  • スクリプトを実行する。

などが共通な処理であったので、これらをまとめて、いい感じにDockerコンテナを起動してその中でスクリプトを実行するコマンドラインツールを作りました。

github.com

Goで実装したシングルバイナリなので、Githubのリリースページからバイナリをダウンロードして、パスの通ったディレクトリに配置すればすぐに使えます。

使い方

まずは単純に

$ buildsh

を実行してみます。これだけで、デフォルトでDockerイメージkohkimakimoto/buildsh:latest(2Gくらいあります)をダウンロードして、カレントディレクトリをコンテナ内の/buildにマウントした状態でコンテナを起動します。そのままbashでログインした状態になるので、たとえばPHPのテストだったら

$ php phpunit

などを実行すればテストができます。PHPPython,nodeなど主なLLのランタイムを入れてあるので、ホストマシンの環境に関係なくすぐにテストやビルドができます。作業が終わったら

$ exit

すれば、ホストマシンにもどってコンテナが破棄されます。特定の環境でちょっとしたことを動作確認したい場合などに便利に使えます。

そしてテストやビルドのシェルスクリプトを実行させたい場合は

$ buildsh test.sh

のようにスクリプトファイルを指定して実行すれば、そのスクリプトがDockerコンテナ内で実行されます。これで用意するスクリプトはテストを実行する部分のみでよくなり、「それをDockerコンテナで実行するスクリプト」を書く必要がなくなりました。

設定ファイル

使用するDockerイメージを変更したい場合などは、設定ファイルを利用することができます。.buildsh.ymlをカレントディレクトリに配置してください。以下のような設定ができます。

use_cache: true
docker_image: kohkimakimoto/buildsh:latest
additional_docker_options: --net=host -v=/var/run/docker.sock:/var/run/docker.sock
environment:
  FOO: bar
  FOO2: bar2
home_in_container: /build/src/github.com/kohkimakimoto/buildsh

詳細はREADME.mdを参照していただくとして、いくつかピックアップして概要を説明します。

use_cache

use_cacheはtrueにするとカレントディレクトリ配下に.buildsh/cacheディレクトリを作り、コンテナ内でパスを環境変数BUILDSH_CACHEDIRに設定します。これはスクリプト実行ごとに破棄されてしまうコンテナ内のデータを保存するときに使うことができます。たとえばyarn installを以下のようにすれば、キャッシュを保持できて、次回以降の処理の高速化が望めます。

$ yarn install --cache-folder=$BUILDSH_CACHEDIR/yarn

.gitignore.buildshを追加するのを忘れずに。。。

docker_image

docker_imageは利用するDockerイメージです。自分の用途に合わせて好きなイメージを使えます。

additional_docker_options

additional_docker_optionsは内部で実行しているdocker runに付け加えるオプションを指定できます。例のように-v=/var/run/docker.sock:/var/run/docker.sockを利用すればDockerコンテナ内から新しくDockerコンテナを立ち上げることもできたりします。

home_in_container

home_in_containerはコンテナ内でホストのカレントディレクトリがマウントされるパスを変更できます。デフォルトは/buildですが、例えばGoのテストやビルドをおこなうとき、GOPATHの関係上/build/src/github.com/kohkimakimoto/buildshのようにGOの流儀にそったパスに配置したいことがあります。この設定を使うことでそのようなケースに対応できます。

実装について

このツールはつまるところ、テストやビルド目的の使い捨てコンテナである場合docker runにセットするオプションや引数がおおむね共通化できるので、それをまとめたラッパーコマンドというわけです。

もともとはbashスクリプトで書いていたこともあって、実装は単純なdocker runのラッパーコマンドになっているので、メインのコードはbuildsh.goのみです。なのでをこれを見れば何をやっているのか大体わかりますので、細かいことはソースを見たほうが早いかもしれません。

mattn/memoのzsh補完

Golang実装のメモコマンド

Big Sky :: golang でメモ専用コマンド「memo」作った。

GitHub - mattn/memo: 📓 Memo Life For You

が便利だったので、雑にzsh補完を書きました。

_memo_options() {
    local -a __memo_options
    __memo_options=(
        '--help:show help'
        '-h:show help'
        '--version:print the version'
        '-v:print the version'
     )
    _describe -t option "option" __memo_options
}

_memo_sub_commands() {
    local -a __memo_sub_commands
    __memo_sub_commands=(
     'new:create memo'
     'n:create memo'
     'list:list memo'
     'l:list memo'
     'edit:edit memo'
     'e:edit memo'
     'delete:delete memo'
     'd:delete memo'
     'grep:grep memo'
     'g:grep memo'
     'config:configure'
     'c:configure'
     'serve:start http server'
     's:start http server'
     'help:Shows a list of commands or help for one command'
     'h:Shows a list of commands or help for one command'
     )
    _describe -t command "command" __memo_sub_commands
}

_memo_list() {
    local -a __memo_list
    PRE_IFS=$IFS
    IFS=$'\n'
    __memo_list=($(memo list))
    IFS=$PRE_IFS
    _describe -t memo "memo" __memo_list
}

_memo () {
    local state line

    _arguments \
        '1: :->objects' \
        '*: :->args' \
        && ret=0

    case $state in
        objects)
            case $line[1] in
                -*)
                    _memo_options
                    ;;
                *)
                    _memo_sub_commands
                    ;;
            esac
            ;;
        args)
            last_arg="${line[${#line[@]}-1]}"

            case $last_arg in
                edit|e|delete|d)
                    _memo_list
                    ;;
                *)
                    ;;
            esac
            ;;
        *)
            _files
            ;;
    esac
}
compdef _memo memo

~/.zshrcとかにコピペして使ってください。

※2017-02-10 追記: 本家リポジトリに取り込んでもらいました。https://github.com/mattn/memo/blob/master/misc/completion.zsh

systemd-nspawnで/sys/fs/selinuxがマウントできないので警告される件は無視することにした

ただのメモです。

CentOS7でsystemd-nspawnの動作を検証してるなか、SELinuxを無効にした状態でコンテナを起動させると/sys/fs/selinuxがマウントできないと警告がでてきました。

# systemd-nspawn -D /var/tmp/mycontainer 
Spawning container mycontainer on /var/tmp/mycontainer.
Press ^] three times within 1s to kill container.
Failed to create directory /var/tmp/mycontainer//sys/fs/selinux: No such file or directory
Failed to create directory /var/tmp/mycontainer//sys/fs/selinux: No such file or directory
-bash-4.2# 

/sys/fs/selinuxSELinuxを無効にすると存在しないディレクトリ。調べたらsystemd-nspawnはHAVE_SELINUXマクロでこのディレクトリのマウントを制御しています

systemd/nspawn.c at v219 · systemd/systemd · GitHub

そしてログを出力しているところはココ

systemd/nspawn.c at v219 · systemd/systemd · GitHub

ソースを見る限りその後の処理は続行しているし、問題なさそうかな〜と考えていたら、

systemd-nspawn: dont try to mount non existing selinux directories by tblume · Pull Request #4569 · systemd/systemd · GitHub

で、同様の問題の当たっていたヒトがこの警告を消すためにログレベルをLOG_DEBUGからLOG_WARNINGにさげるPRを送っていてmasterにマージされていました。なので、今後CentOS(systemd)のバージョンが上がれば、この警告は消えるので、現状無視することにした、というお話でした。

CentOS7でサーバを構築する際におこなっている基本設定

以前書いた

CentOS6でサーバを構築する際におこなっている基本設定 - オープンソースこねこね

のCentOS7バージョンです。調査中の内容もあるので、情報は随時更新する予定です。

※ここではサーバのロールにかかわらず行う基本的な作業をまとめています。通常この環境の上にWebサーバ(httpd, nginxなど)やDB(MySQLなど)をインストールすることになります。

※本文中のコマンドは基本的にrootユーザで行っているものとします。一般ユーザを使い必要に応じて都度root権限を使うようにしている方はsudoをつけるなど、コマンドを読み替えてください。

デフォルトターゲットの確認

systemctl get-defaultで取得。CentoOS6までのデフォルトランレベルに相当します。

# systemctl get-default
multi-user.target

multi-user.targetになっていることを確認します。

サービスの起動設定の確認

サービスの管理はsystemdによる管理に刷新されています。

systemctl list-unit-filesでサービスの一覧と状態を取得します。

# systemctl list-unit-files --type service --no-pager
UNIT FILE                                   STATE   
arp-ethers.service                          disabled
auditd.service                              enabled
auth-rpcgss-module.service                  static  
autovt@.service                             disabled
blk-availability.service                    disabled
brandbot.service                            static  
chrony-dnssrv@.service                      static  

STATEの意味は以下の通り。

  • enable: 自動起動有効
  • disabled: 自動起動無効
  • static: 他のユニットに依存。有効/無効が設定できないもの。

不要と思われるサービスはサーバリソースの無駄使いになるので、以下のコマンドで停止設定にしておきます。

# systemctl disable nginx.service
# systemctl stop nginx.service

OSの起動時に実行させたいサービスは

# systemctl enable nginx.service
# systemctl start nginx.service

としておきます。

ディクスサイズの確認

# df -h

サーバスペックにあったディクスサイズになっていることや、パーティションを確認します。

ロケールの設定

現在のロケールの設定を確認します。

# localectl status

利用可能なロケールlocalectl list-localesを調べ、

# localectl list-locales --no-pager

localectl set-localeで変更。

# localectl set-locale LANG=ja_JP.utf8

ロケールを日本語にしたとき、manページを日本語にするために

# yum install man-pages-ja

をインストールします。

タイムゾーンとNTPの設定

現在のタイムゾーンを確認します

# timedatectl status

利用可能なタイムゾーンtimedatectl list-timezonesで調べます

# timedatectl list-timezones --no-pager

timedatectl set-timezoneで変更。

# timedatectl set-timezone Asia/Tokyo

RTC in local TZ: yesの設定になっていると警告がでるので以下を設定する

# timedatectl set-local-rtc 0

CentOS7では時刻同期サービスが2つあって従来のntpdと新しいchronydがある。最小構成でOSインストールした場合は chronydだけがインストールされている状態になっているはず。それぞれ設定ファイルは

/etc/chrony.confまたは/etc/ntp.confです。設定ファイルのフォーマットはほとんど同じなので

server 0.centos.pool.ntp.org iburst

のような、時刻問い合わせ先サーバの設定を確認します。

# systemctl start ntpd.service
or
# systemctl start chronyd.service

でサービスを起動します。

最後にtimedatectlでntpによる時刻合わせを有効化します。

# timedatectl set-ntp yes

ネットワーク関連の設定

CentOS7ではネットワーク周りの管理がNetworkManagerを使った方式に変更されています。 NetworkManagerのコマンドラインインターフェースであるnmcliを使って設定するのが標準の方法ですが、 ひとまず従来CentOS6の方法でできることは、それを踏襲してます(非推奨の方法だと思うので、のちのち更新予定です)。

まず、従来のifconfigなどがデフォルトでは提供されていないのでnet-toolsパッケージをインストールします。

# yum install net-tools

ホスト名

従来/etc/sysconfig/networkに記載していたホスト名はCentOS7では/etc/hostnameに記述します。

ネットワーク・インターフェースの確認

CentOS6のときと同様に/etc/sysconfig/network-scripts配下のifcfg-xxxを確認します。 設定を変更したら、

systemctl restart network

で反映させます。

IPアドレスの確認

# ifconfig

IPアドレスの設定が、設定通りか確認します。

ネットワークの疎通確認

# ping yahoo.co.jp

pingが外に向けて通るかを確認します。0% packet lossとなればOK。

# traceroute yahoo.co.jp

外につなぎに行く際にどのようなIPを経由するかを確認。ネットワーク管理者ではないので、ざっと結果を眺めるだけです。

yumリポジトリ

CentOS6のときと同様。epelとremiは(個人的に)必須なのでインストールします。epelは直接yumからインストールできます。

# yum install epel-release
# yum install http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

その他、設定ファイルなどはCentOS6のときと同様。

bash-completion

CentOS6のときと同様。素のbashのままだとzshになれた補完脳にはツライので、bash-completionを入れます。

# yum install bash-completion

インストールするだけで、ある程度補完が効くようになります。カスタマイズは特にしていません。

ファイアウォール (firewalld)

ファイアウォールiptablesからfirewalldに変わりました。 firewalldはiptablesをバックエンドにしたファイアウォール管理のフロントエンド的なもので、実は内部的にはiptablesが依然使われています。とはいえ、設定に関してはやることがガラっと変わっています。

設定のための覚書

firewalldにはゾーンという概念があり、サービスやポートへの接続許可、拒否などの設定をまとめた単位。ゾーンはデフォルトで

  • block
  • dmz
  • drop
  • external
  • home
  • internal
  • public
  • trusted
  • work

があります。ファイアウォールはこのゾーンをNICに割り当てることで、有効化され、機能します。 デフォルトではpublicゾーンが、有効化、割り当てされています。

# firewall-cmd --list-all

で確認できます。ゾーンについては

# firewall-cmd --list-all-zone

で一覧がでます。

デフォルトで有効になっているpublicゾーンは、明示的に許可した通信しか受け入れないようになっているので、基本的にこのゾーンに接続許可するポートやサービスの設定をしておけば、普通のファイアウォールの用途としては要件を満たせそうです。設定はfirewall-cmdを実行することで行います。

設定

いくつか典型的な設定パターンをメモしておきます。

特定の接続先(この例では192.168.0.1/24)からのみサービスへの接続(この例ではSSH)を許可する場合:

# firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.0.1/24" service name="ssh" accept' --permanent

サービスの定義は/usr/lib/firewalld/services/配下にあるxmlファイルを読む。ポート番号などがここに定義されています。

特定の接続先(この例では192.168.0.1/24)からのみポート8000の接続を許可する場合:

# firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.0.1/24" port port="8000" protocol="tcp" accept' --permanent

ポート80の接続を許可する場合:

# firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" port port="80" protocol="tcp" accept' --permanent

ちなみに上記のポートを単純に開放するだけなら

# firewall-cmd --zone=public --add-port=80/tcp --permanent

でも良さそう。個人的に設定の書き方を統一しておきたいので基本的に--add-rich-ruleを使っています。

設定を変更したら

# firewall-cmd --reload

で反映させます。その後確認のために

# firewall-cmd --list-all

でゾーンの設定を表示し、内容をチェックします。

sshd

CentOS6のときと同様。ポート番号を変えて、鍵認証オンリーにします。以下、編集および確認箇所のみを記載します。

/etc/ssh/sshd_config

# 22以外に変えます
Port 2222
# rootログインは禁止
PermitRootLogin no
# パスワード認証は禁止
PasswordAuthentication no

再起動して設定を反映します。

# systemctl restart sshd

ログの管理

syslogに代わる新しいログデーモンとしてjournaldが提供されています。systemdのサービスとして起動されたプログラムの標準出力、標準エラー出力は自動的にjournaldへと送られてログ管理の対象になります。ログはバイナリデータで保存され、専用のjournalctlコマンドを使って検索、表示させるという操作になります。

ログの保存場所はデフォルトで/var/run/log/journal。これは一時領域なので再起動したら消えてしまうため、/var/log/journalに変更する。

# mkdir /var/log/journal
# systemctl restart systemd-journald

上記のように/var/log/journalを作成して、journaldを再起動すればOK。

ログの容量の最大サイズはファイルシステムの10%まで。変更するには/etc/systemd/journald.confないで

SystemMaxUse=500M
RuntimeMaxUse=500M

のようにサイズ指定すればよいです。

参考:http://www.server-memo.net/centos-settings/journald/journald.html

logrotate

ログのローテート設定です。journaldにではなく、サービスが直接ファイルになどに出力したログファイルをローテートします。

CentOS6のときと同様です。ログが無尽蔵に大きくなってディクスサイズを圧迫しないように設定、確認します。 初期設定時の他にhttpdなどのデーモンやログを出力するアプリケーションを追加した時にも目を通します。

/etc/logrotate.d/以下にログの対象ごとに設定ファイルがあるのでそれを確認します。例えばnginxは以下のようになっていたりします。

/etc/logrotate.d/nginx

/var/log/nginx/*log {
    create 0644 nginx nginx
    daily
    rotate 10
    missingok
    notifempty
    compress
    sharedscripts
    postrotate
        /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

実際の設定はログのサイズや保存期間の要件などによって異なるので、それを考慮し設定します。

sysctl

CentOS6と同様、カーネルパラメータの設定です。CentOS6のときは/etc/sysctl.confを編集してましたが/etc/sysctl.d/配下にxxx..confファイルを配置しても設定できるので、そうしてます。(実はCentOS6のときからできたっぽいですが。参考:http://qiita.com/ngyuki/items/002a4a153ac6d8075bdb)

/etc/sysctl.d/override.conf

net.nf_conntrack_max = 1000000
net.netfilter.nf_conntrack_max = 1000000
net.core.somaxconn = 1024
vm.overcommit_memory = 1

設定の内容はCentOS6でサーバを構築する際におこなっている基本設定 - オープンソースこねこねを参照。設定値の反映は以下のコマンドを実行します。

# sysctl --system

参考: http://tsunokawa.hatenablog.com/entry/2015/09/02/175619

以下のコマンドで確認。

# sysctl -a