<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop</title>
	<atom:link href="https://zohead.com/feed" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>使用 Lade 云开发平台部署容器应用</title>
		<link>https://zohead.com/archives/lade-cloud/</link>
		<comments>https://zohead.com/archives/lade-cloud/#comments</comments>
		<pubDate>Mon, 28 Apr 2025 15:35:39 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[Lade]]></category>
		<category><![CDATA[Miniflux]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[云平台]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[数据库]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1948</guid>
		<description><![CDATA[关于 Lade 云开发平台 最近了解到一个比较新的 Lade 云开发平台，Lade 是专为开发者设计的云端平台，可以帮助开发者在云端部署测试应用，并提供基础的免费应用额度，还有多处数据中心服务器可供选择，还默认直接支持 HTTPS 域名以方便连接访问容器应用。 Lade 开发平台默认支持以下编程语言： Go Node.js PHP Python Ruby 以及以下这些常用的数据库： MariaDB Memcached MongoDB MySQL PostgreSQL Redis 对于其它编程语言，Lade 还支持以 Dockerfile 形式运行测试应用容器。 首先可以在 Lade 官方网站注 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="about-lade-cloud">关于 Lade 云开发平台</h2>
<p>最近了解到一个比较新的 <a href="https://www.lade.io/">Lade</a> 云开发平台，Lade 是专为开发者设计的云端平台，可以帮助开发者在云端部署测试应用，并提供基础的免费应用额度，还有多处数据中心服务器可供选择，还默认直接支持 HTTPS 域名以方便连接访问容器应用。</p>
<p>Lade 开发平台默认支持以下编程语言：</p>
<ul>
<li>Go</li>
<li>Node.js</li>
<li>PHP</li>
<li>Python</li>
<li>Ruby</li>
</ul>
<p>以及以下这些常用的数据库：</p>
<ul>
<li>MariaDB</li>
<li>Memcached</li>
<li>MongoDB</li>
<li>MySQL</li>
<li>PostgreSQL</li>
<li>Redis</li>
</ul>
<p>对于其它编程语言，Lade 还支持以 Dockerfile 形式运行测试应用容器。</p>
<p>首先可以在 Lade 官方网站注册账户，然后需要绑定并验证 GitHub 账号后才能创建部署容器应用。</p>
<h2 id="lade-cli">Lade CLI 部署应用</h2>
<p>Lade 网站目前没有提供任何容器应用的部署和管理的功能，容器应用的部署和管理都需要使用 <a href="https://www.lade.io/docs/platform/cli">Lade CLI</a> 工具来完成。</p>
<p>这里以 Linux 系统为例进行简单的介绍，按照页面介绍来安装 Lade CLI：</p>
<pre class="brush: bash; title: ; notranslate">
~# curl -L https://github.com/lade-io/lade/releases/latest/download/lade-linux-amd64.tar.gz | tar xz
~# sudo mv lade /usr/local/bin
</pre>
<p>首先使用 CLI 登录 Lade 账户：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade login
Enter your Lade credentials:
? Username or email: zohead
? Password: 
Logged in as zohead*
</pre>
<p>输入用户名和密码登录成功之后就可以进行创建部署容器应用了，这次我准备先部署一个 <a href="https://miniflux.app/">Miniflux</a> 应用来试试效果。</p>
<p>Miniflux 是一个极简的 RSS 阅读器应用，基于 Go 语言开发，使用 Postgres 数据库保存数据，与其它 RSS 阅读器应用相比功能和界面都很简洁，不过关键还支持抓取 RSS 文章全文、全文检索等功能。</p>
<p>Lade 的收费方式可以参考官网 <a href="https://www.lade.io/pricing">Pricing</a> 页面，并有基础的免费额度，当然免费的额度也是随时有可能取消的：</p>
<ul>
<li>应用：
<ul>
<li>128 MB 内存；</li>
<li>1 CPU；</li>
</ul>
</li>
<li>数据库：
<ul>
<li>128 MB 内存；</li>
<li>1 GB 存储空间；</li>
</ul>
</li>
<li>磁盘：
<ul>
<li>1 GB 磁盘。</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>备注：</strong></p>
<p>2025-06-07 开始 Lade 平台正式取消了免费额度，目前最便宜的 128 MB 内存 / 1 CPU 的应用价格为每月 $1.25，大家自行酌情选择合适的容器收费配置计划。</p>
</blockquote>
<h3 id="app-container">应用容器</h3>
<p>我们首先使用 <code>lade apps create</code> 命令创建新的应用容器，按照提示输入应用容器名称，并选择容器配置和区域即可完成创建，这里就选择免费配置计划和日本东京区域：</p>
<blockquote>
<p><strong>提示：</strong></p>
<ul>
<li>容器名称非常关键，后面很多命令都需要用到，请谨慎输入；</li>
<li>请根据实际应用程序对内存和 CPU 的要求，选择合适的应用配置计划；</li>
<li>免费额度随时有可能取消，如果使用免费配置计划，请注意备份应用数据。</li>
</ul>
</blockquote>
<pre class="brush: bash; title: ; notranslate">
~# lade apps create
? App Name: miniflux
? Plan: 128mb
? Region: Tokyo
</pre>
<p>创建成功之后，可以运行 <code>lade apps list</code> 命令查看容器列表：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade apps list
NAME        PLAN     CREATED           STATUS
miniflux    128mb    43 minutes ago    running
</pre>
<p>另外使用 <code>lade apps show</code> 命令可以查看具体某个应用容器的详细信息和状态，下方输出数据最后的 <code>Web URL</code> 就是 Lade 云开发平台为容器应用自动分配的 HTTPS 访问域名：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade apps show miniflux
Owner:        XXX@XXX.com
Processes:    web: 0/1
Plan:         128mb
Region:       Tokyo
Status:       running
Web URL:      miniflux-zohead.ladeapp.com
</pre>
<p>接着我们就需要在容器上部署实际的应用了，对于 Miniflux 可以从 GitHub 检出最新 v2 版本的代码：</p>
<pre class="brush: bash; title: ; notranslate">
~# mkdir miniflux
~# cd miniflux
~/miniflux# git clone --depth 1 https://github.com/miniflux/v2.git
</pre>
<p>然后使用 <code>lade deploy</code> 命令进行容器应用部署：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade deploy --app miniflux
time=&quot;2025-04-24T20:28:59+08:00&quot; level=error msg=&quot;Can't add file internal/reader/readability/testdata to tar: archive/tar: unknown file mode ?rw-rw-rw-&quot;
Sending build context to Docker daemon  4.563MB
Step 1/11 : FROM golang:1.23.0
1.23.0: Pulling from library/golang
Successfully built e7ae4ac3677b
Successfully tagged registry.lade.io/zohead/miniflux:build-1njzwaheu4
Pushing miniflux build to registry
Build finished use &quot;lade logs -a miniflux -f&quot; to view app logs
</pre>
<p>容器应用部署成功之后，我们可以使用 <code>lade ps</code> 命令确认容器内的进程是否运行正常，可以看到 Lade 部署生成的 <code>miniflux.app</code> 应用程序进程正在运行：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade ps -a miniflux
NAME     PLAN     STARTED         COMMAND
web.1    128mb    14 hours ago    miniflux.app
</pre>
<p>我们还可以使用 <code>lade run</code> 命令实现在应用容器中运行需要的命令，这里就直接运行一个 Bash Shell，运行自定义命令也需要指定一个配置计划，这里还是使用免费配置计划：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade run &quot;bash&quot; -a miniflux -p 128mb
</pre>
<p>容器内的 Bash Shell 启动成功之后，我们可以查看并确认 Miniflux 应用信息，对于 Miniflux 这种 Go 语言应用，Lade 默认部署生成的应用程序在 <code>/go/bin</code> 目录下：</p>
<pre class="brush: bash; title: ; notranslate">
~$ ls -l /go/bin/miniflux.app
-rwxr-xr-x 1 web web 19411204 Apr 24 12:30 /go/bin/miniflux.app
~$ miniflux.app -i
Version: dev
Commit: HEAD
Build Date: undefined
Go Version: go1.23.0
Compiler: gc
Arch: amd64
OS: linux
</pre>
<p>另外根据上面部署命令输出中的提示信息，使用 <code>lade logs -a miniflux -f</code> 命令可以跟踪显示容器应用的运行日志，实际上就是显示容器内应用程序的标准输出和错误输出：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade logs -a miniflux -f
web.1 | level=INFO msg=&quot;The default value for DATABASE_URL is used&quot;
web.1 | dial tcp [::1]:5432: connect: connection refused
</pre>
<p>由于还没有为 Miniflux 应用配置数据库，会出现类似上面的报错信息。</p>
<h3 id="database-addon">数据库扩展</h3>
<p>接着我们需要使用 <code>lade addons create</code> 命令为容器应用配置数据库扩展，下面的 <code>lade addons create postgres</code> 命令即为创建 PostgreSQL 数据库，我仍然使用免费配置计划，并选择和应用相同位置的日本东京区域，以加快数据库访问速度：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade addons create postgres
? Addon Name: miniflux-postgres
? Plan: 128mb
? Region: Tokyo
? Version: 17
? Public: No
</pre>
<p>简单说明一下命令参数：</p>
<ul>
<li><code>Addon Name</code> 为数据库扩展名称，后续扩展管理相关命令也会用到；</li>
<li><code>Version</code> 为数据库版本，<code>lade</code> 命令会列出支持的数据库版本；</li>
<li><code>Public</code> 表示数据库是否允许外部公共访问，我只需要 PostgreSQL 数据库被 Miniflux 应用访问，并不需要公共访问，这里就选 <strong>No</strong> 了。</li>
</ul>
<p>创建成功之后，就可以使用 <code>lade addons show</code> 命令查看数据库扩展的详细信息和状态了：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade addons show miniflux-postgres
Owner:        XXX@XXX.com
Service:      PostgreSQL
Plan:         128mb
Region:       Tokyo
Version:      17
Public:       No
Status:       running
Addon URI:    postgresql://USERNAME:PASSWORD@tyo-sw1.lade.io:30066/XXXX?sslmode=require
</pre>
<p>最后的 <code>Addon URI</code> 就是最关键的数据库连接地址，我们需要为应用容器配置数据库连接地址，地址中包含数据库服务器地址、用户名、密码、数据库名称等数据。</p>
<p>确认数据库运行正常之后，就可以将 PostgreSQL 数据库扩展附加到 Miniflux 应用容器：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade addons attach miniflux-postgres --app miniflux
? Env Name: POSTGRES_URL
</pre>
<p>然后使用 <code>lade env list</code> 命令检查应用容器的环境变量：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade env list -a miniflux
DATABASE_URL=postgresql://USERNAME:PASSWORD@tyo-sw1.lade.io:30066/XXXX?sslmode=require
POSTGRES_URL=postgresql://USERNAME:PASSWORD@tyo-sw1.lade.io:30066/XXXX?sslmode=require
</pre>
<p>可以看到数据库扩展附加之后，会自动为应用容器配置数据库环境变量，包含通用的 <code>DATABASE_URL</code> 和 PostgreSQL 专用的 <code>POSTGRES_URL</code> 环境变量。</p>
<h2 id="miniflux-app-issue">Miniflux 应用容器问题</h2>
<p>我原以为按照上面的步骤为 Miniflux 应用容器配置好 PostgreSQL 数据库扩展之后，Miniflux 应用就可以正常运行了，而实际上容器仍然有报错：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade logs -a miniflux -f
web.1 | the database schema is not up to date: current=v0 expected=v108
</pre>
<p>参考 Miniflux 项目 <a href="https://github.com/miniflux/v2/issues/1680">You must run the SQL migrations, the database schema is not up to date: current=v0 expected=v62</a> Issue 的说明，对于类似 Docker 容器的环境 Miniflux 需要开启 <code>RUN_MIGRATIONS</code> 环境变量。</p>
<p>我们可以使用 <code>lade env set</code> 命令为容器配置或修改环境变量：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade env set RUN_MIGRATIONS=1 -a miniflux
</pre>
<p>稍等一会容器应用自动重启后，就可以看到 Miniflux 应用已经正常运行了：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade logs -a miniflux -f
web.1 | level=INFO msg=&quot;Running database migrations&quot; current_version=0 latest_version=108 driver=postgresql
web.1 | level=INFO msg=&quot;Starting HTTP server&quot; listen_address=:3000
</pre>
<p>现在我们其实就已经可以通过 Lade 云平台为容器应用分配的域名访问 Miniflux Web 管理界面了，当然还是需要用 HTTPS 访问的：</p>
<p><img src="https://images.weserv.nl/?url=https://res.cloudinary.com/digwht2y0/image/upload/v1752420850/lade-miniflux.png" alt="Miniflux Web 管理界面"></p>
<p>不过由于我们还没有为 Miniflux 配置管理员账户，还不能直接登录。</p>
<p>这个时候就需要借助 <code>lade run</code> 命令在应用容器中运行命令创建管理员账户了，下面输出中的 <code>miniflux.app</code> 程序是在容器内运行的：</p>
<pre class="brush: bash; title: ; notranslate">
~$ miniflux.app -create-admin
Enter Username: admin
Enter Password:
level=INFO msg=&quot;Created new admin user&quot; username=admin user_id=1
</pre>
<p>管理员账户创建成功之后，我们就可以通过容器域名访问 Miniflux Web 管理界面进行 RSS 源的配置和订阅管理了。</p>
<p>当然我们也可以开始就通过指定环境变量让 Miniflux 容器自动创建管理员账户，与上面的 <code>RUN_MIGRATIONS</code> 环境变量配合起来就类似：</p>
<pre class="brush: bash; title: ; notranslate">
~# lade env set RUN_MIGRATIONS=1 CREATE_ADMIN=1 ADMIN_USERNAME=admin ADMIN_PASSWORD=password -a miniflux
</pre>
<p>这样通过环境变量指定管理员账户名称和密码也很方便。</p>
<h2 id="summary ">总结</h2>
<p>本文简单介绍了如何使用 Lade 云开发平台的 <code>lade</code> CLI 命令部署配置容器应用，我部署的 Miniflux RSS 阅读器容器应用也运行了一段时间，Lade 云平台还是比较稳定的，只是没有国内的数据中心，访问速度倒是一般。</p>
<p>最后 Lade 云平台的开发者已经确认，目前提供的免费额度仅供开发人员测试使用，免费额度后续随时有可能取消，希望大家不要滥用，如果觉得 Lade 好用也可以考虑付费使用哦，祝大家玩的开心。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/lade-cloud/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>5ber eSIM手机卡上手体验</title>
		<link>https://zohead.com/archives/5ber-esim/</link>
		<comments>https://zohead.com/archives/5ber-esim/#comments</comments>
		<pubDate>Sat, 28 Oct 2023 07:10:07 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[数码]]></category>
		<category><![CDATA[5ber]]></category>
		<category><![CDATA[eSIM]]></category>
		<category><![CDATA[eskimo]]></category>
		<category><![CDATA[Magticom]]></category>
		<category><![CDATA[SIM]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1877</guid>
		<description><![CDATA[关于 5ber eSIM 由于某些原因，目前国内的智能手机基本都不支持 eSIM 功能，例如美版 iPhone 很早就已经没有实体 SIM 卡槽了，而中国大陆的 iPhone 则一直不支持 eSIM。像我现在使用的红米 K40 手机当然也不支持，而小米也有不少海外版机型也已经支持 eSIM 了。 5ber eSIM 则是一种特殊的物理手机卡，其最主要的功能就是让不支持 eSIM 的智能手机也能用上 eSIM，差不多相当于 eSIM.me 的平价替代品。不像 eSIM.me 只能在官网购买之后国际直邮或快递到手，5ber eSIM 有国内的小红书之类的店铺，国内购买起来就方便多了。 需要注意 5 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="about-5ber-esim">关于 5ber eSIM</h2>
<p>由于某些原因，目前国内的智能手机基本都不支持 eSIM 功能，例如美版 iPhone 很早就已经没有实体 SIM 卡槽了，而中国大陆的 iPhone 则一直不支持 eSIM。像我现在使用的红米 K40 手机当然也不支持，而小米也有不少海外版机型也已经支持 eSIM 了。</p>
<p><a href="https://esim.5ber.com/">5ber eSIM</a> 则是一种特殊的物理手机卡，其最主要的功能就是让不支持 eSIM 的智能手机也能用上 eSIM，差不多相当于 <a href="https://esim.me/">eSIM.me</a> 的平价替代品。不像 <a href="http://eSIM.me">eSIM.me</a> 只能在官网购买之后国际直邮或快递到手，5ber eSIM 有国内的小红书之类的店铺，国内购买起来就方便多了。</p>
<p>需要注意 5ber eSIM 手机卡本身并不包含任何套餐，直接插在手机上也没有信号或号码，用户需要在运营商处购买 eSIM 号码或套餐，然后在 5ber.eSIM 手机 App 中激活相应的 eSIM profile。</p>
<p>5ber eSIM 官网的 <a href="https://esim.5ber.com/order">下单</a> 页面目前包含两种套餐：</p>
<ul>
<li>Standard：价格 $12，支持两个免费 eSIM profile，超过两个需要付费；</li>
<li>Premium：价格 $25，eSIM profile 数量无限制（但手机卡本身最多只支持 15 个 eSIM profile）。</li>
</ul>
<p>我购买的是没有划分套餐之前的手机卡，实际上就是现在的 Premium 套餐。如果你也想试试 5ber eSIM，务必在购买下单之前先安装 <a href="https://play.google.com/store/apps/details?id=com.ifreegroup.moesim">5ber.eSIM</a> App 来检查你的 Android 手机（没错，5ber eSIM 只支持 Android 手机）是支持 5ber eSIM 管理的。</p>
<p>例如我的红米 K40 手机虽然支持双卡，但 5ber.eSIM App 显示只有 SIM 1 是支持 eSIM 管理的，因此购买的 5ber eSIM 手机卡也必须安装在 SIM 1 卡槽才能使用：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737369205/5ber-esim-devinfo.jpg" alt="5ber eSIM 卡槽"></p>
<p>这个就是我买的早期的 5ber eSIM 包装：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737369507/5ber-esim-package.jpg" alt="5ber eSIM 包装"></p>
<p>内部也很简单，就是手机卡和取卡针，另外附带了 3 个 SIM 卡袋子，实践证明这 3 个 SIM 卡袋子在出国的时候用来装原有的 SIM 卡还是很好用的：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737369506/5ber-esim-inner.jpg" alt="5ber eSIM 手机卡"></p>
<p>插上 5ber eSIM 手机卡之后，打开 App 就会默认显示 eSIM 管理界面：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737096949/blog/5ber-app-main.jpg" alt="5ber.eSIM App"></p>
<p>默认是没有任何 eSIM profile 的，可以点击右下角的按钮添加 eSIM profile，支持调用相机来扫描 eSIM 激活二维码。你也可以从相册中选择二维码照片添加，这点其实也比较方便，因为有的时候 eSIM 二维码可能是已经保存到手机相册里了。</p>
<h2 id="esim-test">eSIM 测试</h2>
<p>国庆假期我和夫人规划了去格鲁吉亚旅行的行程，所以我就考虑直接在当地换为 5ber eSIM 手机卡，并配合使用格鲁吉亚当地支持 eSIM 的运营商，这样也就不用买临时的实体 SIM 卡了。</p>
<h3 id="eskimo">eskimo</h3>
<p>为了保险起见我还是决定在出国之前先测试一下 5ber eSIM 手机卡的功能是否正常，为此我先申请了 <a href="https://eskimo.travel/ZHIMING50883">eskimo</a> 这个多国漫游的 eSIM 服务商。</p>
<p>eskimo 目前支持在 80 多个国家（中国和我要去的格鲁吉亚都在内）漫游使用，其官方网站和手机 App 上都可以查询到，用户购买的任意数据套餐都是 2 年内有效。</p>
<p>不过 eskimo 本身其实并不是移动运营商，只是跟众多国家的运营商进行合作，另外 eskimo eSIM 只支持数据功能，并没有电话号码，也不支持电话和短信等功能。</p>
<p>通过 Google Play 商店下载安装 <a href="https://play.google.com/store/apps/details?id=travel.eskimo.esim">eskimo App</a> 就可以开始使用了。</p>
<p>eskimo 有个好处就是有免费赠送的少量数据流量，临时拿来爬个墙还是可以的 ^_^，读者如果刚好也有需要，可以通过我的<a href="https://eskimo.travel/ZHIMING50883">邀请链接</a> 在 eskimo 官方网站进行注册，也可以在 eskimo App 中注册时使用我的推荐代码：<strong>ZHIMING50883</strong>，这样就能获取 500MB 的全球数据流量了。</p>
<p>eskimo 的手机 App 还是很简单易用的，首页就是 eSIM 套餐的介绍，点击下方的 <strong>数据</strong> 就可以显示流量使用情况：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737370815/eskimo-app-main.jpg" alt="eskimo App"></p>
<blockquote>
<p><strong>提示</strong></p>
<p>在 eskimo App 首页还可以点击 <strong>优惠券</strong> 按钮，然后输入优惠代码来获取更多免费的数据流量，例如使用 <strong>ESKIMO4U</strong> 或 <strong>DBTF-1</strong> 这两个优惠代码都可以额外再获取 1GB 的数据流量，不过这些优惠代码可不保证一直有效哦。</p>
</blockquote>
<p>点击 App 的 <strong>账户</strong> 界面，然后进入 <strong>网络覆盖范围</strong> 就可以看到 eskimo 支持的国家和地区了，可以看到 eskimo 在国内实际是用的联通 4G 网络：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737371060/eskimo-net-cover.jpg" alt="eskimo 网络覆盖"></p>
<p>如果手机本身不支持 eSIM，即使插上了 5ber eSIM 手机卡，eskimo App 也会报错，截图里的 POCO F3 其实就是我用的红米 K40 手机的海外型号：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737370917/eskimo-esim-err.jpg" alt="eskimo 报错"></p>
<p>这个时候就需要联系客服帮助进行手工激活了，在账户界面进入 <strong>帮助中心</strong>，找到最下面的 <strong>Message Us</strong> 按钮，就可以与在线客服联系：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737370814/eskimo-app-help.jpg" alt="eskimo 在线客服"></p>
<p>我们只需要用英语简单说明一下情况：大概就是我们的手机确实不支持 eSIM，但是装了支持 eSIM 功能的 5ber eSIM 手机卡；一般很快客服就会让你把 5ber eSIM 的设备信息截图发给他。</p>
<p>按照要求把 5ber.eSIM App 中的信息截图发送给在线客服之后，应该就能收到 eskimo 的 eSIM QR Code 邮件了。</p>
<p>然后我们再打开 5ber.eSIM App，通过扫描邮件中的二维码进行激活 eskimo eSIM 的操作，由于激活 eSIM 的时候需要联网下载 eSIM profile，因此需要有一个稳定的 Wi-Fi 连接。</p>
<p>激活成功之后，5ber.eSIM App 里就能看到新的 eSIM profile，我们还可以对新增的 eSIM 进行命名以及标注：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737096833/blog/5ber-eskimo.jpg" alt="5ber eskimo eSIM"></p>
<p>最后我们就可以在 5ber.eSIM App 中开启 eskimo eSIM 了，不过目前 App 还存在一些问题，可能会在开启 eSIM 时会报错，此时需要开启再关闭手机的飞行模式或者重启手机才能开启成功。</p>
<p>eskimo eSIM 使用时可能需要开启手机上的 <strong>漫游时允许数据网络</strong> 的选项，成功开启之后，我简单试了试访问 Google 之类的网站都没有问题。虽然 eskimo 在国内走的是联通网络，但实际看到的 IP 地址显示是新加坡的：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737371060/eskimo-ip.jpg" alt="eskimo IP 地址"></p>
<h3 id="magticom">Magticom</h3>
<p>eskimo 在格鲁吉亚使用的就是 Cellfie 网络，不过由于没有号码和电话、短信功能，可能还会有点不太方便，因此这次我准备只把 eskimo 做备用。</p>
<p>Magticom 是格鲁吉亚目前最大的电信运营商，信号覆盖相对于 Silknet、Cellfie 也更好，这次旅行我就提前在国内购买好 Magticom 的 eSIM 号码和套餐，到了格鲁吉亚就可以直接通过 5ber.eSIM 开启使用了。</p>
<p>Magticom <a href="https://www.magticom.ge/en/mobile/mobile-services/esim-service">官网</a> 有其 eSIM 的相关介绍，我们需要先注册一个 <a href="https://www.magticom.ge/en/mymagti">MyMagti</a> 账户，然后就可以进入 <a href="https://www.magticom.ge/en/mymagti/online-order/number-activation">Purchase a mobile number</a> 页面，我对靓号什么的没什么需求，因此 <strong>Price (Max)</strong> 选为 0 之后，点 <strong>CHECK</strong> 按钮就可以进行选号了：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737372294/magti-number.png" alt="Magticom 选号"></p>
<p>确认下一步之后，就可以选择 SIM 卡类型，这里我们就选择 eSIM，可以看到 eSIM 和物理 SIM 卡都是需要收费 10 格鲁吉亚拉里：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737372598/magti-sim-type.png" alt="Magticom eSIM"></p>
<p>Magticom 目前只支持使用 VISA / MasterCard 卡以及美国运通卡进行付款，付款之前就是最关键的实名认证环节。</p>
<p>身为外国人，我们就只能使用护照进行身份认证了，没有意外实名认证成功之后，就可以使用信用卡付款了，付款成功之后，注册 MyMagti 账户的邮箱就会收到购买的新手机号码的邮件，其中包含具体手机号码还有最关键的 eSIM QR Code 激活二维码，虽然邮件是全格鲁吉亚语的，但翻译之后还是能大体看明白的。</p>
<p>另外 Magticom 也提供了 <a href="https://play.google.com/store/apps/details?id=com.mymagti">MyMagti</a> 手机 App，安装之后使用 MyMagti 账户进行登录，就能看到新购买的手机号，而且也能显示 eSIM QR Code 激活二维码。</p>
<p>MyMagti 手机 App 上还可以查询手机号的有效期、具体使用量，并能直接购买新的套餐，这里建议使用其 <a href="https://www.magticom.ge/en/mobile/tariffs/cocktail">Cocktail</a> 自选混合套餐，这样我们就可以根据实际的旅行需求自行选择有效期、流量、通话分钟数、短信条数。我买的是 30 天有效期、5GB 流量、60 分钟通话的自选套餐，价格为 16.7 格鲁吉亚拉里，比买固定的套餐还是要实惠一些的。</p>
<h2 id="summary">总结</h2>
<p>我这次在格鲁吉亚差不多 10 天假期手机都是插的 5ber eSIM 手机卡，基本都是使用 Magticom 的 eSIM，偶尔会切到 eskimo 的 eSIM 来试试 Cellfie 网络。</p>
<p>我们去了首都第比利斯、库塔伊西这种稍微大的城市，也去了卡兹别克、梅斯蒂亚这种山区，5ber eSIM 手机卡还是很稳定好用的，除了雪山上之类的地方，我基本没遇到没有信号的情况。</p>
<p>当然 5ber.eSIM App 的问题还是希望后续能更新解决，我还遇到过手机已经插了 5ber 的 eSIM 手机卡，但在多个 eSIM profile 之间切换或重启后 App 报未检测到卡片的错误，还好不影响实际 eSIM 使用，一般也需要重启来解决：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737369020/5ber-app-err_szcmfv.jpg" alt="5ber.eSIM App 报错"></p>
<p>有了 5ber eSIM 手机卡，大家就可以在出国之前提前买好当地的 eSIM 号码了，如果不需要手机号码和通话功能，也是可以使用 eskimo eSIM 的。当然使用一些可以直接在国内漫游或者多国漫游的 eSIM 卡（例如 Giffgaff）也是完全没有问题的，祝大家玩的开心。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/5ber-esim/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>特斯拉听播客的几种姿势</title>
		<link>https://zohead.com/archives/tesla-podcast/</link>
		<comments>https://zohead.com/archives/tesla-podcast/#comments</comments>
		<pubDate>Sun, 13 Aug 2023 08:33:05 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[生活]]></category>
		<category><![CDATA[网络]]></category>
		<category><![CDATA[Cloud Caster]]></category>
		<category><![CDATA[Model Y]]></category>
		<category><![CDATA[Podgrab]]></category>
		<category><![CDATA[Podyssey]]></category>
		<category><![CDATA[RSS]]></category>
		<category><![CDATA[喜马拉雅]]></category>
		<category><![CDATA[播客]]></category>
		<category><![CDATA[特斯拉]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1860</guid>
		<description><![CDATA[去年购买的特斯拉 Model Y 标准续航版，目前已经开了差不多 16000 公里，家人用下来总体还是比较满意的。为了方便实时路况导航和听歌之类的，我还订购了 ¥9.99 / 月的高级车载娱乐服务包。一般如果开车的路程稍微长一点，我都习惯听一听订阅的各种播客来打发时间。 自带喜马拉雅 特斯拉默认只能用自带的喜马拉雅来听播客，虽然喜马拉雅的特斯拉车机版本已经比 Android 版本的喜马拉雅干净清爽多了，不过喜马拉雅也有一些限制，主要是不能自己添加 RSS 订阅源，另外基本没有英文的播客节目，有一些不太和谐的中文播客也没有，或者是只有中国版。 当然如果你只是需要在路上简单听一些常见的中文播客，那 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>去年购买的特斯拉 Model Y 标准续航版，目前已经开了差不多 16000 公里，家人用下来总体还是比较满意的。为了方便实时路况导航和听歌之类的，我还订购了 ¥9.99 / 月的高级车载娱乐服务包。一般如果开车的路程稍微长一点，我都习惯听一听订阅的各种播客来打发时间。</p>
<h2 id="ximalaya">自带喜马拉雅</h2>
<p>特斯拉默认只能用自带的喜马拉雅来听播客，虽然喜马拉雅的特斯拉车机版本已经比 Android 版本的喜马拉雅干净清爽多了，不过喜马拉雅也有一些限制，主要是不能自己添加 RSS 订阅源，另外基本没有英文的播客节目，有一些不太和谐的中文播客也没有，或者是只有中国版。</p>
<p>当然如果你只是需要在路上简单听一些常见的中文播客，那特斯拉自带的喜马拉雅应该也比较适合了。</p>
<h2 id="third-podcast">第三方播客服务</h2>
<p>不过还好特斯拉车机还是带了一个还算能用的浏览器，我们就可以通过访问其它播客服务网站来自由听播客了，主要需求还是：</p>
<ul>
<li>支持自行添加播客 RSS 订阅源；</li>
<li>由于直接使用特斯拉自带的流量卡，需要国内能直接访问，也不想在特斯拉上折腾翻墙了；</li>
<li>播客节目的播放历史、进度最好支持在云端保存，这样在其它设备上访问播客服务网站或使用相应的 App 就能同步使用了。</li>
</ul>
<p>目前特斯拉自带的 QQ 音乐、网易云音乐、喜马拉雅 等车载娱乐服务在进行账户登录时，都需要对应的手机端 App 来扫码登录。如果在特斯拉上通过浏览器访问播客服务，也需要考虑账户登录的问题。由于特斯拉浏览器自带的输入法功能有限，也有点难用，使用第三方播客服务进行输入用户名和密码等登录操作最好也别太复杂。</p>
<h3 id="cloud-caster">Cloud Caster</h3>
<p><a href="http://www.cloud-caster.com/">Cloud Caster</a> 本来是我首选的播客服务，基本能满足上面所有的需求，Cloud Caster 没有移动端 App，只要有浏览器就可以访问收听播客，播放列表和进度也能通过云端同步。</p>
<p>Cloud Caster 主要专注于收听播客功能，没有提供播客托管以及社区、评论之类乱七八糟的功能，其界面也比较清淡克制：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737370805/cloud-caster.png" alt="Cloud Caster"></p>
<p>不过像上面截图的清淡克制效果也是有前提的：浏览器需要开启 Ad Block 之类的广告过滤功能，否则页面的左边、右边以及下方都有显眼的广告。当然，目前 Cloud Caster 基本是由网站主个人维护的，也没有什么商业支持，因此网站上加上这些广告来覆盖服务器支出我觉得也很正常。</p>
<p>特斯拉自带的浏览器并不支持像广告过滤之类的扩展功能，因此如果要在车上使用 Cloud Caster 就必须忍受广告了。如果你喜欢 Cloud Caster 的服务，可以通过他们的 <a href="http://www.cloud-caster.com/Home/SupportUs">捐助</a> 页面来支持一下咯。</p>
<p>Cloud Caster 只支持网站的用户名和密码登录，登录时选中记住用户选项，也可以保存用户会话一段时间，偶尔需要输入用户名和密码重新登录也还能接受。</p>
<p>另外 Cloud Caster 目前也有点小问题，主要体现在某些像 <a href="https://xyzfm.space/">小宇宙播客</a> 之类的 RSS 源可能无法正常解析，或者无法更新最新的节目，所以我也基本是当作备用。</p>
<h3 id="podyssey">Podyssey</h3>
<p><a href="https://podyssey.fm/">Podyssey</a> 是我在发现 Cloud Caster 的问题后发现的另一个播客服务，Podyssey 主要特点在于提供了一个给众多用户发现和讨论 Podcast 播客节目的平台，其首页就是 Discover 发现界面：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442881/podyssey-discover.png" alt="Podyssey Discover"></p>
<p>Podyssey 也为 Podcast 播客创作者提供服务，创作者可以添加和分享自己录制的播客节目，并与收听者进行互动，不过看起来并没有播客音视频的托管服务。</p>
<p>Podyssey 也有 Android 和苹果的移动端 App，不过还好都没有广告，虽然界面看起来都稍微有点简陋：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442882/podyssey-podcast.png" alt="Podyssey 播客"></p>
<p>使用一段时间之后我发现，Podyssey 的主要问题在于无法通过云端保存播客节目的播放进度，无论 App 端还是 Web 端，播放进度都是保存在客户端本地的，当然手机 App 端的播放进度也无法同步到 Web 浏览器端，不过这个其实对我来说影响倒是不大。</p>
<p>然而另外一个问题就稍微有点不能接受，虽然我订阅的播客基本都能在 Podyssey 中找到，但 Podyssey 其实并没有提供通过 RSS 源来订阅播客的功能，这样就显得稍微有点封闭了。</p>
<p>Podyssey 还存在用户登录方式比较奇葩的问题，默认只能输入邮箱地址登录，然后需要在收到的邮件里点击 Podyssey 发送的临时登录地址就可以自动登录。</p>
<p>这样在电脑上当然没什么问题，不过在车上登录就需要多一步把手机邮箱里的临时登录地址发到车上的步骤，登录操作虽然只需要偶尔做一下，但在特斯拉上使用 Podyssey 还是显得有点麻烦的，就继续当备用服务了。</p>
<h2 id="podgrab">自建 Podgrab</h2>
<p>用了一段时间上面的 Cloud Caster、Podyssey 还有其它第三方播客服务，我发现这些服务基本都有一些或大或小的限制或问题，最终还是倾向自建 Podcast 播客服务，搜索了一番之后，还是找到了 <a href="https://github.com/akhilrex/podgrab">Podgrab</a> 这个开源项目。</p>
<p>Podgrab 项目的官方定位就是 <strong>self-hosted podcast manager</strong>，支持通过 RSS 源来订阅播客（也可以直接搜索订阅 iTunes 上的播客），当然顺带也支持 OPML 的导入和导出，还可以自动下载新的播客节目并进行管理，并附带了一个比较简单的播客播放器。Podgrab 还可以对播客节目进行标记，方便后续进行快速筛选查看。</p>
<p>由于 Podgrab 后台程序是由 Golang 编写的，因此天然就可以支持跨平台使用。如果你的服务器或者 NAS 存储支持 Docker 功能，那可以直接使用下面官方的镜像地址进行安装：</p>
<p><a href="https://hub.docker.com/r/akhilrex/podgrab/">https://hub.docker.com/r/akhilrex/podgrab/</a></p>
<p>我为了使用方便，就直接在目前网站所在的 VPS 服务器上运行的，不过由于没有自带 Docker 支持，就先在 Linux 系统上编译好之后上传到 VPS 系统里运行了。Podgrab 的编译操作也很简单，只要安装了 Golang 环境，按照官方的 <a href="https://github.com/akhilrex/podgrab/blob/master/docs/ubuntu-install.md">Building from source / Ubuntu Installation Guide</a> 文章来编译应该就没什么问题。</p>
<p>VPS 服务器上最好单独指定一个目录来存放 Podgrab 程序和相关的下载文件，例如我建立了一个 <code>/usr/local/bin/podgrab</code> 目录，然后把编译好的 <code>podgrab</code> 可执行程序文件也上传到这个目录里。</p>
<p>其次是最重要的 Podgrab 配置文件，在刚才的目录下新建一个 <code>.env</code> 环境变量配置文件：</p>
<pre class="brush: bash; title: ; notranslate">
root@debian:/usr/local/bin/podgrab# cat &gt; .env 
CONFIG=.
DATA=./assets
CHECK_FREQUENCY=30
PASSWORD=XXXXX
PORT=8080
</pre>
<p>Podgrab 项目主页也有环境变量的说明，主要是：</p>
<ul>
<li><code>CHECK_FREQUENCY</code>：检查新播客节目的间隔时间，以分钟为单位；</li>
<li><code>PASSWORD</code>：设置密码认证，默认为空就是不需要密码认证，如果像我一样需要在 VPS 服务器上运行 Podgrab，强烈建议启用密码认证；</li>
<li><code>PORT</code>：Podgrab 的监听端口，默认为 <code>8080</code>。</li>
</ul>
<p>配置完成之后，就可以直接运行 <code>./podgrab</code> 命令启动 Podgrab 了，你也可以根据需要按照官方的说明自己配置成服务方式来运行哦。</p>
<p>最后使用 <code>http://host:8800</code>（假设用的就是默认端口）就可以直接访问 Podgrab 管理界面了，如果启用了密码认证，需要注意用户名是固定的 <code>podgrab</code>。</p>
<p>Podgrab 默认的深色风格界面还是挺清爽耐看的，而且和特斯拉的车机系统界面也比较搭，首页可以选择以网格或列表的形式显示：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442877/podgrab.png" alt="Podgrab"></p>
<p>节目列表界面也就是显示标题、描述以及一些操作按钮：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442878/podgrab-episode.png" alt="Podgrab 节目列表"></p>
<p>由于我只需要能在特斯拉的浏览器打开 Podgrab 听播客，而且自带了高级车载娱乐服务包，完全可以在行驶途中使用车载流量播放播客节目，因此也基本不需要 Podgrab 的播客节目下载功能，为了节省 VPS 服务器的存储空间，我在 Podgrab 的设置界面把以下选项也关闭了：</p>
<ul>
<li>Download episodes whenever new podcast is added</li>
<li>Automatically download new episodes to the disk</li>
</ul>
<p>这样我在订阅了 27 个播客，并默认缓存所有节目信息后，VPS 服务器的存储空间也就占用了不到 30MB。</p>
<p>Podgrab 自带的播放器界面看起来就稍微有点简陋，不过在车上也够用了：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442880/podgrab-player.png" alt="Podgrab 播放器"></p>
<p>需要说明的是虽然 Podgrab 内播客节目的播放完成状态是保存在服务器上的，无论在什么客户端使用浏览器打开 Podgrab 看到的播放是否状态是自动同步的；但是具体某个节目的临时播放进度是保存在客户端浏览器本地的，不过实际用起来也基本没什么影响，而且得益于 Podgrab 项目是开源的，如果需要在服务器上保存节目的播放进度也可以自己修改代码来实现哦。</p>
<p>另外即使开启了密码认证，我们也可以使用 <code>http://podgrab:PASSWORD@host:8800</code> 形式的地址直接指定用户名和密码来访问 Podgrab，然后我们把这个地址添加到特斯拉浏览器的收藏夹，这样就能实现打开浏览器点击一下就进入 Podgrab 了，用起来也比 Cloud Caster 和 Podyssey 更加便利了。</p>
<p>综合对比下来，目前看起来还是 Podgrab 自建的方式更符合我的需求，我和夫人在驾车时试用 Cloud Caster 和 Podyssey 一段时间后，现在都换 Podgrab 来收听播客了。Podgrab 除了因为 VPS 服务器在国外加载节目列表偶尔稍等慢一点之外，用起来还是很顺畅的，后面有空了我再改一下 Podgrab 代码来实现保存播放进度到服务器，最后祝大家玩的开心哦。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/tesla-podcast/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用kbpbot实现Keybase静态文件托管</title>
		<link>https://zohead.com/archives/kbpbot-keybase/</link>
		<comments>https://zohead.com/archives/kbpbot-keybase/#comments</comments>
		<pubDate>Sat, 22 Jul 2023 14:13:19 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[网络技术]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[KBFS]]></category>
		<category><![CDATA[kbpbot]]></category>
		<category><![CDATA[Keybase]]></category>
		<category><![CDATA[rsync]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1884</guid>
		<description><![CDATA[关于 Keybase.pub 我之前写过一篇介绍 Keybase 加密网络服务体验 的文章，里面提到 Keybase 对于用户的公共目录提供了 Keybase.pub 这个静态文件托管网站，方便其它用户访问 Keybase KBFS 里保存的公共文件。 所以之前我都会把我的 MDwiki 知识库 也同步更新到我的 Keybase 下的 wiki 公共目录，这样就可以把我的 nocwat.keybase.pub 作为一个备用的 Wiki 网站，如果我的 VPS 服务器无法访问，也可以通过 Keybase.pub 访问 Wiki 知识库。 然而最近我才后知后觉地发现原来 Keybase 从今年 3 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="about-keybase-pub">关于 Keybase.pub</h2>
<p>我之前写过一篇介绍 <a href="https://zohead.com/archives/keybase/">Keybase 加密网络服务体验</a> 的文章，里面提到 Keybase 对于用户的公共目录提供了 Keybase.pub 这个静态文件托管网站，方便其它用户访问 Keybase KBFS 里保存的公共文件。</p>
<p>所以之前我都会把我的 <a href="https://mdwiki.zohead.com/">MDwiki 知识库</a> 也同步更新到我的 Keybase 下的 <strong>wiki</strong> 公共目录，这样就可以把我的 <code>nocwat.keybase.pub</code> 作为一个备用的 Wiki 网站，如果我的 VPS 服务器无法访问，也可以通过 Keybase.pub 访问 Wiki 知识库。</p>
<p>然而最近我才后知后觉地发现原来 Keybase 从今年 3 月份开始就已经关闭了 Keybase.pub 静态文件托管服务，理由则是使用这项服务的用户数量太少，因此也就需要考虑新的 Keybase 静态文件托管方案了。</p>
<h2 id="kbpbot">kbpbot</h2>
<p>我稍微看了一下 Keybase Book 中关于 <a href="https://book.keybase.io/sites">Kebyase Sites</a> 的介绍文章，里面关于 Keybase.pub 的介绍还没有更新，不过还好目前 Keybase 提供的 <a href="https://keybase.io/kbpbot">kbpbot</a> 机器人还是可以正常使用的。</p>
<p>kbpbot 机器人原本是为了支持用户使用自定义域名发布网站而开发的，为自定义域名配置正确的 <strong>CNAME</strong> 和 <strong>TXT</strong> 记录之后，访问域名时 kbpbot 就可以读取指定的 Keybase 目录中的文件，然后将静态文件内容返回给访问者。</p>
<p>由此也可以看出，kbpbot 也必须有访问对应的 Keybase 目录的权限，自定义域名静态文件托管才能正常工作。得益于 Keybase KBFS 文件系统的设计，公共目录、私有目录、团队目录都可以给 kbpbot 赋予访问权限：</p>
<blockquote>
<p><strong>提示</strong></p>
<p>KBFS 文件系统中的所有文件都会自动签名加密，如果需要了解更多关于 KBFS 的使用方式，可以参考 Keybase 官方的 <a href="https://book.keybase.io/docs/files">Introducing the Keybase Filesystem</a> 这篇文章。</p>
</blockquote>
<ul>
<li>
<p><strong>公共目录</strong>：</p>
<p>kbpbot 默认就可以访问用户的所有公共目录，不需要专门配置，例如我的 Wiki 知识库的 KBFS 路径就是：<code>/keybase/public/nocwat/wiki</code>；</p>
</li>
<li>
<p><strong>私有目录</strong>：</p>
<p>用户只需要在私有目录下建立一个自己和 kbpbot 能访问的子目录，这里多个 Keybase 用户名使用逗号隔开即可，例如：<code>/keybase/private/yourname,kbpbot/XXX</code>；</p>
</li>
<li>
<p><strong>团队目录</strong>：</p>
<p>团队目录则更加简单，将 kbpbot 作为读者加入你的团队，然后使用团队目录路径，例如：<code>/keybase/team/yourteam/XXX</code>。</p>
</li>
</ul>
<p>然后是最重要的自定义域名配置，按照 Keybase Sites 文章里介绍的方式添加对应的 DNS 记录即可。例如我的 MDwiki 知识库使用的是 <a href="https://wiki.vp8.win/">wiki.vp8.win</a> 自定义域名，为 kbpbot 添加如下 DNS 记录：</p>
<ul>
<li>
<p><strong>CNAME</strong> 记录：</p>
<p>名称为 <code>wiki</code>，记录值为：<code>kbp.keybaseapi.com</code>；</p>
</li>
<li>
<p><strong>TXT</strong> 记录：</p>
<p>名称为 <code>_keybase_pages.wiki</code>，记录值为：<code>kbp=/keybase/public/nocwat/wiki</code>。</p>
</li>
</ul>
<p>其中比较关键的就是 <strong>TXT</strong> 记录了，<code>kbp=XXX</code> 记录值就是用来告诉 kbpbot 机器人，用户访问此域名时需要访问什么 KBFS 目录。如果你用的是私有目录，把 <strong>TXT</strong> 记录值换成类似于 <code>kbp=/keybase/private/yourname,kbpbot/XXX</code> 就可以了。</p>
<p>域名配置完成生效之后，就可以通过自定义域名访问你的静态站点了。由于 kbpbot 还支持自动申请和更新 Let’s Encrypt HTTPS 证书，用户访问站点时将会自动使用 HTTPS 访问，完全不需要维护 HTTPS 证书之类的，这一点还是非常方便的。</p>
<p>另外 kbpbot 甚至还支持使用 Keybase Git 代码仓库来访问静态文件，只是不支持私有代码仓库（只有自己才能访问私有 Git 仓库），需要使用团队仓库，同样也要将 kbpbot 作为读者加入你的团队。</p>
<p>例如团队名称还是上面的 <code>yourteam</code>，如果建立一个 <code>teamwiki</code> 仓库，就可以使用 <code>keybase://team/yourteam/teamwiki.git</code> 这种地址来 clone 仓库了，然后存放一些静态文件内容并提交。</p>
<p>最后我们把 Git 仓库对应的域名 <strong>TXT</strong> 记录值修改为：<code>kbp=git@keybase:team/yourteam/teamwiki</code>，用户就可以通过域名 HTTPS 访问静态站点了。</p>
<h2 id="sync-kbfs">同步 KBFS</h2>
<p>我的 Wiki 主站是放在目前博客的 VPS 服务器上运行的，整个 Wiki 知识库目录通过 <a href="https://www.resilio.com/individuals/">Resilio Sync</a> 与本地的 Windows / Linux 电脑进行同步，本地使用 Markdown 编辑器修改 Wiki 内容后就可以自动更新到 VPS 服务器上。</p>
<p>Keybase KBFS 对应的 Wiki 目录，我则是直接在 Linux 上使用 <code>rsync</code> 命令将同步过的本地 Wiki 目录更新到 Keybase，好处就是可以基于 <code>rsync</code> 的同步机制自动只更新差异的文件，这里需要先启动 Keybase 应用程序哦：</p>
<pre class="brush: bash; title: ; notranslate">
~# rsync -ruv --exclude=notes.sqlite --exclude=.sync ~/Documents/wiki/ /run/user/$(id -u)/keybase/kbfs/public/nocwat/wiki/
</pre>
<p><code>rsync</code> 的 <code>--exclude</code> 参数可以忽略不需要同步的文件，这里我指定 <code>notes.sqlite</code> 忽略 Markdown 编辑器使用的数据库文件，并指定 <code>.sync</code> 忽略 Resilio Sync 使用的内部同步目录。</p>
<p>Linux 系统上 rsync 的目标目录是：<code>/run/user/$(id -u)/keybase/kbfs</code>，与当前本地用户的用户 ID 有关；Windows 系统如果 Keybase 客户端启用了在资源管理器中显示 KBFS 盘符，rsync 的目标目录可能就是这样：<code>K:\</code>（实际的 KBFS 盘符可能并不是 K 盘）。</p>
<h2 id="summary">总结</h2>
<p>我的 MDwiki 知识库在 Keybase 上的备用网站 <a href="https://wiki.vp8.win/">wiki.vp8.win</a> 目前已经正常运行一段时间了，而且 Keybase 的静态文件托管服务在国内访问也很正常，并没有出现被墙之类的问题。</p>
<p>由于 kbpbot 除了 Keybase 公共、私有和团队 KBFS 目录，还支持使用 Git 仓库，我们也可以将类似 Hexo 这种静态博客通过 Keybase Git 来管理，本地修改构建之后提交到仓库更新，这样小伙伴们通过 Keybase 来托管静态博客也还是比较省事的。</p>
<p>虽然 Keybase 被 Zoom 收购之后发展势头不太好，更新节奏也变慢了，但还是希望 kbpbot 的静态文件托管服务能继续维持下去，毕竟 Keybase 提供的各项加密网络服务还是非常出色的，祝大家玩的开心哦。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/kbpbot-keybase/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Madoko Local本地使用的问题</title>
		<link>https://zohead.com/archives/madoko-local/</link>
		<comments>https://zohead.com/archives/madoko-local/#comments</comments>
		<pubDate>Mon, 27 Mar 2023 16:33:21 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[工具]]></category>
		<category><![CDATA[LaTeX]]></category>
		<category><![CDATA[Madoko]]></category>
		<category><![CDATA[Madoko Local]]></category>
		<category><![CDATA[Markdown]]></category>
		<category><![CDATA[编辑器]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1827</guid>
		<description><![CDATA[关于 Madoko Madoko 是微软研究院之前推出的一款在线 Markdown 编辑器，只不过更加偏向学术使用，主要亮点在于 Madoko 可以和 LaTeX 结合，支持 LaTeX 一些语法和功能。 LaTeX 是一种基于 TEX 的排版系统，功能非常强大，不少写学术论文的研究者们应该都用过，但其比较底层，学习曲线和难度也挺大。Madoko 可以将 LaTeX 与简单的 Markdown 语法相结合，大部分的文档格式和结构可以使用现有的 Markdown 语法，用户也可以使用 LaTeX 的语法和命令进行扩展，可以很轻松地生成 LaTeX 排版效果的文档，默认也支持输出 HTML 和 P [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="about-madoko">关于 Madoko</h2>
<p><a href="https://github.com/koka-lang/madoko" target="_blank">Madoko</a> 是微软研究院之前推出的一款在线 Markdown 编辑器，只不过更加偏向学术使用，主要亮点在于 Madoko 可以和 LaTeX 结合，支持 LaTeX 一些语法和功能。</p>
<p>LaTeX 是一种基于 TEX 的排版系统，功能非常强大，不少写学术论文的研究者们应该都用过，但其比较底层，学习曲线和难度也挺大。Madoko 可以将 LaTeX 与简单的 Markdown 语法相结合，大部分的文档格式和结构可以使用现有的 Markdown 语法，用户也可以使用 LaTeX 的语法和命令进行扩展，可以很轻松地生成 LaTeX 排版效果的文档，默认也支持输出 HTML 和 PDF 文档。</p>
<p>Madoko 官方的在线编辑器 Madoko Editor 网站是：</p>
<p><a href="https://www.madoko.net/" target="_blank">https://www.madoko.net/</a></p>
<p>最近 Madoko Editor 网站访问有点问题，不过我之前也已经把 Madoko Editor 网站备份过了，能够自行部署运行，编辑器的界面和默认的示例文档如下：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737372294/madoko-editor.png" alt="Madoko Editor"></p>
<p>可以看到 Madoko 文档的扩展名是 <code>mdk</code>，而且 mdk 文档基本都是使用现有的 Markdown 语法加上一点 LaTeX 语法，即使不使用 Madoko Editor，也可以用其它 Markdown 编辑器打开进行编辑。</p>
<h2 id="madoko-local">Madoko Local</h2>
<p>mdk 文档在 Madoko Editor 中编辑修改之后，支持保存到 Dropbox、GitHub、OneDrive 以及 Local Disk 本地：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442733/makodo-editor-save.png" alt="Madoko Local Disk 保存"></p>
<p>由于众所周知的原因，前面三个存储目标国内用起来都不太稳定，而且之前我使用 Madoko 的时候就发现用这些云存储进行文档编辑似乎也容易出问题，可能就用微软自己的 OneDrive 做存储稳定一些。</p>
<p>另外考虑到数据放在自己手里才是最安全的，因此我一般都是用 Local Disk 本地保存方式。</p>
<p>Madoko 的本地磁盘访问则需要 <a href="https://github.com/koka-lang/madoko/tree/master/support/madoko-local" target="_blank">Madoko Local</a> 这个程序的配合，另外如果想要本地生成 PDF 也需要使用 Madoko Local。</p>
<p>Madoko Local 是用 Node.js 编写的，本机安装完 Node.js 环境后，运行命令完成安装之后就可以使用 <code>madoko-local</code> 命令了：</p>
<pre class="brush: bash; title: ; notranslate">
~# npm install -g madoko-local
</pre>
<p><code>madoko-local</code> 命令使用起来也非常简单：</p>
<pre class="brush: bash; title: ; notranslate">
~# madoko-local -r -l .
listening on           : http://localhost:8080
connecting securely to : https://www.madoko.net
serving files under    : /home/zzm/Documents

---------------------------------------------------------------
access server at       : http://localhost:8080#secret=XXX
---------------------------------------------------------------
</pre>
<p>最后一个参数就是 Madoko 能够访问的本地目录，<code>.</code> 就表示只导出当前的目录。</p>
<p>Madoko Local 启动之后就会输出绑定的端口（默认为 <code>8080</code>）、导出的本地目录路径，最重要的是包含密码的访问地址，浏览器通过这个访问地址就可以打开 Madoko Editor 界面。</p>
<p>Madoko Local 程序的其它参数可以通过 <code>madoko-local -h</code> 命令来查询，另外也可以使用 <code>config.json</code> 配置文件来配置端口等参数，这里我把 Madoko Local 的监听端口改为了 <code>18080</code>，也可以使用 <code>mountdir</code> 来配置导出的本地目录：</p>
<pre class="brush: bash; title: ; notranslate">
~$ cat &gt; ~/.madoko/config.json
{
  &quot;port&quot;:18080,
  &quot;secret&quot;:&quot;XXX&quot;,
  &quot;origin&quot;:&quot;http://XXX&quot;,
  &quot;mountdir&quot;:&quot;/home/zzm/Documents&quot;
}
</pre>
<p>配置文件所在的 <code>.madoko</code> 目录默认位于当前用户的主目录下，这个主目录的路径也可以通过 <code>madoko-local</code> 命令的 <code>--homedir</code> 参数进行自定义。</p>
<p>如果对应目录下存在有效的 <code>config.json</code> 配置文件，后续就可以直接 <code>madoko-local</code> 命令启动程序而不需要加任何参数了。</p>
<h2 id="madoko-local-issue">Madoko Local 问题</h2>
<p>不过我在使用 Madoko Local 时也发现一些问题：</p>
<ol>
<li>通过 <code>madoko-local</code> 命令来指定端口、密码等各种参数可能存在问题；</li>
<li>如果 <code>madoko-local</code> 程序默认绑定了本地 IPv6 的 <code>::1</code> 地址（也就是 <code>localhost</code> 地址），会报 <code>only serving localhost</code> 错误。</li>
</ol>
<p>为此我稍微修改了一下 Madoko Local Node.js 程序源码，生成了 Madoko Local 最新 0.9.4 版本的 patch 文件，有需要的朋友可以从下面任选一个下载：</p>
<ol>
<li><a href="https://pastebin.com/raw/jcSFMVsH" target="_blank">Pastebin 分享链接</a></li>
<li><a href="https://github.com/zohead/madoko/commit/6fd522fca1f7b25bd51a1150ac988bb24d591636.patch" target="_blank">GitHub 提交</a></li>
</ol>
<p>下载了 patch 文件之后，就可以进入 Madoko Local 的安装目录运行命令进行合并了（假设下载保存的文件名是 <code>madoko-local-npm-v0.9.4.patch</code>）：</p>
<pre class="brush: bash; title: ; notranslate">
~# cd /usr/lib/node_modules/
~# patch -p2 madoko-local-npm-v0.9.4.patch
</pre>
<p>上面命令使用的是 Linux 系统下 <code>npm</code> 默认的全局安装路径 <code>/usr/lib/node_modules</code>，其他系统可以使用 <code>npm list -g | head -n 1</code> 命令来确认。</p>
<p>为了方便，我也直接 fork 了 Madoko 的源仓库，还把自己做的其它的一些修改也提交了：</p>
<p><a href="https://github.com/zohead/madoko" target="_blank">https://github.com/zohead/madoko</a></p>
<p>Madoko Local 程序的代码在 <code>support/madoko-local</code> 子目录下供大家参考。</p>
<p>最后祝跨过三年疫情的朋友们都能玩得开心 ^_^。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/madoko-local/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>香橙派R1 Plus LTS软路由体验</title>
		<link>https://zohead.com/archives/r1-plus-lts/</link>
		<comments>https://zohead.com/archives/r1-plus-lts/#comments</comments>
		<pubDate>Sun, 21 Aug 2022 12:32:40 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[数码]]></category>
		<category><![CDATA[网络]]></category>
		<category><![CDATA[OpenWRT]]></category>
		<category><![CDATA[R1 Plus LTS]]></category>
		<category><![CDATA[红米]]></category>
		<category><![CDATA[路由器]]></category>
		<category><![CDATA[香橙派]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1768</guid>
		<description><![CDATA[去年家里装修好之后为了一步到位就打算把无线网络也升级到 WiFi 6 了，不过可惜买的时候没注意，选了有点坑的 红米 AX3000 路由器（不是老的红米 AX6，这款的实际产品型号为：RA81）。这款路由器用的是高通 IPQ5000 处理器，集成 256MB 内存，WiFi 6 功能用起来虽然也没有太大的问题，只是开机运行时间久了总会有卡卡的感觉，最主要小米的魔改系统没有给 SSH 权限，而网上其它小米路由器的破解 SSH 方法对这款路由器并不起作用。 最近我想了想还是准备上个采用标准 OpenWRT 系统的软路由，红米 AX3000 路由器就准备只用来发射 WiFi 信号了。软路由原本考虑很 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>去年家里装修好之后为了一步到位就打算把无线网络也升级到 WiFi 6 了，不过可惜买的时候没注意，选了有点坑的 <a href="https://www.mi.com/shop/buy/detail?product_id=14538" target="_blank">红米 AX3000 路由器</a>（不是老的红米 AX6，这款的实际产品型号为：RA81）。这款路由器用的是高通 IPQ5000 处理器，集成 256MB 内存，WiFi 6 功能用起来虽然也没有太大的问题，只是开机运行时间久了总会有卡卡的感觉，最主要小米的魔改系统没有给 SSH 权限，而网上其它小米路由器的破解 SSH 方法对这款路由器并不起作用。</p>
<p>最近我想了想还是准备上个采用标准 OpenWRT 系统的软路由，红米 AX3000 路由器就准备只用来发射 WiFi 信号了。软路由原本考虑很多网友用过的友善 <a href="https://www.friendlyelec.com/index.php?route=product/product&amp;path=69&amp;product_id=282" target="_blank">NanoPi R2S</a>，只是最近这款软路由的溢价实在有点多，一番了解之后还是定下了迅龙推出的与 R2S 配置基本相同的 <a href="http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-R1-Plus-LTS-Router.html" target="_blank">Orange Pi R1 Plus LTS</a> 软路由。</p>
<h2 id="intro">介绍</h2>
<p>R1 Plus LTS 软路由的基本硬件参数如下：</p>
<table>
<thead>
<tr>
<th>项目</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>CPU</td>
<td>Rockchip RK3328</td>
</tr>
<tr>
<td>GPU</td>
<td>Mali-450MP2</td>
</tr>
<tr>
<td>内存</td>
<td>1GB LPDDR3</td>
</tr>
<tr>
<td>WAN</td>
<td>裕太微 YT8531C 10M/100M/1000M 以太网</td>
</tr>
<tr>
<td>LAN</td>
<td>Realtek RTL8153B 10M/100M/1000M USB 以太网</td>
</tr>
<tr>
<td>尺寸</td>
<td>63mm X 60.6mm X 27.25mm</td>
</tr>
<tr>
<td>重量</td>
<td>175g</td>
</tr>
</tbody>
</table>
<p>其实香橙派之前还出了一款 R1 Plus 软路由，与我买的 R1 Plus LTS 区别也很小：</p>
<ul>
<li>R1 Plus 用的是 1GB DDR4 内存；</li>
<li>R1 Plus 的 WAN 口使用的是 Realtek RTL8211E PHY。</li>
</ul>
<p>所以 R1 Plus LTS 相当于是 R1 Plus 的低配版，只是可能由于芯片短缺等原因 R1 Plus 现在也基本上买不到了。</p>
<p>我购买的 R1 Plus LTS 软路由是自带官方金属外壳的，而且开发板本身都已经装好固定了的，因此主板本身我也就没拆了，看看软路由的外观：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442932/r1pluslts-appearance.jpg" alt="R1 Plus LTS 软路由"></p>
<p>前面是 TF 卡槽以及 SYS、LAN、WAN 指示灯，后面就是供电接口以及两个网口了。</p>
<p>本来打算用闲置的华为手机充电器，又考虑到放在家里需要长期稳定运行，我也顺便买了官方的 5V 3A USB Type-C 电源适配器：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442932/r1pluslts-charger.jpg" alt="R1 Plus LTS 电源适配器"></p>
<h2 id="system">系统</h2>
<p>我找了一张老的 8GB TF 卡做系统盘，为了稳定也用的是官方提供的最新 OpenWrt 21.02.1 r16325-88151b8303 版本系统，也不考虑第三方固件提供的各种花里胡哨的功能了。</p>
<p>为了方便我也没有使用官方用户手册里推荐的 <code>Win32Diskimager</code> 或者 <code>balenaEtcher</code> 之类的工具来烧写镜像，直接用 Linux 自带的 <code>dd</code> 命令进行烧写：</p>
<blockquote>
<p><strong>备注</strong></p>
<p>我的笔记本自带了 PCIe 的读卡器，所以这里的 <code>dd</code> 命令写的是 <code>/dev/mmcblk0</code> 设备，如果用的是 USB 读卡器，应该就要换成 <code>/dev/sdb</code> 之类的设备。</p>
</blockquote>
<pre class="brush: bash; title: ; notranslate">
~# dd if=openwrt-rockchip-armv8-xunlong_orangepi-r1-plus-lts-ext4-sysupgrade.img of=/dev/mmcblk0 bs=1024k
</pre>
<p>由于 <code>dd</code> 命令没有校验的功能，烧写完成之后可以强制检查一下系统是否正确：</p>
<pre class="brush: bash; title: ; notranslate">
~# e2fsck -f /dev/mmcblk0p2
</pre>
<p>系统烧写完成之后就可以插到软路由上启动了，OpenWRT 系统第一次启动的时候会自动扩容 rootfs 分区，启动完成之后可以登录系统确认 rootfs 分区大小是否正确。</p>
<p>首先通过 SSH 登录或者 LuCI Web 管理界面修改 OpenWRT 的镜像源，这里我用的是清华的镜像源：</p>
<pre class="brush: bash; title: ; notranslate">
~# cat /etc/opkg/distfeeds.conf
src/gz openwrt_core https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/21.02.1/targets/rockchip/armv8/packages
src/gz openwrt_base https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/21.02.1/packages/aarch64_generic/base
~# cat /etc/opkg/customfeeds.conf
src/gz openwrt_luci https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/21.02.1/packages/aarch64_generic/luci
src/gz openwrt_packages https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/21.02.1/packages/aarch64_generic/packages
src/gz openwrt_routing https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/21.02.1/packages/aarch64_generic/routing
src/gz openwrt_telephony https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/21.02.1/packages/aarch64_generic/telephony
</pre>
<p>香橙派官方提供的 OpenWRT 系统自带了 Adblock、Squid、Transmission 甚至 Docker 等软件的支持，用户也可以自行卸载，例如卸载 MiniDLNA：</p>
<pre class="brush: bash; title: ; notranslate">
~# opkg remove luci-i18n-minidlna-zh-cn luci-app-minidlna minidlna
</pre>
<p>通过 SSH 登录可以看到 OpenWRT 系统的 Linux 内核是 5.4 版本的，应该也够用了：</p>
<pre class="brush: bash; title: ; notranslate">
~# uname -a
Linux R1PlusLTS 5.4.154 #0 SMP PREEMPT Sun Oct 24 09:01:35 2021 aarch64 GNU/Linux
</pre>
<p>然后是 CPU 信息：</p>
<pre class="brush: bash; title: ; notranslate">
~# cat /proc/cpuinfo
processor    : 0
BogoMIPS    : 48.00
Features    : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant   : 0x0
CPU part    : 0xd03
CPU revision  : 4
</pre>
<h2 id="luci-issue">LuCI 问题</h2>
<p>值得一提的是我使用 Chrome 浏览器访问 OpenWRT 的 LuCI Web 管理界面经常出现类似 <code>_(...).format is not a function</code> 这样的报错，控制台还有其它错误：</p>
<pre class="brush: plain; title: ; notranslate">
A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
</pre>
<p>后来发现使用 Chrome 无痕式窗口访问 LuCI Web 界面就没有问题，因此怀疑是某些 Chrome 扩展导致的，经常一番禁用扩展并测试，确认是由 <a href="https://chrome.google.com/webstore/detail/tronlink/ibnejdfjmmkpcnlpebklmnkoeoihofec" target="_blank">TronLink</a> 这个波场官方的钱包导致的，禁用 TronLink 扩展就可以正常访问 LuCI 界面。</p>
<p>不过为了能正常使用 TronLink 钱包，我还是在 TronLink 扩展的详情页将 <strong>允许此扩展程序读取和更改您在所访问的网站上留存的所有数据</strong> 选项由 <strong>在所有网站上</strong> 改为了 <strong>点击时</strong>，这样 LuCI 界面和 TronLink 就都可以用了。</p>
<h2 id="performance">性能</h2>
<p>这里我就不做常见的 CPU 性能、加解密性能测试了，因为 RK3328 的性能已经有好多网友测试过了，我主要关注 LAN 口 RTL8153B USB 网卡的性能。</p>
<p>我的笔记本直接带了千兆有线网卡，这样我就将笔记本通过千兆网线接到软路由的 LAN 口，用 <code>iperf</code> 命令做一下简单的测试了，先看看 LAN 口接收数据的性能：</p>
<pre class="brush: bash; title: ; notranslate">
~# iperf -s -w 512K
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 1.00 MByte (WARNING: requested 512 KByte)
------------------------------------------------------------
[ 4] local 192.168.2.1 port 5001 connected with 192.168.2.57 port 65244
[ ID] Interval    Transfer   Bandwidth
[ 4] 0.0-30.0 sec 3.31 GBytes  949 Mbits/sec
</pre>
<p>接着我又测试了 LAN 口发送数据的性能：</p>
<pre class="brush: bash; title: ; notranslate">
root@R1PlusLTS:~# iperf -c 192.168.2.57 -w 512K -t 30
------------------------------------------------------------
Client connecting to 192.168.2.57, TCP port 5001
TCP window size: 1.00 MByte (WARNING: requested 512 KByte)
------------------------------------------------------------
[ 3] local 192.168.2.1 port 44562 connected with 192.168.2.57 port 5001
[ ID] Interval    Transfer   Bandwidth
[ 3] 0.0-30.0 sec 3.30 GBytes  946 Mbits/sec
</pre>
<h2 id="summary">总结</h2>
<p>最后我把 R1 Plus LTS 软路由的联网配置好之后，将原来的红米 AX3000 路由器的 WAN 口接到 R1 Plus LTS 的 LAN 口，并把 AX3000 从 <strong>普通路由</strong> 模式切换为 <strong>有线中继</strong> 模式，这样原有的手机之类的无线终端设备就能继续上网了：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737370582/ax3000-ap.png" alt="红米 AX3000 有线中继"></p>
<p>由于 R1 Plus LTS 软路由只有一个 LAN 口，家里的 NAS 服务器只能接在红米 AX3000 路由器上了。还好 AX3000 使用有线中继模式之后，终端设备与软路由也还在同一网段，而且上面的测试结果可以看出软路由的 LAN 口性能也不是瓶颈，我用支持 WiFi 6 的电脑和手机访问 NAS 服务器的速度也没什么问题，可以满足日常使用需求了。</p>
<p>最后说一下 R1 Plus LTS 软路由的散热，我购买的是自带官方金属外壳的版本，这两个月连续运行下来我发现也挺稳定的，通过命令可以查看 CPU 温度：</p>
<pre class="brush: bash; title: ; notranslate">
root@R1PlusLTS:~# cat /sys/class/thermal/thermal_zone0/temp
56818
</pre>
<p>上面结果中的 57 摄氏度是在南京接近 40 度的高温下室内不开空调看到的，实际使用下来我还没有遇到温度超过 60 摄氏度的情况，看起来挺过这个最热夏天之后，R1 Plus LTS 软路由就可以留下来暂定长期使用了。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/r1-plus-lts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux实现惠尔顿上网认证客户端</title>
		<link>https://zohead.com/archives/wholeton-linux-client/</link>
		<comments>https://zohead.com/archives/wholeton-linux-client/#comments</comments>
		<pubDate>Sun, 15 May 2022 03:34:10 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[WebSocket]]></category>
		<category><![CDATA[惠尔顿]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1745</guid>
		<description><![CDATA[最近公司由于某些特殊原因，需要上一套惠尔顿的上网行为管理系统，局域网内的所有设备在连接互联网时都需要通过惠尔顿上网认证客户端进行登录，或者需要管理员在上网行为管理中设置免监控。 惠尔顿官方只提供了 Windows 上的客户端进行用户登录，也可以访问网页版进行临时登录，效果如下： 不过 Linux 服务器设备就没法登录了，为此我写了个 Python 程序模拟惠尔顿的网页版客户端实现 Linux / macOS 等系统下的上网认证功能。 Python 程序 我编写的 wholeton-auth.py Python 程序可以通过 Pastebin 分享链接 下载，写的很简单粗糙，这里贴出来说明一下： [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>最近公司由于某些特殊原因，需要上一套惠尔顿的上网行为管理系统，局域网内的所有设备在连接互联网时都需要通过惠尔顿上网认证客户端进行登录，或者需要管理员在上网行为管理中设置免监控。</p>
<p>惠尔顿官方只提供了 Windows 上的客户端进行用户登录，也可以访问网页版进行临时登录，效果如下：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737442965/wholeton-login.jpg" alt="惠尔顿上网认证系统网页版"></p>
<p>不过 Linux 服务器设备就没法登录了，为此我写了个 Python 程序模拟惠尔顿的网页版客户端实现 Linux / macOS 等系统下的上网认证功能。</p>
<h2 id="python-wholeton-client">Python 程序</h2>
<p>我编写的 <code>wholeton-auth.py</code> Python 程序可以通过 <a href="https://pastebin.com/raw/5XhfVS2S">Pastebin 分享链接</a> 下载，写的很简单粗糙，这里贴出来说明一下：</p>
<pre class="brush: python; highlight: [20,21,22,23,24,25]; title: ; notranslate">
#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import socket
from datetime import datetime
from uuid import getnode
import urllib
try:
    import urllib2
except Exception:
    from urllib import request as urllib2
try:
    import Cookie as cookies
except Exception:
    from http import cookies
import websocket
import json

wholeton_host = '192.168.1.254'
wholeton_user = 'test'
wholeton_pass = '123456'
wholeton_ip = ''
wholeton_mac = ''
update_secs = 28800

def url_encode(obj):
    try:
        return urllib.urlencode(obj)
    except Exception:
        return urllib.parse.urlencode(obj)

def get_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except Exception:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

def get_mac():
    return ':'.join((&quot;%012x&quot; % getnode())[i:i+2] for i in range(0, 12, 2))

if not wholeton_ip:
    wholeton_ip = get_ip()

if not wholeton_mac:
    wholeton_mac = get_mac()

uri_keys = { 'id' : 0, 'url' : 'mail.126.com', 'user' : wholeton_ip, 'mac' : wholeton_mac }
uri_data = url_encode(uri_keys).replace('%3A', ':')

auth_data = url_encode({ 'param[UserName]' : wholeton_user, 'param[UserPswd]' : wholeton_pass, 'uri' : uri_data, 'force' : 0 })
# convert for python 3
if sys.version_info[0] == 3:
    auth_data = auth_data.encode('ascii')

ws = None
loop = True

try:
    while loop:
        resp = urllib2.urlopen('http://' + wholeton_host + '/user-login-auth?' + uri_data, timeout = 5, data = auth_data)

        # get session cookie
        cookie = cookies.SimpleCookie()
        cookie.load(resp.info()['Set-Cookie'])

        resp_data = resp.read()
        if resp_data:
            print('Login response:')
            print(resp_data)

        ws = websocket.WebSocket()
        ws.connect('ws://' + wholeton_host + '/go-ws/user-auth', cookie = 'fms_session=' + cookie.get('fms_session').value, origin = 'http://' + wholeton_host)

        dt_start = datetime.now()
        while ws:
            try:
                ws_data = ws.recv()
            except KeyboardInterrupt:
                loop = False
                break
            except:
                break

            if ws_data:
                dt_now = datetime.now()
                if (dt_now - dt_start).seconds &gt;= update_secs:
                    break

                print(dt_now)
                print(ws_data)

                ws_obj = json.loads(ws_data)
                if ws_obj and ws_obj[&quot;type&quot;] == &quot;logged-out&quot;:
                    break

        if ws:
            ws.close()
        ws = None
except KeyboardInterrupt:
    pass

if ws:
    ws.close()
</pre>
<p>为了支持比较老的服务器系统，此 Python 程序同时支持 Python 2 和 Python 3，在 Python 2.6 和 Python 3.6 版本上做了测试。为了减少依赖，本程序基本都是用的 Python 自带的模块，并针对 Python 2 和 Python 3 进行了区分处理。</p>
<p>程序首先通过惠尔顿网页版的 HTTP 登录接口进行登录，登录成功之后获取会话信息，然后建立 WebSocket 连接，并保持 WebSocket 连接状态，如果关闭了连接上网认证功能就会自动失效。</p>
<p>由于本程序需要建立 WebSocket 连接，也不需要任何 WebSocket 服务器的功能，所以没有用各种功能齐全的 WebSocket 库，只额外使用了 <a href="https://pypi.org/project/websocket-client/">websocket-client</a> 这个非常简单的 WebSocket 客户端库。</p>
<p>另外最新 1.3.2 版本的 websocket-client 库只支持 Python 3.7 及以上版本，为了能给 Python 2.6 版本使用，本程序使用的是 websocket-client 老一点的 <a href="https://pypi.org/project/websocket-client/0.59.0/">0.59.0</a> 版本，websocket-client 0.59.0 版本支持 Python 2.6 以上或 Python 3.4 以上版本，而且只依赖 <a href="https://pypi.org/project/six/">six</a> 这个 Python 兼容库，同时 six 库也使用 <a href="https://pypi.org/project/six/1.13.0/">1.13.0</a> 版本以兼容 Python 2.6 版本。</p>
<p>可以使用 pip 命令先安装 six 1.13.0 版本和 websocket-client 0.59.0 版本：</p>
<pre class="brush: bash; title: ; notranslate">
~$ pip install six==1.13.0
~$ pip install websocket-client==0.59.0
</pre>
<h2 id="wholton-using">上网认证使用</h2>
<p>上面依赖的库安装好之后，首先需要修改 <code>wholeton-auth.py</code> Python 程序中的配置（上面已经高亮显示了）：</p>
<pre class="brush: plain; title: ; notranslate">
wholeton_host = '192.168.1.254'
wholeton_user = 'test'
wholeton_pass = '123456'
wholeton_ip = ''
wholeton_mac = ''
update_secs = 28800
</pre>
<p>简单说明如下：</p>
<ul>
<li><code>wholeton_host</code> 指定惠尔顿上网行为管理系统 IP 地址；</li>
<li><code>wholeton_user</code> 和 <code>wholeton_pass</code> 指定上网行为管理的用户名和密码；</li>
<li><code>wholeton_ip</code> 为本机 IP 地址，默认为空，程序会自动获取，也可以自行修改覆盖；</li>
<li><code>wholeton_mac</code> 为本机上网网卡（也就是连接上网行为管理系统的网卡）的 MAC 地址，默认为空，程序也会自动获取（这里为了图省事使用的 <code>uuid</code> 库的 <code>getnode</code> 函数获取网卡 MAC 地址），也可以自行修改覆盖；</li>
<li><code>update_secs</code> 指定登录多长时间之后就强制断开连接进行重新登录，单位为秒，默认为 <code>28800</code> 秒也就是 8 个小时。</li>
</ul>
<p>因为我这边使用本程序测试发现惠尔顿的上网认证系统即使在保持 WebSocket 连接打开的情况下，大概 9 ~ 10 个小时之后也会无法上网，所以大家可以根据实际情况测试确认之后，修改程序里的 <code>update_secs</code> 重新登录时间。</p>
<p>修改完配置之后，使用起来就很简单了，根据 Python 版本在终端里运行 <code>python2 wholeton-auth.py</code> 或者 <code>python3 wholeton-auth.py</code> 就可以了，程序运行之后会在标准输出中显示上网认证服务器返回的响应数据。</p>
<p>最后本程序还是需要一直保持运行的，大家可以根据自己的需要通过 screen 或者服务等方式来实现后台运行哦。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/wholeton-linux-client/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>酷走行车记录仪root和GPS时间同步</title>
		<link>https://zohead.com/archives/kuzo-root-gps-time-sync/</link>
		<comments>https://zohead.com/archives/kuzo-root-gps-time-sync/#comments</comments>
		<pubDate>Sat, 26 Feb 2022 13:30:33 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[GPS]]></category>
		<category><![CDATA[root]]></category>
		<category><![CDATA[SuperSU]]></category>
		<category><![CDATA[时间同步]]></category>
		<category><![CDATA[行车记录仪]]></category>
		<category><![CDATA[酷走]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1734</guid>
		<description><![CDATA[首先很惭愧我已经有一年多没有更新博客内容了，之前写过一篇 酷走 Android 行车记录仪研究 的文章，行车记录仪的 ADB 调试已经可以用起来了，不过还存在网络不可用时系统时间不正确的问题，刚好再研究一下行车记录仪的 root 以及通过 GPS 同步记录仪的系统时间。 root 系统 其实酷走这款 Android 行车记录仪系统已经自带了“假” root 权限，只是没有带 su 管理的工具。虽然 SuperSU 很久没更新了，我还是准备安装试试，首先从 SuperSU 官网下载最新 2.82 版本的刷机 zip 包： http://supersuroot.org/downloads/Supe [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>首先很惭愧我已经有一年多没有更新博客内容了，之前写过一篇 <a href="https://zohead.com/archives/kuzo-android-internal/" target="_blank">酷走 Android 行车记录仪研究</a> 的文章，行车记录仪的 ADB 调试已经可以用起来了，不过还存在网络不可用时系统时间不正确的问题，刚好再研究一下行车记录仪的 root 以及通过 GPS 同步记录仪的系统时间。</p>
<h2 id="root-system">root 系统</h2>
<p>其实酷走这款 Android 行车记录仪系统已经自带了“假” root 权限，只是没有带 su 管理的工具。虽然 SuperSU 很久没更新了，我还是准备安装试试，首先从 SuperSU 官网下载最新 2.82 版本的刷机 zip 包：</p>
<pre><code>http://supersuroot.org/downloads/SuperSU-v2.82-201705271822.zip
</code></pre>
<p>解压缩之后，首先使用 adb 命令将系统重新挂载为可读写状态：</p>
<pre class="brush: bash; title: ; notranslate">
~$ adb remount
</pre>
<p>参考之前折腾刷机的经验，先拷贝刷机 zip 包中的文件：</p>
<pre class="brush: bash; title: ; notranslate">
~$ adb push common/Superuser.apk /sdcard/
~$ adb push common/install-recovery.sh /sdcard/
~$ adb push x86/su /sdcard/
~$ adb push x86/supolicy /sdcard/
~$ adb push x86/libsupol.so /sdcard/
</pre>
<p>这个时候可以使用 <code>adb shell</code> 命令登录到行车记录仪，再将文件移动到系统目录：</p>
<pre class="brush: bash; title: ; notranslate">
root@Sf3gr_mrd6_p2_720# mkdir /system/app/SuperSU
root@Sf3gr_mrd6_p2_720# mv /sdcard/Superuser.apk /system/app/SuperSU/
root@Sf3gr_mrd6_p2_720# mv /sdcard/install-recovery.sh /system/bin/
root@Sf3gr_mrd6_p2_720# cp /sdcard/su /system/xbin/
root@Sf3gr_mrd6_p2_720# mkdir /system/bin/.ext
root@Sf3gr_mrd6_p2_720# cp /sdcard/su /system/bin/.ext/.su
root@Sf3gr_mrd6_p2_720# mv /sdcard/su /system/xbin/daemonsu
root@Sf3gr_mrd6_p2_720# mv /sdcard/supolicy /system/xbin/
root@Sf3gr_mrd6_p2_720# mv /sdcard/libsupol.so /system/lib/
</pre>
<p>还需要设置文件权限：</p>
<pre class="brush: bash; title: ; notranslate">
root@Sf3gr_mrd6_p2_720# chmod 0644 /system/app/SuperSU/Superuser.apk
root@Sf3gr_mrd6_p2_720# chmod 0755 /system/bin/install-recovery.sh /system/xbin/su /system/bin/.ext/.su /system/xbin/daemonsu /system/xbin/supolicy /system/lib/libsupol.so
root@Sf3gr_mrd6_p2_720# chcon u:object_r:system_file:s0 /system/app/SuperSU/Superuser.apk /system/bin/install-recovery.sh /system/xbin/su /system/bin/.ext/.su /system/xbin/daemonsu /system/xbin/supolicy /system/lib/libsupol.so
</pre>
<p>不过最后运行 SuperSu 安装命令的时候却出错了：</p>
<pre class="brush: bash; title: ; notranslate">
root@Sf3gr_mrd6_p2_720# /system/xbin/su --install
error: only position independent executables (PIE) are supported.
</pre>
<p>看来是想当然了，我拷贝的是非 PIE 格式的 su 程序导致运行不了。那就重新拷贝正确的 su 程序（需要使用 <code>su.pie</code> 地址无关的可执行程序版本），这次就 adb 命令一步到位了：</p>
<pre class="brush: bash; title: ; notranslate">
~$ adb push common/Superuser.apk /system/app/SuperSU/Superuser.apk
~$ adb push common/install-recovery.sh /system/bin/install-recovery.sh
~$ adb push x86/su.pie /system/xbin/su
~$ adb push x86/su.pie /system/bin/.ext/.su
~$ adb push x86/su.pie /system/xbin/daemonsu
~$ adb push x86/supolicy /system/xbin/supolicy
~$ adb push x86/libsupol.so /system/lib/libsupol.so
</pre>
<p>设置文件权限之后再重新执行 <code>/system/xbin/su --install</code> 命令安装就没有问题了，测试确认 SuperSU App 也可以正常运行了。</p>
<h2 id="gps-time-sync">GPS 时间同步</h2>
<p>为了能修正行车记录仪的系统时间，我在行车记录仪上安装了 <a href="https://play.google.com/store/apps/details?id=com.pautinanet.smarttimesync" target="_blank">Smart Time Sync</a> 这款 App 进行时间同步。Smart Time Sync 支持通过 GPS、NTP  或 HTTP 同步系统时间，不过行车记录仪上都没有装 SIM 卡更没有网络，只能通过 GPS 进行时间同步了。</p>
<p>如果 Android 系统没有 root，Smart Time Sync 只能进行手工同步，因此为了实际使用过程中能实现后台自动时间同步，上面的系统 root 操作还是必须的。</p>
<p>Smart Time Sync 运行效果如下：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737372134/kuzo-smart-time-sync.png" alt="Smart Time Sync"></p>
<p>App 使用起来也很简单，首先在 Source 源选择 <code>GPS</code>，然后点 <code>Sync Time</code> 按钮就可以手工同步系统时间了，当然前提是设备必须放在有 GPS 信号的地方。</p>
<p>点击后面的设置图标就可以配置后台自动进行时间同步了，现在我的行车记录仪即使掉电了保存的录像文件时间也是正确的了。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/kuzo-root-gps-time-sync/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ARM64 Linux下ARM容器使用yum的问题</title>
		<link>https://zohead.com/archives/arm64-arm-container-yum/</link>
		<comments>https://zohead.com/archives/arm64-arm-container-yum/#comments</comments>
		<pubDate>Thu, 29 Oct 2020 16:52:31 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[aarch64]]></category>
		<category><![CDATA[ARM]]></category>
		<category><![CDATA[ARM64]]></category>
		<category><![CDATA[armhf]]></category>
		<category><![CDATA[RPM]]></category>
		<category><![CDATA[YUM]]></category>
		<category><![CDATA[容器]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1689</guid>
		<description><![CDATA[ARM 容器问题 前段时间需要验证某客户的 32 位 ARM Linux 程序，由于我们只有现成的 ARM64 设备和系统，而 ARM64 兼容 32 位 armel 和 armhf，因此想到直接在 ARM64（aarch64）Linux 系统中运行 32 位 ARM（armhf）容器来进行测试。 客户程序使用的是 CentOS 7 32 位 ARM 开发环境，这里我使用 LXC 官方镜像来创建一个完整的 CentOS 7 容器系统： 32 位 ARM 容器可以正常启动，但使用 yum 安装任何软件包会报错： yum 修改 上面 yum 命令的报错提示找不到 repo 为 base/7/arm [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="arm-container-issue">ARM 容器问题</h2>
<p>前段时间需要验证某客户的 32 位 ARM Linux 程序，由于我们只有现成的 ARM64 设备和系统，而 ARM64 兼容 32 位 armel 和 armhf，因此想到直接在 ARM64（aarch64）Linux 系统中运行 32 位 ARM（armhf）容器来进行测试。</p>
<p>客户程序使用的是 CentOS 7 32 位 ARM 开发环境，这里我使用 LXC 官方镜像来创建一个完整的 CentOS 7 容器系统：</p>
<pre class="brush: bash; title: ; notranslate">
~ # lxc-create -n arm -t download --dir=/var/lib/lxc/arm/rootfs -- --server mirrors.tuna.tsinghua.edu.cn/lxc-images -d centos -r 7 -a armhf
</pre>
<p>32 位 ARM 容器可以正常启动，但使用 yum 安装任何软件包会报错：</p>
<pre class="brush: bash; title: ; notranslate">
bash-4.2# yum install openssh-server
Loaded plugins: fastestmirror
Determining fastest mirrors


 One of the configured repositories failed (Unknown),
 and yum doesn't have enough cached data to continue. At this point the only
 safe thing yum can do is fail. There are a few ways to work &quot;fix&quot; this:

     1. Contact the upstream for the repository and get them to fix the problem.

     2. Reconfigure the baseurl/etc. for the repository, to point to a working
        upstream. This is most often useful if you are using a newer
        distribution release than is supported by the repository (and the
        packages for the previous distribution release still work).

     3. Run the command with the repository temporarily disabled
            yum --disablerepo=&lt;repoid&gt; ...

     4. Disable the repository permanently, so yum won't use it by default. Yum
        will then just ignore the repository until you permanently enable it
        again or use --enablerepo for temporary usage:

            yum-config-manager --disable &lt;repoid&gt;
        or
            subscription-manager repos --disable=&lt;repoid&gt;

     5. Configure the failing repository to be skipped, if it is unavailable.
        Note that yum will try to contact the repo. when it runs most commands,
        so will have to try and fail each time (and thus. yum will be be much
        slower). If it is a very temporary problem though, this is often a nice
        compromise:

            yum-config-manager --save --setopt=&lt;repoid&gt;.skip_if_unavailable=true

Cannot find a valid baseurl for repo: base/7/armv8l
</pre>
<h2 id="yum-patch">yum 修改</h2>
<p>上面 yum 命令的报错提示找不到 repo 为 <code>base/7/armv8l</code> 的地址，我们可以在容器中使用 <code>uname</code> 命令确认当前的系统架构为 <code>armv8l</code>（ARM64 内核运行 32 位 ARM 程序）：</p>
<pre class="brush: bash; title: ; notranslate">
bash-4.2# uname -m
armv8l
</pre>
<p>而在主机端则可以看到真正的系统架构为 <code>aarch64</code>：</p>
<pre class="brush: bash; title: ; notranslate">
~ # uname -m
aarch64
</pre>
<p>正是由于 yum 不能正确识别 <code>armv8l</code>这个特殊的系统架构，导致命令执行失败。</p>
<p>我们可以修改 <code>/usr/lib/python2.7/site-packages/rpmUtils/arch.py</code> 文件，使 yum 可以正确处理 <code>armv8l</code> 架构，以下为 patch 修改内容：</p>
<pre class="brush: diff; title: ; notranslate">
diff -rNp a/arch.py b/arch.py
*** a/arch.py   Thu Oct 29 23:46:21 2020
--- b/arch.py   Thu Oct 29 23:48:55 2020
*************** arches = {
*** 73,78 ****
--- 73,79 ----
      &quot;armv5tel&quot;: &quot;noarch&quot;,
  
      #arm hardware floating point
+     &quot;armv8l&quot;: &quot;armv7hl&quot;,
      &quot;armv7hnl&quot;: &quot;armv7hl&quot;,
      &quot;armv7hl&quot;: &quot;noarch&quot;,
  
*************** def getBaseArch(myarch=None):
*** 442,447 ****
--- 443,450 ----
          return &quot;ppc&quot;
      elif myarch.startswith(&quot;arm64&quot;):
          return &quot;arm64&quot;
+     elif myarch == &quot;armv8l&quot;:
+         return &quot;armhfp&quot;
      elif myarch.startswith(&quot;armv7h&quot;):
          return &quot;armhfp&quot;
      elif myarch.startswith(&quot;arm&quot;):
</pre>
<p>此时再运行 yum 就可以正确找到软件包了：</p>
<pre class="brush: bash; title: ; notranslate">
bash-4.2# yum install openssh-server
Loaded plugins: fastestmirror
Determining fastest mirrors
 * base: mirrors.bfsu.edu.cn
 * extras: mirrors.bfsu.edu.cn
 * updates: mirrors.bfsu.edu.cn
base                                                                                                                                                                                              | 3.6 kB  00:00:00
extras                                                                                                                                                                                            | 2.9 kB  00:00:00
updates                                                                                                                                                                                           | 2.9 kB  00:00:00
(1/4): base/7/armhfp/group_gz                                                                                                                                                                     | 153 kB  00:00:00
(2/4): extras/7/armhfp/primary_db                                                                                                                                                                 | 170 kB  00:00:00
(3/4): updates/7/armhfp/primary_db                                                                                                                                                                | 562 kB  00:00:00
(4/4): base/7/armhfp/primary_db                                                                                                                                                                   | 4.1 MB  00:00:00
Resolving Dependencies
--&gt; Running transaction check
---&gt; Package openssh-server.armv7hl 0:7.4p1-21.el7 will be installed
--&gt; Processing Dependency: libwrap.so.0 for package: openssh-server-7.4p1-21.el7.armv7hl
--&gt; Running transaction check
---&gt; Package tcp_wrappers-libs.armv7hl 0:7.6-77.el7 will be installed
--&gt; Finished Dependency Resolution

Dependencies Resolved

=========================================================================================================================================================================================================================
 Package                                                    Arch                                             Version                                                 Repository                                     Size
=========================================================================================================================================================================================================================
Installing:
 openssh-server                                             armv7hl                                          7.4p1-21.el7                                            base                                          446 k
Installing for dependencies:
 tcp_wrappers-libs                                          armv7hl                                          7.6-77.el7                                              base                                           64 k

Transaction Summary
=========================================================================================================================================================================================================================
Install  1 Package (+1 Dependent package)

Total download size: 510 k
Installed size: 1.0 M
Is this ok [y/d/N]: y
Downloading packages:
(1/2): tcp_wrappers-libs-7.6-77.el7.armv7hl.rpm                                                                                                                                                   |  64 kB  00:00:00
(2/2): openssh-server-7.4p1-21.el7.armv7hl.rpm                                                                                                                                                    | 446 kB  00:00:00
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                                                                                    1.7 MB/s | 510 kB  00:00:00
Running transaction check
Running transaction test


Transaction check error:
  package tcp_wrappers-libs-7.6-77.el7.armv7hl is intended for a different architecture
  package openssh-server-7.4p1-21.el7.armv7hl is intended for a different architecture
</pre>
<p>不过这次轮到 rpm 报错了，提示 RPM 包的架构不相符。</p>
<h2 id="rpm-patch">rpm 修改</h2>
<p>我们先试着手工安装 yum 下载下来但安装失败的 RPM 包，可以看到还是相同的报错：</p>
<pre class="brush: bash; title: ; notranslate">
bash-4.2# rpm -ivh /var/cache/yum/armhfp/7/base/packages/tcp_wrappers-libs-7.6-77.el7.armv7hl.rpm
Preparing...                          ################################# [100%]
        package tcp_wrappers-libs-7.6-77.el7.armv7hl is intended for a different architecture
</pre>
<p>这也是由于 rpm 命令不能正确处理 <code>armv8l</code> 架构，其实对于这种 RPM 包的架构不相符的情况，我们可以为 rpm 命令增加 <code>--ignorearch</code> 参数强制安装：</p>
<pre class="brush: bash; title: ; notranslate">
bash-4.2# rpm -ivh --ignorearch /var/cache/yum/armhfp/7/base/packages/tcp_wrappers-libs-7.6-77.el7.armv7hl.rpm
Preparing...                          ################################# [100%]
Updating / installing...
   1:tcp_wrappers-libs-7.6-77.el7     ################################# [100%]
</pre>
<p>当然我们的最终目的是要让 yum 能够自动安装软件包，因此还需要让 rpm 命令也能正确识别 <code>armv8l</code> 架构。</p>
<p>为此我们需要修改 <code>/usr/lib/rpm/rpmrc</code> 文件，将 <code>armv8l</code> 视为 <code>armv7hnl</code>：</p>
<pre class="brush: diff; title: ; notranslate">
diff -rNp a/rpmrc b/rpmrc 
*** a/rpmrc     Fri Oct 30 00:15:17 2020
--- b/rpmrc     Fri Oct 30 00:16:32 2020
*************** arch_compat: armv5tel: armv4tl
*** 398,403 ****
--- 398,404 ----
  arch_compat: armv4tl: armv4l
  arch_compat: armv4l: armv3l
  arch_compat: armv3l: noarch
+ arch_compat: armv8l: armv7hnl
  arch_compat: armv7hnl: armv7hl
  arch_compat: armv7hl: noarch
</pre>
<p>此时我们再使用 yum 命令就可以正确下载和安装软件包了：</p>
<pre class="brush: bash; title: ; notranslate">
bash-4.2# yum install openssh-server
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.bfsu.edu.cn
 * extras: mirrors.bfsu.edu.cn
 * updates: mirrors.bfsu.edu.cn
Resolving Dependencies
--&gt; Running transaction check
---&gt; Package openssh-server.armv7hl 0:7.4p1-21.el7 will be installed
--&gt; Finished Dependency Resolution

Dependencies Resolved

=========================================================================================================================================================================================================================
 Package                                                  Arch                                              Version                                                 Repository                                      Size
=========================================================================================================================================================================================================================
Installing:
 openssh-server                                           armv7hl                                           7.4p1-21.el7                                            base                                           446 k

Transaction Summary
=========================================================================================================================================================================================================================
Install  1 Package

Total size: 446 k
Installed size: 921 k
Is this ok [y/d/N]: y
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Warning: RPMDB altered outside of yum.
  Installing : openssh-server-7.4p1-21.el7.armv7hl                                                                                                                                                                   1/1
  Verifying  : openssh-server-7.4p1-21.el7.armv7hl                                                                                                                                                                   1/1

Installed:
  openssh-server.armv7hl 0:7.4p1-21.el7

Complete!
</pre>
<p>后续安装其它软件包也都没有问题了，例如安装基础开发包：</p>
<pre class="brush: bash; title: ; notranslate">
bash-4.2# yum -y install gcc make openssl-devel glibc-static openssl-static
</pre>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/arm64-arm-container-yum/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>2020春季印象 - 蔷薇</title>
		<link>https://zohead.com/archives/2020-spring-3/</link>
		<comments>https://zohead.com/archives/2020-spring-3/#comments</comments>
		<pubDate>Mon, 31 Aug 2020 15:44:03 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[植物]]></category>
		<category><![CDATA[春季]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1678</guid>
		<description><![CDATA[马上就要进入金秋九月了，拖延症日常发作的我才想起来要把 2020 春季印象系列的蔷薇篇给补上。虽然下面的蔷薇科植物中的野蔷薇和玫瑰我不是很确定，这两位主角是在小区旁边的野公园发现的，但前段时间再去公园却发现都已经被人挖走了，不记录下来实为憾事。 蛇莓 首先写蛇莓（蔷薇科，蛇莓属）是因为它实在是很常见，不说小时候在农村山里路边到处都是，就现在住的小区四处走走也能看见不少，蛇莓开的小黄花也挺可爱： 小时候家里大人都告诫不要吃蛇莓果实，蛇莓果实看着虽然和野草莓有点像，但摘下来其实硬邦邦的，想想大概也是不好吃的 ^_^： 蓬蘽 既然说到了蛇莓，那就必须得有学名为蓬蘽（蔷薇科，悬钩子属）的野草莓了，小时 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>马上就要进入金秋九月了，拖延症日常发作的我才想起来要把 2020 春季印象系列的蔷薇篇给补上。虽然下面的蔷薇科植物中的野蔷薇和玫瑰我不是很确定，这两位主角是在小区旁边的野公园发现的，但前段时间再去公园却发现都已经被人挖走了，不记录下来实为憾事。</p>
<h2 id="Duchesnea_indica">蛇莓</h2>
<p>首先写蛇莓（蔷薇科，蛇莓属）是因为它实在是很常见，不说小时候在农村山里路边到处都是，就现在住的小区四处走走也能看见不少，蛇莓开的小黄花也挺可爱：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443081/Duchesnea_indica_1.jpg" alt="蛇莓"></p>
<p>小时候家里大人都告诫不要吃蛇莓果实，蛇莓果实看着虽然和野草莓有点像，但摘下来其实硬邦邦的，想想大概也是不好吃的 ^_^：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443082/Duchesnea_indica_2.jpg" alt="蛇莓果实"></p>
<h2 id="Rubus_hirsutus">蓬蘽</h2>
<p>既然说到了蛇莓，那就必须得有学名为蓬蘽（蔷薇科，悬钩子属）的野草莓了，小时候夏秋时节路边最好的野果子就是野草莓了。</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443117/Rubus_hirsutus_1.jpg" alt="蓬蘽"></p>
<p>蓬蘽的果实观感就比茅莓、覆盆子要好，颗粒小且紧密，覆盖的毛也很少，那时基本都是摘多个蓬蘽果子稍微擦一擦就直接塞入口中。</p>
<p>可惜我在这边还没见过开花结果的野草莓呢，希望不久能再体验野外的蓬蘽的果实。</p>
<h2 id="Rosa_multiflora">野蔷薇</h2>
<p>我是于 5 月份的一个周末傍晚在小区旁边的野公园的树荫下发现一株开着白花的野蔷薇（蔷薇科，蔷薇属）的：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443111/Rosa_multiflora_1.jpg" alt="野蔷薇"></p>
<p>再稍加探寻，又发现一株：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443112/Rosa_multiflora_2.jpg" alt="野蔷薇"></p>
<p>以及不太显眼的另一个品种：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443114/Rosa_multiflora_3.jpg" alt="野蔷薇"></p>
<p>再继续走上一段，在靠近小区围栏的杂草丛里也发现不少：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443115/Rosa_multiflora_4.jpg" alt="野蔷薇"></p>
<p>从上面的照片可以看出这片野蔷薇基本不会是专人种植的，能在这个无人管理的野公园里看到它们我还是很欣喜的。</p>
<p>然而令人伤心的是，上个月我再去这片草丛时只能看到被挖开的一个个土坑，几乎所有的野蔷薇基本都被人挖走，大概只有树荫下的那一两株能幸免，希望来年还能看到野蔷薇的身影。</p>
<h2 id="Rosa_rugosa">玫瑰</h2>
<p>我同样也是在野蔷薇的杂草丛里看到这些玫瑰（蔷薇科，蔷薇属）的，不过没有拍下详细的茎叶之类的特征，我不太能区分，还希望有同学能指正呢：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443115/Rosa_rugosa_1.jpg" alt="玫瑰"></p>
<p>被左下角的阿拉伯婆婆纳点缀的玫瑰是如此美丽，可惜它们的命运也是被贼人肆意挖走：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443116/Rosa_rugosa_2.jpg" alt="玫瑰"></p>
<h2 id="Rubus_parvifolius">茅莓</h2>
<p>与野蔷薇们几步之遥的茅莓（蔷薇科，悬钩子属）的颜值看起来似乎就没那么高了：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443120/Rubus_parvifolius_1.jpg" alt="茅莓"></p>
<p>当然贼人似乎也就不会惦记它们了：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443121/Rubus_parvifolius_2.jpg" alt="茅莓"></p>
<p>我希望再走一趟还能拍到茅莓的果实。</p>
<h2 id="Spiraea_salicifolia">绣线菊</h2>
<p>绣线菊（蔷薇科，绣线菊属）我是在九龙湖边的小公园里看到的：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443128/Spiraea_salicifolia_1.jpg" alt="绣线菊"></p>
<p>后来在公司旁边小区的绿化带里也看到了粉红色的绣线菊：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443128/Spiraea_salicifolia_2.jpg" alt="绣线菊"></p>
<h2 id="Kerria_japonica">棣棠花</h2>
<p>我把棣棠花（蔷薇科，棣棠花属）放在这里，不仅因为它也是蔷薇科植物，而且在小公园里看到它之后，再次回顾时却发现那几株棣棠花也被施工砍掉了 -_-#：</p>
<p><img src="https://images.weserv.nl/?url=http://res.cloudinary.com/digwht2y0/image/upload/v1737443102/Kerria_japonica_1.jpg" alt="棣棠花"></p>
<p>似乎有点遗憾，但还是不妨碍后续观察不同品种花色以及单瓣、重瓣的棣棠花的。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/2020-spring-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.290 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2026-04-04 05:40:58 -->

<!-- Compression = gzip -->