Python入門者の集い #6 に参加してきました
突然ですが、煽られたのでこちらの勉強会にLTしてきました。
発表したもの
最近PyQやっているので、PyQの紹介とPythonでライブラリ使ってLinuxと仲良くなりたい話をしました。 ファイルディスクリプタのところは自分でもスライド書いてて理解を整理できたので発表するのも勉強になってよい。 黒曜石を使ったのですが良かったけど、下むいて発表してしまったので、 事前の発表練習も必要だなと反省。
その他感想
LTでPillowというライブラリを使ったモザイクアートがおもしろかった。 嫁は二次元にいると言っていて愛を感じた。 作ったものを公開するとたのしそうだし、周りからの反応もあると嬉しいだろうなあ。
懇親会で何人かに声をかけて貰えて、参考になったと言ってもらえたのは嬉しかったです。 次に発表するときは内容が濃いものにしたいので、Pythonで何か作ったものを発表したい。
発表する敷居が低いのでお勧めの勉強会です。
Apache Usedslotの見方
Apache Usedslotの見方をまとめてみた
稼働状況の表示(ステータス情報表示)
稼働中のプロセス数,待機中のプロセス数および各プロセスのステータス(R,W,Lなど)をWebブラウザに表示 この情報を基に,StartServers,MinSpareServers,MaxSpareServers,MaxClientsディレクティブなどをチューニングできる
server-statusハンドラの指定
ステータス情報の表示機能を利用するには,次に示すようにserver-statusハンドラを指定 Webサーバのステータス情報はアクセス制御して,エンドユーザには非公開にするのが一般的
<Location /server-status> SetHandler server-status </Location>
URLの指定
ステータス情報を表示するには,Webブラウザやcurlで次の形式でURLを指定
http://IPアドレス/server-status?auto
autoはプレーンテキスト形式で表示できる
# curl http://127.0.0.1/server-status/?auto Total Accesses: 4366246 Total kBytes: 77563940 CPULoad: .34465 Uptime: 150727 ReqPerSec: 28.9679 BytesPerSec: 526949 BytesPerReq: 18190.8 BusyWorkers: 13 IdleWorkers: 7 Scoreboard: K_KKKW._.K..K._.K___KK.W.K_.K..............................................(略)..........
※ステータス情報の表示機能で取得できる情報(auto指定がある場合)
- Total accesses:合計アクセス回数
- Total kBytes:合計通信量
- CPU load:CPU使用率
- Uptime:サーバプロセスの稼働時間(秒)
- ReqperSec:1秒当たりのリクエスト数
- BytesPerSec:1秒当たりの通信量
- BytesPerReq:1リクエスト当たりの通信量
- BusyWorkers:リクエスト処理中のサーバプロセス(スレッド)数
- idle workers:リクエスト待ち状態のサーバプロセス(スレッド)数
- Scoreboard:個々のスレッドの動作状況
※稼働中のサーバプロセスの状態の記号
- _ : リクエスト待ち状態
- S : 起動処理中
- R : クライアントからのリクエストを受信中
- W : リクエストの処理実行およびクライアントへレスポンス送信中
- K : 持続型接続状態でリクエスト受信待ち
- D : ルックアップ中
- C : 接続を終了中
- L : ログ出力処理中
- G : graceful restartにおける処理終了待ち
- I : スレッド停止中
- . : 起動していない状態
見方のPoint
- 「R」 or「W」 が多ければ待機プロセスを増やしたほうがいいので、MaxClients を増やす。
- 「.」が多ければ、ServerLimit を減らす。
- 「_」が多ければ、待機中プロセスが多いので、MinSpareServers と MaxSpareServers を減らす。
監視では
.と_を抜かした個数を分子にして、.を抜かした個数を分母にすれば以下のことがわかる。
- Apache の同時接続数の割合を監視することができる
- MaxClients に対しての 使用中のスロット数 の割合を監視対象の数値とすることができる
スケーラビリティの技術の基礎
サーバやアプリをクラウドに置くと安いし便利ってだけではなく、 クラウドで実現可能なことは全世界に展開するような WEBスケーラブルなシステムが作れることが可能になったこと。 技術的には大変だけど... 全世界に展開するようなWEBシステムに携わるかはわからないけど、 大規模分散システムの原理原則や考え方を知っておくのは何かの役に立つかもしれない。
複製管理における基礎技術について
べき等性
重複実行してもよい処理のこと
分散システムにおける一般的な問題
クライアントがあるリクエストに対し完了通知がこないときに、 リクエストを再送すると、すでに処理済みである場合がある
対応策1
重複実行してもよい(べき等・Idempotent)処理か確認し、 そうであれば何度でもリクエストに対し処理を起動する(固定ファイル新規作成処理など)
対応策2
ミドルウェアまたはアプリケーションがリクエストにIDを振って、 処理完了を記録することで処理済みの処理を2回起動しないようにする(課金処理など) 処理がまだなら課金処理をして、処理済みだったら終了通知を発行させる処理を入れれば、 同じリクエストを投げても、2重課金がおきない。
ACID
複数のデータストアに対するトランザクション(ひとかたまりの論理的な操作)の性質と 書き込みなどのトランザクション実施時において、ACID実現のために必要なこと(高コスト)
- Atomicity(原子性):含まれる操作がすべて実行されるか、すべて実行されないかのいずれかの結果になる
- 原子性のため、全ノードで全操作の実施可否を確認し、全ノードが実施可能である時の実施
- Consistency(一貫性):アプリケーションが定める不変条件をみたす
- 一貫性のための判断を実施可否確認時に行う
- Isolation(孤立性):複数のトランザクションは互いに干渉しない(外部から、途中過程や部分的に実施された状態にアクセスできることはない)
- 孤立性のため、実施時には影響がおよぶ領域への他の読み書きをブロックする
- Durability(耐久性):一度実施することが確定すると、その変更は永続的に反映される。
- トランザクション処理中、完了したあとにノードが落ちても処理が保たれている。
2PCプロトコル
Two-Phase Commitment(2PC)Protocol
ACIDを達成するための有名なプロトコル
参加者はcommitまたはabortに投票
クォーラム(定足数)
読み書き操作に関する一貫性のみを考えると、 必ずしも全ノードを同一の状態に揃える必要はない
- 最新値を読み出すことができる
- 最新値がシステム内に複数存在することはない
過半数と多数決 書き込みと読み込みの数を変えてコストを調整できる。
2QW(書き込み数) > n かつ QW(書き込み数) + QR(読み込み数) > n(全ノード数)
2PCとクォーラムはノードの増減は想定していないけど、 実際は増減する。障害やオートスケールなど
過半数の合意を取ることでノード増減してもクォーラムなどでも対応できる。
一貫性
データストアに格納された値が最新値(書き込みの発生順序が守られている)に関して データストア(を実現する複数ノード)で合意を取ることが前提(しかし高コスト) これがそうでない場合はどうだろうか。
複数を用いたデータストアに複数のノードからの書き込みと読み込みを行うと タイミングでは書き込みの発生順序・最新値がことなるように外から見えてしまう。
一貫性モデル
データを管理するデータストアと、アプリケーションプログラムの間の約束事
- アプリケーションプログラムが守るべきルール
- データストアが保証する性質
順序一貫性(Sequential Consistency)
どのような捜査の結果も、すべての読み書き操作があたかも直線的な順序で行われたとしたときの結果と同一となる 複数を用いたデータストアに複数のノードからの書き込みと読み込みを行うとタイミングでは書き込みの発生順序・最新値がことなるように外から見えてしまうことは許さない!!!! 内部実装としては、複数間で「次に扱う書き込み」について常に合意しながら進めることになる
因果一貫性(Causal Consistency)
因果的に関連している可能性のある書き込みは、必ず同じ順序で観測される 順序一貫性で許されなかった複数を用いたデータストアに複数のノードからの書き込みと読み込みを行うとタイミングでは書き込みの発生順序・最新値がことなるように外から見えてしまうことは許される
許されないことは?
aを読んでbを書き込むという因果関係の例
P1:Write x:=a P2:Read x -> a その後、Write x:=b P3:Read x -> a その後、Read x->b P4:Read x -> b その後、Read x->a
P4の観測結果はaを読んでbを書いたという把握できる順序関係に反している!!!
ベクトルタイムスタンプ
イベントの発生順序を判別するために使う論理クロックの一手法 物理クロックは誤差があるかもしれず、物理時間での前後関係をすべて気にする必要もない 時間ではなくて、前後関係がわかるタイムスタンプのこと
概要
各ノードは、「自分や他のノードでいくつかのイベントが起きたか」に関する情報をもつ 自ノードでイベントが発生したらインクリメント 他ノードへのメッセージ送信時には、自身が持つ情報を添えて送り、受信側はその情報を使って自身が持つ情報を更新
A[5,2,8]<Y[5,5,10] # Yの方が後 C[7,5,8]?Y[5,5,10] # どっちが先か後か断言できない
因果一貫性を実現するには書き込みマルチキャストだけをカウントする
オーバヘッドが少ないので、負荷を気にせずできる。
弱一貫性(Weak Consistency)
アプリケーション側から一貫性を保たせるには、API側で最新値を取らせる(Lockなど)、順序一貫性よりかは軽い。
- 順序一貫性を保証する「同期変数」への操作を提供して、アプリケーションプログラム側において、一貫性が必要な場合に制御可能にする
- データストアへLockをとるイメージ
- すべての先行の書き込みがすべての場所で完了するまで、同期変数への操作は許容されない
- 読み込み前に同期操作を行えば、最新値を読み取れる
- Lockをとってから読めば、最新値をとれる
- すべての先行の同期変数へのオペレーションが完了するまで、データへの操作は許容されない
- 書き込み後に同期操作を行えば、進行中・部分的な書き込み、書き込み結果の伝搬を完了できる
イベンチュアル一貫性/結果一貫性(Eventual COnsistency)
更新は全てのコピーに伝搬される(更新がなければすべての複製は同一に収束していく) 書き込み同士の衝突はなくて、読み込みが必ずしも最新でなくても構わないデータストアに適している(DNS,Webキャッシュ) クラウドでは、スケーラビリティを重視するために採用されている(書き込み同士の衝突もあるし、読み込みは最新がよいが、それでも採用したほうが良いと考える)
クラウドサービスにおける設計
AmazonSQS
Amazon Simple Queue Service (SQS) マイクロサービス、分散システム、およびサーバーレスアプリケーション用の完全マネージド型メッセージキュー。
- メッセージの送受信のためのキュー機能を提供する。
- キューは複製されている。キューが飛んでデータが吹っ飛ばないようにしている。
- 全てのキューが同じデータを持つ状態に「収束」していく(イベンチュアル一貫性)
- キューを読み出した時、その前におくられたメッセージが含まれるとは限らない(FIFOとは言っていない。)
キューもworkerも落ちる可能性がある。
- 疎結合なアーキテクチャに寄与する
- 提供者と利用者の待ち合わせなし
- 空いたインスタンスが随時タスクを請負可能
- 少なくとも一回実行のための仕様(2回以上しないという仕様は含まれていない)
- 受信されたメッセージは、キューから取り除かれるけど、一定時間後に再びキューに含まれる
- メッセージを受信してタスクを請け負ったプロセスが落ちる可能性を想定
- タスクが完了したら、明示的にメッセージ削除をする
- メッセージを重複して受信、処理がされる可能性がある。(処理中なのにメッセージが復活するケースなど)
AmazonSimpleDB/DynamoDB
イベンチュアル一貫性のあるデータストアで、Read/Writeするとなにがおきるか説明されてあるドキュメント。
Amazon DynamoDB
- 整合性があり、10 ミリ秒未満のレイテンシーを必要
- すべての規模のアプリケーションに対応した、高速かつフレキシブルなNoSQLデータベースサービス
- 完全マネージド型のクラウドデータベース
- ドキュメントとキー値のストアモデルの両方をサポート
- 支払いはスループットに発生する。1秒間に何回書き込みできるようにするか。
料金概要
仮想的な測定指標に対してお金を払う。わかりづらい。
1 つの書き込みキャパシティーユニット (WCU) は、1 秒あたり最大 1 回の書き込みを提供 1 つの読み込みキャパシティーユニット (RCU) は、1 秒あたり最大 2 回の読み込みを提供
1 日に 500 万回の書き込みと500万回の結果的に整合性のある読み込みを実行する必要があるとします
結果的に整合性のある読み込み 常に最新というわけではない。誤差は出てくる。 1秒間に500万アクセスきても大丈夫だし、値段もわかる。 オートスケールさせると値段が見えにくくなってくる。
Amazon DynamoDB では、ユーザー定義プライマリキーを使用したGET/PUTオペレーションがサポート
- BatchGetItems
- BatchGetItems演算は、複数の項目の属性を複数のテーブルから、それぞれのプライマリキーを使用して返す。16件の応答のサイズ制限は1MBで、最大100個の項目を返す。強い整合性と結果整合性の両方をサポート
- 16件の応答のサイズ制限は1MBで、最大100個の項目というクラウドとしては微小量の割合だけど、重い処理をしたときに他のユーザへ影響がでないようにしている。
- BatchWriteItem
- UpdateItem
Amazon DynamoDB では、スカラー値でアトミックなインクリメント演算とデクリメント演算を行うことができる。 アトミックとは今の値を読み込んで、+1して書き込むではなく、一度の処理でやる。 その他の書き込みなどは防がない。 タイムアウトなどしたら、書き込みはべき等ではない。 UpdateItemは正確な値を取りたいときには辞めたほうがいい。
Amazon DynamoDBはデータストアだけど、並行システム、マルチスレッドシステムの知識がないと使えない。 ユーザ側に並行システムを持つことに、これらの知識が求められている。
Aliceとボブがいる。今の価格が10とする。 Aliceが価格を8に更新する。Bobが後で価格を12に変更。するとAliceの価格が上書きされる。 値引きと値上げがタイミングによって起きている。 これを防ぐためにはユーザのプログラミングでUPDATEを投げる時には状況が変わっていることを前提にする。 もし価格が10なら8にする。もし価格が10なら12にする。というAPIを呼ぶ。 こういう可能性が知っていないと、気づけ無いし対応できない。 マルチスレッドプログラミングとおなじ。並行プログラミングもしっていないとだめ。 Lockすれば解決できるけど、ブロックしたくない、誰かのために他のひとを待たせたくないからこういう仕様。 悲観的、楽観的な発想。
DynamoDBにはScanもある。 1回のScanリクエストで、最大1MBのデータを取得 一度の処理で全データをみるのはやめてほしい。
ブロックさせない、他のシステムが遅くさせることはさせない。 プログラミングの不備があると、不整合によってユーザの手元のデータが消える。ユーザに試されている。
Key-Value Storeにおける思想
- RDB
- 依存関係の整理(正規化)に基づく設計,保存データ量の最小化
- ex)「ID→氏名、会社名」「会社名→会社住所」
- 実行時にSQLクエリに応じた複雑な処理が発生
- 上の2テーブルを結合してIDから会社住所をひっぱってくる
- 実行時にコストをとるような考え
- 依存関係の整理(正規化)に基づく設計,保存データ量の最小化
- KVS
- あらかじめクエリパターンを考慮して、実行時には高速なget/putだけで済むような設計
- ex)頻発するなら、「ID→会社名、会社住所」DBも、「会社住所→ID,氏名」DBもつくればいい
- keyから引っ張ってくる、冗長的、書き込む時は書き直さないと正確に値を引っ張ってこれない、
- あらかじめクエリパターンを考慮して、実行時には高速なget/putだけで済むような設計
内部版のDynamo
要求・仮定
- 1MB未満くらいのデータの読み書き(keyへのget/put)ができればいい
- 複数のデータにまたがる操作はない
- Amazonの通販サイトの多くの部分がこれで動く
- 可用性のために一貫性をゆるめる(ACIDのC)
- 一つのkeyに対する読み書きだけ考えていて、一連操作性(I)のための保証は考えていない
- 分布の99.9%以上に対し、読み書き数百msec未満
- 一貫性や耐久性と性能などトレードオフは設定可能にしてある
- 常に書き込み可能に
- 2フェーズコミットや、イベンチュアル一貫性も使っていない
- 古典的には、読み込みを軽くし、一貫性のための調整コスト(ブロックや失敗)は書き込みが追う
- ex)書き込み全・読み込み1のQuorum
- 一貫性のための競合解決方法は、アプリ開発者が決める柔軟性を残す
- 「物理時間で最新のものを残す」などだけではなく
- 少しずつノードを追加可能・対称性を重視(どのノードも同じ)・非中央集権・ノードは異種混合になっている
読み書きの実装
- "Sloppy"(だらしない)Quorum
- 読み書きリクエストを受け取ったノードは、N個の複製に送信し、読み書きそれぞれに定められた一定数(R,W)以上の確認をもって成功とする ※必ずR+W >N(全ノード数), 2W>Nにするとは言っていない
- 書き込みの周知が想定ノードに届かなかった場合、別の代替ノードに送信 (可能であれば元のノードに書き戻す)
- 耐久性への保証を犠牲にして性能を上げる設定
- 基本的にはメモリ上のバッファを使って、そこへの書き込み完了を持って確認を返信する
- バックグラウンドで随時二次記憶に書き込む(その前にクラッシュするとその複製では書き込みが失われる)
- N個の書き込み周知先のうち、1つだけ二次記憶にすぐに書かせることで耐久性を少し向上
書き込み競合
- 常に書き込み可能
- 書き込みを受け付けた後で複製間の同期を行う
- 並行する書き込みや一時的なネットワーク分断により、複数の「最新」値が存在しうる
- ベクトルタイムスタンプを使ってバージョンを管理する
- 把握できる因果順序がある場合、自動的に競合解決可能
- そうでない場合は、次の書き込み手に意味を考慮した競合解決が委ねられる
- アプリ依存の競合解決の例
- ショッピングカート内の商品リストへ追加
- セッションではなく二次記憶に保存している
- 以上は論文から読み取れる内容
- 同じ仕組みで「ショッピングカート内の商品リストから削除」を実現するとどうなるか
- 削除はどうしているかは不明
- 操作ログだけをもっているのかもしれない
- 実際の発生率(複数の最新値が存在するときは)
CAP定理
- 複数を用いるアプリケーションは、下記のうち3つのうち2つのみを実現することができる
- 定理よりかは原理原則
- Consistency(一貫性):複数全体での結果一致を守ってのデータ更新やその耐久性(Durability)、一貫性の程度もある
- Availability(可用性):(十分に早く)結果を返答する
- Partition Tolerance(ネットワーク分断への耐性):システムの一部が分断・故障しても機能する
CAP定理とクラウド
- First Tier:リクエスト処理を行うWEBページ
- すぐに応答を返さないといけない。一貫性を妥協して古い情報をみせるとか。APが重要でCを妥協する。
- ユーザ体験を大事にする気持ち。
- Second Tier:スケーラブルなKey-Valueデータ
Inner Tiers/Backend:データベースやインデックスバッチ分析処理など
フロントエンド側において、CよりAPを優先する例
CAP定理の位置づけ
- バックエンド側ではCが必要
- Cのための技術は確立されている(RDBとか)のに、Cを実現すると、APが犠牲になるのか
- 今のRDBははやいので、十分APを実現している論
- いやいや、想定しているスケーラビリティが違うし、コストが違うと反論
- CAP定理とは高いスケーラビリティを実現するために一貫性を弱める事を受け入れることが低コストな解であることが多いということ
BASEトランザクション
ACIDすてたもの ACIDに対するBASE
- Basic Availability;基本的に常に利用可能,部分的にでもリクエストが返ってる(すべての機能ではないかもしれないが)
- Soft-State Replication:耐久性のあるメモリ上のデータストア(キャッシュ)を活用、2次記憶に書き込むのが正義ではない
- Eventual Consistency:イベンチュアル一貫性(書き込み操作完了時には、その最新値がシステム内で合意されたとは限らない)
「D.Pritchett(EBay),Base An Acid Alternative,2008」
※少しの不整合やACID違反を「正常動作」と定義することを真剣に検討すべき [Vogles(AmazonCTO)] 何らかの不整合がでることも0ではない
今までの話はクラウドでしかできないスケーラビリティがある。 サーバやアプリをクラウドに置くと安いってだけの話ではない。 技術的には大変だけど全世界に展開するようなWEBスケーラブルなシステムが作れることが可能になった。
REST(Representational State Transfer)
REST(Representational State Transfer)
Webシステムアーキテクチャの原則を定義したもの
原則
- Client-server
- Stateless
- Cacheable
- Uniform
- interface
- Layered system
- Code-on-demand(optional)
こういう考え方にしたがって作ろうよっていうもの Webシステムのデザインといってよい
以下のものが求められている
- 可用性
- いつでもちゃんと返事が来る,応答待ちにならない
- スケーラビリティー
- 大量のリクエストを捌ける
- 耐故障性
- サーバ側でマシンが落ちていても構わない
WEB系企業が推奨しているけど、原則6個を全て使えとは言っていない。
原則6個が言っていること
- Client-server:ユーザインタフェースとデータストレージの関心事を分離
- ユーザインタフェースのポータビリティ、サーバのスケーラビリティ
- Stateless:リクエスト内にそれを理解するための情報を全て含み、サーバ側に状態を持たせない
- スケーラビリティ、監視の容易性、故障からの復帰の容易性
- ステートフルだとやりとりしていたサーバが落ちたら情報が消える。
- Cacheable:キャッシュ可能なものを明確に区別
- 効率、スケーラビリティ
- Uniform interface:単一インターフェースに統一
- 単純さ、インターフェースと機能の分離
- オブジェクト指向プログラミングのメソッドが沢山でてくる。
- インターフェースはGET、PUT、POST、DELETEの4種類にする。
- デザインをするのはGET、PUT、POST、DELETEのでしよう。
- Layered system: 階層構造による不要な詳細の隠蔽
- 複雑さの軽減、依存性の低減
- 自分の直下のレイヤーだけを知っていればいい
- Code-on-demand(optional):必要な機能の、クライアント側へのオンデマンドでの読み込み(オプション)
- 実行時の拡張性
シェルスクリプト②(declare,配列,連想配列)
declareでの変数宣言
オプションをつけることで、変数に属性を付与できる
オプション | 意味 |
---|---|
-r | 変数を読み取り専用 |
-i | 変数を整数とする |
-a | 変数を配列とする |
-A | 変数を連想配列とする |
指定しない場合は、型は文字列になる
読み取り専用ならreadonlyコマンドをつかうとよい
配列
配列は要素を並べて格納し、インデックスという番号で参照できるデータ構造。 インデックスは0からはじまる。
- 配列の作成
配列変数の右辺に()を書いて、要素を記述する。これを複合代入と呼びます。
# fruit=(apple orange banana)
空の配列
# list=()
declareでの配列宣言
# declare -a arr1
- 要素の参照
配列の要素の参照は${配列名[インデックス]}
# echo ${arr1[0]}
インデックスを指定しない場合は、インデックス0が参照される。
要素の数を取得するにはインデックス"@"を指定 ${#配列名[@}}
# echo ${#arr1[@]}
- インデックスを利用した代入
bashの配列は連続している必要がないので、空のある配列は複合代入がつくれる。 以下だとインデックス2と4が空文字列の配列ができる。
# arr2=(test0 test1 [3]=test3 [5]=test5 test6 )
- 配列の中身を変更
# test3=(hoge1 hogehoge) # test3[1]=hoge2 # # echo ${test3[0]} # echo ${test3[1]}
- 要素の削除
# unset test3[1]
- すべての要素の参照
配列のすべての要素を参照する場合は、インデックスに*や@を指定
# test4=(hoge0 hoge1 hoge2 hoge3 hoge4) # echo ${test4[*]} # echo ${test4[@]}
""でくくった違いも"$@"と"$"と同じで、 "${配列[@]}"は要素が個別の文字列として展開される "${配列[]}"は配列全体をIFS変数の最初の1文字(たいていはスペース)で連結した1つの文字列として展開される 基本的には"${配列[@]}"を使うのがよい。
配列のコピーに使える
# test4_2=("${test4[@]}")
- 要素の追加
"${配列[@]}"の他の使い方は、元の配列の先頭や末尾に要素を追加した、新しい配列の作成です
# test4_3=(hoge5 hoge6 "${test4[@]}") # 配列の先頭に要素を追加 # test4_4=("${test4[@]}" hoge7 hoge8) # 配列の末尾に要素を追加
- 値の存在するインデックスの取得
${!配列名[*]}や&{!配列名[@]}みたいに、配列名の前に!をつけると、配列の中で値が存在するインデックス一覧を取得できる
# echo ${!arr2[@]}
連想配列
連想配列はkeyとvalueのデータ構造 hashとかmapとかdictとか言われるやつ
declare -Aで連想配列宣言をする。 これを忘れると配列になってしまうので、注意。
# declare -A user=([id]=1 [name]=testuser)
- 要素の参照
# echo ${user[id]} # echo ${user[name]}
# echo ${#user[@]}
- 要素の代入
連想配列ではキーを指定して、キーに対応する値があれば上書き。なければキーと値を追加します。
# user[name]=testuser2 # 上書き # user[country]=Japan # 新規にキーと値を追加
- 要素の削除
- unsetコマンド
# unset user[name]
新しいシェルプログラミングの教科書はわかりやすくておすすめです。
- 作者: 三宅英明
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2017/11/21
- メディア: Kindle版
- この商品を含むブログを見る
Apache2.2の設定
久しぶりにやったので、CentOS6.9でやりました... ipもよしなにお願いします。 2017年にApache2.2で申し訳ない
Apache2.2の設定
CentOS6系の設定を想定なのでApache2.2です。(古いね)
- 以下内容
VirtualHostの設定をしてドメインにアクセスできること 名前ベースの仮想ホストで複数のホスト名をつける SSL通信ができること
Apacheインストール
インストール
# yum install httpd # httpd -version Server version: Apache/2.2.15 (Unix)
自動起動の設定
# chkconfig --list |grep httpd httpd 0:off 1:off 2:off 3:off 4:off 5:off 6:off # chkconfig httpd on # chkconfig --list |grep httpd httpd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
Apache設定ファイル
全体の設定と範囲を指定した設定の2つあります。 この範囲を「セクションコンテナ」といいます。 セクションコンテナの中で設定した項目はセクションコンテナ内だけの設定になるのです。
セクションコンテナの種類
〜 - ディレクトリを示す
〜 - ファイルを示す
〜 - URLを示す
Apacheの設定ファイルは、行毎に「ディレクティブ」と呼ばれる設定内容を記述します。 Apache2.2と2.4では記述の仕方が違うので設定ミスに注意が必要。 2.4使うならNginxを使ったほうがいいかも。。
ディレクティブの大文字小文字の区別はされません。
優先順位は
編集が終わったら設定ファイルの書式が間違っていないかを確認するようにしましょう。
# service httpd configtest Syntax OK
名前ベースの仮想ホスト
リクエストヘッダ内で指定されているHost:ヘッダーの情報からアクセス先のホストを識別できる
VirtualHostの設定
設定をスッキリさせたいので、conf.d配下に vhosts.confというVirtualHost専用の設定ファイルを作りました。 VirtualHostで指定したIPアドレスに一致した場合のみ設定が適用されます。
# cat conf.d/vhosts.conf # 仮想ホストのIPアドレスを指定(Apache2.4では不要) NameVirtualHost *:80 <VirtualHost *:80> ServerAdmin sysadmin@test1.jp DocumentRoot /var/www/vhosts/www.apachetest1.jp ServerName www.apachetest1.jp </VirtualHost> <VirtualHost *:80> ServerAdmin sysadmin@test1.jp DocumentRoot /var/www/vhosts/www.apachetest2.jp ServerName www.apachetest2.jp </VirtualHost>
DocumentRootも作っておくます。 index.htmlも置いておきました。
# mkdir /var/www/vhosts/www.apachetest1.jp/ # cat /var/www/vhosts/www.apachetest1.jp/index.html apachetest1 is OK # mkdir /var/www/vhosts/www.apachetest2.jp # cat /var/www/vhosts/www.apachetest2.jp/index.html apachetest2 is OK
動作確認してみましょう。 Hostヘッダーを指定してcurlコマンドを実行すると見事index.htmlの情報をとってこれました;-)
# curl -H 'Host: www.apachetest1.jp' 192.168.33.111 apachetest1 # curl -H 'Host: www.apachetest2.jp' 192.168.33.111 apachetest2
Apache再起動時に以下のメッセージが出たので、hostsに記載。localhostで名前解決させればよさそう。
[root@www ~]# service httpd restart Stopping httpd: [ OK ] Starting httpd: httpd: apr_sockaddr_info_get() failed for www.apachetest1.jp httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName [ OK ] # vim /etc/hosts # cat /etc/hosts 127.0.0.1 www.apachetest1.jp localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 www.apachetest1.jp localhost localhost.localdomain localhost6 localhost6.localdomain6 [root@www ~]# service httpd restart Stopping httpd: [ OK ] Starting httpd: [ OK ]
QUIZ:ブラウザでindex.htmlの中身を確認したい場合はどうすればいいでしょうか?
SSL通信
let's encryptはローカルドメインには使えんので、今回はおれおれ証明書で! 本番環境ではlet's encryptもしくはverisignやcybertrustやrapid-sslなど商用のCAを使ってください!
商用CAもCSRをCAに送って、入金して、証明書がもらえたら、よしなに設定の流れです。
SSL化する理由はなんでしょうか
- データの改竄防止
- データ盗聴防止
- CA(認証局)による認証によって、本物であることを証明する
何が必要でしょうか
てなわけでインストール
# yum install -y mod_ssl openssl # httpd -M | grep ssl Syntax OK ssl_module (shared)
OpenSSLコマンドで以下のファイルを用意します。 複数ドメインを持つ場合は命名規則を立てたほうがよいですね。 CommonName.作成日.ファイルタイプなど
鍵の置き場所はmod_sslをインストールしたらデフォルトはconf.d/ssl.confになると思います。 また、443ポートで受けます。
# grep SSLCert conf.d/ssl.conf # Point SSLCertificateFile at a PEM encoded certificate. If SSLCertificateFile /etc/pki/tls/certs/localhost.crt SSLCertificateKeyFile /etc/pki/tls/private/localhost.key # grep Listen conf.d/ssl.conf Listen 443
今回はvhosts.confに設置場所を以下に指定してそこに置いてみましょう。
mkdir /etc/httpd/conf/ssl # vim vhosts.conf <VirtualHost *:443> ServerName www.apachetest1.jp DocumentRoot /var/www/test1 SSLEngine on SSLCertificateFile /etc/httpd/conf/ssl/server.crt SSLCertificateKeyFile /etc/httpd/conf/ssl/server.key </VirtualHost>
デフォルトのssl.confでSSLEngine on
はコメントアウトしておきましょう~~
# grep SSLEngine /etc/httpd/conf.d/ssl.conf # SSLEngine on
上のコメントアウトをしなかったら、以下のエラーメッセージが出てApacheが起動しなくなりました..
[error] Illegal attempt to re-initialise SSL for server (theoretically shouldn't happen!)
秘密鍵の作成
# openssl genrsa -out server.key 2048
genrsa: RSA形式の秘密鍵を作成 -out 秘密鍵: 秘密鍵の名前指定 2048: 2048バイト長の鍵を作成
CSR作成
今回はwww.apachetest1.jpだけHTTPS化
# openssl genrsa -out server.key 2048 Generating RSA private key, 2048 bit long modulus .........................................+++ ...+++ e is 65537 (0x10001) [root@localhost ssl]# openssl req -new -sha256 -key server.key -out server.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]:JP State or Province Name (full name) []:Tokyo Locality Name (eg, city) [Default City]:Shinjuku-ku Organization Name (eg, company) [Default Company Ltd]:Default Company Ltd Organizational Unit Name (eg, section) []:section Common Name (eg, your name or your server's hostname) []:www.apachetest1.jp Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
CommonNameとbit長など間違いないか確認
# openssl req -in server.csr -text Certificate Request: Data: Version: 0 (0x0) Subject: C=JP, ST=Tokyo, L=Shinjuku-ku, O=Default Company Ltd, OU=Technology, CN=www.apachetest1.jp Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:ca:d9:de:81:97:43:ff:21:d9:4b:7c:26:50:e1:
- CSR作成の前に以下の項目を確認しておくといいです。
DN フィールド | 説明 | 例 |
---|---|---|
Country Name (2 letter code) | 二文字の国名 | JP |
State or Province Name (full name) | 都道府県 | Tokyo, Tokyo-to |
Locality Name (eg, city) | 市区町村 | Shinjuku-ku |
Organization Name (eg, company) | 会社名・組織名 | Default Company Ltd |
Organizational Unit Name(eg, section) | 部署名 | Technology |
Common Name (eg, YOUR name) | ドメイン名と同。間違えるとサイトみれなくなるので注意!!! | 「http://」や「https://」は入力不要 |
www有りの場合 | www.example.com | |
wwwなしの場合 | example.com | |
サブドメインの場合 | japan.example.com | |
サブドメイン無制限の場合 | *.example.com | |
Email Address | メールアドレス | test@example.jp(値なしでも可) |
A challenge password | 値なし | |
An optional company name | 値なし |
証明書を作成し自己署名する
# openssl x509 -in server.csr -days 365 -req -signkey server.key -out server.crt Signature ok subject=/C=JP/ST=Tokyo/L=Shinjuku-ku/O=Default Company Ltd/OU=Technology/CN=www.apachetest1.jp Getting Private key
-req: 入力ファイルがCSRファイルであることを指定 -days: 証明書の有効期限 x509: X.509形式の証明書を作成 -signkey 秘密鍵ファイル: 自己証明書作成時に使用するオプション。秘密鍵ファイルを指定
iptablesの設定を行う
iptablesがインストールされていることを確認
# which iptables /sbin/iptables
iptablesのポリシー
以下の基本ポリシーを”DROP”(拒否)、”ACCEPT”(許可)のどちらかに設定します。
INPUT:サーバーに入ってくる通信のポリシー。基本ポリシーを”DROP”にして、あとで個別のポートに対して許可する設定にする。 OUTPUT:サーバーから出て行く通信のポリシー。基本ポリシーは”ACCEPT“にする。 FORWARD:受信したデータを他のサーバーへ転送する際に適用される設定。ここでは、特に転送するサーバーは無いので”DROP”。
現状設定されていないです。
# iptables -nvL --line Chain INPUT (policy ACCEPT 6332 packets, 472K bytes) num pkts bytes target prot opt in out source destination Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 3243 packets, 254K bytes) num pkts bytes target prot opt in out source destination
以下の設定ファイルを作成していきます。 記載内容はさくらインターネットさんの記事を参照で
# vim /etc/sysconfig/iptables
設定を反映させます。
# service iptables restart iptables: Setting chains to policy ACCEPT: filter [ OK ] iptables: Flushing firewall rules: [ OK ] iptables: Unloading modules: [ OK ] iptables: Applying firewall rules: [ OK ]
22,53,80,443ポートが開放されていることが確認できました。
# iptables -nvL --line Chain INPUT (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 2 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:0x3F/0x00 3 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:!0x17/0x02 state NEW 4 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:0x3F/0x3F 5 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmp type 8 limit: up to 1/min burst 10 mode srcip htable-expire 120000 6 11 576 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 7 0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp spt:53 8 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 flags:0x17/0x02 limit: up to 1/min burst 10 mode srcip htable-expire 120000 9 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 10 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 Chain FORWARD (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 6 packets, 448 bytes) num pkts bytes target prot opt in out source destination
コマンドなら以下ですね
iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
graceful再起動で反映
/etc/init.d/httpd graceful
https://www.apachetest1.jpにアクセスできることを確認
シェルスクリプト①
シェルスクリプトまとめ
一行目のおまじない
1行目にシェバンと呼ぶ、bashで動作しますよというおまじないがある。
#!/bin/bash
変数
変数宣言では空白文字列は入れてはだめ
変数=値
declareで宣言していない場合は値は文字列とされる。
値にスペースやタブを含みたい場合はシングル[ダブル]クォートでくくる
test='test test'
空白文字も格納できる
empty1=
or
empty2=''
変数を参照する場合は変数名の前に$をつける
$test
宣言していない変数を参照してもエラーにならず、空白文字として実行されてしまうので注意
環境変数
環境変数は親プロセスから子プロセスに引き継がれるもので、親プロセスのシェルで設定されていた環境変数の値は子プロセスにコピーされコマンドから参照可能。
printenvコマンドで環境変数の一覧を表示可能
環境変数の定義
exportコマンドで環境変数の定義が可能
CONFIG_NAME=test #変数CONFIG_NAMEを宣言 export CONFIG_NAME #exportで環境変数にする
or
export CONFIG_NAME=test #一行で変数定義と環境変数にするのもできる
特殊なシェル変数
ホームディレクトリの表示
- $HOME
カレントディレクトリの表示
- $PWD
ログインユーザーのログインシェル
- $SHELL
現在動作しているbashコマンドのフルパス
- $BASH
現在動作しているbashのバージョン情報
- $BASH_VERSION
現在実行しているシェルスクリプトの行番号を格納している。 いま何行目なのか出力できるのでデバックなどに使える。
- $LINENO
ロケールを指定
- $LANG
localeコマンドで現在設定されているロケールが確認できる
PATHは、シェルがコマンドを探すディレクトリを指定するための変数 ディレクトリは:で区切る コマンド検索パスと呼ばれ、左から順に探していく
- $PATH
コマンドを配置するためのディレクトリを作成した場合、コマンドを探すパスを追加したい場合、このPATHを使う。 以下は現在のパスに加えてホームディレクトリ配下のbinディレクトリからもコマンドを探すように指定
PATH=$HOME/bin:$PATH
IFSはシェルの区切り文字を指定するための変数 bashは文字列を単語に分割する必要があるときは、ここの文字列を区切り文字として使う。 通常はスペース、タブ、改行が設定されている。
$ echo "$IFS" |od -a 0000000 sp ht nl nl 0000004
odコマンドはファイルの内容を8進数で表示しますが、-aをつけると、制御文字などが文字の名前で出力される sp=スペース、ht=タブ、nlは改行
位置パラメータ
$1,$2のように1から始まる数値を名前に持つ変数で、 引数を参照することができる。
位置パラメータをすべて参照する場合は、$*や$@で可能
ダブルクォートでくくると動作が変わるので注意 "$@"=位置パラメータそれぞれが文字列として展開される "$*"=連結した一つの文字パラメータとして展開されてしまう。
なので$@を使ったほうが意図しない動作を起こさない。
特殊パラメータ
変数名 | 内容 |
---|---|
$# | 位置パラメータの個数 |
$? | 直前に実行したコマンドの終了ステータス値 |
$$ | 現在のプロセスのプロセスID |
$! | 最後に実行したバックグラウンドコマンドのプロセスID |
シェルスクリプトに与えられた引数の個数
- $#
コマンド終了ステータス値 0は成功,0以外は失敗
- $?
現在のプロセスIDを表示
- $$
ユニークなファイル名を作る時にも使える
tempfile=/tmp/tempfile$$
バックグラウンドのプロセスIDを表示
- $!
バックグラウンドで実行したプロセスをkillするときに使える