<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.mnrst.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.mnrst.com/" rel="alternate" type="text/html" hreflang="ja" /><updated>2026-05-02T10:09:51+00:00</updated><id>https://blog.mnrst.com/feed.xml</id><title type="html">mnrst</title><subtitle>クラウドエンジニアの検証生活</subtitle><entry><title type="html">GPU なし Ubuntu で Ollama のカスタムモデルを試してみた</title><link href="https://blog.mnrst.com/2025/03/30/ollama-custom-model.html" rel="alternate" type="text/html" title="GPU なし Ubuntu で Ollama のカスタムモデルを試してみた" /><published>2025-03-30T00:00:00+00:00</published><updated>2025-03-30T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/03/30/ollama-custom-model</id><content type="html" xml:base="https://blog.mnrst.com/2025/03/30/ollama-custom-model.html"><![CDATA[<p>既存の言語モデルをベースに追加学習させたり、RAG で検索拡張したり、カスタムパラメーターでモデルをカスタムしたりと、LLM を単に使うだけではなくシステムの一部として何がどうなるのか、少し興味があります。</p>

<p>これらをやり始めると、Apple シリコン の GPU では物足りなく感じて、NVIDIA の GPU が欲しくなってきます。</p>

<p>そこで今回は、GPU なし Ubuntu で Ollama のカスタムモデルを試してみました。</p>

<h2 id="multipass-で-ubuntu-検証環境を用意">Multipass で Ubuntu 検証環境を用意</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>multipass launch <span class="nt">-n</span> primary <span class="nt">--cpus</span> 4 <span class="nt">--memory</span> 4G <span class="nt">--disk</span> 20G 24.04

<span class="nv">$ </span>multipass shell
</code></pre></div></div>

<h2 id="ollama-をインストール">Ollama をインストール</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl <span class="nt">-fsSL</span> https://ollama.com/install.sh | sh
</code></pre></div></div>

<p>最後に下記のメッセージが表示されたので、CPU オンリーモードです。</p>

<pre><code class="language-txt">WARNING: No NVIDIA/AMD GPU detected. Ollama will run in CPU-only mode.
</code></pre>

<h2 id="既存の言語モデルを試す">既存の言語モデルを試す</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ollama run llama3.2:1b
</code></pre></div></div>

<pre><code class="language-txt">&gt;&gt;&gt; 日本の首都は？
東京です。

&gt;&gt;&gt; /bye
</code></pre>

<h2 id="モデルファイルを作成し-test-モデルを作成">モデルファイルを作成し test モデルを作成</h2>

<p><code class="language-plaintext highlighter-rouge">SYSTEM</code> は、日本語で記述しても意図した結果になりませんでした。</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt; Modelfile
FROM llama3.2:1b
PARAMETER temperature 0.8
SYSTEM "You're the most knowledgeable person in the world. Add a relevant piece of trivia to your answer to the question."
</span><span class="no">EOF

</span><span class="nv">$ </span>ollama create <span class="nb">test</span> <span class="nt">-f</span> Modelfile
</code></pre></div></div>

<h2 id="カスタムモデル-test-の動作確認">カスタムモデル test の動作確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ollama run <span class="nb">test</span>
</code></pre></div></div>

<p>意図した通りになりましたが、ウンチクの内容はハルシネーションです。</p>

<pre><code class="language-txt">&gt;&gt;&gt; 日本の首都は？
東京都です。Trivia：東京電力が持有的東武京王線の駅である渋谷 Station では、1995年7月に、東急西南線と東急東線の駅間で最高速度100 km/h (62 mph) を設定しました。これは日本で初めてとなりました。

&gt;&gt;&gt; 日本の首都は？
Tokyoです。Trivia：東京には多くのテナントがありますが、最も知られているのは東京タワースクエアです。この広い天井下部は1958年に完成し、その後改名されて「東京タワースクエア」と呼ばれました。

&gt;&gt;&gt; /bye
</code></pre>

<h2 id="検証が終わったら後片付け">検証が終わったら後片付け</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">exit</span>

<span class="nv">$ </span>multipass stop

<span class="nv">$ </span>multipass delete primary

<span class="nv">$ </span>multipass purge
</code></pre></div></div>

<h2 id="おまけdiffusionbee-で生成した本記事のイメージ画像">（おまけ）DiffusionBee で生成した本記事のイメージ画像</h2>

<p><img src="/assets/img/2025-03-30-ollama-custom-model-01.png" alt="2025-03-30-ollama-custom-model-01.png" /></p>

<h2 id="参考">参考</h2>

<p><a href="https://github.com/ollama/ollama/blob/main/docs/modelfile.md">Ollama Model File</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[既存の言語モデルをベースに追加学習させたり、RAG で検索拡張したり、カスタムパラメーターでモデルをカスタムしたりと、LLM を単に使うだけではなくシステムの一部として何がどうなるのか、少し興味があります。]]></summary></entry><entry><title type="html">Azure App Service のサイドカーコンテナーにも対応するカスタムコンテナーを試してみた</title><link href="https://blog.mnrst.com/2025/03/22/appservice-container.html" rel="alternate" type="text/html" title="Azure App Service のサイドカーコンテナーにも対応するカスタムコンテナーを試してみた" /><published>2025-03-22T00:00:00+00:00</published><updated>2025-03-22T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/03/22/appservice-container</id><content type="html" xml:base="https://blog.mnrst.com/2025/03/22/appservice-container.html"><![CDATA[<p>Azure App Service のマルチコンテナーアプリは、プレビューの Docker Compose で対応可能です。</p>

<p>カスタムコンテナーは後継の機能なので、Docker Compose はプレビューのまま終わってしまうのでしょうか。</p>

<p>そこで今回は、後継のサイドカーコンテナーにも対応したカスタムコンテナー機能を Azure CLI で試してみました。</p>

<h2 id="azure-cli-のバージョン確認">Azure CLI のバージョン確認</h2>

<p>バージョン 2.70.0 からカスタムコンテナーに対応しています。</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>az version
<span class="o">{</span>
  <span class="s2">"azure-cli"</span>: <span class="s2">"2.70.0"</span>,
  <span class="s2">"azure-cli-core"</span>: <span class="s2">"2.70.0"</span>,
  <span class="s2">"azure-cli-telemetry"</span>: <span class="s2">"1.1.0"</span>,
  <span class="s2">"extensions"</span>: <span class="o">{}</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="app-service-plan-を作成するところまでは既存と同じ">App Service Plan を作成するところまでは既存と同じ</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">prefix</span><span class="o">=</span>mnrasc
<span class="nv">region</span><span class="o">=</span>japaneast

az group create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--location</span> <span class="nv">$region</span>

az appservice plan create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-plan</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--sku</span> B1 <span class="se">\</span>
  <span class="nt">--is-linux</span>
</code></pre></div></div>

<h2 id="カスタムコンテナー対応の-web-アプリを作成">カスタムコンテナー対応の Web アプリを作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az webapp create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--plan</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-plan</span> <span class="se">\</span>
  <span class="nt">--sitecontainers-app</span>
</code></pre></div></div>

<h2 id="メインのカスタムコンテナーを-nginx-で作成">メインのカスタムコンテナーを Nginx で作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az webapp sitecontainers create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--container-name</span> nginx <span class="se">\</span>
  <span class="nt">--image</span> nginx <span class="se">\</span>
  <span class="nt">--target-port</span> 80 <span class="se">\</span>
  <span class="nt">--is-main</span> <span class="nb">true</span>
</code></pre></div></div>

<h2 id="azure-ポータルでカスタムコンテナーを確認">Azure ポータルでカスタムコンテナーを確認</h2>

<p><img src="/assets/img/2025-03-22-appservice-container-01.png" alt="2025-03-22-appservice-container-01.png" /></p>

<h2 id="おまけ常時接続設定">（おまけ）常時接続設定</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az webapp config <span class="nb">set</span> <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--always-on</span> <span class="nb">true</span>
</code></pre></div></div>

<h2 id="検証が終わったら後片付け">検証が終わったら後片付け</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az group delete <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--yes</span>
</code></pre></div></div>

<h2 id="参考">参考</h2>

<p><a href="https://learn.microsoft.com/ja-jp/cli/azure/release-notes-azure-cli#march-04-2025">Azure CLI バージョン 2.70.0 リリースノート</a></p>

<p><a href="https://learn.microsoft.com/en-us/cli/azure/webapp/sitecontainers?view=azure-cli-latest">az webapp sitecontainers</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[Azure App Service のマルチコンテナーアプリは、プレビューの Docker Compose で対応可能です。]]></summary></entry><entry><title type="html">Mac の Multipass で Ubuntu Desktop を試してみた</title><link href="https://blog.mnrst.com/2025/03/16/multipass-ubuntu-desktop.html" rel="alternate" type="text/html" title="Mac の Multipass で Ubuntu Desktop を試してみた" /><published>2025-03-16T00:00:00+00:00</published><updated>2025-03-16T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/03/16/multipass-ubuntu-desktop</id><content type="html" xml:base="https://blog.mnrst.com/2025/03/16/multipass-ubuntu-desktop.html"><![CDATA[<p>クリーンな検証環境が必要な時は、別途仮想マシンを用意するのが手っ取り早いです。</p>

<p>昔と違って仮想マシンを作るのに、ISO イメージから OS インストールをしなくて済むのは便利です。</p>

<p>SSH やリモートデスクトップで仮想マシンに接続するので、手元のマウスとキーボードで操作できます。</p>

<p>そこで今回は、Mac の Multipass で Ubuntu Desktop を試してみました。</p>

<h2 id="検証用の仮想マシンを作成">検証用の仮想マシンを作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew <span class="nb">install </span>multipass

<span class="nv">$ </span>multipass <span class="nt">--version</span>
multipass   1.15.1+mac
multipassd  1.15.1+mac

<span class="nv">$ </span>multipass launch <span class="nt">-n</span> primary <span class="nt">--cpus</span> 4 <span class="nt">--memory</span> 4G <span class="nt">--disk</span> 20G 24.04

<span class="nv">$ </span>multipass list
Name                    State             IPv4             Image
primary                 Running           192.168.64.3     Ubuntu 24.04 LTS

<span class="nv">$ </span>multipass shell
</code></pre></div></div>

<h2 id="ubuntu-に-desktop-と-xrdp-をインストール">Ubuntu に Desktop と xrdp をインストール</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update

<span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> ubuntu-desktop xrdp
</code></pre></div></div>

<h2 id="ログインパスワードを設定">ログインパスワードを設定</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>passwd ubuntu
</code></pre></div></div>

<h2 id="リモートデスクトップソフトから接続">リモートデスクトップソフトから接続</h2>

<p><img src="/assets/img/2025-03-16-multipass-ubuntu-desktop-01.png" alt="2025-03-16-multipass-ubuntu-desktop-01.png" /></p>

<h2 id="仮想マシンを停止">仮想マシンを停止</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>multipass stop

<span class="nv">$ </span>multipass list
Name                    State             IPv4             Image
primary                 Stopped           <span class="nt">--</span>               Ubuntu 24.04 LTS
</code></pre></div></div>

<h2 id="仮想マシンが不要になったら後片付け">仮想マシンが不要になったら後片付け</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>multipass delete primary

<span class="nv">$ </span>multipass purge
</code></pre></div></div>

<h2 id="参考">参考</h2>

<p><a href="https://learn.microsoft.com/ja-jp/azure/virtual-machines/linux/use-remote-desktop?tabs=azure-cli">xrdp をインストールして、Ubuntu でリモート デスクトップを使用するように構成する
</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[クリーンな検証環境が必要な時は、別途仮想マシンを用意するのが手っ取り早いです。]]></summary></entry><entry><title type="html">Nested Hyper-V な Windows 11 を Azure VM で作ってみた</title><link href="https://blog.mnrst.com/2025/03/09/nested-hyperv-win11-azure.html" rel="alternate" type="text/html" title="Nested Hyper-V な Windows 11 を Azure VM で作ってみた" /><published>2025-03-09T00:00:00+00:00</published><updated>2025-03-09T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/03/09/nested-hyperv-win11-azure</id><content type="html" xml:base="https://blog.mnrst.com/2025/03/09/nested-hyperv-win11-azure.html"><![CDATA[<p>検証用の仮想マシンが必要になった時、自分の物理端末に仮想化ソフトを入れたり、自作 PC に入れた VMware 環境を使って検証していました。（一昔前までは）</p>

<p>今ではクラウドファースト。検証用の仮想マシンが必要になったら、いつでもどこからでも検証環境が用意できるので大変便利です。</p>

<p>今回は Hyper-V を使った検証をするために、Nested Hyper-V な Windows 11 を Azure VM で作ってみました。</p>

<h2 id="検証用の仮想マシンを作成">検証用の仮想マシンを作成</h2>

<p>Nested Hyper-V が可能な VM サイズにしています。</p>

<p>また、デフォルトの NSG に、後から自身の IP アドレスを許可するようにしています。</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">region</span><span class="o">=</span>japaneast
<span class="nv">prefix</span><span class="o">=</span>mnrw11

az group create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--location</span> <span class="nv">$region</span>

az vm create <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vm</span> <span class="se">\</span>
  <span class="nt">--os-disk-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vmOSDisk</span> <span class="se">\</span>
  <span class="nt">--image</span> microsoftwindowsdesktop:windows-11:win11-24h2-avd:latest <span class="se">\</span>
  <span class="nt">--license-type</span> Windows_Client <span class="se">\</span>
  <span class="nt">--size</span> Standard_E2ds_v5 <span class="se">\</span>
  <span class="nt">--storage-sku</span> Standard_LRS <span class="se">\</span>
  <span class="nt">--admin-username</span> azureuser <span class="se">\</span>
  <span class="nt">--assign-identity</span> <span class="se">\</span>
  <span class="nt">--nsg-rule</span> NONE <span class="se">\</span>
  <span class="nt">--public-ip-address-dns-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span>

az network nsg rule create <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--name</span> AllowRDP <span class="se">\</span>
  <span class="nt">--nsg-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vmNSG</span> <span class="se">\</span>
  <span class="nt">--priority</span> 100 <span class="se">\</span>
  <span class="nt">--source-address-prefixes</span> <span class="si">$(</span>curl <span class="nt">-s</span> inet-ip.info<span class="si">)</span> <span class="se">\</span>
  <span class="nt">--destination-port-ranges</span> 3389 <span class="se">\</span>
  <span class="nt">--access</span> Allow <span class="se">\</span>
  <span class="nt">--protocol</span> Tcp
</code></pre></div></div>

<h2 id="hyper-v-を有効化">Hyper-V を有効化</h2>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Enable-WindowsOptionalFeature</span><span class="w"> </span><span class="nt">-Online</span><span class="w"> </span><span class="nt">-FeatureName</span><span class="w"> </span><span class="nx">Microsoft-Hyper-V-All</span><span class="w">
</span></code></pre></div></div>

<p><img src="/assets/img/2025-03-09-nested-hyperv-win11-azure-01.png" alt="2025-03-09-nested-hyperv-win11-azure-01.png" /></p>

<h2 id="おまけ日本語化">（おまけ）日本語化</h2>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Install-Language</span><span class="w"> </span><span class="nx">ja-jp</span><span class="w">
</span><span class="n">Set-WinUserLanguageList</span><span class="w"> </span><span class="nx">ja</span><span class="p">,</span><span class="nx">en-US</span><span class="w"> </span><span class="nt">-Force</span><span class="w">
</span><span class="n">Set-WinUILanguageOverride</span><span class="w"> </span><span class="nt">-Language</span><span class="w"> </span><span class="nx">ja-JP</span><span class="w">
</span><span class="n">Set-WinHomeLocation</span><span class="w"> </span><span class="nt">-GeoId</span><span class="w"> </span><span class="nx">122</span><span class="w">
</span><span class="n">Set-TimeZone</span><span class="w"> </span><span class="nt">-Id</span><span class="w"> </span><span class="s2">"Tokyo Standard Time"</span><span class="w">
</span><span class="n">Set-WinSystemLocale</span><span class="w"> </span><span class="nt">-SystemLocale</span><span class="w"> </span><span class="nx">ja-JP</span><span class="w">
</span><span class="n">Copy-UserInternationalSettingsToSystem</span><span class="w"> </span><span class="nt">-WelcomeScreen</span><span class="w"> </span><span class="nv">$True</span><span class="w"> </span><span class="nt">-NewUser</span><span class="w"> </span><span class="nv">$True</span><span class="w">
</span><span class="n">Restart-Computer</span><span class="w">
</span></code></pre></div></div>

<h2 id="後片付け">後片付け</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az group delete <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--yes</span>
</code></pre></div></div>

<h2 id="参考">参考</h2>

<p><a href="https://techcommunity.microsoft.com/blog/azure-ai-services-blog/nested-virtualization-on-azure--a-step-by-step-guide/4368074">Nested virtualization on Azure : a step-by-step guide</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[検証用の仮想マシンが必要になった時、自分の物理端末に仮想化ソフトを入れたり、自作 PC に入れた VMware 環境を使って検証していました。（一昔前までは）]]></summary></entry><entry><title type="html">React Native 開発を効率化する Expo フレームワークを試してみた</title><link href="https://blog.mnrst.com/2025/03/01/expo-react-native.html" rel="alternate" type="text/html" title="React Native 開発を効率化する Expo フレームワークを試してみた" /><published>2025-03-01T00:00:00+00:00</published><updated>2025-03-01T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/03/01/expo-react-native</id><content type="html" xml:base="https://blog.mnrst.com/2025/03/01/expo-react-native.html"><![CDATA[<p>単一のコードベースでクロスプラットフォームアプリが開発できる React Native ですが、デバイスごとに動作確認するのは大変面倒です。</p>

<p>そこで、開発の生産性を向上する事ができる Expo という開発フレームワークを使ってみました。</p>

<h2 id="検証用アプリを作成">検証用アプリを作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npx create-expo-app@latest MnrExpo
</code></pre></div></div>

<p>下記が表示されれば OK です。</p>

<pre><code class="language-txt">✅ Your project is ready!
</code></pre>

<h2 id="デフォルト状態の検証用アプリを確認">デフォルト状態の検証用アプリを確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>MnrExpo

npm run ios
</code></pre></div></div>

<p><img src="/assets/img/2025-03-01-expo-react-native-01.png" alt="2025-03-01-expo-react-native-01.png" /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Android は先にシミュレーターを起動しておく必要がある</span>
flutter emulators <span class="nt">--launch</span> Medium_Phone_API_35

npm run android
</code></pre></div></div>

<p><img src="/assets/img/2025-03-01-expo-react-native-02.png" alt="2025-03-01-expo-react-native-02.png" /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm run web
</code></pre></div></div>

<p><img src="/assets/img/2025-03-01-expo-react-native-03.png" alt="2025-03-01-expo-react-native-03.png" /></p>

<h2 id="expo-を起動">Expo を起動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npx expo start
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">a</code> や <code class="language-plaintext highlighter-rouge">i</code> や <code class="language-plaintext highlighter-rouge">w</code> で各環境を同時に確認する事ができます。</p>

<h2 id="コードを修正">コードを修正</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>code app/<span class="se">\(</span>tabs<span class="se">\)</span>/index.tsx
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Welcome</code> を <code class="language-plaintext highlighter-rouge">Hello World</code> に変更します。</p>

<p>変更前。</p>

<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">&lt;</span><span class="nc">ThemedText</span> <span class="na">type</span><span class="p">=</span><span class="s">"title"</span><span class="p">&gt;</span>Welcome!<span class="p">&lt;/</span><span class="nc">ThemedText</span><span class="p">&gt;</span>
</code></pre></div></div>

<p>変更後。</p>

<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">&lt;</span><span class="nc">ThemedText</span> <span class="na">type</span><span class="p">=</span><span class="s">"title"</span><span class="p">&gt;</span>Hello World!<span class="p">&lt;/</span><span class="nc">ThemedText</span><span class="p">&gt;</span>
</code></pre></div></div>

<h2 id="各環境が同時に修正されるのを確認">各環境が同時に修正されるのを確認</h2>

<p><img src="/assets/img/2025-03-01-expo-react-native-04.png" alt="2025-03-01-expo-react-native-04.png" /></p>

<h2 id="参考">参考</h2>

<p><a href="https://docs.expo.dev/tutorial/create-your-first-app/">Create your first app</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[単一のコードベースでクロスプラットフォームアプリが開発できる React Native ですが、デバイスごとに動作確認するのは大変面倒です。]]></summary></entry><entry><title type="html">Apple シリコン Mac で VSCode の拡張機能を使用せず Flutter CLI を試してみた</title><link href="https://blog.mnrst.com/2025/02/24/flutter.html" rel="alternate" type="text/html" title="Apple シリコン Mac で VSCode の拡張機能を使用せず Flutter CLI を試してみた" /><published>2025-02-24T00:00:00+00:00</published><updated>2025-02-24T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/02/24/flutter</id><content type="html" xml:base="https://blog.mnrst.com/2025/02/24/flutter.html"><![CDATA[<p>単一のコードベースでクロスプラットフォームアプリを開発できる Flutter を CLI で試してみました。</p>

<p>前提として、Apple シリコン Mac に Xcode や Android Studio はインストール済みです。</p>

<h2 id="brew-で-flutter-をインストール">brew で Flutter をインストール</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew <span class="nb">install </span>flutter

<span class="nv">$ </span>flutter <span class="nt">--version</span>
Flutter 3.29.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 35c388afb5 <span class="o">(</span>13 days ago<span class="o">)</span> • 2025-02-10 12:48:41 <span class="nt">-0800</span>
Engine • revision f73bfc4522
Tools • Dart 3.7.0 • DevTools 2.42.2
</code></pre></div></div>

<h2 id="flutter-環境を診断">Flutter 環境を診断</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter doctor
Doctor summary <span class="o">(</span>to see all details, run flutter doctor <span class="nt">-v</span><span class="o">)</span>:
<span class="o">[</span>✓] Flutter <span class="o">(</span>Channel stable, 3.29.0, on macOS 15.3.1 24D70 darwin-arm64, locale ja-JP<span class="o">)</span>
<span class="o">[</span>✓] Android toolchain - develop <span class="k">for </span>Android devices <span class="o">(</span>Android SDK version 35.0.1<span class="o">)</span>
<span class="o">[</span>✓] Xcode - develop <span class="k">for </span>iOS and macOS <span class="o">(</span>Xcode 16.2<span class="o">)</span>
<span class="o">[</span>✓] Chrome - develop <span class="k">for </span>the web
<span class="o">[</span>✓] Android Studio <span class="o">(</span>version 2024.2<span class="o">)</span>
<span class="o">[</span>✓] VS Code <span class="o">(</span>version 1.97.2<span class="o">)</span>
<span class="o">[</span>✓] Connected device <span class="o">(</span>3 available<span class="o">)</span>
<span class="o">[</span>✓] Network resources

• No issues found!
</code></pre></div></div>

<h2 id="flutter-アプリを作成">Flutter アプリを作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter create my_app

<span class="nv">$ </span><span class="nb">cd </span>my_app
</code></pre></div></div>

<h2 id="flutter-アプリを静的解析">Flutter アプリを静的解析</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter analyze
Analyzing my_app...                                                     
No issues found! <span class="o">(</span>ran <span class="k">in </span>4.8s<span class="o">)</span>
</code></pre></div></div>

<h2 id="flutter-アプリのテストコードを実行">Flutter アプリのテストコードを実行</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter <span class="nb">test
</span>00:03 +1: All tests passed!
</code></pre></div></div>

<h2 id="接続デバイスを確認">接続デバイスを確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter devices
Found 3 connected devices:
  macOS <span class="o">(</span>desktop<span class="o">)</span>                 • macos                 • darwin-arm64   • macOS 15.3.1 24D70 darwin-arm64
  Mac Designed <span class="k">for </span>iPad <span class="o">(</span>desktop<span class="o">)</span> • mac-designed-for-ipad • darwin         • macOS 15.3.1 24D70 darwin-arm64
  Chrome <span class="o">(</span>web<span class="o">)</span>                    • chrome                • web-javascript • Google Chrome 133.0.6943.127
</code></pre></div></div>

<h2 id="macos-アプリを起動">MacOS アプリを起動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter run <span class="nt">-d</span> macos
</code></pre></div></div>

<p><img src="/assets/img/2025-02-24-flutter-01.png" alt="2025-02-24-flutter-01.png" /></p>

<h2 id="web-アプリを起動">Web アプリを起動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter run <span class="nt">-d</span> chrome
</code></pre></div></div>

<p><img src="/assets/img/2025-02-24-flutter-02.png" alt="2025-02-24-flutter-02.png" /></p>

<h2 id="エミュレーターを確認">エミュレーターを確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter emulators
2 available emulators:
Id                  • Name                • Manufacturer • Platform
apple_ios_simulator • iOS Simulator       • Apple        • ios
Medium_Phone_API_35 • Medium Phone API 35 • Generic      • android
</code></pre></div></div>

<h2 id="エミュレーターを起動">エミュレーターを起動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter emulators <span class="nt">--launch</span> apple_ios_simulator

<span class="nv">$ </span>flutter emulators <span class="nt">--launch</span> Medium_Phone_API_35
</code></pre></div></div>

<h2 id="エミュレーターのデバイス-id-を確認">エミュレーターのデバイス ID を確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter devices
Found 5 connected devices:
  sdk gphone64 arm64 <span class="o">(</span>mobile<span class="o">)</span>     • emulator-5554                        • android-arm64  • Android 15 <span class="o">(</span>API 35<span class="o">)</span> <span class="o">(</span>emulator<span class="o">)</span>
  iPad mini <span class="o">(</span>A17 Pro<span class="o">)</span> <span class="o">(</span>mobile<span class="o">)</span>    • 60277030-E9F4-43A7-B5AE-EBFFE11F49B2 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-18-2 <span class="o">(</span>simulator<span class="o">)</span>
  macOS <span class="o">(</span>desktop<span class="o">)</span>                 • macos                                • darwin-arm64   • macOS 15.3.1 24D70 darwin-arm64
  Mac Designed <span class="k">for </span>iPad <span class="o">(</span>desktop<span class="o">)</span> • mac-designed-for-ipad                • darwin         • macOS 15.3.1 24D70 darwin-arm64
  Chrome <span class="o">(</span>web<span class="o">)</span>                    • chrome                               • web-javascript • Google Chrome 133.0.6943.127
</code></pre></div></div>

<h2 id="ios-アプリを起動">iOS アプリを起動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter run <span class="nt">-d</span> 60277030-E9F4-43A7-B5AE-EBFFE11F49B2
</code></pre></div></div>

<p><img src="/assets/img/2025-02-24-flutter-03.png" alt="2025-02-24-flutter-03.png" /></p>

<h2 id="android-アプリを起動">Android アプリを起動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>flutter run <span class="nt">-d</span> emulator-5554
</code></pre></div></div>

<p><img src="/assets/img/2025-02-24-flutter-04.png" alt="2025-02-24-flutter-04.png" /></p>

<h2 id="参考">参考</h2>

<p><a href="https://docs.flutter.dev/reference/flutter-cli">flutter: The Flutter command-line tool</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[単一のコードベースでクロスプラットフォームアプリを開発できる Flutter を CLI で試してみました。]]></summary></entry><entry><title type="html">Terraform で Firebase プロジェクトと Web アプリを登録して CLI で静的サイトの Hosting を作ってみた</title><link href="https://blog.mnrst.com/2025/02/22/firebase-hosting-terraform.html" rel="alternate" type="text/html" title="Terraform で Firebase プロジェクトと Web アプリを登録して CLI で静的サイトの Hosting を作ってみた" /><published>2025-02-22T00:00:00+00:00</published><updated>2025-02-22T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/02/22/firebase-hosting-terraform</id><content type="html" xml:base="https://blog.mnrst.com/2025/02/22/firebase-hosting-terraform.html"><![CDATA[<p>最初は Web GUI で動作確認をしてから、同じパターンを何度も繰り返せるように CLI で再現手順を作成する事がよくあります。</p>

<p>今回は、Terraform で Firebase プロジェクトと Web アプリを登録して CLI で静的サイトの Hosting を作ってみました。</p>

<h2 id="terraform-で-firebase-プロジェクトを登録">Terraform で Firebase プロジェクトを登録</h2>

<p><a href="https://firebase.google.com/docs/projects/terraform/get-started?hl=ja#general-workflow-terraform-and-firebase">Terraform と Firebase を使ってみる</a></p>

<p>こちらの Firebase ドキュメントを参考に、<code class="language-plaintext highlighter-rouge">main.tf</code> を作成します。</p>

<p><code class="language-plaintext highlighter-rouge">mnr-fb-test</code> の箇所は、Google Cloud のプロジェクト ID なので、重複しないよう変更します。</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Terraform configuration to set up providers by version.</span>
<span class="k">terraform</span> <span class="p">{</span>
  <span class="nx">required_providers</span> <span class="p">{</span>
    <span class="nx">google</span><span class="err">-</span><span class="nx">beta</span> <span class="p">=</span> <span class="p">{</span>
      <span class="nx">source</span>  <span class="p">=</span> <span class="s2">"hashicorp/google-beta"</span>
      <span class="nx">version</span> <span class="p">=</span> <span class="s2">"~&gt; 5.0"</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1"># Configures the provider to use the resource block's specified project for quota checks.</span>
<span class="k">provider</span> <span class="s2">"google-beta"</span> <span class="p">{</span>
  <span class="nx">user_project_override</span> <span class="p">=</span> <span class="kc">true</span>
<span class="p">}</span>

<span class="c1"># Configures the provider to not use the resource block's specified project for quota checks.</span>
<span class="c1"># This provider should only be used during project creation and initializing services.</span>
<span class="k">provider</span> <span class="s2">"google-beta"</span> <span class="p">{</span>
  <span class="nx">alias</span>                 <span class="p">=</span> <span class="s2">"no_user_project_override"</span>
  <span class="nx">user_project_override</span> <span class="p">=</span> <span class="kc">false</span>
<span class="p">}</span>

<span class="c1"># Creates a new Google Cloud project.</span>
<span class="k">resource</span> <span class="s2">"google_project"</span> <span class="s2">"default"</span> <span class="p">{</span>
  <span class="k">provider</span> <span class="p">=</span> <span class="nx">google</span><span class="err">-</span><span class="nx">beta</span><span class="p">.</span><span class="nx">no_user_project_override</span>

  <span class="nx">name</span>       <span class="p">=</span> <span class="s2">"mnr-fb-test"</span>
  <span class="nx">project_id</span> <span class="p">=</span> <span class="s2">"mnr-fb-test"</span>
  <span class="c1"># Required for any service that requires the Blaze pricing plan</span>
  <span class="c1"># (like Firebase Authentication with GCIP)</span>
  <span class="c1"># billing_account = "000000-000000-000000"</span>

  <span class="c1"># Required for the project to display in any list of Firebase projects.</span>
  <span class="nx">labels</span> <span class="p">=</span> <span class="p">{</span>
    <span class="s2">"firebase"</span> <span class="p">=</span> <span class="s2">"enabled"</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1"># Enables required APIs.</span>
<span class="k">resource</span> <span class="s2">"google_project_service"</span> <span class="s2">"default"</span> <span class="p">{</span>
  <span class="k">provider</span> <span class="p">=</span> <span class="nx">google</span><span class="err">-</span><span class="nx">beta</span><span class="p">.</span><span class="nx">no_user_project_override</span>
  <span class="nx">project</span>  <span class="p">=</span> <span class="nx">google_project</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">project_id</span>
  <span class="nx">for_each</span> <span class="p">=</span> <span class="nx">toset</span><span class="p">([</span>
    <span class="s2">"cloudbilling.googleapis.com"</span><span class="p">,</span>
    <span class="s2">"cloudresourcemanager.googleapis.com"</span><span class="p">,</span>
    <span class="s2">"firebase.googleapis.com"</span><span class="p">,</span>
    <span class="c1"># Enabling the ServiceUsage API allows the new project to be quota checked from now on.</span>
    <span class="s2">"serviceusage.googleapis.com"</span><span class="p">,</span>
  <span class="p">])</span>
  <span class="nx">service</span> <span class="p">=</span> <span class="nx">each</span><span class="p">.</span><span class="nx">key</span>

  <span class="c1"># Don't disable the service if the resource block is removed by accident.</span>
  <span class="nx">disable_on_destroy</span> <span class="p">=</span> <span class="kc">false</span>
<span class="p">}</span>

<span class="c1"># Enables Firebase services for the new project created above.</span>
<span class="k">resource</span> <span class="s2">"google_firebase_project"</span> <span class="s2">"default"</span> <span class="p">{</span>
  <span class="k">provider</span> <span class="p">=</span> <span class="nx">google</span><span class="err">-</span><span class="nx">beta</span>
  <span class="nx">project</span>  <span class="p">=</span> <span class="nx">google_project</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">project_id</span>

  <span class="c1"># Waits for the required APIs to be enabled.</span>
  <span class="nx">depends_on</span> <span class="p">=</span> <span class="p">[</span>
    <span class="nx">google_project_service</span><span class="p">.</span><span class="nx">default</span>
  <span class="p">]</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="terraform-を実行して-firebase-プロジェクトを作成">Terraform を実行して Firebase プロジェクトを作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>terraform init

terraform plan

terraform apply
</code></pre></div></div>

<h2 id="firebase-プロジェクトが作成された事を確認">Firebase プロジェクトが作成された事を確認</h2>

<p><a href="https://console.firebase.google.com/">Firebase へようこそ</a></p>

<p>こちらにアクセスして Firebase プロジェクトが作成された事を確認します。</p>

<p><img src="/assets/img/2025-02-22-firebase-hosting-terraform-01.png" alt="2025-02-22-firebase-hosting-terraform-01.png" /></p>

<p>この時点では、アプリな何も登録されていません。</p>

<p><img src="/assets/img/2025-02-22-firebase-hosting-terraform-02.png" alt="2025-02-22-firebase-hosting-terraform-02.png" /></p>

<h2 id="terraform-で-web-アプリを登録">Terraform で Web アプリを登録</h2>

<p><code class="language-plaintext highlighter-rouge">main.tf</code> に下記を追加します。</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Creates a Firebase Web App in the new project created above.</span>
<span class="k">resource</span> <span class="s2">"google_firebase_web_app"</span> <span class="s2">"default"</span> <span class="p">{</span>
  <span class="k">provider</span> <span class="p">=</span> <span class="nx">google</span><span class="err">-</span><span class="nx">beta</span>

  <span class="nx">project</span>      <span class="p">=</span> <span class="nx">google_project</span><span class="p">.</span><span class="nx">default</span><span class="p">.</span><span class="nx">project_id</span>
  <span class="nx">display_name</span> <span class="p">=</span> <span class="s2">"My Web App"</span>

  <span class="c1"># Wait for Firebase to be enabled in the Google Cloud project before creating this App.</span>
  <span class="nx">depends_on</span> <span class="p">=</span> <span class="p">[</span>
    <span class="nx">google_firebase_project</span><span class="p">.</span><span class="nx">default</span><span class="p">,</span>
  <span class="p">]</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>terraform plan

terraform apply
</code></pre></div></div>

<h2 id="web-アプリが登録された事を確認">Web アプリが登録された事を確認</h2>

<p><img src="/assets/img/2025-02-22-firebase-hosting-terraform-03.png" alt="2025-02-22-firebase-hosting-terraform-03.png" /></p>

<h2 id="firebase-cli-で-web-アプリを作成">Firebase CLI で Web アプリを作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm <span class="nt">-v</span>
11.1.0

<span class="nv">$ </span>npm <span class="nb">install</span> <span class="nt">-g</span> firebase-tools

<span class="nv">$ </span>firebase <span class="nt">--version</span>
13.31.2

<span class="nv">$ </span>firebase login

<span class="nv">$ </span><span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt; .firebaserc
{
  "projects": {
    "default": "mnr-fb-test"
  }
}
</span><span class="no">EOF

</span><span class="nv">$ </span><span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt; firebase.json
{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}
</span><span class="no">EOF

</span><span class="nv">$ </span><span class="nb">mkdir </span>public

<span class="nv">$ </span><span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh"> &gt; public/index.html
&lt;!DOCTYPE html&gt;
&lt;html lang="ja"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Hello World!&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</span><span class="no">EOF
</span></code></pre></div></div>

<h2 id="web-アプリを-firebase-hosting-にデプロイ">Web アプリを Firebase Hosting にデプロイ</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>firebase deploy <span class="nt">--only</span> hosting

<span class="o">===</span> Deploying to <span class="s1">'mnr-fb-test'</span>...

i  deploying hosting
i  hosting[mnr-fb-test]: beginning deploy...
i  hosting[mnr-fb-test]: found 1 files <span class="k">in </span>public
✔  hosting[mnr-fb-test]: file upload <span class="nb">complete
</span>i  hosting[mnr-fb-test]: finalizing version...
✔  hosting[mnr-fb-test]: version finalized
i  hosting[mnr-fb-test]: releasing new version...
✔  hosting[mnr-fb-test]: release <span class="nb">complete</span>

✔  Deploy <span class="nb">complete</span><span class="o">!</span>

Project Console: https://console.firebase.google.com/project/mnr-fb-test/overview
Hosting URL: https://mnr-fb-test.web.app
</code></pre></div></div>

<p><img src="/assets/img/2025-02-22-firebase-hosting-terraform-04.png" alt="2025-02-22-firebase-hosting-terraform-04.png" /></p>

<h2 id="参考">参考</h2>

<p><a href="https://firebase.google.com/docs/hosting/quickstart?hl=ja">Firebase Hosting を使ってみる</a></p>

<p><a href="https://firebase.google.com/docs/cli?hl=ja">Firebase CLI リファレンス</a></p>

<p><a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/firebase_web_app">google_firebase_web_app</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[最初は Web GUI で動作確認をしてから、同じパターンを何度も繰り返せるように CLI で再現手順を作成する事がよくあります。]]></summary></entry><entry><title type="html">Azure App Service の SSH 上で Spring Boot を動かしてみた</title><link href="https://blog.mnrst.com/2025/02/16/appservice-springboot.html" rel="alternate" type="text/html" title="Azure App Service の SSH 上で Spring Boot を動かしてみた" /><published>2025-02-16T00:00:00+00:00</published><updated>2025-02-16T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/02/16/appservice-springboot</id><content type="html" xml:base="https://blog.mnrst.com/2025/02/16/appservice-springboot.html"><![CDATA[<p>前回の「<a href="/2025/02/15/appservice-sqldatabase-privateendpoint.html">Azure App Service からプライベートエンドポイント経由の Azure SQL Database へパスワードレス Entra 認証を試してみた</a>」の続きです。</p>

<p>実際に Java の Spring Boot を使用して、マネージド ID を使用したパスワードレス認証を試してみました。</p>

<h2 id="app-service-内の-java-バージョン確認">App Service 内の Java バージョン確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>java <span class="nt">-version</span>
Picked up JAVA_TOOL_OPTIONS: <span class="nt">-Djava</span>.net.preferIPv4Stack<span class="o">=</span><span class="nb">true 
</span>openjdk version <span class="s2">"17.0.12"</span> 2024-07-16 LTS
OpenJDK Runtime Environment Microsoft-9889599 <span class="o">(</span>build 17.0.12+7-LTS<span class="o">)</span>
OpenJDK 64-Bit Server VM Microsoft-9889599 <span class="o">(</span>build 17.0.12+7-LTS, mixed mode, sharing<span class="o">)</span>
</code></pre></div></div>

<h2 id="app-service-内に必要なツールをインストール">App Service 内に必要なツールをインストール</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>apt <span class="nb">install</span> <span class="nt">-y</span> maven unzip zip vim
</code></pre></div></div>

<h2 id="maven-のバージョン確認">Maven のバージョン確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mvn <span class="nt">-version</span>
Picked up JAVA_TOOL_OPTIONS: <span class="nt">-Djava</span>.net.preferIPv4Stack<span class="o">=</span><span class="nb">true 
</span>Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.12, vendor: Microsoft, runtime: /usr/lib/jvm/msopenjdk-17-amd64
Default locale: en_US, platform encoding: UTF-8
OS name: <span class="s2">"linux"</span>, version: <span class="s2">"5.15.173.1-1.cm2"</span>, <span class="nb">arch</span>: <span class="s2">"amd64"</span>, family: <span class="s2">"unix"</span>
</code></pre></div></div>

<h2 id="spring-boot-cli-をインストール">Spring Boot CLI をインストール</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl <span class="nt">-s</span> <span class="s2">"https://get.sdkman.io"</span> | bash

<span class="nv">$ </span><span class="nb">source</span> <span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.sdkman/bin/sdkman-init.sh"</span>

<span class="nv">$ </span>sdk <span class="nb">install </span>springboot

<span class="nv">$ </span>spring <span class="nt">--version</span>
Picked up JAVA_TOOL_OPTIONS: <span class="nt">-Djava</span>.net.preferIPv4Stack<span class="o">=</span><span class="nb">true 
</span>Spring CLI v3.4.2
</code></pre></div></div>

<h2 id="検証用コンソールアプリを作成">検証用コンソールアプリを作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>spring init <span class="nt">--build</span><span class="o">=</span>maven <span class="nt">--java-version</span><span class="o">=</span>17 midtest

<span class="nv">$ </span><span class="nb">cd </span>midtest

<span class="nv">$ </span>vi src/main/java/com/example/midtest/DemoApplication.java
</code></pre></div></div>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.example.midtest</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">org.springframework.boot.SpringApplication</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.boot.autoconfigure.SpringBootApplication</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.sql.Connection</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.sql.ResultSet</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.sql.Statement</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.microsoft.sqlserver.jdbc.SQLServerDataSource</span><span class="o">;</span>

<span class="nd">@SpringBootApplication</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">DemoApplication</span> <span class="o">{</span>

	<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
		<span class="nc">SpringApplication</span><span class="o">.</span><span class="na">run</span><span class="o">(</span><span class="nc">DemoApplication</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>

		<span class="nc">SQLServerDataSource</span> <span class="n">ds</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SQLServerDataSource</span><span class="o">();</span>
        <span class="n">ds</span><span class="o">.</span><span class="na">setServerName</span><span class="o">(</span><span class="s">"mnrappsql-sqlsrv.privatelink.database.windows.net"</span><span class="o">);</span>
        <span class="n">ds</span><span class="o">.</span><span class="na">setDatabaseName</span><span class="o">(</span><span class="s">"sqldb"</span><span class="o">);</span>
        <span class="n">ds</span><span class="o">.</span><span class="na">setAuthentication</span><span class="o">(</span><span class="s">"ActiveDirectoryManagedIdentity"</span><span class="o">);</span>

        <span class="k">try</span> <span class="o">(</span>
            <span class="nc">Connection</span> <span class="n">connection</span> <span class="o">=</span> <span class="n">ds</span><span class="o">.</span><span class="na">getConnection</span><span class="o">();</span>
            <span class="nc">Statement</span> <span class="n">stmt</span> <span class="o">=</span> <span class="n">connection</span><span class="o">.</span><span class="na">createStatement</span><span class="o">();</span>
            <span class="nc">ResultSet</span> <span class="n">rs</span> <span class="o">=</span> <span class="n">stmt</span><span class="o">.</span><span class="na">executeQuery</span><span class="o">(</span><span class="s">"SELECT @@VERSION;"</span><span class="o">)</span>
        <span class="o">)</span> <span class="o">{</span>
            <span class="k">while</span> <span class="o">(</span><span class="n">rs</span><span class="o">.</span><span class="na">next</span><span class="o">())</span> <span class="o">{</span>
                <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"SQL Server Version: "</span> <span class="o">+</span> <span class="n">rs</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span>
            <span class="o">}</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
	<span class="o">}</span>

<span class="o">}</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi pom.xml
</code></pre></div></div>

<p>以下を <code class="language-plaintext highlighter-rouge">&lt;dependencies&gt;</code> に追加。</p>

<pre><code class="language-xml:pom.xml">		&lt;dependency&gt;
			&lt;groupId&gt;com.microsoft.sqlserver&lt;/groupId&gt;
			&lt;artifactId&gt;mssql-jdbc&lt;/artifactId&gt;
			&lt;version&gt;12.8.1.jre11&lt;/version&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;com.microsoft.azure&lt;/groupId&gt;
			&lt;artifactId&gt;msal4j&lt;/artifactId&gt;
			&lt;version&gt;1.19.0&lt;/version&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;com.azure&lt;/groupId&gt;
			&lt;artifactId&gt;azure-identity&lt;/artifactId&gt;
			&lt;version&gt;1.13.3&lt;/version&gt;
		&lt;/dependency&gt;
</code></pre>

<h2 id="検証用コンソールアプリの動作確認">検証用コンソールアプリの動作確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./mvnw spring-boot:run
（中略）
SQL Server Version: Microsoft SQL Azure <span class="o">(</span>RTM<span class="o">)</span> - 12.0.2000.8 
        Oct  2 2024 11:51:41 
        Copyright <span class="o">(</span>C<span class="o">)</span> 2022 Microsoft Corporation

<span class="o">[</span>INFO] <span class="nt">------------------------------------------------------------------------</span>
<span class="o">[</span>INFO] BUILD SUCCESS
<span class="o">[</span>INFO] <span class="nt">------------------------------------------------------------------------</span>
<span class="o">[</span>INFO] Total <span class="nb">time</span>:  48.205 s
<span class="o">[</span>INFO] Finished at: 2025-02-16T00:16:21Z
<span class="o">[</span>INFO] <span class="nt">------------------------------------------------------------------------</span>
</code></pre></div></div>

<p><img src="/assets/img/2025-02-16-appservice-springboot-01.png" alt="2025-02-16-appservice-springboot-01.png" /></p>

<h2 id="参考">参考</h2>

<p><a href="https://learn.microsoft.com/ja-jp/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-ver16">Microsoft SQL Server 用 JDBC Driver のダウンロード</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[前回の「Azure App Service からプライベートエンドポイント経由の Azure SQL Database へパスワードレス Entra 認証を試してみた」の続きです。]]></summary></entry><entry><title type="html">Azure App Service からプライベートエンドポイント経由の Azure SQL Database へパスワードレス Entra 認証を試してみた</title><link href="https://blog.mnrst.com/2025/02/15/appservice-sqldatabase-privateendpoint.html" rel="alternate" type="text/html" title="Azure App Service からプライベートエンドポイント経由の Azure SQL Database へパスワードレス Entra 認証を試してみた" /><published>2025-02-15T00:00:00+00:00</published><updated>2025-02-15T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/02/15/appservice-sqldatabase-privateendpoint</id><content type="html" xml:base="https://blog.mnrst.com/2025/02/15/appservice-sqldatabase-privateendpoint.html"><![CDATA[<p>今でも環境変数にパスワードを設定してプログラムから利用するの事があるのですが、時代の流れはパスワードレス。</p>

<p>プログラムからパスワードレスで Azure リソースにアクセスできる、マネージド ID が以前からあります。</p>

<p>このマネージド ID を使用して、Azure SQL Dataase への接続をパスワードレスで認証する事ができます。</p>

<p>そこで、Azure App Service の通常盤とコンテナー版でそれぞれ sqlcmd を使用して、インフラ的な動作を確認してみました。</p>

<h2 id="azure-app-sercice-通常盤の検証環境を構築">Azure App Sercice 通常盤の検証環境を構築</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">region</span><span class="o">=</span>japaneast
<span class="nv">prefix</span><span class="o">=</span>mnrappsql

az group create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--location</span> <span class="nv">$region</span>

az network vnet create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vnet</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--address-prefixes</span> 10.0.0.0/24

az network nsg create <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-nsg</span>

az network vnet subnet create <span class="se">\</span>
  <span class="nt">--name</span> app-subnet <span class="se">\</span>
  <span class="nt">--vnet-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vnet</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--address-prefixes</span> 10.0.0.0/26 <span class="se">\</span>
  <span class="nt">--network-security-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-nsg</span>

az network vnet subnet create <span class="se">\</span>
  <span class="nt">--name</span> sql-subnet <span class="se">\</span>
  <span class="nt">--vnet-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vnet</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--address-prefixes</span> 10.0.0.64/26 <span class="se">\</span>
  <span class="nt">--network-security-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-nsg</span> <span class="se">\</span>
  <span class="nt">--disable-private-endpoint-network-policies</span> <span class="nb">true

</span>az appservice plan create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-plan</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--is-linux</span> <span class="se">\</span>
  <span class="nt">--sku</span> B1

az webapp create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-app</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--plan</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-plan</span> <span class="se">\</span>
  <span class="nt">--assign-identity</span> <span class="se">\</span>
  <span class="nt">--runtime</span> <span class="s2">"JAVA:17-java17"</span>

az webapp config <span class="nb">set</span> <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-app</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--always-on</span> <span class="nb">true

</span>az webapp config access-restriction add <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-app</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--priority</span> 100 <span class="se">\</span>
  <span class="nt">--rule-name</span> MyIP <span class="se">\</span>
  <span class="nt">--action</span> Allow <span class="se">\</span>
  <span class="nt">--ip-address</span> <span class="si">$(</span>curl <span class="nt">-s</span> inet-ip.info<span class="si">)</span>

az webapp vnet-integration add <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-app</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--vnet</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vnet</span> <span class="se">\</span>
  <span class="nt">--subnet</span> app-subnet

<span class="c"># external-admin-sid は自分の Entra ユーザーオブジェクト ID に変更</span>
az sql server create <span class="se">\</span>
  <span class="nt">--enable-ad-only-auth</span> <span class="se">\</span>
  <span class="nt">--external-admin-principal-type</span> User <span class="se">\</span>
  <span class="nt">--external-admin-name</span> myname <span class="se">\</span>
  <span class="nt">--external-admin-sid</span> 9ae16906-c0ff-4599-9889-d67e4895d086 <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-sqlsrv</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span>

az sql db create <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--server</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-sqlsrv</span> <span class="se">\</span>
  <span class="nt">--name</span> sqldb <span class="se">\</span>
  <span class="nt">--service-objective</span> Basic <span class="se">\</span>
  <span class="nt">--backup-storage-redundancy</span> Local

az sql server firewall-rule create <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--server</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-sqlsrv</span> <span class="se">\</span>
  <span class="nt">--name</span> LocalAccess <span class="se">\</span>
  <span class="nt">--start-ip-address</span> <span class="si">$(</span>curl <span class="nt">-s</span> inet-ip.info<span class="si">)</span> <span class="se">\</span>
  <span class="nt">--end-ip-address</span> <span class="si">$(</span>curl <span class="nt">-s</span> inet-ip.info<span class="si">)</span>

az network private-endpoint create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-pe</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--vnet-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vnet</span> <span class="se">\</span>
  <span class="nt">--subnet</span> sql-subnet <span class="se">\</span>
  <span class="nt">--private-connection-resource-id</span> <span class="si">$(</span>az sql server show <span class="se">\</span>
    <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
    <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-sqlsrv</span> <span class="se">\</span>
    <span class="nt">--query</span> <span class="nb">id</span> <span class="se">\</span>
    <span class="nt">--output</span> tsv<span class="si">)</span> <span class="se">\</span>
  <span class="nt">--group-ids</span> sqlServer <span class="se">\</span>
  <span class="nt">--connection-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-conn</span>  

az network private-dns zone create <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--name</span> privatelink.database.windows.net

az network private-dns <span class="nb">link </span>vnet create <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--zone-name</span> privatelink.database.windows.net <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-link</span> <span class="se">\</span>
  <span class="nt">--virtual-network</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vnet</span> <span class="se">\</span>
  <span class="nt">--registration-enabled</span> <span class="nb">false

</span>az network private-endpoint dns-zone-group create <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--endpoint-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-pe</span> <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-zg</span> <span class="se">\</span>
  <span class="nt">--private-dns-zone</span> privatelink.database.windows.net <span class="se">\</span>
  <span class="nt">--zone-name</span> sql
</code></pre></div></div>

<h2 id="azure-ポータルの-sql-database-クエリエディターから-app-service-のマネージド-id-を許可">Azure ポータルの SQL Database クエリエディターから App Service のマネージド ID を許可</h2>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">USER</span> <span class="p">[</span><span class="n">mnrappsql</span><span class="o">-</span><span class="n">app</span><span class="p">]</span> <span class="k">FROM</span> <span class="k">EXTERNAL</span> <span class="n">PROVIDER</span><span class="p">;</span>
<span class="k">ALTER</span> <span class="k">ROLE</span> <span class="n">db_datareader</span> <span class="k">ADD</span> <span class="n">MEMBER</span> <span class="p">[</span><span class="n">mnrappsql</span><span class="o">-</span><span class="n">app</span><span class="p">];</span>
<span class="k">GO</span>
</code></pre></div></div>

<p><img src="/assets/img/2025-02-15-appservice-sqldatabase-privateendpoint-01.png" alt="2025-02-15-appservice-sqldatabase-privateendpoint-01.png" /></p>

<h2 id="azure-ポータルの-app-service-の-ssh-から動作確認">Azure ポータルの App Service の SSH から動作確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt update

apt <span class="nb">install</span> <span class="nt">-y</span> dnsutils bzip2 curl jq

<span class="c"># 名前解決の確認</span>
nslookup mnrappsql-sqlsrv.database.windows.net

Server:         127.0.0.11
Address:        127.0.0.11#53

Non-authoritative answer:
mnrappsql-sqlsrv.database.windows.net   canonical name <span class="o">=</span> mnrappsql-sqlsrv.privatelink.database.windows.net.
Name:   mnrappsql-sqlsrv.privatelink.database.windows.net
Address: 10.0.0.68

<span class="c"># 環境変数の確認</span>
<span class="nb">env</span> | <span class="nb">grep </span>IDENTITY

<span class="nv">IDENTITY_HEADER</span><span class="o">=</span>5021c390-f002-439e-9329-4755fd72da45
<span class="nv">IDENTITY_ENDPOINT</span><span class="o">=</span>http://169.254.130.3:8081/msi/token

<span class="c"># Azure Instance Metadata Service の確認</span>
curl <span class="nt">-H</span> x-identity-header:<span class="nv">$IDENTITY_HEADER</span> <span class="se">\</span>
  <span class="s2">"</span><span class="nv">$IDENTITY_ENDPOINT</span><span class="s2">?resource=https://database.windows.net&amp;api-version=2019-08-01"</span> <span class="se">\</span>
  | jq <span class="nb">.</span>

<span class="o">{</span>
  <span class="s2">"access_token"</span>: <span class="s2">"eyJ0e..........................SxYGQ"</span>,
  <span class="s2">"expires_on"</span>: <span class="s2">"1739660871"</span>,
  <span class="s2">"resource"</span>: <span class="s2">"https://database.windows.net"</span>,
  <span class="s2">"token_type"</span>: <span class="s2">"Bearer"</span>,
  <span class="s2">"client_id"</span>: <span class="s2">"1449f973-6cff-4d90-b184-ee0648cc2523"</span>
<span class="o">}</span>

<span class="c"># マネージド ID 認証対応版 sqlcmd をダウンロード</span>
wget https://github.com/microsoft/go-sqlcmd/releases/download/v1.8.2/sqlcmd-linux-amd64.tar.bz2

<span class="nb">tar </span>xvfj sqlcmd-linux-amd64.tar.bz2

<span class="c"># sqlcmd でマネージド ID 認証を使用して接続 </span>
./sqlcmd <span class="se">\</span>
  <span class="nt">-S</span> mnrappsql-sqlsrv.privatelink.database.windows.net <span class="se">\</span>
  <span class="nt">-d</span> sqldb <span class="se">\</span>
  <span class="nt">--authentication-method</span> ActiveDirectoryManagedIdentity

<span class="c"># SQL 実行</span>
<span class="k">select</span> @@version<span class="p">;</span>
go

<span class="nb">exit</span>
</code></pre></div></div>

<p><img src="/assets/img/2025-02-15-appservice-sqldatabase-privateendpoint-02.png" alt="2025-02-15-appservice-sqldatabase-privateendpoint-02.png" /></p>

<h2 id="azure-app-sercice-コンテナー版の検証環境を構築">Azure App Sercice コンテナー版の検証環境を構築</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az acr create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span>acr <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--sku</span> Basic <span class="se">\</span>
  <span class="nt">--admin-enabled</span> <span class="nb">true

mkdir</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span> <span class="o">&amp;&amp;</span> <span class="nb">cd</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span>

<span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="sh">"</span><span class="no">EOF</span><span class="sh">" &gt; Dockerfile
FROM ubuntu/nginx

RUN date &gt; /var/www/html/index.html

ENV SSH_PASSWD "root:Docker!"
RUN apt-get update </span><span class="se">\</span><span class="sh">
        &amp;&amp; apt-get install -y --no-install-recommends dialog </span><span class="se">\</span><span class="sh">
        &amp;&amp; apt-get update </span><span class="se">\</span><span class="sh">
  &amp;&amp; apt-get install -y --no-install-recommends openssh-server </span><span class="se">\</span><span class="sh">
  &amp;&amp; echo "</span><span class="nv">$SSH_PASSWD</span><span class="sh">" | chpasswd
COPY sshd_config /etc/ssh/
COPY init.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/init.sh

EXPOSE 80 2222
ENTRYPOINT ["init.sh"]
</span><span class="no">EOF

</span><span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="sh">"</span><span class="no">EOF</span><span class="sh">" &gt; sshd_config
Port 2222
ListenAddress 0.0.0.0
LoginGraceTime 180
X11Forwarding yes
Ciphers aes128-cbc,3des-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr
MACs hmac-sha1,hmac-sha1-96
StrictModes yes
SyslogFacility DAEMON
PasswordAuthentication yes
PermitEmptyPasswords no
PermitRootLogin yes
</span><span class="no">EOF

</span><span class="nb">cat</span> <span class="o">&lt;&lt;</span><span class="sh">"</span><span class="no">EOF</span><span class="sh">" &gt; init.sh
#!/bin/bash
set -e

eval </span><span class="si">$(</span><span class="nb">printenv</span> | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s2">"s/^</span><span class="se">\(</span><span class="s2">[^=]</span><span class="se">\+\)</span><span class="s2">=</span><span class="se">\(</span><span class="s2">.*</span><span class="se">\)</span><span class="nv">$/</span><span class="s2">export </span><span class="se">\1</span><span class="s2">=</span><span class="se">\2</span><span class="s2">/p"</span> | <span class="nb">sed</span> <span class="s1">'s/"/\\\"/g'</span> | <span class="nb">sed</span> <span class="s1">'/=/s//="/'</span> | <span class="nb">sed</span> <span class="s1">'s/$/"/'</span> <span class="o">&gt;&gt;</span> /etc/profile<span class="si">)</span><span class="sh">

service ssh start

nginx -g "daemon off;"
</span><span class="no">EOF

</span>az acr build <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--registry</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span>acr <span class="se">\</span>
  <span class="nt">--image</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span> <span class="se">\</span>
  <span class="nb">.</span>

az webapp create <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-web</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--plan</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-plan</span> <span class="se">\</span>
  <span class="nt">--assign-identity</span> <span class="se">\</span>
  <span class="nt">--deployment-container-image-name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span>acr.azurecr.io/<span class="k">${</span><span class="nv">prefix</span><span class="k">}</span>:latest <span class="se">\</span>
  <span class="nt">--docker-registry-server-user</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span>acr <span class="se">\</span>
  <span class="nt">--docker-registry-server-password</span> <span class="si">$(</span>az acr credential show <span class="se">\</span>
    <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
    <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span>acr <span class="se">\</span>
    <span class="nt">--query</span> <span class="s2">"passwords[0].value"</span> <span class="se">\</span>
    <span class="nt">--output</span> tsv<span class="si">)</span>

az webapp config <span class="nb">set</span> <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-web</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--always-on</span> <span class="nb">true

</span>az webapp config access-restriction add <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-web</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--priority</span> 100 <span class="se">\</span>
  <span class="nt">--rule-name</span> MyIP <span class="se">\</span>
  <span class="nt">--action</span> Allow <span class="se">\</span>
  <span class="nt">--ip-address</span> <span class="si">$(</span>curl <span class="nt">-s</span> inet-ip.info<span class="si">)</span>

az webapp vnet-integration add <span class="se">\</span>
  <span class="nt">--name</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-web</span> <span class="se">\</span>
  <span class="nt">--resource-group</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-rg</span> <span class="se">\</span>
  <span class="nt">--vnet</span> <span class="k">${</span><span class="nv">prefix</span><span class="k">}</span><span class="nt">-vnet</span> <span class="se">\</span>
  <span class="nt">--subnet</span> app-subnet
</code></pre></div></div>

<h2 id="azure-ポータルの-sql-database-クエリエディターから追加した-app-service-のマネージド-id-を許可">Azure ポータルの SQL Database クエリエディターから追加した App Service のマネージド ID を許可</h2>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">USER</span> <span class="p">[</span><span class="n">mnrappsql</span><span class="o">-</span><span class="n">web</span><span class="p">]</span> <span class="k">FROM</span> <span class="k">EXTERNAL</span> <span class="n">PROVIDER</span><span class="p">;</span>
<span class="k">ALTER</span> <span class="k">ROLE</span> <span class="n">db_datareader</span> <span class="k">ADD</span> <span class="n">MEMBER</span> <span class="p">[</span><span class="n">mnrappsql</span><span class="o">-</span><span class="n">web</span><span class="p">];</span>
<span class="k">GO</span>
</code></pre></div></div>

<h2 id="azure-ポータルの-app-service-の-ssh-からコンテナー版の動作確認">Azure ポータルの App Service の SSH からコンテナー版の動作確認</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt update

apt <span class="nb">install</span> <span class="nt">-y</span> dnsutils bzip2 curl jq wget

nslookup mnrappsql-sqlsrv.database.windows.net

Server:         127.0.0.11
Address:        127.0.0.11#53

Non-authoritative answer:
mnrappsql-sqlsrv.database.windows.net   canonical name <span class="o">=</span> mnrappsql-sqlsrv.privatelink.database.windows.net.
Name:   mnrappsql-sqlsrv.privatelink.database.windows.net
Address: 10.0.0.68

<span class="nb">env</span> | <span class="nb">grep </span>IDENTITY
<span class="nv">IDENTITY_HEADER</span><span class="o">=</span>da89ad08-dbf1-4852-b920-c672389c3b9d
<span class="nv">IDENTITY_ENDPOINT</span><span class="o">=</span>http://169.254.131.5:8081/msi/token

curl <span class="nt">-H</span> x-identity-header:<span class="nv">$IDENTITY_HEADER</span> <span class="se">\</span>
  <span class="s2">"</span><span class="nv">$IDENTITY_ENDPOINT</span><span class="s2">?resource=https://database.windows.net&amp;api-version=2019-08-01"</span> <span class="se">\</span>
  | jq <span class="nb">.</span>

<span class="o">{</span>
  <span class="s2">"access_token"</span>: <span class="s2">"eyJ0e..........................T66mHg"</span>,
  <span class="s2">"expires_on"</span>: <span class="s2">"1739661867"</span>,
  <span class="s2">"resource"</span>: <span class="s2">"https://database.windows.net"</span>,
  <span class="s2">"token_type"</span>: <span class="s2">"Bearer"</span>,
  <span class="s2">"client_id"</span>: <span class="s2">"18167057-ac51-4a5b-813b-1b766ec3668d"</span>
<span class="o">}</span>

wget https://github.com/microsoft/go-sqlcmd/releases/download/v1.8.2/sqlcmd-linux-amd64.tar.bz2

<span class="nb">tar </span>xvfj sqlcmd-linux-amd64.tar.bz2

./sqlcmd <span class="se">\</span>
  <span class="nt">-S</span> mnrappsql-sqlsrv.privatelink.database.windows.net <span class="se">\</span>
  <span class="nt">-d</span> sqldb <span class="se">\</span>
  <span class="nt">--authentication-method</span> ActiveDirectoryManagedIdentity

<span class="k">select</span> @@version<span class="p">;</span>
go

<span class="nb">exit</span>
</code></pre></div></div>

<h2 id="参考">参考</h2>

<p><a href="https://qiita.com/mnrst/items/d19d39b30592c9250542">Azure Web App for Containers で Azure Container Registry と連携したお手軽 CI/CD を試してみた</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[今でも環境変数にパスワードを設定してプログラムから利用するの事があるのですが、時代の流れはパスワードレス。]]></summary></entry><entry><title type="html">Mac に仮想化ソフトの QEMU を入れて VyOS のディスクイメージを作成してみた</title><link href="https://blog.mnrst.com/2025/02/08/qemu-vyos.html" rel="alternate" type="text/html" title="Mac に仮想化ソフトの QEMU を入れて VyOS のディスクイメージを作成してみた" /><published>2025-02-08T00:00:00+00:00</published><updated>2025-02-08T00:00:00+00:00</updated><id>https://blog.mnrst.com/2025/02/08/qemu-vyos</id><content type="html" xml:base="https://blog.mnrst.com/2025/02/08/qemu-vyos.html"><![CDATA[<p>Mac で無料の仮想化ソフトといえば、VirtualBox や VMware Fusion ですが、QEMU という選択肢もあります。</p>

<p>そこで、QEMU を使用して、無料のソフトウェアルーターとして有名な VyOS のディスクイメージを作成してみました。</p>

<h2 id="qemu-をインストール">QEMU をインストール</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>qemu
</code></pre></div></div>

<h2 id="vyos-の-iso-イメージをダウンロード">VyOS の ISO イメージをダウンロード</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://github.com/vyos/vyos-nightly-build/releases/download/1.5-rolling-202502030007/vyos-1.5-rolling-202502030007-generic-amd64.iso
</code></pre></div></div>

<h2 id="qemu-用の空のディスクイメージを作成">QEMU 用の空のディスクイメージを作成</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-img create <span class="nt">-f</span> qcow2 vyos-202502030007.qcow2 2G
</code></pre></div></div>

<h2 id="iso-イメージから仮想マシンを起動">ISO イメージから仮想マシンを起動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-x86_64 <span class="se">\</span>
  <span class="nt">-m</span> 2048 <span class="se">\</span>
  <span class="nt">-hda</span> vyos-202502030007.qcow2 <span class="se">\</span>
  <span class="nt">-cdrom</span> vyos-1.5-rolling-202502030007-generic-amd64.iso <span class="se">\</span>
  <span class="nt">-boot</span> d
</code></pre></div></div>

<p>起動直後の画面。</p>

<p><img src="/assets/img/2025-02-08-qemu-vyos-01.png" alt="qemu-vyos-01.png" /></p>

<p>仮想マシンにログイン直後の画面。</p>

<p><img src="/assets/img/2025-02-08-qemu-vyos-02.png" alt="qemu-vyos-02.png" /></p>

<h2 id="空のディスクに-vyos-をインストールして電源オフ">空のディスクに VyOS をインストールして電源オフ</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">install </span>image

poweroff
</code></pre></div></div>

<h2 id="ディスクから仮想マシンを起動">ディスクから仮想マシンを起動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-x86_64 <span class="se">\</span>
  <span class="nt">-m</span> 2048 <span class="se">\</span>
  <span class="nt">-hda</span> vyos-202502030007.qcow2
</code></pre></div></div>

<p>起動直後の画面。</p>

<p><img src="/assets/img/2025-02-08-qemu-vyos-03.png" alt="qemu-vyos-03.png" /></p>

<h2 id="参考">参考</h2>

<p><a href="https://www.qemu.org/">QEMU</a></p>

<p><a href="https://vyos.net/get/nightly-builds/">VyOS rolling release</a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[Mac で無料の仮想化ソフトといえば、VirtualBox や VMware Fusion ですが、QEMU という選択肢もあります。]]></summary></entry></feed>