問題の発生
SSHでリモートのLinuxサーバーにログインした直後、あるいは新しいDockerコンテナを起動した時などに、apt-get upgradeのような単純なコマンドやPerlスクリプトを実行すると、ターミナルに大量の警告が表示されることがあります。
locale: Cannot set LC_ALL to default locale: No such file or directory
/usr/bin/locale: Cannot set LC_MESSAGES to default locale: No such file or directory
/usr/bin/locale: Cannot set LC_COLLATE to default locale: No such file or directory
これらの警告は、単に見栄えが悪いだけではありません。プロセスが完全に停止することは稀ですが、Perlスクリプトが失敗したり、ターミナルで「文字化け」が発生したりする原因になります。特に、英語以外の名前を含むデータベースのパースなど、繊細な文字エンコーディングを扱うスクリプトの場合、この設定の不備が実際のデータ破損につながる恐れもあります。
発生原因:環境のミスマッチ
このエラーは通常、ローカルコンピュータとリモートサーバー間のコミュニケーションの乖離に起因します。主な原因は以下の2つです。
- SSH変数のパススルー: ケースの約90%がこれが原因です。ローカルマシン(MacやLinuxノートPCなど)が
en_US.UTF-8に設定されている場合、SSH経由で接続すると、クライアントはその設定をサーバーに「エクスポート」しようとします。サーバー側にその特定の言語パックが生成されていないと、エラーが発生します。 - 最小構成のOSインストール: クラウドイメージやDockerコンテナは、容量を節約するために機能を最小限に抑えていることがよくあります。デフォルトでロケールが一つも生成されていない場合があり、システムが汎用的な「C」や「POSIX」状態のままになっていることがあります。
解決方法
この問題は、サーバーに不足している言語を認識させるか、ローカルマシンの設定を無視するようにサーバーに指示することで解決できます。
方法1:ロケールを生成する(推奨)
DebianやUbuntuでは、ロケールの定義をシステムが理解できるバイナリ形式にコンパイルする必要があります。まず、localesパッケージが実際にインストールされていることを確認します。
sudo apt-get update && sudo apt-get install -y locales
次に、必要な特定のロケールを生成します。ほとんどのユーザーは、標準的な米国英語のUTF-8を使用すれば問題ありません:
sudo locale-gen en_US.UTF-8
最後に、再起動後も変更が維持されるよう、システムのデフォルトを設定します:
sudo update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
Arch Linuxユーザーへの注意: /etc/locale.gen内のen_US.UTF-8 UTF-8のコメントアウトを解除してから、locale-genコマンドを実行する必要があります。
方法2:SSHのワークアラウンド
サーバーへのルート権限がない場合は、ローカルの~/.bashrcまたは~/.zshrcファイルを編集することでエラーを回避できます。ファイルの最後に以下の行を追加してください:
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
source ~/.bashrcを実行して、すぐに設定を適用します。これにより、現在のセッションで標準フォーマットの使用が強制され、サーバー側の不完全なデフォルト設定をバイパスできます。
方法3:SSHサーバーの設定を修正する
システム管理者として、すべてのユーザーに対してこの問題が発生しないようにしたい場合は、クライアントから送信されるロケール変数を無視するようにSSHデーモンを設定できます。設定ファイルを開きます:
sudo nano /etc/ssh/sshd_config
AcceptEnv LANG LC_*という行を探し、先頭に#を追加してコメントアウトします:
#AcceptEnv LANG LC_*
sudo systemctl restart sshでサービスを再起動します。これで、ユーザーのPCの設定に関わらず、サーバーは自身の内部ロケール設定を厳密に使用するようになります。
方法4:Dockerコンテナでの対応
最小構成のDockerイメージ(ubuntu:latestやdebian:stable-slimなど)では、この問題がよく発生します。恒久的に修正するには、Dockerfileに以下の行を追加してください。イメージサイズが3〜5MBほど増加しますが、後のエンコーディングに関するトラブルを防ぐことができます:
RUN apt-get update && apt-get install -y locales && \
sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
ENV LC_ALL en_US.UTF-8
確認
修正が正しく適用されたか確認するために、localeコマンドを実行します。正常なシステムであれば、先頭に「Cannot set」というエラーが表示されることなく、変数のリストが返されるはずです:
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_ALL=en_US.UTF-8
プロのヒント:環境をクリーンに保つ
新しい本番環境を構築する際、私は常に初期のBashスクリプトやAnsibleプレイブックにロケール生成を含めるようにしています。実行には10秒もかかりませんが、ログを汚す煩わしいPerlの警告を防ぐことができます。
URLやBase64データなど、エンコードされた文字列を扱うスクリプトをデバッグする場合は、文字化けを避けるために環境がUTF-8に準拠していることを確認してください。エンコードされた文字列を手動ですばやくチェックするには、URL Encoder/Decoderのようなツールが便利です。ブラウザベースで、処理中に不正なロケール設定によってデータが破損していないかを手軽に確認できます。

