RedHat系におけるRPMパッケージを扱うYUM、Debian系におけるDEBパッケージを扱うAPT、これらはサーバー管理において重要なわけですが、絶妙な度合いで、おざなりに扱ってもわりとなんとか運用出来てしまう感があります。そのため今一度、こんな感じが今風のスタンダードじゃないっすかね(キリッ という構成を説明してみます。
ぶっちゃけ、たいしたことないネタの集合体なので、タイトルに下駄を履かせました。
そもそもパッケージは必要なのか
言うまでもなく必須です。理由は、インストール物のファイル管理が容易になるのと、インストール時間を短縮できるからです。既存のパッケージでconfigureオプションが物足りない時や、RPMパッケージが存在しない場合は作成することになります。最近はプロビジョニング・ツールによって全て自動化できるので、超簡素なコンパイルのものはレシピに落とし込んで終わりにしたくなるかもですが、上記理由により悪手であると断言できます。
パッケージの作成自体はただの慣れの問題なので、サクッと修得してしまいましょう。
パッケージ管理の全体構成
まずは全体構成図になります。以降はここから切り出して説明していきます。パッケージ作成
ここでは、パッケージ作成作業を、好きな仮想環境で行いましょう、というだけです。
普通にコンパイルからインストールして動作確認をしたり、パッケージ作成作業をしていると、ゴミが溜まっていくので、ポイ捨てできる環境でやったほうが気が楽だからです。
パッケージ作成手順そのものについては割愛!
(参考記事:CentOS7でのRPMパッケージ作成手順(sshguard編))
S3にレポジトリを作成
ApacheやNginxで独自レポジトリを運用する時代は終わりました。サーバー管理が面倒なので、S3のWeb Site Hostingを使いましょう。
S3の準備をする
バケットを作って、Web Site Hostingを有効にします。そして、こんな感じでツリーを作成します。
1 2 3 4 5 6 7 8 9 |
. ├── centos # クライアントがHTTPアクセスしにくるところ ├── centos-packages # 作成者がアップロードするところ │ └── 7 │ ├── SRPM │ └── x86_64 # .rpm置き場 ├── debian # クライアントがHTTPアクセスしにくるところ └── debian-packages # 作成者がアップロードするところ └── jessie # .deb置き場 |
S3のアクセス制限
たとえOSSのパッケージだとしても、自作パッケージを野放しに公開しておくのはよくないですから、どちらかの方法でアクセス制限をします。ソースアドレスで制限
S3のバケットポリシーの編集にて、こんな感じで制限できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "Id": "Policy1456810921862", "Version": "2012-10-17", "Statement": [ { ..., "Condition": { "IpAddress": { "aws:SourceIp": [ "1.2.3.4", "3.2.1.0/24" ] } } } ] } |
VPCエンドポイントで制限
外からのアクセスが不要ならば、VPCエンドポイントで制限してしまえます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "Id": "Policy1456810921862", "Version": "2012-10-17", "Statement": [ { ..., "Condition": { "StringNotEquals": { "aws:sourceVpce": "vpce-1234abcd" } } } ] } |
Lambdaでレポジトリ・メタデータの自動更新
S3にアップロードされたら、自動的にLambdaが起動して、レポジトリのメタデータが更新されるようにします。
IAMにロールを作成
まずは、Lambdaに割り当てるRoleを作成します。内容は大雑把にするとこんな感じ。レポジトリ更新用Lambdaスクリプトを作成
UserDataにBashをぶっ込みつつEC2に処理を行わせるスクリプトです。超頑張ったらLambdaでもレポジトリのメタデータを生成できるでしょうが、どう考えても無駄なので、最安インスタンスを立ち上げて、普通にコマンドベースでやったほうがよいという判断です。YUM版
APT版
LambdaのEvent Sourcesを登録
YUMとAPTにそれぞれ、パッケージファイルと同階層に “re-index” というファイルが置かれたら発動するよう、Event Sourcesを設定します。パッケージは複数一気にアップロードすることがあるので、.rpm をトリガーにしてしまうと、複数処理が走って色々面倒になるので、独自ルールを設けて運用することにしました。空ファイルをピロっとアップするだけなので、特に苦はないです。
パッケージファイルをアップロード
.rpm や .deb を所定の位置にアップロードし、re-index ファイルを置くと Lambda が走ります。完了すると、re-index ファイルは無くなり、クライアントがアクセスする方のディレクトリに、最新のパッケージとメタデータが入っています。全体のツリー状況としてはこんな感じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
./ ├── centos │ └── 7 │ └── x86_64 │ ├── repodata │ │ ├── 3e24f101c94d-filelists.xml.gz │ │ ├── 807c8cf5dbf9-other.sqlite.bz2 │ │ ├── 9b1b2a31078b-filelists.sqlite.bz2 │ │ ├── 9fbc896c2ad9-primary.xml.gz │ │ ├── b89fbda6cdd2-other.xml.gz │ │ ├── d6cb4b85a394-primary.sqlite.bz2 │ │ └── repomd.xml │ └── nginx-1.8.1-1.el7.ngx.x86_64.rpm ├── centos-packages │ └── 7 │ └── x86_64 │ └── nginx-1.8.1-1.el7.ngx.x86_64.rpm ├── debian │ └── jessie │ ├── dists │ │ └── jessie │ │ ├── main │ │ │ ├── binary-amd64 │ │ │ │ ├── Packages │ │ │ │ ├── Packages.bz2 │ │ │ │ ├── Packages.gz │ │ │ │ └── Release │ │ │ └── Contents-amd64.gz │ │ └── Release │ └── pool │ └── main │ └── n │ └── nginx │ └── nginx-dbg_1.8.0-1~jessie_amd64.deb └── debian-packages └── jessie └── nginx-dbg_1.8.0-1~jessie_amd64.deb |
クライアントの設定
これでレポジトリとしては利用できるようになったので、クライアントで利用できるかを確認します。YUMの場合はbaseurlに
を書きます。あとはそれぞれのパッケージ検索などを実行して表示されればOKで、出てこなければ yum clear all や apt-get update してみるなど。それでダメなら、何かエラーが出てると思います。
キャッシュサーバーの構築
プロビジョニング・ツールのレシピをGitで管理して、PullRequestのたびにCIテストを走らせていると、結構な頻度でどこかのパッケージ・レポジトリでエラーになって停止したりします。
テストだけならまだしも、他にイメージ自動作成や、インスタンス起動時などにもレシピを実行するタイミングがある場合、「レポジトリエラーの回避」は実は結構高い重要度となります。
パッケージ以外にも、wget などで外部アクセスをレシピに盛り込むと、そこがエラーになって止まる可能性はそれなりにあり、外部サイトの利用については予めポリシーを決めておく必要があります。
パッケージと外部サイト利用のポリシー
非常に良い例として、fluentd があります。fluentdのインストール手順はよく出来ている反面、外部アクセスが必要になるものです。
1 |
curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh |
中身を見ると、GPG鍵のインポートと、レポジトリ情報を作成してから、td-agent をインストールしています。これがもし、鍵のサーバーが落ちていたら、レシピがそのままなら自分達ではどうにもできなくなる、ということになります。
そのためレシピでは、鍵とレポジトリ情報を先にファイルとして保存し、外部アクセスは td-agent のインストールから必要とするようにしています。
他にも、zabbix は zabbix-release というパッケージをダウンロードしてインストールすることで、鍵とレポジトリ情報を取得できますが、zabbix-release の .rpm を wget できなければ、やはりエラーになります。そのため、zabbix-release のRPMは、S3の自前レポジトリにコピーしておくという方針にしています。
ポリシーをまとめると、
となりますが、4つ目の外部レポジトリの利用は、もし障害が発生したら解決に手間がかかってしまうため、自分達である程度のコントロールができるよう、以下のように対策をとります。
キャッシュサーバーを経由して耐障害性を向上する
外部レポジトリを直接見にいくと、障害の問題だけでなく、転送量的にもそれなりに迷惑をかけてしまうことになります。これを解決するために、クライアントにはキャッシュサーバーを経由してもらいます。yum.conf に以下のように記述することで、簡単にプロキシ経由にすることができます。
1 |
proxy=http://package-cache.example.com/ |
パッケージファイルは半永久的にキャッシュできるため、こうすることで、二度目以降はキャッシュサーバーからパッケージをダウンロードすることになり、外部レポジトリにアクセス不要になります。転送量的にも障害的にも解決できて一石二鳥です。
障害が出るとしたら、キャッシュサーバそのものの障害か、新しいパッケージの取得が必要になったと同時に、外部レポジトリに障害(またはアクセスしづらい状況)が発生した場合のみになり、かなり耐障害性が向上することが見込まれます。
で、今回はキャッシュサーバーには squid を採用してみました。squid は多機能なので難しく見えますが、今回の部分だけならば、かなり簡単な設定で済みます。
Squidのインストール
インストールします。
1 |
yum install squid |
今回はアクセス制限をDigest認証にします。アカウント情報のテキストを作成します。
1 |
UserName:SquidPassword |
メインの設定を追記編集します。下記設定のバージョンはver3.xです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
auth_param digest program /usr/lib64/squid/digest_pw_auth /etc/squid/digest.auth auth_param digest children 3 auth_param digest realm Yum Proxy auth_param digest nonce_garbage_interval 5 minutes auth_param digest nonce_max_duration 30 minutes auth_param digest nonce_max_count 50 acl password proxy_auth REQUIRED http_access allow localnet http_access allow localhost http_access allow password http_access deny all cache_dir ufs /var/spool/squid 4192 16 256 cache_mem 128 MB maximum_object_size 128 MB maximum_object_size_in_memory 16 MB |
※2016/3/10追記
もし、パッケージの最新情報を的確に捉えたいならば、YUMの場合は repomd.xml をキャッシュしないように設定します。
1 2 3 |
acl REPOMD urlpath_regex .*repomd\.xml$ cache deny REPOMD cache allow all |
再起動します。
1 |
service squid restart |
クライアントのyum設定を追記編集します。squidのポートは3128です。
1 |
proxy=http://UserName:SquidPassword@package-cache.example.com:3128/ |
Squidの運用
たいしてリソースを使うシステムじゃないので、ほどほどにメモリを使って、余裕ある程度にディスク容量を使えば、問題なく運用できます。が、キャッシュヒット率などを見ると面白いので、運用コマンドについて軽く説明しておきます。キャッシュヒット率などの確認
アクセス元毎の情報を表示してくれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ squidclient mgr:client_list Address: 1.2.3.4 Name: 1.2.3.4 Currently established connections: 0 ICP Requests 0 HTTP Requests 21376 TCP_HIT 13456 63% TCP_MISS 236 1% TCP_REFRESH_HIT 700 3% TCP_REFRESH_MISS 25 0% TCP_MEM_HIT 697 3% TCP_DENIED 6262 29% |
キャッシュファイルの確認
1つ1つのキャッシュオブジェクトの詳細を確認できます。EXの決定条件は色々あるっぽいですが、基本は以下。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ squidclient mgr:objects ... KEY BD85377090C9084486227A67EF70E8C6 GET http://ftp.riken.jp/Linux/centos/7/os/x86_64/Packages/NetworkManager-1.0.6-27.el7.x86_64.rpm STORE_OK IN_MEMORY SWAPOUT_DONE PING_DONE CACHABLE,DISPATCHED,VALIDATED LV:1456812660 LU:1456812660 LM:1448464812 EX:-1 0 locks, 0 clients, 1 refs Swap Dir 0, File 0X00016E inmem_lo: 0 inmem_hi: 2131034 swapout: 2131034 bytes queued ... |
キャッシュサーバーの耐障害性
最後に、キャッシュサーバーが落ちたらどうするか、について考えておきます。利用するタイミングが、自動テストや自動イメージ作成の時だけならば、パッケージを取得できなくなっても本番サービスには影響がないため、それほど耐障害性に気を使う必要はないでしょう。せいぜい、EC2に構築して、「Amazon EC2 Auto Recovery」に任せておき、監視を設定しておけば十分です。
もしAutoScalingのようなサーバーの自動起動において、最初にレシピを走らせてパッケージ取得をする必要があるかもしれない場合。まずはイメージ作成の時点でパッケージ操作を完了させてしまうことができないか、を最初に検討すべきでしょう。それでも必要であると判断した場合は、Squidを2台以上たてて、兄弟キャッシュの設定をしつつ、ロードバランサ経由にする、といった構成になるでしょう。
まとめ
仮想環境や、AWS、キャッシュサーバーを寄せ集めて、Linuxパッケージの管理をできるだけ 楽に 安全に する構成を考えてみました。それだけではなく、費用的にも極小でいけるので、良いこと盛りだくさんです。長く使うものですし、早い段階でこういった部分を作りこんでしまうことは、テキトーに運用している時の、どんどん汚く運用の流れがボケていくような不安を払拭できるでしょう。
とか偉そうなこと書いている私も、最近になってようやく、それなりに十分に納得いくシステムになってきたな、という感触なんですけども:-)