@znz blog ZnZ の memo のようなもの https://blog.n-z.jp/ Tue, 31 Dec 2024 06:03:12 +0000 Tue, 31 Dec 2024 06:03:12 +0000 Jekyll v4.3.4 - macOSがsudo_localに対応してpam_tid.so設定が永続化できるようになっていた <p>macOS のどのバージョンからなのか確認していませんが、 <code class="language-plaintext highlighter-rouge">sudo_local</code> に対応して <code class="language-plaintext highlighter-rouge">pam_tid.so</code> (Touch ID 対応) 設定が永続化できるようになっていたのに最近気付きました。</p> <!--more--> <h2 id="確認バージョン">確認バージョン</h2> <ul> <li>macOS Sequoia 15.2</li> <li>macOS Sonoma 14.7.1</li> </ul> <h2 id="設定方法">設定方法</h2> <p><code class="language-plaintext highlighter-rouge">.template</code> がついているファイルをコピーして、 <code class="language-plaintext highlighter-rouge">sudo -e</code> (他環境の <code class="language-plaintext highlighter-rouge">sudoedit</code> と同じ意味だが macOS には <code class="language-plaintext highlighter-rouge">sudoedit</code> がない) などでコメントアウトされている <code class="language-plaintext highlighter-rouge">pam_tid.so</code> の行の行頭の <code class="language-plaintext highlighter-rouge">#</code> を削除して有効にします。</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo cp</span> /etc/pam.d/sudo_local<span class="o">{</span>.template,<span class="o">}</span> <span class="nb">sudo</span> <span class="nt">-e</span> /etc/pam.d/sudo_local </code></pre></div></div> <h2 id="気付いたきっかけ">気付いたきっかけ</h2> <p>Sequoia 15.2 に上がったタイミングでいつものように内容が元に戻っている <code class="language-plaintext highlighter-rouge">/etc/pam.d/sudo</code> を編集しようと思ったときに、いつもより内容をちゃんとみてみると、 <code class="language-plaintext highlighter-rouge">auth include sudo_local</code> の行に気付きました。</p> <p>そして <code class="language-plaintext highlighter-rouge">sudo_local.template</code> というファイルの存在にも気付いて、 内容を確認すると <code class="language-plaintext highlighter-rouge">pam_tid.so</code> の設定の永続化を想定しているようでした。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% cat /etc/pam.d/sudo </span><span class="gp">#</span><span class="w"> </span><span class="nb">sudo</span>: auth account password session <span class="go">auth include sudo_local auth sufficient pam_smartcard.so auth required pam_opendirectory.so account required pam_permit.so password required pam_deny.so session required pam_permit.so % cat /etc/pam.d/sudo_local.template </span><span class="gp">#</span><span class="w"> </span>sudo_local: <span class="nb">local </span>config file which survives system update and is included <span class="k">for </span><span class="nb">sudo</span> <span class="gp">#</span><span class="w"> </span>uncomment following line to <span class="nb">enable </span>Touch ID <span class="k">for </span><span class="nb">sudo</span> <span class="gp">#</span>auth sufficient pam_tid.so </code></pre></div></div> <h2 id="その他の-macos-での対応状況">その他の macOS での対応状況</h2> <p>ひとつ前の Sonoma でも対応しているのは確認できて、 ちょっと検索した感じだともっと前のバージョンから対応していそうな可能性がありました。</p> <h2 id="まとめ">まとめ</h2> <p><code class="language-plaintext highlighter-rouge">sudo_local</code> で <code class="language-plaintext highlighter-rouge">pam_tid.so</code> の設定が macOS の更新をしても残せそうということがわかりました。 (この設定後の更新がまだないので確認はできていない。)</p> <p><code class="language-plaintext highlighter-rouge">sudo</code> の設定変更はミスすると <code class="language-plaintext highlighter-rouge">sudo</code> が使えなくなって非常に困るので、 その設定を毎回しなくてよくなるというのは非常に良さそうでした。</p> <p><a href="https://ruby-jp.github.io/">ruby-jp Slack</a> の <code class="language-plaintext highlighter-rouge">#apple_followers</code> でちょっと発言してみたところ、 <code class="language-plaintext highlighter-rouge">sudo_local</code> のことだけじゃなくて、そもそも <code class="language-plaintext highlighter-rouge">pam_tid.so</code> を知らなかったという話もあったので、 <code class="language-plaintext highlighter-rouge">sudo</code> のときのパスワード入力が面倒だと思っていた人は設定してみてください。</p> Mon, 16 Dec 2024 02:30:00 +0000 https://blog.n-z.jp/blog/2024-12-16-sudo-local-pam-tid.html https://blog.n-z.jp/blog/2024-12-16-sudo-local-pam-tid.html osx blog
- rubocopコマンドで使うstandard gemベースの設定を更新した <p><a href="/blog/2020-10-21-rubocop-and-standard.html">rubocopコマンドでstandard gemベースの設定を使う</a> で設定した内容から、 いつの間にか変更が必要になっていたので、設定を更新しました。</p> <!--more--> <h2 id="動作確認バージョン">動作確認バージョン</h2> <ul> <li>ruby 3.2.6</li> <li>rubocop 1.68.0</li> <li>standard 1.42.1</li> <li>standard-custom 1.0.2</li> <li>standard-performance 1.5.0</li> </ul> <h2 id="差分">差分</h2> <p>最初に差分をのせておきます。</p> <p><code class="language-plaintext highlighter-rouge">require:</code> の <code class="language-plaintext highlighter-rouge">standard/cop/block_single_line_braces</code> を <code class="language-plaintext highlighter-rouge">standard-custom</code> に変更しています。</p> <p><code class="language-plaintext highlighter-rouge">inherit_gem:</code> に <code class="language-plaintext highlighter-rouge">standard-custom: config/base.yml</code> と <code class="language-plaintext highlighter-rouge">standard-performance: config/base.yml</code> を足しています。</p> <div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/.rubocop.yml b/.rubocop.yml index 59563e0..da89d44 100644 </span><span class="gd">--- a/.rubocop.yml </span><span class="gi">+++ b/.rubocop.yml </span><span class="p">@@ -6,10 +6,12 @@</span> require: - rubocop-performance - rubocop-rails - rubocop-capybara <span class="gd">-- standard/cop/block_single_line_braces </span><span class="gi">+- standard-custom </span><span class="err"> </span> inherit_gem: standard: config/base.yml <span class="gi">+ standard-custom: config/base.yml + standard-performance: config/base.yml </span><span class="err"> </span> AllCops: Exclude: </code></pre></div></div> <h2 id="きっかけ">きっかけ</h2> <p>仕事で関わっている rails アプリで <code class="language-plaintext highlighter-rouge">.github/dependabot.yml</code> に</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="na">allow</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">dependency-type</span><span class="pi">:</span> <span class="s">all</span> </code></pre></div></div> <p>を追加したところ、 <code class="language-plaintext highlighter-rouge">standard-performance</code> gem の更新の pull request も作成されていて、 気になって調べたところ、 Performance の cop が <code class="language-plaintext highlighter-rouge">rubocop-performance</code> gem に分離された影響で Performance department の設定が <code class="language-plaintext highlighter-rouge">standard-performance</code> gem に分離されているようでした。</p> <p><code class="language-plaintext highlighter-rouge">standard/cop/block_single_line_braces</code> として <code class="language-plaintext highlighter-rouge">standard</code> gem に同梱されていた独自 cop がいつの間にか <code class="language-plaintext highlighter-rouge">standard-custom</code> gem に分離されていました。</p> <h2 id="最小設定">最小設定</h2> <p>現状で新規設定するなら必要なものは以下のようになりそうです。</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">require</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">rubocop-performance</span> <span class="pi">-</span> <span class="s">standard-custom</span> <span class="na">inherit_gem</span><span class="pi">:</span> <span class="na">standard</span><span class="pi">:</span> <span class="s">config/base.yml</span> <span class="na">standard-custom</span><span class="pi">:</span> <span class="s">config/base.yml</span> <span class="na">standard-performance</span><span class="pi">:</span> <span class="s">config/base.yml</span> </code></pre></div></div> <p>他の設定については、 <a href="/blog/2020-10-21-rubocop-and-standard.html">以前の記事</a> を参考にしてください。</p> <h2 id="最後に">最後に</h2> <p><code class="language-plaintext highlighter-rouge">standard</code> gem を直接使わずに <code class="language-plaintext highlighter-rouge">rubocop</code> 経由で設定だけ使うということをしているので、 <code class="language-plaintext highlighter-rouge">standard</code> gem の変更もある程度追いかけておく必要がありそうでした。</p> Thu, 12 Dec 2024 10:00:00 +0000 https://blog.n-z.jp/blog/2024-12-12-standard-performance.html https://blog.n-z.jp/blog/2024-12-12-standard-performance.html ruby rubocop blog
- RBS入りのWEBrick gemをリリースした <p><a href="https://rubygems.org/gems/webrick">WEBrick</a> 1.9.0 として RBS による型情報つきの gem をリリースできたので、そのメモです。</p> <!--more--> <h2 id="バージョン">バージョン</h2> <p>動作確認に使ったバージョンはこのあたりでした。</p> <ul> <li>ruby 3.3.5</li> <li>rbs 3.6.1</li> <li>steep 1.8.3</li> <li>webrick 1.8.2 → 1.9.0</li> </ul> <h2 id="使い方">使い方</h2> <p><code class="language-plaintext highlighter-rouge">rbs_collection.yaml</code> がなければ <code class="language-plaintext highlighter-rouge">rbs collection init</code> で作成するなどして用意しておいて、以下のように <code class="language-plaintext highlighter-rouge">gems</code> に <code class="language-plaintext highlighter-rouge">- name: webrick</code> を追加して、 <code class="language-plaintext highlighter-rouge">rbs collection install</code> や <code class="language-plaintext highlighter-rouge">rbs collection update</code> を実行すると、 <code class="language-plaintext highlighter-rouge">rbs_collection.lock.yaml</code> が更新されて steep などで型情報が使えるようになりました。</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">gems</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">webrick</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">rbs_collection.lock.yaml</code> には以下のように <code class="language-plaintext highlighter-rouge">source.type: rubygems</code> で追加されていました。</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">webrick</span> <span class="na">version</span><span class="pi">:</span> <span class="s">1.9.0</span> <span class="na">source</span><span class="pi">:</span> <span class="na">type</span><span class="pi">:</span> <span class="s">rubygems</span> </code></pre></div></div> <p>ffi 1.17.0 は <a href="https://github.com/ffi/ffi/issues/1107">https://github.com/ffi/ffi/issues/1107</a> の問題があるので、 以下のように <code class="language-plaintext highlighter-rouge">name: ffi</code> に <code class="language-plaintext highlighter-rouge">ignore: true</code> を設定したものを <code class="language-plaintext highlighter-rouge">gems:</code> に追加しています。</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">gems</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">webrick</span> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ffi</span> <span class="na">ignore</span><span class="pi">:</span> <span class="kc">true</span> <span class="c1"># https://github.com/ffi/ffi/issues/1107</span> </code></pre></div></div> <h2 id="現状の型情報">現状の型情報</h2> <p>typeprof で生成された <a href="https://github.com/ruby/webrick/pull/115">https://github.com/ruby/webrick/pull/115</a> やソースコードを参考にしつつ、 <code class="language-plaintext highlighter-rouge">rbs prototype rb</code> で生成した <code class="language-plaintext highlighter-rouge">*.rbs</code> を更新する方法で、 <a href="https://github.com/ruby/webrick/pull/151">https://github.com/ruby/webrick/pull/151</a> として追加しました。</p> <p>2週間ぐらいかけて一通りざっと対応しただけなので、 まだ <a href="https://github.com/ruby/webrick/pull/155">https://github.com/ruby/webrick/pull/155</a> のように間違っている部分や 考慮不足で使いにくい部分もあると思うので、改善案などあれば pull request や issue を 作成してもらえると良さそうです。</p> <h2 id="型付け中にひっかかった部分">型付け中にひっかかった部分</h2> <h3 id="singletonclassname">singleton(ClassName)</h3> <p>例外クラスの指定のようにクラスオブジェクト自体が引数になるときは、 <code class="language-plaintext highlighter-rouge">singleton(ClassName)</code> のように <code class="language-plaintext highlighter-rouge">singleton</code> を使う、 というのが知らないと難しそうだった。</p> <pre><code class="language-rbs">def self.register: (Numeric seconds, singleton(Exception) exception) -> Integer </code></pre> <h3 id="const_set-で定義されている定数">const_set で定義されている定数</h3> <p><code class="language-plaintext highlighter-rouge">webrick/httpstatus</code> <code class="language-plaintext highlighter-rouge">const_set</code> されている定数は以下で補ったので、 VSCode に Steep 拡張機能を入れているときに <code class="language-plaintext highlighter-rouge">WEBrick::HTTPStatus::RC_OK: 200</code> などがホバーで確認できたり、補完がきいたりして便利です。</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'webrick'</span> <span class="nb">puts</span> <span class="no">WEBrick</span><span class="o">::</span><span class="no">HTTPStatus</span><span class="o">::</span><span class="nb">constants</span><span class="p">.</span><span class="nf">grep</span><span class="p">(</span><span class="sr">/\ARC_/</span><span class="p">).</span><span class="nf">map</span><span class="p">{</span><span class="s2">"</span><span class="si">#{</span><span class="n">_1</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="no">WEBrick</span><span class="o">::</span><span class="no">HTTPStatus</span><span class="p">.</span><span class="nf">const_get</span><span class="p">(</span><span class="n">_1</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span><span class="p">}</span> <span class="nb">puts</span> <span class="no">WEBrick</span><span class="o">::</span><span class="no">HTTPStatus</span><span class="o">::</span><span class="no">CodeToError</span><span class="p">.</span><span class="nf">each_value</span><span class="p">.</span><span class="nf">map</span><span class="p">{</span><span class="s2">"class </span><span class="si">#{</span><span class="n">_1</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sr">/::/</span><span class="p">).</span><span class="nf">last</span><span class="si">}</span><span class="s2"> < </span><span class="si">#{</span><span class="n">_1</span><span class="p">.</span><span class="nf">superclass</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sr">/::/</span><span class="p">).</span><span class="nf">last</span><span class="si">}</span><span class="se">\n</span><span class="s2">end"</span><span class="p">}</span> </code></pre></div></div> <h3 id="method-alias-chain-されているメソッド">method alias chain されているメソッド</h3> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Non-overloading method definition of `parse` in `::WEBrick::HTTPRequest` cannot be duplicated(RBS::DuplicatedMethodDefinition) </code></pre></div></div> <p>は <code class="language-plaintext highlighter-rouge">https.rbs</code> に重複定義があったので、 <code class="language-plaintext highlighter-rouge">| ...</code> を追加して、</p> <pre><code class="language-rbs"> alias orig_parse parse def parse: (?(TCPSocket | OpenSSL::SSL::SSLSocket)? socket) -> void | ... </code></pre> <p>のように定義して回避しました。</p> <p><code class="language-plaintext highlighter-rouge">orig_parse</code> に再定義前の型が保存されているわけではなく、 再定義した <code class="language-plaintext highlighter-rouge">parse</code> と同じ型になってしまうようなので、 厳密には <code class="language-plaintext highlighter-rouge">alias</code> ではなく <code class="language-plaintext highlighter-rouge">def orig_parse: 元の型</code> にした方が良さそうでしたが、 直接使うメソッドではないと思って、そこはがんばらずに自動生成されたままにしました。</p> <h3 id="いろいろな-body">いろいろな body</h3> <p><code class="language-plaintext highlighter-rouge">webrick/httpresponse</code> は <code class="language-plaintext highlighter-rouge">body</code> に悩んで、コメントの</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1"># Body may be:</span> <span class="c1"># * a String;</span> <span class="c1"># * an IO-like object that responds to +#read+ and +#readpartial+;</span> <span class="c1"># * a Proc-like object that responds to +#call+.</span> </code></pre></div></div> <p>を参考にして、</p> <pre><code class="language-rbs"> interface _CallableBody def call: (_Writer) -> void end attr_accessor body: String | _ReaderPartial | _CallableBody </code></pre> <p>にしました。</p> <p><code class="language-plaintext highlighter-rouge">#read</code> は呼ばれていなかったので、 <code class="language-plaintext highlighter-rouge">_Reader & _ReaderPartial</code> ではなく <code class="language-plaintext highlighter-rouge">_ReaderPartial</code> だけにしました。</p> <p>書き込みは <code class="language-plaintext highlighter-rouge">write</code> のみだったので、 <code class="language-plaintext highlighter-rouge">socket</code> の型は <code class="language-plaintext highlighter-rouge">_Writer</code> にしました。</p> <h3 id="-つきメソッド"><code class="language-plaintext highlighter-rouge">=</code> つきメソッド</h3> <p><code class="language-plaintext highlighter-rouge">=</code> つきのメソッドの返り値でちょっと悩んでしまいましたが、他の RBS ファイルを確認すると右辺の値の型をそのまま書くようだったので、そうしておきました。</p> <h3 id="singleton">singleton</h3> <p><code class="language-plaintext highlighter-rouge">set_redirect</code> は <code class="language-plaintext highlighter-rouge">singleton</code> を使って Redirect 系の例外クラスならどれでも受け付けるように</p> <pre><code class="language-rbs"> def set_redirect: (singleton(WEBrick::HTTPStatus::Redirect) status, URI::Generic | String url) -> bot </code></pre> <p>にしました。</p> <h3 id="read_body">read_body</h3> <p><code class="language-plaintext highlighter-rouge">IO?</code> 型が渡せなくなるらしいという話があったので、 <code class="language-plaintext highlighter-rouge">IO socket</code> を <code class="language-plaintext highlighter-rouge">IO? socket</code> に変更した方がいいかもしれないと思ったのですが、 返り値も <code class="language-plaintext highlighter-rouge">String?</code> になってしまうので、とりあえずそのままにしました。 実用上問題があれば、ユースケースと一緒に pull request を作ってほしいです。</p> <p>最初は <code class="language-plaintext highlighter-rouge">void block</code> にしていたのですが <code class="language-plaintext highlighter-rouge">void</code> は返り値以外で書ける位置が制限されているらしいので <code class="language-plaintext highlighter-rouge">top</code> に変更しました。</p> <pre><code class="language-rbs"> def read_body: (IO socket, body_chunk_block block) -> String | (nil socket, top block) -> nil </code></pre> <h3 id="servlet-の-config-や-options-の問題">Servlet の config や options の問題</h3> <p><code class="language-plaintext highlighter-rouge">AbstractServlet</code> は <code class="language-plaintext highlighter-rouge">@config</code> が <code class="language-plaintext highlighter-rouge">HTTPServer</code> で <code class="language-plaintext highlighter-rouge">FileHandler</code> は <code class="language-plaintext highlighter-rouge">Hash[Symbol, untyped]</code> で困ったので、</p> <pre><code class="language-rbs"> class AbstractServlet @server: HTTPServer interface _Config def []: (Symbol) -> untyped end @config: _Config </code></pre> <p>にしました。</p> <p><code class="language-plaintext highlighter-rouge">@options</code> も <code class="language-plaintext highlighter-rouge">AbstractServlet</code> は <code class="language-plaintext highlighter-rouge">Array[untyped]</code> で <code class="language-plaintext highlighter-rouge">FileHandler</code> は <code class="language-plaintext highlighter-rouge">Hash[Symbol, untyped]</code> なので、 <code class="language-plaintext highlighter-rouge">AbstractServlet</code> の方は <code class="language-plaintext highlighter-rouge">untyped</code> にしました。</p> <h3 id="返り値の型">返り値の型</h3> <p><code class="language-plaintext highlighter-rouge">do_GET</code> などが <code class="language-plaintext highlighter-rouge">AbstractServlet</code> は <code class="language-plaintext highlighter-rouge">-> bot</code> で <code class="language-plaintext highlighter-rouge">FileHandler</code> で <code class="language-plaintext highlighter-rouge">-> void</code> にしたのは型エラーにはならなかったので、継承で返り値がこのように変わるのは大丈夫のようです。</p> <h3 id="enumerable-の型">Enumerable の型</h3> <p><code class="language-plaintext highlighter-rouge">rbs prototype rb</code> で自動生成されただけの <code class="language-plaintext highlighter-rouge">cgi.rbs</code> に型エラーがあると思って確認すると、こんな感じで <code class="language-plaintext highlighter-rouge">include Enumerable[untyped]</code> になっていないからでした。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% cat a.rb class C include Enumerable def each yield nil end end % rbs prototype rb a.rb class C include Enumerable </span><span class="gp"> def each: () { (untyped) -></span><span class="w"> </span>untyped <span class="o">}</span> -> untyped <span class="go">end </span></code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">rbs prototype rb</code> は対応していなくて、 <code class="language-plaintext highlighter-rouge">typeprof</code> なら対応してそうでした。 <code class="language-plaintext highlighter-rouge">rbs prototype</code> には <code class="language-plaintext highlighter-rouge">rbs prototype runtime</code> もあるらしく、そちらなら対応しているらしいです。</p> <h3 id="位置引数でも-block-でも受けとる場合の型">位置引数でも &block でも受けとる場合の型</h3> <p><code class="language-plaintext highlighter-rouge">webrick/httpserver.rbs</code> で</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">def</span> <span class="nf">mount_proc</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="nb">proc</span><span class="o">=</span><span class="kp">nil</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span> <span class="nb">proc</span> <span class="o">||=</span> <span class="n">block</span> <span class="k">raise</span> <span class="no">HTTPServerError</span><span class="p">,</span> <span class="s2">"must pass a proc or block"</span> <span class="k">unless</span> <span class="nb">proc</span> <span class="n">mount</span><span class="p">(</span><span class="n">dir</span><span class="p">,</span> <span class="no">HTTPServlet</span><span class="o">::</span><span class="no">ProcHandler</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="nb">proc</span><span class="p">))</span> <span class="k">end</span> </code></pre></div></div> <p>に対応する型として、</p> <pre><code class="language-rbs"> def mount_proc: (String dir, ?HTTPServlet::ProcHandler::_Callable proc) -> void | (String dir, ?nil proc) { (HTTPRequest, HTTPResponse) -> void } -> void </code></pre> <p>としてみました。</p> <h2 id="型付け後からリリースまでにひっかかった部分">型付け後からリリースまでにひっかかった部分</h2> <p>確認のしやすさの都合で、 <code class="language-plaintext highlighter-rouge">bitclust</code> の型付け作業をしている作業ディレクトリで webrick の rbs も作成していました。</p> <p>そこから <code class="language-plaintext highlighter-rouge">ruby/webrick</code> にコピーして pull request を作成しました。</p> <p>その前に手元で <code class="language-plaintext highlighter-rouge">rake build</code> して <code class="language-plaintext highlighter-rouge">rake install:local</code> して動作確認していました。</p> <p>その結果、 <code class="language-plaintext highlighter-rouge">manifest.yaml</code> は <code class="language-plaintext highlighter-rouge">gemspec</code> と同じトップではなく <code class="language-plaintext highlighter-rouge">sig/manifest.yaml</code> に置く必要があるとわかりました。</p> <p>リリースされる gem に rbs ファイルを含めるには <code class="language-plaintext highlighter-rouge">webrick.gemspec</code> で <code class="language-plaintext highlighter-rouge">sig/**/*.rbs</code> などを <code class="language-plaintext highlighter-rouge">s.files</code> に追加する必要がありました。</p> <p><code class="language-plaintext highlighter-rouge">stringio</code> を <code class="language-plaintext highlighter-rouge">manifest.yaml</code> に入れるとエラーになって困ったので調べてみると、 <a href="https://github.com/ruby/rbs/tree/master/core">https://github.com/ruby/rbs/tree/master/core</a> にあったので、 <code class="language-plaintext highlighter-rouge">core</code> のライブラリは不要なのかも、と思って書かなかったらエラーが消えました。 <code class="language-plaintext highlighter-rouge">require</code> が必要なのに <code class="language-plaintext highlighter-rouge">stringio</code> が <code class="language-plaintext highlighter-rouge">core</code> に入っているのは分類ミスのようなので、 そのうち移動するかもしれないようです。</p> <p>直接のリリース権限はないので、他の標準添付から分離されたり、されつつある gem と同じように <a href="https://github.com/ruby/webrick/blob/master/.github/workflows/push_gem.yml">https://github.com/ruby/webrick/blob/master/.github/workflows/push_gem.yml</a> が追加されて、タグの push でリリースができました。</p> <p>最後の <code class="language-plaintext highlighter-rouge">Create GitHub release</code> の <code class="language-plaintext highlighter-rouge">GITHUB_TOKEN</code> の secrets の設定ミスがあったらしく、 タグを消して push しなおしたら、gem のリリースの方でリリース済みバージョンということでそこまで進まなかったので、 今回だけ <code class="language-plaintext highlighter-rouge">gh release create v1.9.0 --verify-tag --generate-notes</code> は手元で実行しました。</p> <h2 id="そもそもの経緯">そもそもの経緯</h2> <p>RBS による型付け作業を開始した <a href="https://github.com/rurema/bitclust">bitclust</a> が webrick にも依存していて、 webrick の使っている部分だけ型付けをしてエラーがでないようにしていました。</p> <p>しばらくして bitclust の型付けである程度ノウハウがたまったので、 webrick の型がちゃんとついている方が良さそうと思ったので、 bitclust 自体の作業を中断して、 webrick の方を一気に対応しました。</p> <h2 id="最後に">最後に</h2> <p>webrick は production 環境では使うべきでない、というものなので、一般公開用のサーバーとして使うことはないと思いますが、 限定された環境でのサーバーやソースコードを読む対象としてはまだまだ使われることがあると思うので、 機会があれば webrick の rbs を有効利用してみてください。</p> Tue, 05 Nov 2024 03:00:00 +0000 https://blog.n-z.jp/blog/2024-11-05-webrick-with-rbs.html https://blog.n-z.jp/blog/2024-11-05-webrick-with-rbs.html ruby rbs blog
- Kaigi on Rails 2024に現地参加した <p>年末に思い出しながら、 <a href="https://kaigionrails.org/2024/">Kaigi on Rails 2024</a> の参加記録を書いておきます。</p> <p>特に何もなければ今まで通りオンラインで視聴すればいいかと思っていたのですが、 出張として行けることになったので、初めて現地参加しました。</p> <!--more--> <h2 id="会場">会場</h2> <p>有明ということで、 最近の<a href="https://ll.jus.or.jp/">LLイベント</a>の会場の近くだったので、 あまり迷わずに行けました。</p> <p>宿は、近くのカプセルホテルが一番安かったのですが、以前にとまったときにシャワーのみで辛かったので、 他のところにしました。</p> <h2 id="発表">発表</h2> <p>2トラックで聞きたい話が重なっていることも多くて迷ったのですが、 事前の勉強会で発表一覧を予習していたのも参考にしつつ決めてみていました。</p> <p>きいた中では <a href="https://kaigionrails.org/2024/talks/izumitomo/">デプロイを任されたので、教わった通りにデプロイしたら障害になった件 〜俺のやらかしを越えてゆけ〜</a> が特に良かったです。 怒られの話だとつらそうだからきかなかったという人もいたようですが、そういうことはなく、そういう点では安心してきける話でした。</p> <p><a href="https://kaigionrails.org/2024/talks/kozy4324/">ActiveRecord SQLインジェクションクイズ (Rails 7.1.3.4)</a> はバージョンによるのでは? と思う問題もありましたが、 たぶん新しい方の話だろうと思って答えていたら全問正解できました。 後で気付きましたが、タイトルに対象バージョンが書いてありました。</p> <p>他にもいい話や参考になりそうな話が多かったです。</p> <h2 id="ブース">ブース</h2> <p>休憩時間や発表をみるのを諦めていた時間にまわりました。</p> <p>大阪万博のチケット1枚が当たったのが一番の収穫でした。</p> <p>他にもいろいろ楽しめました。</p> <h2 id="感想">感想</h2> <p>セッションの内容だけなら、オンライン視聴や後日公開された録画を見るだけでも良いのですが、 現地参加ならではの集中してきける環境や、セッション以外の交流も楽しめました。</p> <p>会場は東京まで行くことを考えるとどこでも大差ないかと思っていましたが、 近くの宿のとりやすさを考えると、ある程度差があったと思いました。</p> Sat, 26 Oct 2024 14:59:00 +0000 https://blog.n-z.jp/blog/2024-10-26-kaigionrails2024.html https://blog.n-z.jp/blog/2024-10-26-kaigionrails2024.html event ruby rails blog
- 松江Ruby会議11に参加して発表してきた <p>年末に思い出しながら、 <a href="https://matsue.rubyist.net/matrk11/">松江Ruby会議11</a> の参加記録を書いておきます。</p> <!--more--> <h2 id="きっかけ">きっかけ</h2> <p>同僚の人が CFP に応募してみたらどうかという感じの一覧にあったので応募してみたところ、 通ったので発表しに行くことになりました。</p> <h2 id="会場">会場</h2> <p>松江駅前だったので、迷わずに行けました。</p> <p>松江は久しぶりで、前回行ったのがいつだったのかはっきりしないのですが、 手元の記録に残っているのが 2007-10-27 で、 その日の松江のイベントを調べてみると、 <a href="https://japan.cnet.com/release/10296558/">Ruby技術者認定試験(第1回)</a>の記念受験のときだったようです。</p> <h2 id="感想">感想</h2> <p>人手が足りないので手伝ってほしいという話が他にもあって、 どこも人は足りていないんだなあという感じでした。</p> <h2 id="自分の発表">自分の発表</h2> <p><a href="https://github.com/rurema">rurema</a> の Markdown 対応の進捗報告や手伝ってほしいところなどの話をしました。</p> <p>スライドはいつも通り <a href="https://slide.rabbit-shocker.org/authors/znz/rurema-2024-10/">Rabbit Slide Show</a> (<a href="https://rubygems.org/gems/rabbit-slide-znz-rurema-2024-10">RubyGems</a>), <a href="http://www.slideshare.net/znzjp/ruby-ruby-11-2024-10-05">SlideShare</a>, <a href="https://speakerdeck.com/znz/rubynori-ben-yu-rihuarensumaniyuarunoxian-zai-towei-lai">Speaker Deck</a> にあげています。(ソースは <a href="https://github.com/znz/rurema-2024-10">github</a> にあげています。)</p> <amp-iframe src="https://slide.rabbit-shocker.org/authors/znz/rurema-2024-10/viewer.html" width="640" height="524" sandbox="allow-scripts allow-same-origin" scrolling="no" allowfullscreen=""> </amp-iframe> <div> <p><a href="https://slide.rabbit-shocker.org/authors/znz/rurema-2024-10/" title="Rubyの日本語リファレンスマニュアルの現在と未来">Rubyの日本語リファレンスマニュアルの現在と未来</a></p> </div> <h2 id="会議後">会議後</h2> <p>何人かで松江城まで歩いていって、イベントをやっているのをみに行きました。</p> <p>翌日もまた松江城に行って、お城の中まで入るなどの観光をしてから帰りました。</p> Sat, 05 Oct 2024 14:59:00 +0000 https://blog.n-z.jp/blog/2024-10-05-matrk11.html https://blog.n-z.jp/blog/2024-10-05-matrk11.html event ruby blog
- docker-webtop を使って VPS 上でブラウザーを動かす <p><a href="https://github.com/linuxserver/docker-webtop">linuxserver/docker-webtop</a> を使って VPS 上でブラウザーを開きっぱなしにする環境ができたので、そのメモです。</p> <!--more--> <h2 id="やりたかったこと">やりたかったこと</h2> <p>VPS 上でブラウザーを動かして、Cookie Clicker のように開きっぱなしにして放置しておくと良いものに使いたい、と思っていました。</p> <h2 id="失敗案">失敗案</h2> <p><code class="language-plaintext highlighter-rouge">libvirt</code> で VNC を有効にして WireGuard 経由で接続するという案もあって、 環境構築はうまくいったのですが、 ConoHa VPS の環境で KVM が有効にできなくて遅すぎて実用になりませんでした。</p> <p>方法としては <code class="language-plaintext highlighter-rouge">virsh edit</code> で <code class="language-plaintext highlighter-rouge"><video></code> の上に以下のように追記するか、 <code class="language-plaintext highlighter-rouge">virt-xml $name --add-device --graphics "vnc,port=5900,listen=$(ip -br addr show dev wg0 | awk '{sub("/.*","");print $3}'),keymap=ja,passwd=1234"</code> のように <code class="language-plaintext highlighter-rouge">virt-xml</code> で追加すればうまくいきました。</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><graphics</span> <span class="na">type=</span><span class="s">'vnc'</span> <span class="na">port=</span><span class="s">'5900'</span> <span class="na">autoport=</span><span class="s">'no'</span> <span class="na">listen=</span><span class="s">'2001:db8:a987:6543::c0'</span> <span class="na">keymap=</span><span class="s">'ja'</span> <span class="na">passwd=</span><span class="s">'1234'</span><span class="nt">></span> <span class="nt"><listen</span> <span class="na">type=</span><span class="s">'address'</span> <span class="na">address=</span><span class="s">'2001:db8:a987:6543::c0'</span><span class="nt">/></span> <span class="nt"></graphics></span> </code></pre></div></div> <p>変更しなくても動くかもしれませんが、なんとなく遅そうな気がしたので、 <code class="language-plaintext highlighter-rouge"><model type='cirrus' vram='16384' heads='1' primary='yes'/></code> は適当に <code class="language-plaintext highlighter-rouge"><model type='virtio' vram='65536' heads='1' primary='yes'/></code> に変更しました。 <code class="language-plaintext highlighter-rouge">qxl</code> は指定する属性が違っているようで単純な置き換えは難しそうだったので、 <code class="language-plaintext highlighter-rouge">virtio</code> を試しました。</p> <p>前述のように KVM が使えなくて遅すぎたので、この変更の影響がどのくらいあったのかはわかりませんでした。</p> <h2 id="webtop-とは">webtop とは?</h2> <p><a href="https://zenn.dev/mkj/articles/292a70b4f4e5e8">GUIと日本語環境が使えるお手軽Docker環境の使い方</a> で GUI 環境を Docker で簡単に使う方法として紹介されていました。</p> <p><a href="https://github.com/linuxserver/docker-webtop">linuxserver/docker-webtop: Ubuntu, Alpine, Arch, and Fedora based Webtop images, Linux in a web browser supporting popular desktop environments.</a> を見るとわかるように、いくつかのディストリビューションとデスクトップ環境が用意されています。</p> <p>その中で KasmVNC のサーバーも一緒に動いていて、それをブラウザーで開いてリモートから使える、という仕組みになっているようです。</p> <p>検索するときに webtop だけだと他のものもひっかかるので、「docker webtop」や「linuxserver webtop」で検索すると良さそうです。</p> <h2 id="最終的な-composeyaml-の例">最終的な compose.yaml の例</h2> <p>最終的にはこのような <code class="language-plaintext highlighter-rouge">compose.yaml</code> で動かしています。 初回起動の処理は時間がかかるのと、失敗することがあるようなので、接続してみてうまく初期設定されていないようなら、 <code class="language-plaintext highlighter-rouge">./data</code> も含めて消して作りなおすのが良さそうです。</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span> <span class="na">services</span><span class="pi">:</span> <span class="na">webtop</span><span class="pi">:</span> <span class="na">image</span><span class="pi">:</span> <span class="s">lscr.io/linuxserver/webtop:ubuntu-kde</span> <span class="na">container_name</span><span class="pi">:</span> <span class="s">webtop</span> <span class="na">security_opt</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">seccomp:unconfined</span> <span class="c1">#optional</span> <span class="na">environment</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">PUID=1000</span> <span class="pi">-</span> <span class="s">PGID=1000</span> <span class="pi">-</span> <span class="s">TZ=Asia/Tokyo</span> <span class="pi">-</span> <span class="s">SUBFOLDER=/</span> <span class="c1">#optional</span> <span class="pi">-</span> <span class="s">TITLE=Webtop</span> <span class="c1">#optional</span> <span class="pi">-</span> <span class="s">DOCKER_MODS=linuxserver/mods:universal-package-install</span> <span class="pi">-</span> <span class="s">INSTALL_PACKAGES=etckeeper|fonts-takao|bash-completion</span> <span class="pi">-</span> <span class="s">LC_ALL=ja_JP.UTF-8</span> <span class="na">volumes</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">./data:/config</span> <span class="pi">-</span> <span class="s">/var/run/docker.sock:/var/run/docker.sock</span> <span class="c1">#optional</span> <span class="na">ports</span><span class="pi">:</span> <span class="pi">-</span> <span class="s2">"</span><span class="s">[2001:db8:a987:6543::c0]:3000:3000"</span> <span class="c1"># - 3001:3001</span> <span class="c1"># devices:</span> <span class="c1"># - /dev/dri:/dev/dri #optional</span> <span class="na">shm_size</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1gb"</span> <span class="c1">#optional</span> <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span> </code></pre></div></div> <h3 id="日本語化">日本語化</h3> <p>参考にしたサイトでは <a href="https://github.com/karaage0703/docker-webtop/blob/c05f865d3ec17272efd68261b636ccd90048b6d8/container-ubuntu/Dockerfile">Dockerfile</a> で <code class="language-plaintext highlighter-rouge">apt-get</code> を使ってインストールしていましたが、 <code class="language-plaintext highlighter-rouge">DOCKER_MODS=linuxserver/mods:universal-package-install</code> という公式の方法でインストールできました。 <code class="language-plaintext highlighter-rouge">INSTALL_PACKAGES</code> の指定は <code class="language-plaintext highlighter-rouge">|</code> 区切りで複数パッケージのインストールができました。</p> <p>公式サイトの例にある latest は alpine なので <code class="language-plaintext highlighter-rouge">font-noto-cjk</code> ですが、 Debian や Ubuntu だと <code class="language-plaintext highlighter-rouge">fonts-noto-cjk</code> と <code class="language-plaintext highlighter-rouge">fonts</code> の <code class="language-plaintext highlighter-rouge">s</code> がつくので、 <code class="language-plaintext highlighter-rouge">image:</code> だけ変えても ubuntu だとフォントがインストールされなくて、 しばらく悩んでいました。</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span> <span class="na">services</span><span class="pi">:</span> <span class="na">webtop</span><span class="pi">:</span> <span class="na">image</span><span class="pi">:</span> <span class="s">lscr.io/linuxserver/webtop:latest</span> <span class="na">container_name</span><span class="pi">:</span> <span class="s">webtop</span> <span class="na">security_opt</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">seccomp:unconfined</span> <span class="c1">#optional</span> <span class="na">environment</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">PUID=1000</span> <span class="pi">-</span> <span class="s">PGID=1000</span> <span class="pi">-</span> <span class="s">TZ=Asia/Tokyo</span> <span class="pi">-</span> <span class="s">SUBFOLDER=/</span> <span class="c1">#optional</span> <span class="pi">-</span> <span class="s">TITLE=Webtop</span> <span class="c1">#optional</span> <span class="pi">-</span> <span class="s">DOCKER_MODS=linuxserver/mods:universal-package-install</span> <span class="pi">-</span> <span class="s">INSTALL_PACKAGES=font-noto-cjk|font-ipa</span> <span class="pi">-</span> <span class="s">LC_ALL=ja_JP.UTF-8</span> <span class="na">volumes</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">./data:/config</span> <span class="pi">-</span> <span class="s">/var/run/docker.sock:/var/run/docker.sock</span> <span class="c1">#optional</span> <span class="na">ports</span><span class="pi">:</span> <span class="pi">-</span> <span class="s2">"</span><span class="s">[2001:db8:a987:6543::c0]:3000:3000"</span> <span class="c1"># - 3001:3001</span> <span class="c1"># devices:</span> <span class="c1"># - /dev/dri:/dev/dri #optional</span> <span class="na">shm_size</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1gb"</span> <span class="c1">#optional</span> <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span> </code></pre></div></div> <h3 id="volumes">volumes</h3> <p>volumes で共有している <code class="language-plaintext highlighter-rouge">./data</code> にホームディレクトリの内容があるので、適当にホスト側との共有に使えそうです。</p> <h3 id="ports">ports</h3> <p>例のまま <code class="language-plaintext highlighter-rouge">3000:3000</code> だけで起動してしまうと VPS の外からも丸見えになってしまったので、 WireGuard で使っている IPv6 アドレスに制限しています。</p> <p>ブラウザーで開くのは <code class="language-plaintext highlighter-rouge">open -na "Google Chrome" --args --user-data-dir="$HOME/tmp/chrome-user-data" --window-size=1280,1024 'http://[2001:db8:a987:6543::c0]:3000'</code> のように普段のブラウザーとは分離しています。</p> <p>再接続でリサイズすると落ちることがあったので、念のためにサイズも固定しています。</p> <p><code class="language-plaintext highlighter-rouge">3001</code> の <code class="language-plaintext highlighter-rouge">https</code> 接続の方はまだ試していません。</p> <h2 id="kasmvnc-での操作">KasmVNC での操作</h2> <p>左にメニューが隠れていて、それを開いてクリップボードの操作などができました。</p> <p>メニューを開いているときに上にスピーカーの共有などのスイッチもあったので、 それをオンにしてリモートで YouTube などで音声を再生すると、ちゃんと聞こえました。</p> <p>ブラウザーを閉じる前にメニューから切断をしておくと、今のところ再接続で落ちていません。</p> <h2 id="仕組みをちょっと深堀り">仕組みをちょっと深堀り</h2> <p>複数ディストリビューション共通で systemd ではなく <a href="https://www.skarnet.org/software/s6/index.html">s6</a> を使っているようでした。</p> <p>例にあった <code class="language-plaintext highlighter-rouge">DOCKER_MODS</code> の指定は <a href="https://github.com/linuxserver/docker-mods">linuxserver/docker-mods</a> が関係しているようで、 <code class="language-plaintext highlighter-rouge">linuxserver/mods:universal-package-install</code> は <a href="https://github.com/linuxserver/docker-mods/tree/universal-package-install">universal-package-install ブランチ</a> にありました。</p> <p><code class="language-plaintext highlighter-rouge">INSTALL_PIP_PACKAGES</code> にも対応しているようなので、 それでインストールできる環境の構築は <code class="language-plaintext highlighter-rouge">compose.yaml</code> だけで完結できそうです。</p> <p>他の <code class="language-plaintext highlighter-rouge">DOCKER_MODS</code> も <a href="https://github.com/linuxserver/docker-mods">linuxserver/docker-mods</a> などから探すと便利そうです。</p> <p><code class="language-plaintext highlighter-rouge">DOCKER_MODS</code> の複数指定は <code class="language-plaintext highlighter-rouge">INSTALL_PACKAGES</code> と同じように <code class="language-plaintext highlighter-rouge">|</code> 区切りのようです。</p> <h2 id="まとめ">まとめ</h2> <p><code class="language-plaintext highlighter-rouge">docker-webtop</code> でリモートでのブラウザーの開きっぱなしが実現できました。</p> <p><a href="https://www.linuxserver.io/blog/webtop-2-0-the-year-of-the-linux-desktop">Webtop 2.0 - The year of the Linux desktop</a> によると <a href="https://github.com/linuxserver/docker-vscodium">docker-vscodium</a> などの他のアプリ用のイメージも用意されているようなので、 用途にあうものがあれば簡単に使えそうです。</p> Wed, 18 Sep 2024 09:00:00 +0000 https://blog.n-z.jp/blog/2024-09-18-docker-webtop.html https://blog.n-z.jp/blog/2024-09-18-docker-webtop.html linux webtop blog
- resticバックアップの設定を調整した <p><a href="/blog/2024-08-08-restic-backup.html">前回の記事</a>では exclude 指定が途中までだったので、 その見直しなどをしてバックアップを続けているので、その差分などのメモです。</p> <!--more--> <h2 id="確認バージョン">確認バージョン</h2> <ul> <li>rclone v1.67.0</li> <li>restic 0.17.0</li> </ul> <h2 id="前提条件">前提条件</h2> <ul> <li>バックアップ用の <code class="language-plaintext highlighter-rouge">backup-operator</code> というユーザーを既に作成済みです。</li> <li><a href="https://infini-cloud.net/ja/index.html">InfiniCLOUD</a> で「外部アプリ接続」を許可して WebDAV の接続情報を取得済みで、 バックアップ容量を確認しやすくしたり、不要になったときに削除しやすくしたりするために、データセット名 <code class="language-plaintext highlighter-rouge">/files/restic/ホスト名</code> でデータセットを作成しています。 (この記事を参考にして登録するなら紹介コード <code class="language-plaintext highlighter-rouge">FRBVA</code> を使うと 5GB 貰えるそうです)</li> </ul> <h2 id="systemd-unit-の変更">systemd unit の変更</h2> <p>unit はこのように変更しました。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit] Description=Restic Backup [Service] Type=oneshot User=backup-operator Group=backup-operator AmbientCapabilities=CAP_DAC_READ_SEARCH Environment=RESTIC_PASSWORD_FILE=/home/backup-operator/.config/rclone/InfiniCLOUD.restic.password RESTIC_REPOSITORY=rclone:InfiniCLOUD-restic: ExecStart=/usr/local/bin/restic unlock ExecStart=/usr/local/bin/restic backup --exclude-caches --one-file-system --tag scheduled,boot /boot ExecStart=/usr/local/bin/restic backup --exclude-caches --one-file-system --exclude-file=/home/backup-operator/.config/rclone/restic.excludes.txt --tag scheduled,root / ExecStart=/usr/local/bin/restic check --with-cache --read-data-subset=5G ExecStart=/usr/local/bin/restic forget --prune --keep-hourly 24 --keep-daily 30 --keep-monthly 6 --keep-weekly 4 --keep-yearly 3 Nice=5 IOSchedulingClass=best-effort IOSchedulingPriority=5 </code></pre></div></div> <p>変更点としては、以下のような感じです。</p> <ul> <li>mount したときの latest は boot よりも root になっていてほしいので、順番を入れ替えた。</li> <li><code class="language-plaintext highlighter-rouge">--tag</code> に <code class="language-plaintext highlighter-rouge">boot</code> と <code class="language-plaintext highlighter-rouge">root</code> のパーティション名も足した。</li> <li><code class="language-plaintext highlighter-rouge">--exclude-file</code> で除外設定ファイルを指定した。</li> </ul> <h2 id="除外設定ファイル">除外設定ファイル</h2> <p><code class="language-plaintext highlighter-rouge">/home/backup-operator/.config/rclone/restic.excludes.txt</code> には以下の内容を設定しました。 サーバーによっては存在しないファイルもありますが、複数サーバーで共通にしています。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/aquota.group /aquota.user /dev/** /lost+found/** /swapfile /tmp/** /var/lib/docker/** /var/tmp/** </code></pre></div></div> <p>それぞれ以下のような理由で設定しています。</p> <ul> <li>ユーザーごとの使用量をチェックしやすくするために入れている quota のファイルを無視</li> <li><code class="language-plaintext highlighter-rouge">/dev/</code> は <code class="language-plaintext highlighter-rouge">--one-file-system</code> で除外されるはずですが、 <code class="language-plaintext highlighter-rouge">/run</code> などとの動作の比較のため明示的に除外</li> <li><code class="language-plaintext highlighter-rouge">/lost+found/</code> はバックアップしても問題はないのですが、不要なことが多いので除外</li> <li><code class="language-plaintext highlighter-rouge">/swapfile</code> はスワップパーティションではなくスワップファイルで設定しているサーバーがあったため、無駄にバックアップ容量を使ってしまうので除外</li> <li><code class="language-plaintext highlighter-rouge">/tmp</code> や <code class="language-plaintext highlighter-rouge">/var/tmp</code> は不要なファイルのみ存在するべきなので除外</li> <li><code class="language-plaintext highlighter-rouge">/var/lib/docker</code> は docker の中は別の方法でバックアップすべきなので除外</li> </ul> <p><code class="language-plaintext highlighter-rouge">/var/cache</code> も除外しても良いのですが、まだ除外せずに様子をみています。</p> <h2 id="restic-mount">restic mount</h2> <p>マウントするディレクトリを用意してマウントします。 <code class="language-plaintext highlighter-rouge">restic mount</code> は <code class="language-plaintext highlighter-rouge">Ctrl-c</code> で止めるまで他の操作ができなくなるので、別端末で中身を確認します。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% sudo -u backup-operator mkdir /tmp/restic % restic.sh mount /tmp/restic </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">fusermount: exec: "fusermount": executable file not found in $PATH</code> で <code class="language-plaintext highlighter-rouge">mount</code> に失敗するときは <code class="language-plaintext highlighter-rouge">fuse</code> パッケージを入れる必要がありました。</p> <p>別端末で <code class="language-plaintext highlighter-rouge">ls</code> や別シェルを開いて確認します。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% sudo -u backup-operator ls -al /tmp/restic/snapshots/latest/ % sudo -u backup-operator /bin/bash </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">systemd-run --pipe</code> 経由だと <code class="language-plaintext highlighter-rouge">Ctrl-c</code> でちゃんと止まらなかったので、 <code class="language-plaintext highlighter-rouge">sudo -u backup-operator fusermount -u /tmp/restic</code> で止めました。</p> <p>最初は気付かずに二度目以降のマウントがうまくいかないと勘違いして、 <code class="language-plaintext highlighter-rouge">sudo RESTIC_PASSWORD_FILE=/home/backup-operator/.config/rclone/InfiniCLOUD.restic.password RESTIC_REPOSITORY=rclone:InfiniCLOUD-restic: XDG_CONFIG_HOME=/home/backup-operator/.config restic mount /tmp/restic</code> のように root 権限でマウントし直してしまっていましたが、 ちゃんと止まっていなかっただけでした。</p> <h2 id="macos-のバックアップ">macOS のバックアップ</h2> <p>macOS は以下のような設定をして <code class="language-plaintext highlighter-rouge">while sleep 3600; do date; ~/.config/rclone/restic-backup.sh; date; done</code> を開きっぱなしの端末の1タブで回しています。 出かけるときなどには簡単に止められるように自動実行にはしていません。</p> <p><code class="language-plaintext highlighter-rouge">Library</code> の中身がバックアップできていないので、そちらは今のところ Time Machine バックアップ任せになっています。</p> <p>除外にはバックアップファイルの他に、容量の大きいファイルやディスクイメージファイルを指定していて、 こちらも今後さらに検討が必要そうです。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% cat ~/.config/rclone/restic-backup.sh #!/bin/bash # usage: # ~/.config/rclone/restic-backup.sh # env tag=manual ~/.config/rclone/restic-backup.sh #if [[ -n $(pgrep 'restic' | grep 'restic backup') ]]; then if [ -n "$(pgrep 'restic')" ]; then echo 'restic is already running...' 1>&2 exit 0 fi set -ux : "${tag=scheduled}" ~/.config/rclone/restic.sh unlock ~/.config/rclone/restic.sh backup --exclude-caches --one-file-system --skip-if-unchanged --tag "${tag},Movies" "$HOME/Movies" ~/.config/rclone/restic.sh backup --exclude-caches --one-file-system --skip-if-unchanged --tag "${tag},Downloads" "$HOME/Downloads" ~/.config/rclone/restic.sh backup --exclude-caches --one-file-system --exclude-file="$HOME/.config/rclone/restic.excludes.txt" --tag "${tag},home" "$HOME" ~/.config/rclone/restic.sh check --with-cache --read-data-subset=5G ~/.config/rclone/restic.sh forget --prune --keep-hourly 24 --keep-daily 30 --keep-monthly 6 --keep-weekly 4 --keep-yearly 3 % cat ~/.config/rclone/restic.excludes.txt # macOS /Users/*/.Trash /Users/*/Library/** # [...] node_modules *~ *.o *.lo *.pyc # .gnupg .#* # lima,colima cidata.iso basedisk diffdisk # misc /Users/*/.dropbox/** /Users/*/.npm/** /Users/*/.ollama/models/** /Users/*/.rustup/** # my temp data /Users/*/.anyenv/** /Users/*/s/github.com/ruby/ruby/build/** *.sparsebundle *.qcow2 diff.img # 別途バックアップ /Users/*/Downloads/** /Users/*/Movies/** # 別途バックアップ予定 # Library の中で個別にバックアップ可能なもの </code></pre></div></div> <h2 id="最後に">最後に</h2> <p><code class="language-plaintext highlighter-rouge">restic</code> でのバックアップの現状の差分をまとめてみました。</p> <p>とりあえず継続的にバックアップが取れているので、 VPS の OS のバージョンアップなどの大きな変更もやりやすくなったので、 古いままになっているのを更新していきたいと思っています。</p> Tue, 27 Aug 2024 10:00:00 +0000 https://blog.n-z.jp/blog/2024-08-27-restic-backup-cont.html https://blog.n-z.jp/blog/2024-08-27-restic-backup-cont.html linux restic blog
- 大阪Ruby会議04に参加した <p>年末に思い出しながら、 <a href="https://rubykansai.github.io/osaka04/">大阪Ruby会議04</a> の参加記録を書いておきます。 今回はスタッフとして受付をしていたり、スポンサーLTで発表したりしました。</p> <!--more--> <h2 id="会場">会場</h2> <p>中之島フェスティバルタワー・ウエスト4階 中之島会館というところで、同じフロアの反対側に美術館の入口がありました。 4階までの行きかたはちょっとわかりにくかったですが、案内を探せばすぐに行けました。</p> <p>地下鉄に乗り換えれば会場の近くまで行けるようでしたが、乗り換え待ちや移動などを考えると、梅田から直接歩いた方が早そうだったので、梅田まで電車で出た後は歩いていきました。</p> <h2 id="途中">途中</h2> <p>発表中の受付は他のスタッフの人にまかせて、発表をきいていました。 色々な話があって面白かったです。</p> <h2 id="自分の発表">自分の発表</h2> <p>仕事で関わっていたお客様の Rails アプリで devise-two-factorを 4.x から 5.x に上げた話をしました。 Rails と同時に上げる必要があり、かつデータベースのデータの持ち方の非互換があるので、もし下げる場合にそなえた対応もしていた、という話でした。</p> <p>スライドはいつも通り <a href="https://slide.rabbit-shocker.org/authors/znz/devise-two-factor-from-4-to-5/">Rabbit Slide Show</a> (<a href="https://rubygems.org/gems/rabbit-slide-znz-devise-two-factor-from-4-to-5">RubyGems</a>), <a href="http://www.slideshare.net/znzjp/devise-two-factor-gem-4-x-5-x">SlideShare</a>, <a href="https://speakerdeck.com/znz/devise-two-factorwo4-dot-xkara5-dot-xnishang-getahua">Speaker Deck</a> にあげています。(ソースは <a href="https://github.com/znz/devise-two-factor-from-4-to-5">github</a> にあげています。)</p> <amp-iframe src="https://slide.rabbit-shocker.org/authors/znz/devise-two-factor-from-4-to-5/viewer.html" width="640" height="524" sandbox="allow-scripts allow-same-origin" scrolling="no" allowfullscreen=""> </amp-iframe> <div> <p><a href="https://slide.rabbit-shocker.org/authors/znz/devise-two-factor-from-4-to-5/" title="devise-two-factorを4.xから5.xに上げた話">devise-two-factorを4.xから5.xに上げた話</a></p> </div> <h2 id="昼食">昼食</h2> <p>数名でドーチカまで歩いていって、食事をして帰りました。</p> <h2 id="懇親会">懇親会</h2> <p>発表についての反応がもらえたり、いろんな人と交流ができたりして良かったです。</p> <h2 id="感想">感想</h2> <p>あまりメモを取らずにできるだけ話を聞くのに集中していましたが、キーワードだけでもメモをとっておかないと、後から思い出すのに不便だったので、来年はまた工夫したいと思いました。</p> Sat, 24 Aug 2024 14:59:00 +0000 https://blog.n-z.jp/blog/2024-08-24-osakarubykaigi04.html https://blog.n-z.jp/blog/2024-08-24-osakarubykaigi04.html event ruby blog
- restic+rcloneでInfiniCLOUDにバックアップするようにした <!--more--> <h2 id="確認バージョン">確認バージョン</h2> <ul> <li>Ubuntu 20.04.6 LTS</li> <li>rclone v1.67.0</li> <li>restic 0.17.0</li> </ul> <h2 id="前提条件">前提条件</h2> <ul> <li>バックアップ用の <code class="language-plaintext highlighter-rouge">backup-operator</code> というユーザーを既に作成済みです。</li> <li><a href="https://infini-cloud.net/ja/index.html">InfiniCLOUD</a> で「外部アプリ接続」を許可して WebDAV の接続情報を取得済みで、 バックアップ容量を確認しやすくしたり、不要になったときに削除しやすくしたりするために、データセット名 <code class="language-plaintext highlighter-rouge">/files/restic/ホスト名</code> でデータセットを作成しています。 (この記事を参考にして登録するなら紹介コード <code class="language-plaintext highlighter-rouge">FRBVA</code> を使うと 5GB 貰えるそうです)</li> </ul> <h2 id="インストール">インストール</h2> <p>apt や snap でインストールできるバージョンは古いので、最新版をダウンロードしてインストールしました。</p> <p>rclone は <a href="https://rclone.org/downloads/">https://rclone.org/downloads/</a> から「Intel/AMD - 64 Bit」の .deb をダウンロードしてインストールしました。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% wget -N https://downloads.rclone.org/v1.67.0/rclone-v1.67.0-linux-amd64.deb % sudo dpkg -i rclone-v1.67.0-linux-amd64.deb </span></code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">restic</code> は <a href="https://restic.readthedocs.io/en/stable/020_installation.html#official-binaries">Official Binaries</a> にリンクがある GitHub Releases からダウンロードしてインストールしました。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% wget -N https://github.com/restic/restic/releases/download/v0.17.0/restic_0.17.0_linux_amd64.bz2 % bunzip2 restic_0.17.0_linux_amd64.bz2 % sudo install restic_0.17.0_linux_amd64 /usr/local/bin/restic </span></code></pre></div></div> <p><a href="https://restic.readthedocs.io/en/latest/080_examples.html#full-backup-without-root">Full backup without root</a> に書いてあるようにバックアップを実行するユーザーにだけ実行できるようにして、 <code class="language-plaintext highlighter-rouge">setcap</code> するという方法もあるのですが、 今回は <a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#AmbientCapabilities="><code class="language-plaintext highlighter-rouge">systemd.exec</code> の <code class="language-plaintext highlighter-rouge">AmbientCapabilities=</code></a> を使いました。</p> <p><code class="language-plaintext highlighter-rouge">setcap</code> を使うなら以下のような感じになります。 <code class="language-plaintext highlighter-rouge">setcap</code> の方が <code class="language-plaintext highlighter-rouge">rclone</code> には権限が渡らなくて安全だと思うのですが、 今回は利便性をとりました。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">% sudo install -o root -g $</span>GID <span class="nt">-m</span> 750 restic_0.17.0_linux_amd64 /usr/local/bin/restic <span class="go">% sudo setcap cap_dac_read_search=+ep /usr/local/bin/restic </span></code></pre></div></div> <h2 id="補完">補完</h2> <p><code class="language-plaintext highlighter-rouge">restic generate</code> で補完設定や man page などを生成できるので、 bash と zsh の補完設定だけ生成しました。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% sudo restic generate --bash-completion /etc/bash_completion.d/restic --zsh-completion /usr/local/share/zsh/site-functions/_restic writing bash completion file to /etc/bash_completion.d/restic writing zsh completion file to /usr/local/share/zsh/site-functions/_restic </span></code></pre></div></div> <h2 id="rclone-の設定">rclone の設定</h2> <p><code class="language-plaintext highlighter-rouge">rclone config</code> で以下のような感じで設定しました。</p> <ul> <li><code class="language-plaintext highlighter-rouge">n) New remote</code></li> <li><code class="language-plaintext highlighter-rouge">name> InfiniCLOUD-restic</code></li> <li><code class="language-plaintext highlighter-rouge">Storage> webdav</code></li> <li><code class="language-plaintext highlighter-rouge">url></code> データセットの WebDAV 接続 URL</li> <li><code class="language-plaintext highlighter-rouge">vendor> other</code></li> <li><code class="language-plaintext highlighter-rouge">user></code> ユーザーID</li> <li><code class="language-plaintext highlighter-rouge">y) Yes type in my own password</code> でアプリパスワード</li> <li><code class="language-plaintext highlighter-rouge">bearer_token></code> は空のまま</li> </ul> <p>今回は他の環境で作成した設定を流用するために <code class="language-plaintext highlighter-rouge">touch</code> でファイル作成して直接編集しました。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% sudo -u backup-operator rclone config touch % sudoedit /home/backup-operator/.config/rclone/rclone.conf </span></code></pre></div></div> <p>動作確認します。 エラーが出なければ URL は認証情報は大丈夫そうです。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% sudo -u backup-operator rclone lsjson InfiniCLOUD-restic: [ ] </span></code></pre></div></div> <p>(ここで <a href="https://rclone.org/webdav/">https://rclone.org/webdav/</a> を参考にして <code class="language-plaintext highlighter-rouge">vendor = owncloud</code> などを試してみても <code class="language-plaintext highlighter-rouge">mtime</code> は保存されなかったので、 InfiniCLOUD は <code class="language-plaintext highlighter-rouge">X-OC-Mtime</code> には対応していないようですが、 <code class="language-plaintext highlighter-rouge">restic</code> で保存してくれるので問題なさそうです。)</p> <h2 id="restic-の設定">restic の設定</h2> <p><code class="language-plaintext highlighter-rouge">restic</code> の自動化に必要なので、 パスワードジェネレーターで生成したパスワードをファイルに保存しておきます。 将来は <a href="https://wiki.archlinux.jp/index.php/Systemd-creds">systemd-creds</a> 管理にしたいですが、 現状は普通のファイルに保存しています。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% sudo touch /home/backup-operator/.config/rclone/InfiniCLOUD.restic.password % sudo chmod 400 /home/backup-operator/.config/rclone/InfiniCLOUD.restic.password % sudo chown backup-operator: /home/backup-operator/.config/rclone/InfiniCLOUD.restic.password % sudo tee /home/backup-operator/.config/rclone/InfiniCLOUD.restic.password </span></code></pre></div></div> <h2 id="restic-init">restic init</h2> <p>数秒の待ちの後、初期化が完了します。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% sudo -u backup-operator RESTIC_PASSWORD_FILE=/home/backup-operator/.config/rclone/InfiniCLOUD.restic.password RESTIC_REPOSITORY=rclone:InfiniCLOUD-restic: restic init created restic repository 994a5f36a5 at rclone:InfiniCLOUD-restic: Please note that knowledge of your password is required to access the repository. Losing your password means that your data is irrecoverably lost. </span></code></pre></div></div> <h2 id="ラッパースクリプトの用意">ラッパースクリプトの用意</h2> <p><code class="language-plaintext highlighter-rouge">systemd</code> の <code class="language-plaintext highlighter-rouge">service</code> と同じ権限での動作確認や各種サブコマンドの実行のため、 <code class="language-plaintext highlighter-rouge">systemd-run</code> を経由して実行するラッパースクリプトを用意しておきます。</p> <p>実行結果を直接見えるようにするのに <code class="language-plaintext highlighter-rouge">--scope</code> だと <code class="language-plaintext highlighter-rouge">Unknown assignment: AmbientCapabilities=CAP_DAC_READ_SEARCH</code> になってうまくいかなかったので、 <code class="language-plaintext highlighter-rouge">--pipe</code> を使っています。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% vi restic.sh % sudo install restic.sh /usr/local/bin/ % rm restic.sh % cat /usr/local/bin/restic.sh </span><span class="gp">#</span><span class="o">!</span>/bin/sh <span class="gp">exec sudo systemd-run --pipe -p AmbientCapabilities=CAP_DAC_READ_SEARCH --uid=backup-operator --gid=backup-operator -E RESTIC_PASSWORD_FILE=/home/backup-operator/.config/rclone/InfiniCLOUD.restic.password -E RESTIC_REPOSITORY=rclone:InfiniCLOUD-restic: restic "$</span>@<span class="s2">" </span></code></pre></div></div> <p>ファイルが少ない <code class="language-plaintext highlighter-rouge">/etc</code> を使って動作確認しました。 ファイルが読み込めないというエラーが出ていないので、 <code class="language-plaintext highlighter-rouge">AmbientCapabilities=CAP_DAC_READ_SEARCH</code> で大丈夫だと確認できました。</p> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% restic.sh backup -n --exclude-caches --one-file-system /etc Running as unit: run-u177.service repository 994a5f36 opened (version 2, compression level auto) created new cache in /home/backup-operator/.cache/restic no parent snapshot found, will read all files [0:00] 0 index files loaded Files: 3509 new, 0 changed, 0 unmodified Dirs: 686 new, 0 changed, 0 unmodified Would add to the repository: 15.144 MiB (10.576 MiB stored) processed 3509 files, 13.836 MiB in 0:00 </span></code></pre></div></div> <h2 id="systemd-の-unit-ファイル作成">systemd の unit ファイル作成</h2> <ul> <li><code class="language-plaintext highlighter-rouge">User=</code>, <code class="language-plaintext highlighter-rouge">Group=</code>, <code class="language-plaintext highlighter-rouge">AmbientCapabilities=</code> は今までの説明の通り、一般ユーザー権限でフルバックアップするための設定です。</li> <li><code class="language-plaintext highlighter-rouge">Environment=</code> の設定は <code class="language-plaintext highlighter-rouge">restic</code> のドキュメントを参考にしてください。</li> <li><code class="language-plaintext highlighter-rouge">ExecStart=</code> のコマンドは <a href="https://wiki.archlinux.jp/index.php/Restic">Restic - ArchWiki</a> のバックアップスクリプトを参考にしました。</li> <li><a href="https://restic.readthedocs.io/en/latest/060_forget.html"><code class="language-plaintext highlighter-rouge">forget</code> の引数</a> は後日調整したいです。</li> <li><code class="language-plaintext highlighter-rouge">--exclude-file</code> は外してしまいましたが、後日調整したいです。</li> <li><code class="language-plaintext highlighter-rouge">--exclude-caches</code> は <a href="https://bford.info/cachedir/">CACHEDIR.TAG</a> をみてくれるようになるオプションです。</li> <li><code class="language-plaintext highlighter-rouge">--one-file-system</code> は <code class="language-plaintext highlighter-rouge">rsync</code> の同名のオプションと同じで <code class="language-plaintext highlighter-rouge">/proc</code> などを無視するためにつけています。</li> <li><code class="language-plaintext highlighter-rouge">Nice=</code> は <code class="language-plaintext highlighter-rouge">nice</code> コマンドを経由したときのデフォルトが 10 なので、その中間の 5 にしました。</li> <li><code class="language-plaintext highlighter-rouge">IOSchedulingPriority=</code> は <code class="language-plaintext highlighter-rouge">IOSchedulingClass=best-effort</code> でのデフォルトが 4 のようなので、それより優先度を 1 だけ低くして 5 にしました。 (<a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#IOSchedulingPriority=">https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#IOSchedulingPriority=</a>)</li> <li>timer はシステム起動から 15 分間待って、後は90分から150分間隔で実行するようにしました。(<a href="https://speakerdeck.com/moriwaka/systemd-intro?slide=87">https://speakerdeck.com/moriwaka/systemd-intro?slide=87</a>)</li> </ul> <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">% sudo EDITOR=vi systemctl edit --force --full restic-backup.service % sudo EDITOR=vi systemctl edit --force --full restic-backup.timer % systemctl cat restic-backup.service </span><span class="gp">#</span><span class="w"> </span>/etc/systemd/system/restic-backup.service <span class="go">[Unit] Description=Restic Backup [Service] Type=oneshot User=backup-operator Group=backup-operator AmbientCapabilities=CAP_DAC_READ_SEARCH Environment=RESTIC_PASSWORD_FILE=/home/backup-operator/.config/rclone/InfiniCLOUD.restic.password RESTIC_REPOSITORY=rclone:InfiniCLOUD-restic: ExecStart=/usr/local/bin/restic unlock ExecStart=/usr/local/bin/restic backup --exclude-caches --one-file-system --tag scheduled / ExecStart=/usr/local/bin/restic backup --exclude-caches --one-file-system --tag scheduled /boot ExecStart=/usr/local/bin/restic check --with-cache --read-data-subset=5G ExecStart=/usr/local/bin/restic forget --prune --keep-hourly 24 --keep-daily 30 --keep-monthly 6 --keep-weekly 4 --keep-yearly 3 Nice=5 IOSchedulingClass=best-effort IOSchedulingPriority=5 % systemctl cat restic-backup.timer </span><span class="gp">#</span><span class="w"> </span>/etc/systemd/system/restic-backup.timer <span class="go">[Unit] Description=Restic Backup [Timer] OnBootSec=15min OnUnitActiveSec=90min Persistent=true RandomizedDelaySec=1h [Install] WantedBy=timers.target % sudo systemctl enable --now restic-backup.timer Created symlink /etc/systemd/system/timers.target.wants/restic-backup.timer → /etc/systemd/system/restic-backup.timer. % systemctl list-timers </span></code></pre></div></div> <h2 id="全体的な感想や今後の予定など">全体的な感想や今後の予定など</h2> <p>以前に試したときは <a href="https://rclone.org/crypt/">rclone の crypt</a> を重ねていましたが、 <code class="language-plaintext highlighter-rouge">restic</code> 自体が (今回の設定例では <code class="language-plaintext highlighter-rouge">RESTIC_PASSWORD_FILE</code> で指定している) パスワードで暗号化しているので、 不要そうでした。</p> <p><code class="language-plaintext highlighter-rouge">restic</code> は <a href="https://restic.readthedocs.io/en/latest/100_references.html#backups-and-deduplication">Deduplication に対応</a>していて、 <a href="https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html#preparing-a-new-repository">Preparing a new repository</a> の表にあるように <code class="language-plaintext highlighter-rouge">restic</code> 0.14.0 以上で対応している Repository version 2 では、圧縮も対応していて、 実際のバックアップは元のディスクより小さくなっていて良い感じでした。</p> <p>リストアも <a href="https://gihyo.jp/admin/serial/01/ubuntu-recipe/0766">第766回 高度なことが簡単にできる多機能バックアップツール、Restic[後編]</a> で紹介されている <code class="language-plaintext highlighter-rouge">restic mount</code> で、 普通のファイル操作を使えて楽そうでした。</p> <p><code class="language-plaintext highlighter-rouge">--exclude-file</code> は <code class="language-plaintext highlighter-rouge">restic.sh snapshots --no-lock</code> と <code class="language-plaintext highlighter-rouge">restic.sh diff --no-lock ID1 ID2</code> などで変化しているファイルをみて、 データベースのバイナリは除外して <code class="language-plaintext highlighter-rouge">ExecStartPre=</code> でダンプをした方が良さそう、とか調整したいと思っています。</p> <p>timer での実行間隔や <code class="language-plaintext highlighter-rouge">restic forget</code> の保存期間の指定も検討が必要だと思っています。</p> <p>3-2-1 バックアップルールのため、 <code class="language-plaintext highlighter-rouge">restic copy</code> を使うか、 <code class="language-plaintext highlighter-rouge">restic backup</code> を直接別の repository にも実行するか、 なども検討していきたいです。</p> Thu, 08 Aug 2024 10:00:00 +0000 https://blog.n-z.jp/blog/2024-08-08-restic-backup.html https://blog.n-z.jp/blog/2024-08-08-restic-backup.html linux restic blog
- DokkuへのデプロイをHerokuish BuildpacksからDockerfile Deploymentに変更した <p>自分専用 Rails アプリの Dokku へのデプロイに <a href="https://dokku.com/docs/deployment/builders/herokuish-buildpacks/">Herokuish Buildpacks</a> を使っていましたが、 Heroku が Ruby 対応を更新してくれるのに依存していて、 Ruby 自体のリリースから使えるようになるまでの待ちが長くて困ることが続いたので、 <a href="https://dokku.com/docs/deployment/builders/dockerfiles/">Dockerfile Deployment</a> に切り替えました。</p> <!--more--> <h2 id="動作確認バージョン">動作確認バージョン</h2> <ul> <li>dokku 0.34.7</li> <li>herokuish 0.9.2</li> <li>ruby 3.2.4 から 3.2.5 (ここが問題)</li> <li>rails 7.1.3.4</li> </ul> <h2 id="現状確認">現状確認</h2> <p>Dependabot の pull request で開発環境用の <code class="language-plaintext highlighter-rouge">Dockerfile.local</code> の <code class="language-plaintext highlighter-rouge">FROM ruby:3.2.4</code> が <code class="language-plaintext highlighter-rouge">FROM ruby:3.2.5</code> に更新されたので、 <code class="language-plaintext highlighter-rouge">.ruby-version</code> なども更新して <code class="language-plaintext highlighter-rouge">git push</code> したところ、以下のようにビルドに失敗しました。</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-----> Compiling Ruby/Rails Command: 'set -o pipefail; curl -L --fail --retry 5 --retry-delay 1 --connect-timeout 90 --max-time 600 https://heroku-buildpack-ruby.s3.us-east-1.amazonaws.com/heroku-22/ruby-3.2.5.tgz -s -o - | tar zxf - ' failed on attempt 1 of 3. Command: 'set -o pipefail; curl -L --fail --retry 5 --retry-delay 1 --connect-timeout 90 --max-time 600 https://heroku-buildpack-ruby.s3.us-east-1.amazonaws.com/heroku-22/ruby-3.2.5.tgz -s -o - | tar zxf - ' failed on attempt 2 of 3. ! ! The Ruby version you are trying to install does not exist: ruby-3.2.5 ! ! Heroku recommends you use the latest supported Ruby version listed here: ! https://devcenter.heroku.com/articles/ruby-support#supported-runtimes ! ! For more information on syntax for declaring a Ruby version see: ! https://devcenter.heroku.com/articles/ruby-versions ! remote: ! Failure during app build </code></pre></div></div> <p>対応されたら <a href="https://devcenter.heroku.com/changelog">Heroku Changelog</a> に情報が載るはず、ということで数日待っていたのですが、 <a href="https://www.ruby-lang.org/en/news/2024/07/26/ruby-3-2-5-released/">7月26日のリリース</a> から 7月29日の<a href="https://devcenter.heroku.com/changelog-items/2965">Node.js 20.16.0 now available</a>、 7月31日の<a href="https://devcenter.heroku.com/changelog-items/2966">Heroku-20, Heroku-22 and Heroku-24 stacks updated</a>、 8月2日の<a href="https://devcenter.heroku.com/changelog-items/2967">Early August 2024 PHP updates</a> と別の更新だけ続いていて、 いつリリースされるかわからないものに依存するのは良くないと思ったので、 Ruby 3.3 系にして一時的な解決をするのではなく、 Dockerfile Deployment に切り替えることにしました。</p> <h2 id="本番用-dockerfile">本番用 Dockerfile</h2> <p><a href="https://github.com/znz/rails7-example">znz/rails7-example</a> に Rails 7.1 で生成した本番用 Dockerfile を用意していたので、 それをコピーして使いました。</p> <p>以下の点を変更しました。</p> <ul> <li><code class="language-plaintext highlighter-rouge">ARG RUBY_VERSION=3.2.5</code> のバージョンを更新</li> <li><code class="language-plaintext highlighter-rouge">FromAsCasing: 'as' and 'FROM' keywords' casing do not match</code> という警告が出るので <code class="language-plaintext highlighter-rouge">FROM base AS build</code> のように <code class="language-plaintext highlighter-rouge">FROM</code> 行の <code class="language-plaintext highlighter-rouge">as</code> を <code class="language-plaintext highlighter-rouge">AS</code> に変更</li> <li><code class="language-plaintext highlighter-rouge">gem "bootstrap", "< 5"</code> などで依存している <code class="language-plaintext highlighter-rouge">execjs</code> で必要だったので <code class="language-plaintext highlighter-rouge">apt-get install</code> に <code class="language-plaintext highlighter-rouge">nodejs</code> を追加 (build stage と final stage の両方に必要だった)</li> </ul> <p>nodejs が必要なのは bootstrap 4 から移行したり importmap-rails に移行したりすれば消せそうだと思っているので、一時的なものとして Debian パッケージの nodejs を使いました。 依存を外すか、ちゃんと最新の node を使うかどうかは別途対応したいと思っています。</p> <h2 id="その他のファイル">その他のファイル</h2> <p>Dockerfile の <code class="language-plaintext highlighter-rouge">ENTRYPOINT ["/rails/bin/docker-entrypoint"]</code> で使っている <code class="language-plaintext highlighter-rouge">bin/docker-entrypoint</code> もコピーしました。</p> <p><code class="language-plaintext highlighter-rouge">chown -R rails:rails db log storage tmp</code> でエラーになったので、今のところ使っていない <code class="language-plaintext highlighter-rouge">storage/.keep</code> も追加しました。</p> <p><code class="language-plaintext highlighter-rouge">config/database.yml</code> で <code class="language-plaintext highlighter-rouge">ENV.fetch('DATABASE_URL', '').sub(/^postgres/, "postgis")</code> のように <code class="language-plaintext highlighter-rouge">url</code> を設定していたら、 <code class="language-plaintext highlighter-rouge">assets:precompile</code> で <code class="language-plaintext highlighter-rouge">ActiveRecord::AdapterNotSpecified: database configuration does not specify adapter (ActiveRecord::AdapterNotSpecified)</code> というエラーになってしまったので、コメントにあった適当な URL をデフォルト値として使って adapter がわかるようにしました。</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">production</span><span class="pi">:</span> <span class="na">url</span><span class="pi">:</span> <span class="s"><%= ENV.fetch('DATABASE_URL', 'postgres://myuser:mypass@localhost/somedatabase').sub(/^postgres/, "postgis") %></span> </code></pre></div></div> <h2 id="gemfile-から-ruby-を削除">Gemfile から ruby を削除</h2> <p><a href="https://bundler.io/guides/gemfile_ruby.html">Gemfile の ruby</a> は主に Heroku 用の指定だときいているので、 herokuish Buildpacks を使わないなら不要 (<code class="language-plaintext highlighter-rouge">.ruby-version</code> や <code class="language-plaintext highlighter-rouge">Dockerfile</code> での指定と重複するため) ということで、 削除しました。 Gemfile.lock も更新して <code class="language-plaintext highlighter-rouge">RUBY VERSION</code> も消えました。</p> <h2 id="感想">感想</h2> <p>できるだけ差分を小さくして、さっさと Dockerfile Deployment に移行したかったのですが、 意外と差分が増えてしまいました。</p> <p>これで Heroku の対応を待たずに Docker images の更新だけ待てば良くなったので、バージョンを上げやすくなりました。</p> Mon, 05 Aug 2024 03:00:00 +0000 https://blog.n-z.jp/blog/2024-08-05-dokku-dockerfile-deploy.html https://blog.n-z.jp/blog/2024-08-05-dokku-dockerfile-deploy.html dokku ruby rails blog