PHP-FPMに環境変数を渡す

  • 15
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

PHPを実行する環境として、FPMも有用です。ただし、FPM内から環境変数を参照するには、いくつもの壁が立ちはだかっています。

環境変数の使い方

データベースのアクセスキーやAPIのキーなどのような、Gitに残すのが適当でない情報や、データベースサーバの位置や環境設定の切り替え用のフラグなど、環境ごとに変更が必要な情報は、環境変数に入れておいてアクセスする、というのがサーバサイドでの定石のようになっています。

そういうわけで、システム側で環境変数を用意してPHPから読み取らせようとしたのですが、見事に失敗してしまいました。

PHP-FPMとは

Webサーバで動的なページを供給しようとする場合、いくつかの手段があります。

  1. Webサーバから別プロセスを呼び出す
  2. Webサーバにプログラムを組み込む
  3. 動的なページを処理するアプリケーションサーバを用意して、Webサーバがそちらに投げる

いちばん最初のがもともとのCGIですが、これではプロセスの生成コストがかさんでしまうので、高速化を追求する中では下火となっていきました。そして、PHPでは2番めのように、Apacheにmod_phpを追加して動かす、という形態もよく取られます。

ただ、ご存知のようにApacheへPHPを組み込むと、両者がからみ合って動作設定が複雑化する、片方だけ取り替えることができないなど、密結合にしたことによる問題が生じてきます。ということで、「アプリだけ動くサーバ⇔フロントエンドのHTTPサーバ」という2つに分ける方法も取られるようになってきました。

RailsなどRuby系でよく使われるUnicornやNode.jsのように、アプリケーションサーバ自体がHTTPで結果を返してももちろん構わないのですが、バックエンドアプリ用に開発されたFastCGIという専用のプロトコルもあります。そして、FastCGIで結果を返すPHP用のアプリケーションサーバ(PHP純正)がPHP-FPMです。

第一の壁…FPM自体

まずは、PHP-FPMのマニュアルを確認してみましたが、その中にclear_envという項目がありました。PHP 5.6以上や、PHP 5.4/5.5系でもリビジョンによっては、特に設定しないと環境変数がクリアされてしまうようです。

選択肢としては、「同じPHP-FPMの設定ファイルで環境変数も設定する」という方法と、「clear_env = falseとして、外からの環境変数を通す」という2つの手段があります。ここで設定するほうが楽かもしれませんが、あとあとcronなどFPMを経由しないでPHPを実行する際に同じ値を使えなくて煩雑になる、ということを考えて、clear_env = falseとすることで、第一の壁を外すことにしました(後述の事情により、むやみやたらな環境変数が来る心配はありません)。

第二の壁…systemd

Webサーバとして動かすには、FPMも常駐が必要なので、ふつうは何かしらのデーモン管理システムから起動することになります。そういうわけで、たとえばRHEL7系列であればsystemd経由で動かすことになるのですが、こちらも環境変数をクリアします

もちろん、「第三者が適当に設定した環境変数で動作が影響すべきでない」ということを考えれば、その動作は適切なのですが、今回は「あえて環境変数を設定している」ので、それは通してほしいものです。

で、調べてみると、EnvironmentFileとしてNAME=val形式の行を連ねたファイルを指定できるということだったので、ここで指定することでPHP-FPMにも環境変数が伝わるようにしておきます。そして、きちんと書いておけば同じファイルをシェルスクリプトとして読むこともできますので、シェルスクリプトでその設定ファイルをsourceしてexportすれば、1ファイルの設定でFPMにもシェルにも同じ環境変数を設定可能です。

第2.5の壁…PHP自体

順序としてはいちばん手前に来る壁のはずなのですが、これは迂回できるので最後に回しました。

これだけ設定して環境変数がPHP-FPMから見えるようになっても、$_ENVに値が入らないことがあります。php.iniの設定にvariables_orderというのがあって、よくGPCSと、$_ENVを生成しないような設定となっているのです。

設定を変更してももちろん構わないのですが、「警告」にあるように、FastCGI環境では$_SERVERにも環境変数が設定されるようになっていますので、確実にFPMで実行するのであれば$_SERVERから取ってしまうことができます。また、getenv()関数で取得しても構いません。

この投稿は PHP Advent Calendar 201516日目の記事です。