Linuxでメモリ使用量が増え続けた
AmazonLinux 2017.03にnginx乗せて稼働させてたら、なぜかメモリ使用量が、1日ちょっとで2GB後半にさしかかりました。cacheではなくusedを徐々に消費してたので、これは何かリークしてる疑い。
プロセス等を見てもメモリを使っているものもいないし、疑いのあるプロセスをrestartしても改善しませんでした。
何だろうなーと調べてみたらすぐ出てきました。特にAmazonLinuxは関係なし。
上記ページと同じく、NSSの問題で、curlがdentryというディレクトリ階層構造の管理をしているキャッシュを多く消費してしまうと。curlでhttpsの監視が動いており、それが5分に1回なので、徐々にリークした様子。1時間に100MB程度ずつ増加していたようです。
回避策は、NSS_SDB_USE_CACHE=YES
という環境変数を指定すること。とりあえず監視スクリプトにexport NSS_SDB_USE_CACHE=YES
を追加したら、増加が止まりました。
溜まったキャッシュは、以下のようにして解放されました。
echo 2 > /proc/sys/vm/drop_caches
>>
でも>
でもどっち動きますが多分>
の方が適切?2
という値は対象のキャッシュによって異なります。
値 | クリアする対象キャッシュ |
---|---|
1 | ページキャッシュ |
2 | dentry,inode |
3 | ページキャッシュ,dentry,inode |
まとめ
じわじわ増えてく系のメモリリークは怖いです。調べたらたくさん出てきたので、割とメジャーな要因みたいですね。
Slabは知らなかったので調べてみたら、ざっくり言うとカーネル側のキャッシュとのこと。細かいメモリ周りはやっぱよくわかってない感。
参考
minikubeで自分の作ったimageを使う
前回minikubeの実行環境を作りました。公開されているnginxのimageを使いましたが、実際は自分でimageを作成し、それを実行させることがほとんどだと思います。minikubeはdockerを内包しているので、そこでdocker buildすればimageが登録され、使うことができます。
nginxに静的ページを追加したコンテナを作って試す
まず、minikubeのdockerを使うために以下を実行します。
eval $(minikube docker-env)
次にお試し用のpage.htmlとDockerfileを作ります。
# page.html echo '<html> <head><title>this is page</title></head> <body>original page</body> </html>' > page.html # Dockerfile echo 'FROM nginx:1.11 COPY page.html /usr/share/nginx/html/page.html' > Dockerfile
ファイル構成としてはこんな形になります。
k8s/ ・・・名前はなんでもよくここにいる状態でeval $(minikube docker-env) |- Dockerfile +- page.html
imageを作ります。タグはlatestだと動かないので、適当にバージョンつけます。
docker build -t example/nginxpage:0.1.0 .
k8sで動かします。
# コンテナ起動 kubectl run nginxpage --image=example/nginxpage:0.1.0 --port=80 # Macからアクセスできるように kubectl expose deploy nginxpage --name=nginxpage-nodeport --type="NodePort"
ブラウザで確認します。
minikube service nginxpage-nodeport
標準ブラウザが立ち上がり、nginxのwelcomeページが表示されるので、URL欄に/page.html
を追加します。
そうすると、original page
と表示され、自分で作ったコンテナが利用できていることがわかります。
参考
ローカルでkubernetesを動かせるminikubeを試す
kubernetes(以降k8s)は、GKE(GoogleContainerEngine)を使うのが簡単らしいですが、とりあえずローカル実行できる環境が欲しくなったりします。
minikubeは、VirtualBoxなどの上にk8sの環境を構築してくれるツールです。今回はとりあえずMac上にk8s環境を作り、nginxを動かしてアクセスするところまでやってみます。
インストール
以下の2つのツールと関連するものをインストールします。
- kubectl: k8sの操作を行うコマンド
- minikube: ローカルにk8s環境を作るコマンド
kubectlのインストール
gcloudコマンドでインストール管理できるので、gcloudコマンドを入れます。
ダウンロードページから、MacOS用の、tar.gzをダウンロードし解凍し、install.shを実行します。
tar xzvf google-cloud-sdk-145.0.0-darwin-x86_64.tar.gz google-cloud-sdk/install.sh
注意点として、Python2.7が必要です。Mac標準なら大丈夫で、何か入れてたら、頑張って2.7にしましょう。pyenv(今env系は何がデファクトなんだろうか)とか入れればできるのかな。
kubectlは、gcloud componentsコマンドでインストールできます。
gcloud components install kubectl
他にもGCP関連のツール群はこのgcloud componentsで入れられます。
minikubeのインストール
今回はVirtualBox上に作るので、先にダウンロードページからdmgを落としてきてインストールします。
次にminikubeです。リリースページにあるコマンドでインストールします。最後にsudo で移動させてるので管理者パスワードを聞かれます。*1
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.18.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
k8s環境の構築
構築と書いてますが、以下コマンドで済みます。(が少し時間かかります)
minikube start
k8sのイメージを落としてきて、起動し、kubectlに登録してくれます。終わったら、kubectlで接続先のk8s環境を確認します。
kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE * minikube minikube minikube
他の環境がないととりあえずminikubeだけです。GKEとか使うとここが増えます。
nginxを動かしてアクセスする
以下コマンドでnginxコンテナを動かします。
kubectl run nginx --image=nginx:1.11 --port=80
--image
オプションでdockerのimage名とtagを指定します。--port
でコンテナが公開しているポートを指定します。
k8s上でコンテナは動き出しますが、これだけだとまだMacからアクセスはできません。以下でにアクセスできるようにします。
kubectl expose deployment nginx --name=nginx-nodeport --port=80 --target-port=80 --type="NodePort"
kubectl expose deployment
の後のnginx
は、kubectl run
の後に指定した名前(今回はnginx
)と同じです。
これでMacからアクセスできるようになるんですが、どのIPとポートなのかわかりません。これはminikubeが直接ブラウザで開いてくれるので、それを使います。
minikube service nginx-nodeport
minikube service
の後に指定してあるnginx-nodeport
は、手前のkubectl expose
で指定した--name
の値です。
これで標準ブラウザが立ち上がり、VirtualBoxの仮想マシンIPと自動で割り当てられたポートでページが開かれます。今回だとnginxのwelcomeページが表示されます。
URLだけ欲しい場合は--url
オプションをつけます。
minikube service nginx-nodeport --url # portは自動的に割り当てられます http://192.168.99.100:31430
なぜMacからk8sのコンテナにアクセスできるのか
とりあえずやるところだけ先に書きましたが、ネットワーク周りをもう少し。今回幾つか手順を踏んで、Macからk8sのコンテナにアクセスできるようになっています。この先の説明に出てくるIPやポートは、今回私が試したものなので、環境によって異なる場合があります。
まず、MacとVirtualBoxの仮想マシン間は、専用のネットワークが構成されて、つながっています。これは192.168.99.0/24です。
k8sは自身のネットワークを構成します。10.0.0.0(サブネットマスクパッとわからず)で、k8sネットワークが作られています。このネットワークは、Macからは直接アクセスできません。
そこで、kubectl expose
の--type="NodePort"
を使って、k8sネットワーク上のアクセス先と、ノード(VirtualBoxの仮想マシン)をつなげています。今回だとMac<->VirtualBoxネットワークとk8sネットワークをつなげるために、kubectl expose
しているという方が正確です。本来は冗長化した複数のコンテナのアクセス先をまとめるためにkubectl expose
を使います。
--type=“NodePort”
によって、VirtualBox仮想マシン192.168.99.100:31430(ポートは自動的に割り当てられる)が、k8sネットワーク上の10.0.0.161:80(IPは自動的に割り当てられる)にフォワードされるようになっています。k8sの方はkubectl get service
で確認できます。VirtualBoxの方は、前述のminikube service nginx-portnode --url
です。
kubectl get service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes 10.0.0.1 <none> 443/TCP 2d nginx-nodeport 10.0.0.161 <nodes> 80:31430/TCP 27m
アクセスの流れは以下になります。
Mac(VirtualBox network) 192.168.99.1 ↓ minikube仮想マシン(VirtualBox network) 192.168.99.100:31430 ↓ nginx-nodeport (k8s network)10.0.0.161:80 ↓ nginx container(k8s networkのどこかIPとポート)
後始末
起動したものを止める方法
kubectl get service kubectl delete service nginx-nodeport kubectl get service kubectl get deployment kubectl delete deployment nginx kubectl get deployment minikube stop
まとめ
とりあえずローカルで動かすことができました。*2 公開、運用することなどを考えると、GKEが良さそうではありますが、まずは試すという点ではminikubeでもいいのかなと思います。