ひつまぶし食べたいhttps://www.hitsumabushi.org/2021-10-12T02:00:00+09:00メモ代わりのブログCloudFormation Stackを安全に削除する2021-10-12T02:00:00+09:002021-10-12T02:00:00+09:00hitsumabushitag:www.hitsumabushi.org,2021-10-12:/blog/2021/10/12/0200.html
<p>以前、<a href="https://www.hitsumabushi.org/blog/2019/01/25/1454.html">CloudFormation の不満点</a> というのを書いたが、諦めて大半のCfnをterraformに移行した。<br/>
その際、cfn リソースをきれいにするために、cfn stackを安全に削除する必要があり、その方法をメモしておく。</p>
<h2 id="_1">概要</h2>
<p>アイデアは簡単で以下の通り。</p>
<ol>
<li>cfn の操作のみが許可されているIAM roleを作成する</li>
<li>上記roleを指定して cfn stackをdeleteする</li>
</ol>
<h2 id="_2">モチベーション</h2>
<p>そもそも以前の記事でも、予期せぬ手作業の …</p>
<p>以前、<a href="https://www.hitsumabushi.org/blog/2019/01/25/1454.html">CloudFormation の不満点</a> というのを書いたが、諦めて大半のCfnをterraformに移行した。<br/>
その際、cfn リソースをきれいにするために、cfn stackを安全に削除する必要があり、その方法をメモしておく。</p>
<h2 id="_1">概要</h2>
<p>アイデアは簡単で以下の通り。</p>
<ol>
<li>cfn の操作のみが許可されているIAM roleを作成する</li>
<li>上記roleを指定して cfn stackをdeleteする</li>
</ol>
<h2 id="_2">モチベーション</h2>
<p>そもそも以前の記事でも、予期せぬ手作業の変更があった場合に、ドリフト検出やChange setで検出・対応が難しいことを問題としていた。<br/>
cfn stackを削除しようと思った場合、ドリフトしている状態だと、単純には削除できない。一旦cfn updateすれば簡単かもしれないが、何か手作業で変更されているかもしれない状況だと、updateしたくない。<br/>
cfn updateをせずに、安全にcfn stackをdeleteしたい。</p>
<h2 id="_3">方法</h2>
<h3 id="cfniam-role">cfnの操作しかできないIAM roleを作成する</h3>
<p>以下の通り、terraformで作成した。</p>
<div class="highlight"><pre><span></span><code><span class="kr">resource</span><span class="w"> </span><span class="nc">"aws_iam_role"</span><span class="w"> </span><span class="nv">"delete_cfn_stack"</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"delete-cfn-stack"</span>
<span class="w"> </span><span class="na">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"/"</span>
<span class="w"> </span><span class="na">assume_role_policy</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">jsonencode</span><span class="p">({</span>
<span class="w"> </span><span class="na">Version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"2012-10-17"</span>
<span class="w"> </span><span class="na">Statement</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="na">Action</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"sts:AssumeRole"</span>
<span class="w"> </span><span class="na">Effect</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Allow"</span>
<span class="w"> </span><span class="na">Sid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span>
<span class="w"> </span><span class="nb">Principal</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="na">Service</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"cloudformation.amazonaws.com"</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">]</span>
<span class="w"> </span><span class="p">})</span>
<span class="p">}</span>
<span class="kr">resource</span><span class="w"> </span><span class="nc">"aws_iam_role_policy"</span><span class="w"> </span><span class="nv">"delete_cfn_stack_policy"</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"cfn_policy"</span>
<span class="w"> </span><span class="na">role</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">aws_iam_role.delete_cfn_stack.id</span>
<span class="w"> </span><span class="na">policy</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">jsonencode</span><span class="p">({</span>
<span class="w"> </span><span class="na">Version</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"2012-10-17"</span>
<span class="w"> </span><span class="na">Statement</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="na">Effect</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Allow"</span>
<span class="w"> </span><span class="na">Action</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="s2">"cloudformation:*"</span><span class="p">,</span>
<span class="w"> </span><span class="p">]</span>
<span class="w"> </span><span class="na">Resource</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"*"</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">]</span>
<span class="w"> </span><span class="p">})</span>
<span class="p">}</span>
</code></pre></div>
<h3 id="role">上記のroleでスタック削除する</h3>
<div class="highlight"><pre><span></span><code><span class="nb">export</span><span class="w"> </span><span class="nv">STACK_NAME</span><span class="o">=</span>stack_name
<span class="c1"># 失敗するdeleteを実行</span>
aws<span class="w"> </span>cloudformation<span class="w"> </span>delete-stack<span class="w"> </span>--role-arn<span class="w"> </span>arn:aws:iam::_aws_account_id_:role/delete-cfn-stack<span class="w"> </span>--stack-name<span class="w"> </span><span class="si">${</span><span class="nv">STACK_NAME</span><span class="si">}</span>
<span class="c1"># resource idのリストを取得</span>
aws<span class="w"> </span>cloudformation<span class="w"> </span>describe-stack-resources<span class="w"> </span>--stack-name<span class="w"> </span><span class="si">${</span><span class="nv">STACK_NAME</span><span class="si">}</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>jq<span class="w"> </span><span class="s1">'.StackResources[].LogicalResourceId'</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>xargs
<span class="c1"># 削除しないリソースとして上の結果を指定</span>
aws<span class="w"> </span>cloudformation<span class="w"> </span>delete-stack<span class="w"> </span>--role-arn<span class="w"> </span>arn:aws:iam::_aws_account_id_:role/delete-cfn-stack<span class="w"> </span>--stack-name<span class="w"> </span><span class="si">${</span><span class="nv">STACK_NAME</span><span class="si">}</span><span class="w"> </span>--retain-resources<span class="w"> </span><span class="o">(</span>↑の結果をペースト<span class="o">)</span>
</code></pre></div>
<h2 id="_4">終わり</h2>
<p>地道にterraform importしてから、上記をコツコツ(スクリプト化して)実行して、cfn stackを削除した。<br/>
terraform importは単純に差分がなくなるまでtf fileを修正するだけなので、そんなに困ることはない。<br/>
これで安心・安全にリソース管理できるようになった。</p>1Password CLIを使ってTOTPを取得する2021-10-12T01:00:00+09:002021-10-12T01:00:00+09:00hitsumabushitag:www.hitsumabushi.org,2021-10-12:/blog/2021/10/12/0100.html
<p>2要素認証を必須にしたAWSのswitch roleで、temporary credentialsをCLIで取得したかった。<br/>
TOTPの数字を毎回調べるのが面倒で、簡単にできないか調べたところ、1Password CLIというのがあった。1Passwordユーザなので、これを利用する。<br/>
ただし、当然のことだが、2要素認証のデバイスとして、1Passwordが利用されている状況とする。</p>
<h2 id="1password-cli">1Password CLIの初期設定</h2>
<p>1Password CLI を利用できるようにする。<br/>
<a href="https://support.1password.com/command-line-getting-started/">1Password CLIのGetting Started</a>を見て、初期設定 …</p>
<p>2要素認証を必須にしたAWSのswitch roleで、temporary credentialsをCLIで取得したかった。<br/>
TOTPの数字を毎回調べるのが面倒で、簡単にできないか調べたところ、1Password CLIというのがあった。1Passwordユーザなので、これを利用する。<br/>
ただし、当然のことだが、2要素認証のデバイスとして、1Passwordが利用されている状況とする。</p>
<h2 id="1password-cli">1Password CLIの初期設定</h2>
<p>1Password CLI を利用できるようにする。<br/>
<a href="https://support.1password.com/command-line-getting-started/">1Password CLIのGetting Started</a>を見て、初期設定する。</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>op<span class="w"> </span>signin<span class="w"> </span>_1password_url_<span class="w"> </span>_signin_address_
...<span class="w"> </span><span class="c1"># Secret Key, Password, (設定していればTOTPの6-digit)を聞かれる</span>
</code></pre></div>
<p>短縮形が <code>_1password_url_</code> から自動的に決まるが、どうしても指定したい場合には、 <code>--shorthand</code>オプションで指定する。</p>
<h2 id="totp">TOTPの取得</h2>
<p>通常の1password appのように、30minアクセスがなければ、ロックされるため、パスワードの入力が必要。<br/>
1passwordのURLが <code>example.1password.com</code> であれば、</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nb">eval</span><span class="w"> </span><span class="k">$(</span>op<span class="w"> </span>signin<span class="w"> </span>example<span class="k">)</span>
</code></pre></div>
<p>として、再認証する。<br/>
環境変数 <code>OP_SESSION_example</code> に認証したときのsession情報が載る。(<code>--session</code> オプションで各コマンドごとに渡しても良い)</p>
<p>TOTPを取得するには、以下のコマンドを実行する。</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>op<span class="w"> </span>get<span class="w"> </span>totp<span class="w"> </span><span class="s2">"UUID or Name"</span>
</code></pre></div>
<p>UUIDで指定したい場合には、 <code>op list items --categories Login</code> の結果をjqなどで調べて、取得する。</p>
<h2 id="_1">終わり</h2>
<p>以上で概ねやりたかった、CLIでTOTPを取得することができるようになった。<br/>
最近1PasswordはLinux Desktop向けのアプリを出していたりしているので、もう少し真面目に使っていきたい所存。</p>
<p>ちなみに、複数のプロファイルの切り替えなどで便利なように、 <a href="https://github.com/99designs/aws-vault">99designs/aws-vault</a>というのもあるらしいが、特に使わずに自前シェルスクリプトで使っている。</p>Python でコメント付きYAMLを扱う時には ruamel.yaml が便利だった2019-02-12T16:30:00+09:002019-02-12T16:30:00+09:00hitsumabushitag:www.hitsumabushi.org,2019-02-12:/blog/2019/02/12/1630.html
<h2 id="_1">資料</h2>
<ul>
<li><a href="https://yaml.readthedocs.io/en/latest/">Document: ruamel.yaml</a></li>
<li><a href="https://bitbucket.org/ruamel/yaml/">Repository: ruamel.yaml</a></li>
</ul>
<h2 id="_2">背景</h2>
<p>とある yamlで書かれたconfigファイル群(数百ファイル)を一括で変更したいことがあった。<br/>
sedで変更するには少し難しかったので、パースしてから条件判定して、書き換えたい。</p>
<ul>
<li>コメントは消したくない</li>
<li>ブロックスタイルのままにしたい</li>
<li>diff を最小限にしたい (細かい中身を知らないので、チェックするのが面倒)</li>
</ul>
<h2 id="ruamelyaml">ruamel.yaml</h2>
<p>python で yaml を扱うときは …</p>
<h2 id="_1">資料</h2>
<ul>
<li><a href="https://yaml.readthedocs.io/en/latest/">Document: ruamel.yaml</a></li>
<li><a href="https://bitbucket.org/ruamel/yaml/">Repository: ruamel.yaml</a></li>
</ul>
<h2 id="_2">背景</h2>
<p>とある yamlで書かれたconfigファイル群(数百ファイル)を一括で変更したいことがあった。<br/>
sedで変更するには少し難しかったので、パースしてから条件判定して、書き換えたい。</p>
<ul>
<li>コメントは消したくない</li>
<li>ブロックスタイルのままにしたい</li>
<li>diff を最小限にしたい (細かい中身を知らないので、チェックするのが面倒)</li>
</ul>
<h2 id="ruamelyaml">ruamel.yaml</h2>
<p>python で yaml を扱うときは、PyYAML が有名だと思う。<br/>
PyYAMLを使う場合、yamlをloadしてdumpすると、フロースタイルなのは変更できるが、<br/>
コメントは消えてしまうのに対応するのが簡単ではない(と思っている)。</p>
<p>ruamel.yaml はPyYAMLをフォークしたもので、YAML 1.2 をサポートしているし、コメントやスタイル、キーの順番を保つloaderが実装されている。<br/>
https://yaml.readthedocs.io/en/latest/overview.html</p>
<h3 id="_3">使い方</h3>
<p>使い方としては、 load, dumpの代わりに、 <code>round_trip_load</code>, <code>round_trip_dump</code> を使えば良い。<br/>
オプションは自分が使っているconfigに合わせて使えば良い。</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">ruamel.yaml</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="n">filepath</span><span class="p">):</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="s1">'r+'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">ruamel</span><span class="o">.</span><span class="n">yaml</span><span class="o">.</span><span class="n">round_trip_load</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">preserve_quotes</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="c1"># 必要な処理をする</span>
<span class="c1"># data["foo"] = "bar"</span>
<span class="k">if</span> <span class="n">rewrite</span><span class="p">:</span>
<span class="n">f</span><span class="o">.</span><span class="n">seek</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">ruamel</span><span class="o">.</span><span class="n">yaml</span><span class="o">.</span><span class="n">round_trip_dump</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">explicit_start</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">truncate</span><span class="p">()</span>
<span class="k">for</span> <span class="n">pathname</span><span class="p">,</span> <span class="n">dirnames</span><span class="p">,</span> <span class="n">filenames</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="s1">'.'</span><span class="p">):</span>
<span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">filenames</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">pathname</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">process</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">pathname</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
</code></pre></div>CloudFormation の不満点2019-01-25T14:54:00+09:002019-01-25T14:54:00+09:00hitsumabushitag:www.hitsumabushi.org,2019-01-25:/blog/2019/01/25/1454.html
<p>以下では「手作業で」というのは、「CloudFormation管理外で」という意味で使う。</p>
<h2 id="_1">要点</h2>
<ul>
<li>CloudFormation は、リソースが何かの理由で手作業で変更されていた場合に安全に操作できない</li>
<li>Drift 検出は誤検出が多すぎて使いづらい</li>
<li>Change Set は動いている状態との差分を見ていないので、信用できない</li>
</ul>
<h2 id="_2">経緯</h2>
<p>CloudFormation で管理されているリソースすべてについて、新しいタグをつけたくなった。<br/>
ただ、各サービ …</p>
<p>以下では「手作業で」というのは、「CloudFormation管理外で」という意味で使う。</p>
<h2 id="_1">要点</h2>
<ul>
<li>CloudFormation は、リソースが何かの理由で手作業で変更されていた場合に安全に操作できない</li>
<li>Drift 検出は誤検出が多すぎて使いづらい</li>
<li>Change Set は動いている状態との差分を見ていないので、信用できない</li>
</ul>
<h2 id="_2">経緯</h2>
<p>CloudFormation で管理されているリソースすべてについて、新しいタグをつけたくなった。<br/>
ただ、各サービスの担当者ごとにある程度自由にオペレーションできるため、手動で変更されていないか、一応調べておこうと思い、<br/>
<a href="https://aws.amazon.com/jp/blogs/news/new-cloudformation-drift-detection/">2018-11 にリリースされたドリフト検出</a> を使って、<br/>
手作業で実施された変更点もCloudFormationに取り込みつつ、対応しようとした。</p>
<h2 id="_3">期待したこと</h2>
<ul>
<li>CloudFormation のスタックがたくさんあるため、1つ1つ細かく差分を見て修正することが難しい</li>
<li>ドリフト検出されたところだけ1つずつ修正していって、Change Setを作りながら現状に合わせたい</li>
</ul>
<h2 id="_4">実際</h2>
<h3 id="_5">ドリフト検出</h3>
<ul>
<li>サポートされるリソースが少ない</li>
<li><a href="https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-stack-drift-resource-list.html">Resources that Support Drift Detection</a></li>
<li>そもそも誤検出が多い</li>
<li><a href="https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-stack-drift.html?shortFooter=true#drift-considerations">Detecting Unmanaged Configuration Changes to Stacks and Resources</a></li>
<li>LBのプロパティなど、配列で定義されるところをデフォルト値で埋められただけで、ドリフトが検出される</li>
<li>エッジケースの(とAWSが主張している)誤検出に該当するのか、目で見て考える以外の方法がない</li>
</ul>
<h3 id="change-set">Change Set</h3>
<ul>
<li>実際に動いているシステムとの差分を見ていない</li>
<li>前回の設定とのスタックの差分を見ている</li>
<li>手作業で変更されているとChange Setの実行に失敗する(実際には差分がないので)</li>
<li>その場合 Update Stack するしかない</li>
</ul>
<p>例えば、手作業でタグを増やしたとして、翌営業日にCloudFormationに反映したい、と思った場合、<br/>
ドリフト検出で誤検知を目でフィルタしつつ(誤検知だと確信が持てるかはわからない)、<br/>
CloudFormation のパラメータを書き換えて、Update Stackしないといけない。<br/>
(一応、Update Stack 前に Change Set で差分を見て、execute して失敗することを確認したほうが安心感はある。)</p>
<h2 id="_6">まとめ</h2>
<p>手作業の変更をできないようにしておく以外にない。<br/>
あるいは、本格的にCloudFormationを利用し始めるまでの間に Terraform に移行する。</p>
<p>(なんでChange SetをRunnning Stateとの比較にしなかったんだろう...)</p>DebianでLuaJITTeXを使いたい2018-10-31T19:01:00+09:002018-10-31T19:01:00+09:00hitsumabushitag:www.hitsumabushi.org,2018-10-31:/blog/2018/10/31/1901.html
<p>そろそろLuaTex使ってみたいなと思ったところ、LuaJITTeXの方が早い場合があるということで、試してみようと思った。</p>
<h2 id="_1">参考</h2>
<ul>
<li><a href="http://www.fugenji.org/~thomas/texlive-guide/luajitlatex.html">luajittex のセットアップ</a><ul>
<li>そもそもこのサイトの情報が有用 <a href="http://www.fugenji.org/~thomas/texlive-guide/index.html">TeX Live を使おう──主に Linux ユーザのために──</a></li>
</ul>
</li>
</ul>
<h2 id="_2">未解決の問題</h2>
<ul>
<li><code>/var/lib/texmf/fmtutil.cnf-TEXLIVEDIST</code> が自動生成されている、とコメントされているが、元ファイルがわからない</li>
</ul>
<div class="highlight"><pre><span></span><code>### This file was automatically generated by update-fmtutil.
#
# Any local change will be overwritten. Please see the documentation
# of updmap on …</code></pre></div>
<p>そろそろLuaTex使ってみたいなと思ったところ、LuaJITTeXの方が早い場合があるということで、試してみようと思った。</p>
<h2 id="_1">参考</h2>
<ul>
<li><a href="http://www.fugenji.org/~thomas/texlive-guide/luajitlatex.html">luajittex のセットアップ</a><ul>
<li>そもそもこのサイトの情報が有用 <a href="http://www.fugenji.org/~thomas/texlive-guide/index.html">TeX Live を使おう──主に Linux ユーザのために──</a></li>
</ul>
</li>
</ul>
<h2 id="_2">未解決の問題</h2>
<ul>
<li><code>/var/lib/texmf/fmtutil.cnf-TEXLIVEDIST</code> が自動生成されている、とコメントされているが、元ファイルがわからない</li>
</ul>
<div class="highlight"><pre><span></span><code>### This file was automatically generated by update-fmtutil.
#
# Any local change will be overwritten. Please see the documentation
# of updmap on how to override things from here.
#
###
</code></pre></div>
<h2 id="_3">準備</h2>
<p>パッケージインストール</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>texlive-lang-japanese<span class="w"> </span>texlive-latex-extra<span class="w"> </span>texlive-luatex
<span class="c1"># 参考文献とか書くとき</span>
sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>texlive-bibtex-extra<span class="w"> </span>biber
</code></pre></div>
<p><code>/var/lib/texmf/fmtutil.cnf-TEXLIVEDIST</code> を編集したい。(未解決の項目に書いた通り、元ファイルがわからないので直接編集している)</p>
<div class="highlight"><pre><span></span><code><span class="gu">@@ -6,7 +6,7 @@</span>
<span class="w"> </span>###
<span class="w"> </span>dviluatex luatex language.def,language.dat.lua dviluatex.ini
<span class="w"> </span>etex pdftex language.def -translate-file=cp227.tcx *etex.ini
<span class="gd">-#! luajittex luajittex language.def,language.dat.lua luatex.ini</span>
<span class="gi">+luajittex luajittex language.def,language.dat.lua luatex.ini</span>
<span class="w"> </span>luatex luatex language.def,language.dat.lua luatex.ini
<span class="w"> </span>mf mf-nowin - -translate-file=cp227.tcx mf.ini
<span class="w"> </span>pdfetex pdftex language.def -translate-file=cp227.tcx *pdfetex.ini
<span class="gu">@@ -14,7 +14,7 @@</span>
<span class="w"> </span>tex tex - tex.ini
<span class="w"> </span>dvilualatex luatex language.dat,language.dat.lua dvilualatex.ini
<span class="w"> </span>latex pdftex language.dat -translate-file=cp227.tcx *latex.ini
<span class="gd">-#! luajitlatex luajittex language.dat,language.dat.lua lualatex.ini</span>
<span class="gi">+luajitlatex luajittex language.dat,language.dat.lua lualatex.ini</span>
<span class="w"> </span>lualatex luatex language.dat,language.dat.lua lualatex.ini
<span class="w"> </span>mptopdf pdftex - -translate-file=cp227.tcx mptopdf.tex
<span class="w"> </span>pdflatex pdftex language.dat -translate-file=cp227.tcx *pdflatex.ini
</code></pre></div>
<h2 id="_4">使い方</h2>
<div class="highlight"><pre><span></span><code>luajittex<span class="w"> </span>--fmt<span class="o">=</span>luajitlatex.fmt<span class="w"> </span><span class="o">{</span>tex<span class="w"> </span>file<span class="o">}</span>
</code></pre></div>
<p>問題なくコンパイルできれば、pdfが生成されるはず。</p>
<h2 id="vscodetex">VSCodeでTeXする</h2>
<p><a href="https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop">LaTeX Workshop</a> をインストールして、以下の設定をする。<br/>
参考文献が必要なときは、 <code>luajittex_with_bib</code> のレシピでビルドするように書いている。</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"latex-workshop.chktex.enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"latex-workshop.latex.recipes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"luajittex"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"tools"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"luajitlatex"</span><span class="p">]</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"luajittex_with_bib"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"tools"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="s2">"luajitlatex"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"biber"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"luajitlatex"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"luajitlatex"</span>
<span class="w"> </span><span class="p">]</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">],</span>
<span class="w"> </span><span class="nt">"latex-workshop.latex.tools"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"luajitlatex"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"luajittex"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="s2">"--cmdx"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"--synctex=1"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"--fmt=luajitlatex.fmt"</span><span class="p">,</span>
<span class="w"> </span><span class="s2">"%DOCFILE%"</span>
<span class="w"> </span><span class="p">]</span>
<span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"biber"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"biber"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="s2">"%DOCFILE%"</span>
<span class="w"> </span><span class="p">]</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">],</span>
<span class="w"> </span><span class="nt">"latex-workshop.intellisense.surroundCommand.enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"latex-workshop.synctex.afterBuild.enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div>
<h2 id="tex">その他実際のTeXを書く時に参考になりそうなもののメモ</h2>
<ul>
<li>参考文献まわり<ul>
<li><a href="https://ja.wikipedia.org/wiki/BibTeX#.E6.9B.B8.E8.AA.8C.E6.83.85.E5.A0.B1.E3.83.95.E3.82.A1.E3.82.A4.E3.83.AB">BibTeXの書誌情報の書き方知りたいとき: Wikipedia</a></li>
<li>参考文献を探したい時: Google Scholar</li>
</ul>
</li>
</ul>Debian でログインシェルをzshにしている人が snappy を使う場合の注意2018-04-11T10:58:00+09:002018-04-11T10:58:00+09:00hitsumabushitag:www.hitsumabushi.org,2018-04-11:/blog/2018/04/11/1058.html
<h2 id="snappy">Snappy について</h2>
<ul>
<li><a href="https://www.ubuntu.com/desktop/snappy">Snappy</a></li>
<li><a href="https://snapcraft.io/">snapcraft</a></li>
</ul>
<p>Canonical が主導しているパッケージシステムで、Universal Linux Package と銘打つように、ポータブルなパッケージングができそうな感じ。<br/>
ポータビリティを上げるために、dockerみたいな感じで、依存ライブラリも全部パッケージに含めてしまうスタイルなので、多少debパッケージよりは大きくなる。<br/>
その分、sidを使っているとよく起きる、共通ライブラリの依存バ …</p>
<h2 id="snappy">Snappy について</h2>
<ul>
<li><a href="https://www.ubuntu.com/desktop/snappy">Snappy</a></li>
<li><a href="https://snapcraft.io/">snapcraft</a></li>
</ul>
<p>Canonical が主導しているパッケージシステムで、Universal Linux Package と銘打つように、ポータブルなパッケージングができそうな感じ。<br/>
ポータビリティを上げるために、dockerみたいな感じで、依存ライブラリも全部パッケージに含めてしまうスタイルなので、多少debパッケージよりは大きくなる。<br/>
その分、sidを使っているとよく起きる、共通ライブラリの依存バージョンの不整合がおきる、という問題は起きない。</p>
<p>snapcraftのページではパッケージングの方法も紹介されているので、配布したい人自身がsnapパッケージを作りやすいはず。<br/>
少なくとも、ディストーションごとのパッケージを各アプリケーション作成者がやるよりは、遥かにやりやすい。<br/>
自動アップデートとかもあるので、サービス提供者が利用者に常に最新版を使ってもらいたい場合などにメリットもある。<br/>
実際、 Ubuntu以外にも、Debian, Arch Linux, Gentoo, Fedora, open SUSE などでも利用できる。</p>
<p>Snappy は、snapd というAPIデーモンが動くことになっている。</p>
<h3 id="snappy_1">snappy のインストール</h3>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>apt<span class="w"> </span>install<span class="w"> </span>snapd
</code></pre></div>
<h3 id="snap">snap パッケージのインストール</h3>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>sudo<span class="w"> </span>snap<span class="w"> </span>install<span class="w"> </span>hello-world
</code></pre></div>
<h2 id="_1">困ったこと</h2>
<ul>
<li>Snappy でインストールしたアプリケーションへのパスが通らない</li>
<li>Snappy でインストールしたデスクトップアプリケーションが、menuに登録されない</li>
</ul>
<h2 id="_2">調べたこと</h2>
<h3 id="_3">バージョン</h3>
<p>Kernel はpinningしているので、少し古い。</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>snap<span class="w"> </span>version
snap<span class="w"> </span><span class="m">2</span>.32.3
snapd<span class="w"> </span><span class="m">2</span>.32.3
series<span class="w"> </span><span class="m">16</span>
debian
kernel<span class="w"> </span><span class="m">4</span>.9.0-3-amd64
</code></pre></div>
<h3 id="snappy_2">snappy でインストールした場合のパス</h3>
<ul>
<li>実態はバージョンごとに、 <code>/snap/_app_name_/_version_/</code> 以下に置かれる。現在利用しているものへのリンクは <code>/snap/_app_name_/current/</code></li>
<li>コマンドは、 <code>/snap/bin/</code> 以下にコピーされる。</li>
<li>デスクトップエントリのファイルは <code>/var/lib/snapd/desktop/applications/*.desktop</code> というファイルが作成される</li>
</ul>
<h3 id="_4">パスの設定方法について</h3>
<p><code>/etc/profile.d/apps-bin-path.sh</code> にパスを設定するためのスクリプトが置かれている。</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>cat<span class="w"> </span>/etc/profile.d/apps-bin-path.sh
<span class="c1">#!/bin/sh --this-shebang-is-just-here-to-inform-shellcheck--</span>
<span class="c1"># Expand $PATH to include the directory where snappy applications go.</span>
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">PATH</span><span class="p">#*/snap/bin</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">PATH</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="nb">export</span><span class="w"> </span><span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:/snap/bin
<span class="k">fi</span>
<span class="c1"># desktop files (used by desktop environments within both X11 and Wayland) are</span>
<span class="c1"># looked for in XDG_DATA_DIRS; make sure it includes the relevant directory for</span>
<span class="c1"># snappy applications' desktop files.</span>
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">XDG_DATA_DIRS</span><span class="p">#*/snapd/desktop</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">XDG_DATA_DIRS</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="nb">export</span><span class="w"> </span><span class="nv">XDG_DATA_DIRS</span><span class="o">=</span><span class="s2">"</span><span class="si">${</span><span class="nv">XDG_DATA_DIRS</span><span class="k">:-</span><span class="p">/usr/local/share:/usr/share</span><span class="si">}</span><span class="s2">:/var/lib/snapd/desktop"</span>
<span class="k">fi</span>
</code></pre></div>
<p>zsh では <code>/etc/profile</code> などを読まないので、パスが設定できていない。<br/>
<code>/etc/zsh/zprofile</code> に、上記の内容を書いて置けば良い。<br/>
あるいは、 <code>/etc/zsh/zprofile</code> で以下のようにする。</p>
<div class="highlight"><pre><span></span><code><span class="k">for</span><span class="w"> </span>i<span class="w"> </span><span class="k">in</span><span class="w"> </span>/etc/profile.d/*.sh<span class="w"> </span><span class="p">;</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="o">[</span><span class="w"> </span>-r<span class="w"> </span><span class="nv">$i</span><span class="w"> </span><span class="o">]</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nb">source</span><span class="w"> </span><span class="nv">$i</span>
<span class="k">done</span>
</code></pre></div>PyPIへパッケージをアップロードする2018-04-05T14:20:00+09:002018-04-05T14:20:00+09:00hitsumabushitag:www.hitsumabushi.org,2018-04-05:/blog/2018/04/05/1420.html
<h1 id="_1">資料</h1>
<ul>
<li>パッケージ構成については <a href="https://github.com/pypa/sampleproject">github.com/pypa/sampleproject</a></li>
<li>https://pypi.python.org/pypi/twine</li>
<li>https://packaging.python.org/tutorials/distributing-packages/</li>
</ul>
<h1 id="_2">手順</h1>
<h2 id="pypi">PyPIへユーザー登録する</h2>
<p>PyPI には普段使われている本番環境とは別に、テスト環境がある。<br/>
アカウントがそれぞれ独立しているので、両方で作成する必要がある。</p>
<ul>
<li><a href="https://pypi.org/">PyPI</a></li>
<li><a href="https://test.pypi.org/">PyPI Test</a></li>
</ul>
<h2 id="pypirc"><code>.pypirc</code> の作成</h2>
<p>以下のように <code>~/.pypirc</code> を作成して、test 環境を利用できるようにしておく。<br/>
平分でパスワードを書くことになる …</p>
<h1 id="_1">資料</h1>
<ul>
<li>パッケージ構成については <a href="https://github.com/pypa/sampleproject">github.com/pypa/sampleproject</a></li>
<li>https://pypi.python.org/pypi/twine</li>
<li>https://packaging.python.org/tutorials/distributing-packages/</li>
</ul>
<h1 id="_2">手順</h1>
<h2 id="pypi">PyPIへユーザー登録する</h2>
<p>PyPI には普段使われている本番環境とは別に、テスト環境がある。<br/>
アカウントがそれぞれ独立しているので、両方で作成する必要がある。</p>
<ul>
<li><a href="https://pypi.org/">PyPI</a></li>
<li><a href="https://test.pypi.org/">PyPI Test</a></li>
</ul>
<h2 id="pypirc"><code>.pypirc</code> の作成</h2>
<p>以下のように <code>~/.pypirc</code> を作成して、test 環境を利用できるようにしておく。<br/>
平分でパスワードを書くことになるので、最低限パーミッションを変えておくことにする。</p>
<div class="highlight"><pre><span></span><code><span class="err">$</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="o">~/</span><span class="p">.</span><span class="n">pypirc</span>
<span class="o">[</span><span class="n">distutils</span><span class="o">]</span>
<span class="k">index</span><span class="o">-</span><span class="n">servers</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">pypi</span>
<span class="w"> </span><span class="n">testpypi</span>
<span class="o">[</span><span class="n">pypi</span><span class="o">]</span>
<span class="n">repository</span><span class="o">=</span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">upload</span><span class="p">.</span><span class="n">pypi</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">legacy</span><span class="o">/</span>
<span class="n">username</span><span class="o">=</span><span class="n">_username_</span>
<span class="n">password</span><span class="o">=</span><span class="n">_password_</span>
<span class="o">[</span><span class="n">testpypi</span><span class="o">]</span>
<span class="n">repository</span><span class="o">=</span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">test</span><span class="p">.</span><span class="n">pypi</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">legacy</span><span class="o">/</span>
<span class="n">username</span><span class="o">=</span><span class="n">_username_</span>
<span class="n">password</span><span class="o">=</span><span class="n">_password_</span>
<span class="err">$</span><span class="w"> </span><span class="n">chmod</span><span class="w"> </span><span class="mi">0600</span><span class="w"> </span><span class="o">~/</span><span class="p">.</span><span class="n">pypirc</span>
</code></pre></div>
<h2 id="_3">パッケージのアップロード</h2>
<h3 id="_4">コマンドのインストール</h3>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>twine
</code></pre></div>
<h3 id="_5">パッケージング前のチェック</h3>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>check-manifest
$<span class="w"> </span>check-manifest
</code></pre></div>
<h3 id="_6">パッケージング</h3>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>wheel
$<span class="w"> </span>python<span class="w"> </span>setup.py<span class="w"> </span>sdist<span class="w"> </span>bdist_wheel
</code></pre></div>
<h3 id="test">Test 環境へアップロード</h3>
<div class="highlight"><pre><span></span><code><span class="o">$</span><span class="w"> </span><span class="n">twine</span><span class="w"> </span><span class="n">upload</span><span class="w"> </span><span class="o">-</span><span class="n">r</span><span class="w"> </span><span class="n">testpypi</span><span class="w"> </span><span class="n">dist</span><span class="o">/*</span>
</code></pre></div>
<p>pip でインストールするには、以下のようにする。</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>--index-url<span class="w"> </span>https://test.pypi.org/simple/<span class="w"> </span>_package_
</code></pre></div>
<h3 id="_7">本番へアップロード</h3>
<div class="highlight"><pre><span></span><code><span class="o">$</span><span class="w"> </span><span class="n">twine</span><span class="w"> </span><span class="n">upload</span><span class="w"> </span><span class="n">dist</span><span class="o">/*</span>
</code></pre></div>Google Cloud Pub/Sub をGolangから使おうとしてハマったことまとめ2018-02-05T20:09:00+09:002018-02-05T20:09:00+09:00hitsumabushitag:www.hitsumabushi.org,2018-02-05:/blog/2018/02/05/2009.html
<h1 id="_1">概要</h1>
<p><a href="https://cloud.google.com/pubsub/overview?hl=ja">Google Pub/Sub</a> を <a href="https://github.com/GoogleCloudPlatform/google-cloud-go">GoのSDK</a> から使おうとしていました。<br/>
やっているといくつか詰まったので、メモしておきます。</p>
<ol>
<li>サービスアカウントを利用するためにCredentials JSONを指定する</li>
<li>サブスクリプションの <code>Pub/Sub サブスクライバー権限</code> を与えても Permission Denied になる</li>
</ol>
<h2 id="credentials-json">サービスアカウントを利用するためにCredentials JSONを指定する</h2>
<p>権限の都合上、サービスアカウントのCredentials JSONを利用して認証 …</p>
<h1 id="_1">概要</h1>
<p><a href="https://cloud.google.com/pubsub/overview?hl=ja">Google Pub/Sub</a> を <a href="https://github.com/GoogleCloudPlatform/google-cloud-go">GoのSDK</a> から使おうとしていました。<br/>
やっているといくつか詰まったので、メモしておきます。</p>
<ol>
<li>サービスアカウントを利用するためにCredentials JSONを指定する</li>
<li>サブスクリプションの <code>Pub/Sub サブスクライバー権限</code> を与えても Permission Denied になる</li>
</ol>
<h2 id="credentials-json">サービスアカウントを利用するためにCredentials JSONを指定する</h2>
<p>権限の都合上、サービスアカウントのCredentials JSONを利用して認証したい、という要件がありました。<br/>
ドキュメントを見ていると、 <a href="https://cloud.google.com/docs/authentication/production?hl=ja">ADC(Application Default Credentials)</a> を利用して認証している場合が多いです。<br/>
これを使う場合、 <code>GOOGLE_APPLICATION_CREDENTIALS</code> という環境変数が設定されていれば、そのファイルを読んでくれるのですが、今回の要件では複数の Credentials を利用したかったので、Go プログラム中で指定する必要がありました。</p>
<p>結論としては、以下のような形でpubsub client を作るときに認証情報を渡すことができます。</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nv">jsonKey</span><span class="p">,</span><span class="w"> </span><span class="nv">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nv">ioutil</span><span class="o">.</span><span class="nf">ReadFile</span><span class="p">(</span><span class="nv">credentialJSONPath</span><span class="p">)</span>
<span class="w"> </span><span class="nv">conf</span><span class="p">,</span><span class="w"> </span><span class="nv">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nv">google</span><span class="o">.</span><span class="nf">JWTConfigFromJSON</span><span class="p">(</span><span class="nv">jsonKey</span><span class="p">,</span><span class="w"> </span><span class="nv">pubsub</span><span class="o">.</span><span class="nv">ScopePubSub</span><span class="p">,</span><span class="w"> </span><span class="nv">pubsub</span><span class="o">.</span><span class="nv">ScopeCloudPlatform</span><span class="p">)</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nv">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nv">nil</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nv">log</span><span class="o">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nv">err</span><span class="p">)</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nv">ctx</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nv">context</span><span class="o">.</span><span class="nf">Background</span><span class="p">()</span>
<span class="w"> </span><span class="nv">ts</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nv">conf</span><span class="o">.</span><span class="nf">TokenSource</span><span class="p">(</span><span class="nv">ctx</span><span class="p">)</span>
<span class="w"> </span><span class="nv">c</span><span class="p">,</span><span class="w"> </span><span class="nv">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nv">pubsub</span><span class="o">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="nv">ctx</span><span class="p">,</span><span class="w"> </span><span class="nv">projectID</span><span class="p">,</span><span class="w"> </span><span class="nv">option</span><span class="o">.</span><span class="nf">WithTokenSource</span><span class="p">(</span><span class="nv">ts</span><span class="p">))</span>
</code></pre></div>
<p>Publisherのサンプルは以下の通りです。</p>
<script src="https://gist.github.com/hitsumabushi/7cf1fa45813208f314b29da84a3ff2cc.js"></script>
<h2 id="pubsub-permission-denied">サブスクリプションの <code>Pub/Sub サブスクライバー</code> 権限を与えても Permission Denied になる</h2>
<p>結論としては、 <code>Pub/Sub サブスクライバー</code> 権限に加えて、 <code>Pub/Sub 閲覧者</code> の権限が必要でした。<br/>
ドキュメント上は、 <code>Pub/Sub サブスクライバー</code> 権限だけで良さそうに見えますが、権限不足だったようでした。<br/>
Subscriberのサンプルは以下の通りです。</p>
<script src="https://gist.github.com/hitsumabushi/baaeefd241e27ab0414763bdc6a93f11.js"></script>さくらのクラウドでN百台を管理するためにterraformとansibleを使っている話2017-12-05T17:48:00+09:002017-12-05T17:48:00+09:00hitsumabushitag:www.hitsumabushi.org,2017-12-05:/blog/2017/12/05/1748.html
<hr/>
<p>これは、<a href="https://qiita.com/advent-calendar/2017/sakura">さくらインターネット Advent Calendar 2017</a> として書いた<a href="https://qiita.com/hitsumabushi/items/e89b763dd4fc41e15136">記事</a> です。</p>
<hr/>
<p>さくらインターネットでは、今年4月からIoTプラットフォームの sakura.io をサービス提供しています。<br/>
sakura.io は、さくらのクラウド上で本番・検証環境を構築しており、数百台のサーバーを利用しています。</p>
<p>私はリリース直前にチームに参加し、開発の傍ら運用改善活動をしていました。<br/>
その結果と …</p>
<hr/>
<p>これは、<a href="https://qiita.com/advent-calendar/2017/sakura">さくらインターネット Advent Calendar 2017</a> として書いた<a href="https://qiita.com/hitsumabushi/items/e89b763dd4fc41e15136">記事</a> です。</p>
<hr/>
<p>さくらインターネットでは、今年4月からIoTプラットフォームの sakura.io をサービス提供しています。<br/>
sakura.io は、さくらのクラウド上で本番・検証環境を構築しており、数百台のサーバーを利用しています。</p>
<p>私はリリース直前にチームに参加し、開発の傍ら運用改善活動をしていました。<br/>
その結果としてTerraform を導入し、Terraform (+ Terraform for さくらのクラウド) + Ansible で運用することになりました。<br/>
導入までの課題と、どのように導入・利用しているのか、について書きたいと思います。</p>
<h2 id="_1">運用の課題</h2>
<h3 id="_2">検証環境と本番環境で構成に差分があり、それに気づきづらい</h3>
<p>mesos+marathon を利用したコンテナ実行環境を使ったマイクロサービスアーキテクチャになっていて、それ以外にも、redis, memcached, メッセージキューなどいろいろなコンポーネントがあります。<br/>
そのため、個別の開発者がクリーンな環境を気軽に用意しづらいです。</p>
<p>インフラの構成を変更したいと思ったとき、個別の開発者がクリーンな環境を個別に作るのは手間がかかってしまうため、直接検証環境でテストしがちです。その際に、行われた変更がそのまま残ってしまい、本番環境との差が残ってしまっているケースがありました。</p>
<h3 id="ansible-playbook">Ansible Playbook の内容と現実に差分がある</h3>
<p>もともとOS内の設定はansibleで管理していたのですが、対象ホストの増加、ansible playbookの肥大化に伴い、実行時間が増えてしまいました。<br/>
それに伴い、<code>--limit</code>, <code>--tags</code> をつけて部分的に実行するようになり、playbook と差分が散見されるようになりました。</p>
<h2 id="_3">方針</h2>
<p>ansible は全部実行しろ、などとルールを作ることは簡単なのですが、上記に書いたとおり、心理的なハードルによって発生している問題だと思いました。そこでルール化することはエンジニアリングの敗北という感じがしたので、差分がわかるように構成管理することと、差分を定期的にチェックして通知することに集中することにしました。</p>
<p>構成管理として、OS内の部分はansibleを利用していたためそのまま利用し、インフラの構成管理としては、ちょうど <a href="https://sacloud.github.io/terraform-provider-sakuracloud/">Terraform for さくらのクラウド</a> もあったため、terraformを利用することにしました。</p>
<p>差分の検知としては、terraform plan, ansible check mode を定期的に実行することにしました。</p>
<h2 id="terraform">terraform 導入のために</h2>
<h3 id="terraform_1">terraform のフォルダ構成</h3>
<p><a href="https://github.com/hashicorp/best-practices/tree/master/terraform">他のクラウドでのベストプラクティス</a> を参考に、フォルダ構成は以下のようにしています。<br/>
terraform plan, apply などする場合には、 <code>pod-xxx</code> ディレクトリ配下で実行します。</p>
<div class="highlight"><pre><span></span><code>.
├── module
│ └── sakuracloud
│ ├── compute
│ │ ├── compute.tf // compute のエントリーポイント。各roleのモジュールへ変数を渡す
│ │ ├── role-hoge // role ごとに作成
│ │ │ └── role-hoge.tf // util の base-wrapperへ変数を渡す。role固有の変数変換はここでやる
│ │ └── util // 他のroleから読まれる
│ │ ├── base-wrapper // instance と service-dns を呼ぶためのやつ。他のroleからはこいつを見る
│ │ ├── instance // 個別のサーバーの、サーバー/ディスク/DNSレコードの作成
│ │ └── service-dns // roleごとに、DNSレコードがある場合(VIPに紐づくレコードなど)
│ ├── dns // さくらのクラウドのドメイン/DNSレコードを管理するために利用
│ │ ├── dns.tf
│ │ └── example
│ │ ├── example.tf
│ │ └── output.tf
│ ├── gslb // さくらのクラウドのGSLBを管理するために利用
│ │ ├── api
│ │ │ └── api.tf
│ │ └── gslb.tf
│ ├── network // さくらのクラウドのスイッチ/ルーターを管理するために利用
│ │ ├── internal
│ │ │ ├── internal.tf
│ │ │ └── output.tf
│ │ ├── network.tf
│ │ └── output.tf
│ └── simple-monitor // さくらのクラウドのシンプル監視を管理するために利用
│ └── simple_monitor.tf
└── providers
└── sakuracloud
└── pod-xxx // システム単位ごとに作る
├── pod-xxx.tf // compute.tf, dns.tf, ... などのモジュールごとのエントリーポイントへ変数を渡す
├── terraform.tfstate.d // tfstate。env (今で言うworkspace) で本番と検証環境をわけてる。
│ ├── dev
│ └── ...
└── terraform.tfvars // 変数をひたすら書く。具体的な値は全てここに書かれているはず。
</code></pre></div>
<p>大体はさくらのクラウドのコンポーネントごとに、moduleを作っているのですが、compute のところは大きく変更しています。<br/>
サーバー作成と同時に、ディスクの作成や、DNSレコードの作成を行うためです。</p>
<p>例えば、role-hoge というロールが2台あるとき、tfvar中では以下のような変数を作っています。</p>
<div class="highlight"><pre><span></span><code><span class="nx">sakuracloud_dns_foo</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">dev</span><span class="p">.</span><span class="nx">zone</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"dev.example.com"</span>
<span class="w"> </span><span class="nx">staging</span><span class="p">.</span><span class="nx">zone</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">"staging.example.com"</span>
<span class="p">}</span>
<span class="nx">sakuracloud_role_hoge</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">dev</span><span class="p">.</span><span class="nx">ipaddresses</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s">"192.168.0.10"</span><span class="p">,</span><span class="w"> </span><span class="s">"192.168.0.11"</span><span class="p">]</span>
<span class="w"> </span><span class="nx">dev</span><span class="p">.</span><span class="nx">server_tags</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s">"hoge"</span><span class="p">,</span><span class="w"> </span><span class="s">"develop"</span><span class="p">,</span><span class="w"> </span><span class="s">"__with_sacloud_inventory"</span><span class="p">,</span><span class="w"> </span><span class="s">"starred"</span><span class="p">]</span>
<span class="w"> </span><span class="nx">dev</span><span class="p">.</span><span class="nx">disk_tags</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s">"hoge"</span><span class="p">]</span>
<span class="w"> </span><span class="nx">staging</span><span class="p">.</span><span class="nx">ipaddresses</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s">"192.168.1.10"</span><span class="p">,</span><span class="w"> </span><span class="s">"192.168.1.11"</span><span class="p">]</span>
<span class="w"> </span><span class="nx">staging</span><span class="p">.</span><span class="nx">server_tags</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s">"hoge"</span><span class="p">,</span><span class="w"> </span><span class="s">"staging"</span><span class="p">,</span><span class="w"> </span><span class="s">"__with_sacloud_inventory"</span><span class="p">,</span><span class="w"> </span><span class="s">"starred"</span><span class="p">]</span>
<span class="w"> </span><span class="nx">staging</span><span class="p">.</span><span class="nx">disk_tags</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">[</span><span class="s">"hoge"</span><span class="p">]</span>
</code></pre></div>
<p>これを使って、dev環境でサーバー作成したとき</p>
<ul>
<li>サーバー名をFQDNに一致させる<ul>
<li><code>hoge-01.dev.example.com</code>, <code>hoge-02.dev.example.com</code> </li>
</ul>
</li>
<li>DNSレコードも合わせて作成する<ul>
<li>Aレコードとして <code>hoge-01.dev.example.com</code> は192.168.0.10, <code>hoge-02.dev.example.com</code> は192.168.0.11 に対応させる</li>
</ul>
</li>
<li>サーバータグとして、ansibleのroleをつける<ul>
<li><code>hoge</code>, <code>develop</code>, <code>staging</code> がansible側で利用しているロール名です</li>
</ul>
</li>
<li>サーバータグとして、 <code>__with_sacloud_inventory</code> をつける<ul>
<li>一応、terraform 管理外のリソースを許すためにつけています</li>
</ul>
</li>
<li>サーバータグとして、 <code>@group=a</code> などのグループタグをつける<ul>
<li>1つ目のサーバーはa、2つ目はb、...4つ目はd、5つ目はaなどとしています</li>
</ul>
</li>
</ul>
<h3 id="terraformimport">既存リソースをterraformにimport</h3>
<p>既存のリソースをインポートします。形式としては、以下のような形式です。</p>
<div class="highlight"><pre><span></span><code><span class="n">terraform</span> <span class="kn">import</span> <span class="s1">'module.hoge.module.base-instance.sakuracloud_server.base[0]'</span> <span class="n">_リソースID_</span>
</code></pre></div>
<p>注意点としては、tfファイルを書いたあと、terraform planすれば、何をインポートしないといけないかわかるのですが、<br/>
その表示では、<code>module.hoge.base-instance.sakuracloud_server.base[0]</code> というように、途中にmoduleがない形式で表示されます。 import するには都度moduleを書く必要があります。</p>
<p>また、さくらのクラウドにはリソースIDがないけど、terraform で管理できるものがあります。例えばDNSレコードがそうです。<br/>
この場合、リソースIDに当たるものを自分で作成する必要があります。<br/>
どのように生成するかは、 terraform for さくらのクラウドのソースを見ればよいのですが、参考までにgolangで生成する例を載せておきます。<br/>
https://play.golang.org/p/OvQw6BdxVf</p>
<h3 id="ansible-dynamic-inventory">ansible の dynamic inventory のスクリプト作成</h3>
<script src="https://gist.github.com/hitsumabushi/73411a6bcb900a05c027ae0c8a39a9b3.js"></script>
<p>上記のような<code>sacloud_inventory.py</code>スクリプトをansibleのリポジトリに実行権限をつけて用意しています。<br/>
それにより、 <code>ansible-playbook -i sacloud_inventory.py ...</code> とすることで、インベントリファイルを書くことなくansibleを実行できます。</p>
<p>このスクリプトで、さくらのクラウドのAPIを叩くところはすべて、 <a href="https://github.com/sacloud/usacloud">usacloud</a> におまかせしています。<br/>
先程の terraform の例であったように、 <code>__with_sacloud_inventory</code> が入っていないものは全部無視し、タグは全てグループ名にすることにしています。<br/>
(13-22行目は、<a href="https://github.com/ansible/awx">Ansible TowerのOSS版であるAWX</a> を試しているために入れています。)</p>
<p>もしかしたら、さくらのクラウドの特殊タグである <code>@group=a</code> などは除いた方が使いやすいかもしれませんが、監視側からも見るために現状はすべて入れています。</p>
<h2 id="ansible">ansible 側の準備</h2>
<p><code>ansible-playbook -i sacloud_inventory.py site.yml --check</code> をしたいのですが、しばしば check modeで実行できていないplaybookや、変更がないのにchangedにしているplaybookがあります。これを直しましょう。<br/>
check modeで実行できていない、典型的なものとしては以下のようなものがあります。</p>
<h3 id="task">別のtaskの実行結果を参照している</h3>
<p>register を使っているタスクAの結果を参照しているタスクBがある場合、check mode 中でAが実行されないため、タスクBのcheck実行時に変数が参照できないエラーが出ます。<br/>
これを防ぐには、register を使っているタスクAに <code>check_mode: no</code> をつけることです。<br/>
check modeでも本当に実行されるようになるため、例えば、ファイルの存在だけを確認している場合など、副作用がない場合のみ、 <code>check_mode: no</code> をつけましょう。</p>
<h3 id="check-modeshell">check modeに対応していない(特にshellモジュール)</h3>
<p>実行する判断を行うようなタスクを作って、その結果次第で、タスクを実行するように書き換えます。<br/>
新しく作成したタスクでは、上述の通り副作用がないようにし、 <code>check_mode: no</code> をつけましょう。</p>
<h3 id="dns">補足: DNSゾーンファイルの更新</h3>
<p>今回、check modeで実行可能にするためにplaybookをみなしていると、ansible で更新されていないものとして、template指定されたDNSゾーンファイルがありました。<br/>
ゾーンファイルは、シリアルを増やしていく必要があるため、面倒だったのだと思います。</p>
<p>確かに、言われるとめんどくさいような気もしますが、やってみると以下のようにすれば良さそうです。</p>
<ol>
<li>現在のzone fileを見て、現在の serial を取り出し、registerで変数に入れる</li>
<li>template ファイルに、 1.で取ったserialを入れて、差分があるかどうかを確かめる</li>
<li>もし、2.で差分があったら serialをインクリメントした上で、template を実行する</li>
</ol>
<p>実際に書いてみると以下のようになると思います。(NSDを利用した場合の例)</p>
<div class="highlight"><pre><span></span><code><span class="n">server</span><span class="o">:</span>
<span class="w"> </span><span class="n">port</span><span class="o">:</span><span class="w"> </span><span class="mi">10053</span>
<span class="w"> </span><span class="n">zonesdir</span><span class="o">:</span><span class="w"> </span><span class="s2">"/etc/nsd/zones"</span>
<span class="o">{%</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">zone</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">dns</span><span class="o">.</span><span class="na">zones</span><span class="w"> </span><span class="o">%}</span>
<span class="n">zone</span><span class="o">:</span>
<span class="w"> </span><span class="n">name</span><span class="o">:</span><span class="w"> </span><span class="s2">"{{ zone }}"</span>
<span class="w"> </span><span class="n">zonefile</span><span class="o">:</span><span class="w"> </span><span class="s2">"{{ zone }}"</span>
<span class="o">{%</span><span class="w"> </span><span class="n">endfor</span><span class="w"> </span><span class="o">%}</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code><span class="o">-</span><span class="w"> </span><span class="n">block</span><span class="p">:</span>
<span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">name</span><span class="p">:</span><span class="w"> </span><span class="n">parse</span><span class="w"> </span><span class="n">serial</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">zone</span><span class="w"> </span><span class="n">file</span>
<span class="w"> </span><span class="n">shell</span><span class="p">:</span><span class="w"> </span><span class="n">grep</span><span class="w"> </span><span class="s2">";Serial"</span><span class="w"> </span><span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">nsd</span><span class="o">/</span><span class="n">zones</span><span class="o">/</span><span class="p">{{</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="p">}}</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">awk</span><span class="w"> </span><span class="s1">'{print $1}'</span>
<span class="w"> </span><span class="n">with_items</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{ dns.zones }}"</span>
<span class="w"> </span><span class="n">register</span><span class="p">:</span><span class="w"> </span><span class="n">old_serials</span>
<span class="w"> </span><span class="n">check_mode</span><span class="p">:</span><span class="w"> </span><span class="n">no</span>
<span class="w"> </span><span class="n">changed_when</span><span class="p">:</span><span class="w"> </span><span class="n">no</span>
<span class="w"> </span><span class="n">tags</span><span class="p">:</span><span class="w"> </span><span class="n">nsd</span>
<span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">name</span><span class="p">:</span><span class="w"> </span><span class="n">check</span><span class="w"> </span><span class="n">whether</span><span class="w"> </span><span class="n">zone</span><span class="w"> </span><span class="n">files</span><span class="w"> </span><span class="n">updated</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="ow">not</span>
<span class="w"> </span><span class="n">template</span><span class="p">:</span><span class="w"> </span><span class="n">src</span><span class="o">=</span><span class="n">zones</span><span class="o">/</span><span class="p">{{</span><span class="w"> </span><span class="n">item</span><span class="o">.</span><span class="mi">1</span><span class="w"> </span><span class="p">}}</span><span class="o">.</span><span class="n">j2</span><span class="w"> </span><span class="n">dest</span><span class="o">=/</span><span class="n">etc</span><span class="o">/</span><span class="n">nsd</span><span class="o">/</span><span class="n">zones</span><span class="o">/</span><span class="p">{{</span><span class="w"> </span><span class="n">item</span><span class="o">.</span><span class="mi">1</span><span class="w"> </span><span class="p">}}</span><span class="w"> </span><span class="n">owner</span><span class="o">=</span><span class="n">root</span><span class="w"> </span><span class="n">group</span><span class="o">=</span><span class="n">root</span><span class="w"> </span><span class="n">mode</span><span class="o">=</span><span class="mi">0644</span>
<span class="w"> </span><span class="n">vars</span><span class="p">:</span>
<span class="w"> </span><span class="n">serial</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{ old_serials.results[item.0].stdout }}"</span>
<span class="w"> </span><span class="n">with_indexed_items</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{ dns.zones }}"</span>
<span class="w"> </span><span class="n">diff</span><span class="p">:</span><span class="w"> </span><span class="n">no</span>
<span class="w"> </span><span class="n">register</span><span class="p">:</span><span class="w"> </span><span class="n">zonefiles_changed</span>
<span class="w"> </span><span class="n">tags</span><span class="p">:</span><span class="w"> </span><span class="n">nsd</span>
<span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">name</span><span class="p">:</span><span class="w"> </span><span class="n">update</span><span class="w"> </span><span class="n">zonefile</span><span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="n">changed</span>
<span class="w"> </span><span class="n">template</span><span class="p">:</span><span class="w"> </span><span class="n">src</span><span class="o">=</span><span class="n">zones</span><span class="o">/</span><span class="p">{{</span><span class="w"> </span><span class="n">item</span><span class="o">.</span><span class="mi">1</span><span class="w"> </span><span class="p">}}</span><span class="o">.</span><span class="n">j2</span><span class="w"> </span><span class="n">dest</span><span class="o">=/</span><span class="n">etc</span><span class="o">/</span><span class="n">nsd</span><span class="o">/</span><span class="n">zones</span><span class="o">/</span><span class="p">{{</span><span class="w"> </span><span class="n">item</span><span class="o">.</span><span class="mi">1</span><span class="w"> </span><span class="p">}}</span><span class="w"> </span><span class="n">owner</span><span class="o">=</span><span class="n">root</span><span class="w"> </span><span class="n">group</span><span class="o">=</span><span class="n">root</span><span class="w"> </span><span class="n">mode</span><span class="o">=</span><span class="mi">0644</span>
<span class="w"> </span><span class="n">vars</span><span class="p">:</span>
<span class="w"> </span><span class="n">serial</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{ old_serials.results[item.0].stdout|int + 1 }}"</span>
<span class="w"> </span><span class="n">when</span><span class="p">:</span><span class="w"> </span><span class="n">zonefiles_changed</span><span class="o">.</span><span class="n">results</span><span class="p">[</span><span class="n">item</span><span class="o">.</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">changed</span>
<span class="w"> </span><span class="n">with_indexed_items</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{ dns.zones }}"</span>
<span class="w"> </span><span class="n">notify</span><span class="p">:</span><span class="w"> </span><span class="n">reload</span><span class="w"> </span><span class="n">nsd</span>
<span class="w"> </span><span class="n">tags</span><span class="p">:</span><span class="w"> </span><span class="n">nsd</span>
</code></pre></div>
<h2 id="_4">定期的な差分チェック</h2>
<p>ここまでくれば、あとはcronなりJenkinsで、<code>terraform plan</code> と <code>ansible-playbook -i sacloud_inventory.py site.yml --check</code> を定期的に走らせて、差分があったらslackなりに通知すればオッケーです。<br/>
Jenkinsからapplyするようにしておけば、より良いと思います。</p>
<h2 id="_5">感想と今後に向けて</h2>
<p>ここではインフラのデプロイをどのように改善してきたのかを書きました。<br/>
usacloud や Terraform for さくらのクラウドがあるおかげで、規模が大きくなっても楽に管理できている実感があります。<br/>
実際、雑に自前スクリプトを書いていたものも捨てていっているところです。</p>
<p>この記事で書いたようにインフラの整合性を確かめることができるようになったので、今後としては、</p>
<ul>
<li>(ansibleの実行を <a href="https://github.com/ansible/awx">AWX</a> で行う(だいたいできた) )</li>
<li>インフラ/アプリケーションのCDを行うための監視改善。特に node/service discovery改善</li>
<li>アプリケーションのCD</li>
<li>terraform, ansible の自動適用<br/>
などを目標に改善活動を行っていく予定です。</li>
</ul>Mesos の sandbox のログローテーションをする2017-12-01T20:01:00+09:002017-12-01T20:01:00+09:00hitsumabushitag:www.hitsumabushi.org,2017-12-01:/blog/2017/12/01/2001.html
<h3 id="_1">結論</h3>
<ul>
<li>http://mesos.apache.org/documentation/latest/logging/#logrotatecontainerlogger</li>
<li><code>LogrotateContainerLogger</code> を使って、 module parameter を設定する</li>
</ul>
<h3 id="_2">概要</h3>
<p>Mesos + Marathon 環境でdockerを動かしている。<br/>
基本的にコンテナのログは fluentd で飛ばしているのだけど、日に日に mesos slave のディスク容量が圧迫されていた。<br/>
調べてみると、 <code>/var/lib/mesos-slave/slaves/</code> 以下にあるフォルダのうち、sandbox のログが肥大化していた。<br/>
sandbox には stdout, stderr があって、それぞれコンテナのstdout, stderrを記録しているファイルで、mesosか …</p>
<h3 id="_1">結論</h3>
<ul>
<li>http://mesos.apache.org/documentation/latest/logging/#logrotatecontainerlogger</li>
<li><code>LogrotateContainerLogger</code> を使って、 module parameter を設定する</li>
</ul>
<h3 id="_2">概要</h3>
<p>Mesos + Marathon 環境でdockerを動かしている。<br/>
基本的にコンテナのログは fluentd で飛ばしているのだけど、日に日に mesos slave のディスク容量が圧迫されていた。<br/>
調べてみると、 <code>/var/lib/mesos-slave/slaves/</code> 以下にあるフォルダのうち、sandbox のログが肥大化していた。<br/>
sandbox には stdout, stderr があって、それぞれコンテナのstdout, stderrを記録しているファイルで、mesosからファイルとして取得することができる。<br/>
長期的なものは fluentd で飛ばしているので問題ないため、障害時や直近の確認のために sandbox を使うことにして、<br/>
短期間でのログローテーションを行うことにした。</p>
<h3 id="_3">設定方法</h3>
<h4 id="logrotatecontainerlogger">LogrotateContainerLogger の利用が可能か調べる</h4>
<p>共有オブジェクト <code>/usr/lib/liblogrotate_container_logger.so</code> にあることを確認する。<br/>
パスが違っても良いが、その場合は以下で出てくるパスも変更する。</p>
<h4 id="_4">モジュールの設定ファイルを書く</h4>
<p><code>/etc/mesos_slave_modules.json</code> として以下を書く。</p>
<div class="highlight"><pre><span></span><code>{
"libraries": [{
"file": "/usr/lib/liblogrotate_container_logger.so",
"modules": [{
"name": "org_apache_mesos_LogrotateContainerLogger"
# パラメータを指定するときはここに書く
}]
}]
}
</code></pre></div>
<h4 id="_5">モジュールの有効化</h4>
<p><code>/etc/mesos-slave/modules</code> として、以下が書かれたファイルを置く。</p>
<div class="highlight"><pre><span></span><code>file:///etc/mesos-slave-modules.json
</code></pre></div>
<p><code>/etc/mesos-slave/container_logger</code> として、以下が書かれたファイルを置く。</p>
<div class="highlight"><pre><span></span><code>org_apache_mesos_LogrotateContainerLogger
</code></pre></div>
<h4 id="mesos-slave">mesos-slave の起動</h4>
<p>mesos-slave を起動する。</p>
<h4 id="_6">動作確認</h4>
<p>sandbox を見ると、 stdout.logrotate.conf, stderr.logrotate.conf が出来ている。</p>
<p><img alt="" src="/images/2017/mesos/mesos_sandbox.png"/></p>
<p>ファイルの中身は</p>
<div class="highlight"><pre><span></span><code><span class="o">/</span><span class="nv">path</span><span class="o">/</span><span class="nv">to</span><span class="o">/</span><span class="nv">stdout</span><span class="o">/</span><span class="w"> </span>{
##<span class="w"> </span><span class="k">logrotate</span><span class="w"> </span>オプション:<span class="w"> </span>パラメータで指定してない場合は<span class="w"> </span><span class="nv">size</span><span class="w"> </span><span class="mi">10481664</span><span class="w"> </span>くらいしかない
}
</code></pre></div>
<p>となっている。</p>