zaki work log

作業ログやら生活ログやらなんやら

[ssh / docker / kubectl] ローカルの標準出力をリモートの標準入力にする

sshdocker execkubectl execで、シェルを起動せずに直接コマンドを実行できるが、sshなどを実行するローカルの標準出力をリモートホストやコンテナの標準入力として処理する方法について。

SSHの場合

前提。
ローカルのhostnameはcloud-devで、リモートはrhel8となっている。

[zaki@cloud-dev ~]$ hostname
cloud-dev
[zaki@cloud-dev ~]$ ssh 192.168.0.27 hostname
rhel8

ローカルのhostnameコマンド実行結果をリモートにパイプし、リモートではそれを標準入力(-)から受けてファイルへリダイレクト。(コマンドにあまり意味はない)

[zaki@cloud-dev ~]$ hostname | ssh 192.168.0.27 sh -c 'cat - > /tmp/hostname.txt'

結果、リモートでは/tmp/hostname.txtが生成され、ローカルのhostnameの結果が保存される。
ローカルにはこのファイルは無い。

[zaki@cloud-dev ~]$ ssh 192.168.0.27 cat /tmp/hostname.txt
cloud-dev
[zaki@cloud-dev ~]$ ls /tmp/hostname.txt
ls: /tmp/hostname.txt にアクセスできません: そのようなファイルやディレクトリはありません

Dockerのコンテナの場合

前提。
debianイメージでデプロイしたコンテナIDa24d81dbfee2のコンテナが動作している。
コンテナでhostnameを実行するとコンテナIDと同じになっている。

[zaki@cloud-dev ~]$ docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED         STATUS        PORTS     NAMES
a24d81dbfee2   debian     "tail -f /dev/null"      2 seconds ago   Up 1 second             relaxed_heisenberg
[zaki@cloud-dev ~]$ docker exec -i a24d hostname
a24d81dbfee2

前述SSHの例と同様に、ローカルのhostnameの実行結果をパイプでdocer execで実行中コンテナの標準入力としてファイルへリダイレクト。
ポイントは-iを付与すること。これが無いと標準入力を引き継げずに入力がなくなり、空ファイルが生成される。
また、-iとセットで使うことの多い-tは、付与するとthe input device is not a TTYとエラーになるので使用しない。

[zaki@cloud-dev ~]$ hostname | docker exec -i a24d sh -c 'cat - > /tmp/hostname.txt'

結果、コンテナ内の/tmp/hostname.txtにローカルのコマンド実行結果が保存される。

[zaki@cloud-dev ~]$ docker exec a24d cat /tmp/hostname.txt
cloud-dev
[zaki@cloud-dev ~]$ ls /tmp/hostname.txt
ls: /tmp/hostname.txt にアクセスできません: そのようなファイルやディレクトリはありません

KubernetesのPodの場合

前提。
HTTPサーバーのPodであるsample-httpが動作している。
Pod名はsample-http-6c94f59975-w89gdでPod内コンテナのホスト名も同じ名称になっている。

[zaki@cloud-dev ~]$ kubectl get pod
NAME                            READY   STATUS    RESTARTS   AGE
sample-http-6c94f59975-w89gd    1/1     Running   0          7d12h
[zaki@cloud-dev ~]$ kubectl exec sample-http-6c94f59975-w89gd -- hostname
sample-http-6c94f59975-w89gd

SSHとDockerの例同様、ローカルのhostnameの実行結果をPodへパイプし、Pod内でファイルへリダイレクトしてみる。
ポイントはDocker同様に-iを付与すること。これが無いと標準入力を引き継げずに入力がなくなり、空ファイルが生成される。
また、やはりdocker execと同様に-tを併用すると Unable to use a TTY - input is not a terminal or the right kind of fileというエラーになるので使用しない。

[zaki@cloud-dev ~]$ hostname | kubectl exec -i sample-http-6c94f59975-w89gd -- sh -c 'cat - > /tmp/hostname.txt'
[zaki@cloud-dev ~]$ kubectl exec sample-http-6c94f59975-w89gd -- cat /tmp/hostname.txt
cloud-dev
[zaki@cloud-dev ~]$ ls /tmp/hostname.txt
ls: /tmp/hostname.txt にアクセスできません: そのようなファイルやディレクトリはありません

使いどころ

個人的によく使うパターンは、ローカルにあるtar.gzのアーカイブファイルを、「リモートに転送してリモート上で展開せず」に、「tar.gzの中身を標準出力で転送してリモートで展開する」ことで、リモート上にtar.gzのアーカイブファイルそのものの転送と展開後の削除を省略したりする。
同じように、(この文脈だと紛らわしいけど)ローカルのコンテナイメージをdocker saveの結果をファイルではなく標準出力でsshにパイプし、リモートでdocker loadすることでイメージのtarファイル作成・転送・展開を一気にできる。

コンテナの場合も、例えばDBの初期構築用SQLファイルをローカルから一気にコンテナ内のコマンドに流したりできる。

SSHは昔から(横着したいときに特に)よく使ってたんだけど、そういえばDockerやKubernetesで同じことできるんだっけ?と思って試してみたらちゃんとできた。