みなさん、こんにちは。
先日の記事では、cronでログインシェルを完全再現して「これで完璧!」と思っていたWithings-syncの自動同期。
しかし、技術の神様はそう簡単に微笑んでくれませんでした。数日は順調に動いていたものの、突然、またデータが同期されなくなってしまったのです。
「手動で実行すれば100%成功するのに、cronだとやっぱりダメ……」
エンジニアなら一度は経験する、この「深淵なる環境変数問題」。今回は、トラブルシューティングの迷宮に深く潜る代わりに、より確実で「力技」に近い解決策をご紹介します。それは、「SSHを経由してログインシェルごと叩き起こす」という方法です。
「なぜか動かない」に時間を溶かさない決断
前回、bash -l -c を使ってログインシェルをシミュレートする設定を行いました。理屈の上ではこれで手動実行と同じ環境になるはずなのですが、数日後にまた「API接続はできているのにデータが空」という現象が再発しました。
正直に言うと、この原因を特定するために、さらに数時間を費やして環境変数のログを比較したり、ライブラリの読み込み順を追ったりするのは、個人の開発としては少しコストが高すぎます。
「シェルから叩けば100%動く。なら、外部からシェルを叩きに行けばいいじゃないか」
そう考えた私は、別のLinuxサーバーからSSH経由でコマンドを送り込む方法に切り替えました。幸い、私の環境には24時間稼働しているサーバーが複数あります。この「別の端末からログインして実行する」というプロセスは、普段ターミナルで行っている操作そのもの。つまり、環境の違いが発生する余地がほとんどないのです。
鍵認証で「パスワードなし」の自動ログインを実現する
もちろん、他の端末から自動実行するために、毎回ログインパスワードを入力するわけにはいきません。そこで、SSHの「公開鍵認証」を設定します。
手順1 – 接続元のLinuxで鍵ペアを作成する
まず、コマンドを「送る側」のサーバーで鍵を作ります。現在は、高速でセキュリティも高い「ed25519」という形式が推奨されています。
ssh-keygen -t ed25519 -C "for_withings_sync"
※パスフレーズを聞かれますが、自動化が目的なので空(そのままEnter)で作成します。
手順2 – 公開鍵を「同期スクリプトがある端末」に登録する
次に、作った公開鍵を「受け取る側(withings-syncが入っている端末)」へコピーします。ssh-copy-id コマンドを使えば一瞬です。
ssh-copy-id ユーザー名@リモートホストのIP
これで、接続先の ~/.ssh/authorized_keys に自動的に鍵が登録されます。
手順3 – パスワードなしでログインできるかテスト
ssh ユーザー名@リモートホストのIP
これでパスワードを聞かれずにログインできれば成功です!
コマンド単発実行のテスト
次に、SSH越しにコマンドが動くか確認します。
ssh ユーザー名@リモートホストのIP ls
接続先のホームディレクトリが表示されましたか?これができれば、あとはスクリプトを呼び出すだけです。
今回は、接続先の端末に withings-sync.sh という簡単なシェルスクリプトを作成しました。中身は以前紹介した withings-sync の実行コマンド(絶対パス指定)を書いただけのものです。
cronの設定 – 司令塔を「別サーバー」へ
最後に、「送る側」のサーバーのcronに以下を登録します。
crontab -e
# 毎日午前2時に、SSH経由で同期スクリプトを実行
0 2 * * * /usr/bin/ssh ユーザー名@リモートホストのIP /path/to/withings-sync.sh >> /tmp/ssh_sync.log 2>&1
これで、毎日午前2時になると、別のサーバーが「おーい、同期してくれ」とSSHでログインし、シェルスクリプトを叩き起こしてくれます。
この方法のメリット – 究極の「安定性」
この方法で解決できるかどうか、数日試してみる予定です。SSHでコマンドを実行することは、ユーザーがキーボードを叩いてログインするのとほぼ同じプロセスを辿るので大丈夫のはずですが、どうなるでしょうか。
cronから直接Pythonを叩くと、どうしても「cron専用の極小環境」という制約に縛られます。しかし、SSH経由であれば、ログイン時に読み込まれる設定(環境変数、パス、pyenvの初期化など)が自然に適用されます。
「本来の問題(なぜcron環境だけ失敗するのか)」は解決していませんが、エンジニアリングにおいて重要なのは「安定して、目的の結果が得られるシステムを構築すること」です。トラブルシューティングに疲れ果てたときは、こうした「アーキテクチャによる解決」を試してみることも一つの正解だと言えるでしょう。
今回の教訓
今回、トラブルシューティングに予想以上に時間がかかっています。そして、未だに根本的な解決には至りません。しかし、求める結果を得られるための試行錯誤の過程で、エンジニアリングの知見がたまるのは間違いないでしょう。今回の教訓をまとめておきます。
- cronの環境変数問題は、時に理屈を超えた挙動をする。
- SSH経由の実行は、ログイン環境をそのまま使えるため極めて安定する。
- 公開鍵認証を設定すれば、セキュリティを保ちつつ完全自動化が可能。
「シェルでは動くのに!」と頭を抱えている方は、このSSH経由の「外出し実行」も試してみてください。驚くほどあっけなく解決するかもしれません。
本日も最後までお読みいただき、ありがとうございました。
それでは、そしてよいIoTライフを!



