CUBE SUGAR CONTAINER

技術系のこと書きます。

ClusterShell を使って複数のマシンを SSH で並列に操作する

複数のマシンを使って動作検証をしていると、ログインやコマンド入力の操作が煩雑になる。 また、複数のマシンに共通で必要な操作があったりすると手数もかさむ。 今回は、そういった問題を緩和できる ClusterShell について扱う。 ClusterShell を使うと、マシンをグループ化して SSH で並列に操作できる。

今回は、次のようなマシンの構成を扱う。 client には ClusterShell をインストールして、他のマシンを操作する。 masterworker[01] は名前通り異なる役割のマシンを想定して用意した。

  • client
    • 192.168.56.10
  • master
    • 192.168.56.20
  • worker1
    • 192.168.56.31
  • worker2
    • 192.168.56.32

上記のマシンは、あらかじめ Ubuntu 20.04 LTS を使って構築してある。 一応、末尾にはおまけとして Vagrant + VirtualBoxを使って仮想マシンを構築するための設定ファイルを用意した。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.3 LTS
Release:    20.04
Codename:   focal
$ uname -srm
Linux 5.4.0-91-generic x86_64

ClusterShell のバージョンは次のとおり。

$ clush --version
clush 1.8.3

もくじ

下準備

client には、まず ClusterShell をインストールする。 sshpass と openssh-client は SSH のために入れておく。

$ sudo apt-get update
$ sudo apt-get -y install sshpass openssh-client clustershell

これで clush コマンドが使えるようになる。

$ clush --version
clush 1.8.3

次に、ホスト名を使って操作したいので /etc/hosts に IP アドレスとの対応関係を書き込んでおく。

$ cat << 'EOF' | sudo tee -a /etc/hosts >/dev/null
192.168.56.10 client
192.168.56.20 master
192.168.56.31 worker1
192.168.56.32 worker2
EOF

次に SSH でログインするための公開鍵を作成する。

$ ssh-keygen -t rsa -P '' -f $HOME/.ssh/id_rsa

client から、その他のホストに公開鍵を使ってログインできるように登録する。 ここの作業は環境構築に使ったツールやイメージなどによって少し変わる。 たとえばパスワード認証が無効になっているイメージだと、この操作では登録できない。 また、Vagrant で作った環境なのでパスワードが vagrant になっている。

$ sshpass -p "vagrant" \
    ssh-copy-id -i $HOME/.ssh/id_rsa.pub -o "StrictHostKeyChecking no" master
$ sshpass -p "vagrant" \
    ssh-copy-id -i $HOME/.ssh/id_rsa.pub -o "StrictHostKeyChecking no" worker1
$ sshpass -p "vagrant" \
    ssh-copy-id -i $HOME/.ssh/id_rsa.pub -o "StrictHostKeyChecking no" worker2

次に、ClusterShell にホスト名とグループの対応関係を登録する。 対応関係は /etc/clustershell/ 以下の設定ファイルで指定する。 設定ファイルは <group-name>: <hostname>,... というフォーマットになっている。 以下では all というグループに、操作対象となるすべてのホストを登録している。 そして、グループ mmaster を、グループ wworker1worker2 を登録している。

$ sudo cp /etc/clustershell/groups.d/local.cfg{,.orig}
$ cat << 'EOF' | sudo tee /etc/clustershell/groups.d/local.cfg >/dev/null
all: master,worker1,worker2
m: master
w: worker1,worker2
EOF

これで ClusterShell を使い始める準備ができた。

個別のホストを指定して操作する

特定のホストを指定してコマンドを実行したいときは -w オプションを使う。 ここでは、それぞれのホストにホスト名を設定した。

$ clush -w master "sudo hostnamectl set-hostname master"
$ clush -w worker1 "sudo hostnamectl set-hostname worker1"
$ clush -w worker2 "sudo hostnamectl set-hostname worker2"

グループを指定して操作する

先ほどの例であれば、別に ssh(1) を直接使って操作しても変わらなかった。 ClusterShell の本領はグループを指定して操作できることにある。 グループを指定するには -g オプションでグループ名を指定すれば良い。 また、-L オプションを指定すると、結果をホスト名のアルファベット順でソートできる。

試しに all グループに対して hostname コマンドを実行してみよう。

$ clush -g all -L hostname
master: master
worker1: worker1
worker2: worker2

上記から、操作対象のすべてのホストに hostname コマンドが実行されたことがわかる。

また、-g all はよく使うので -a オプションがエイリアスとして用意されている。

$ clush -a -L hostname
master: master
worker1: worker1
worker2: worker2

同じように、特定のグループを指定してコマンドを実行してみよう。 以下ではグループ mw を、それぞれ指定している。

$ clush -g m -L hostname
master: master
$ clush -g w -L hostname
worker1: worker1
worker2: worker2

ちゃんとグループに所属しているホストに対してコマンドが実行されていることがわかる。

グループはカンマ区切りで複数指定することもできる。 以下ではグループ mw に対して実行している。

$ clush -g m,w -L hostname
master: master
worker1: worker1
worker2: worker2

複数のホストにファイルをコピーする

ClusterShell では、複数のホストにファイルを scp(1) できる。 ファイルをコピーするには -c オプションでコピーしたいファイルを指定して、コピー先のディレクトリを --dest オプションで指定する。

以下では greet.txt というファイルを、すべてのホストに対して /tmp 以下にコピーしている。

$ echo "Hello, World" > greet.txt
$ clush -g all -c greet.txt --dest /tmp

コピーされたはずのパスを cat(1) すると、ちゃんとファイルがコピーされていることがわかる。

$ clush -g all -L "cat /tmp/greet.txt"
master: Hello, World
worker1: Hello, World
worker2: Hello, World

書き込みに特権が必要なファイルをコピーするときは、少し工夫が必要になる。 具体的には、一度特権が不要なディレクトリにコピーした上で、あらためて特権ユーザでファイルを移動するというもの。 たとえば /etc/hosts をコピーしてみよう。

$ clush -g all -c /etc/hosts --dest /var/tmp
$ clush -g all -L "sudo cp /var/tmp/hosts /etc/hosts"

たとえば master の内容を確認すると、ちゃんとコピーされたことがわかる。

$ clush -w master "cat /etc/hosts" 
master: 127.0.0.1 localhost
master: 127.0.1.1 vagrant
master: 
master: # The following lines are desirable for IPv6 capable hosts
master: ::1     ip6-localhost ip6-loopback
master: fe00::0 ip6-localnet
master: ff00::0 ip6-mcastprefix
master: ff02::1 ip6-allnodes
master: ff02::2 ip6-allrouters
master: 192.168.56.10 client
master: 192.168.56.20 master
master: 192.168.56.31 worker1
master: 192.168.56.32 worker2

まとめ

今回は ClusterShell を使うことで、複数のマシンを SSH で並列に操作する方法を扱った。

おまけ: 環境構築に使った Vagrantfile

今回の環境を作るのに使った Vagrantfile を以下に示す。

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

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  machines = {
    "client" => "192.168.56.10",
    "master" => "192.168.56.20",
    "worker1" => "192.168.56.31",
    "worker2" => "192.168.56.32",
  }

  machines.each do |key, value|
    config.vm.define key do |machine|
      machine.vm.box = "bento/ubuntu-20.04"
      machine.vm.network "private_network", ip: value
      machine.vm.provider "virtualbox" do |vb|
        vb.cpus = "2"
        vb.memory = "1024"
      end
    end
  end

end

あとは以下で環境が用意できる。

$ vagrant up
$ vagrant ssh client

いじょう。