<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Wei 的小站</title>
        <link>https://blog.whe.me</link>
        <description>Wei He，专职 Web 及 iOS 开发。定位美国佛罗里达州。喜欢试验新技术，欢迎来访！</description>
        <atom:link href="https://blog.whe.me/rss.html" rel="self" />
        <language>zh-cn</language>
        <lastBuildDate>Sat, 11 Apr 2026 00:11:48 GMT</lastBuildDate>
        <item>
            <title>Docker Swarm 定时自动清理无用资源</title>
            <link>https://blog.whe.me/post/docker-swarm-auto-prune-all-nodes.html</link>
            <description><![CDATA[
            <div class="toc"></div><p>本文介绍在 Docker Swarm 模式中很容易地配置一个定时在所有节点自动清理无用资源的方法。</p>
<!--more-->
<pre><code class="hljs lang-yaml"><span class="hljs-attr">  system-prune:</span>
<span class="hljs-attr">    image:</span> <span class="hljs-string">docker</span>
<span class="hljs-attr">    volumes:</span>
<span class="hljs-bullet">      -</span> <span class="hljs-string">"/var/run/docker.sock:/var/run/docker.sock"</span>
<span class="hljs-attr">    command:</span> <span class="hljs-string">docker</span> <span class="hljs-string">system</span> <span class="hljs-string">prune</span> <span class="hljs-bullet">--all</span> <span class="hljs-bullet">--force</span>
<span class="hljs-attr">    deploy:</span>
<span class="hljs-attr">      mode:</span> <span class="hljs-string">global</span>
<span class="hljs-attr">      restart_policy:</span>
<span class="hljs-attr">        delay:</span> <span class="hljs-number">24</span><span class="hljs-string">h</span>
</code></pre>
<p>每 24 小时在所有节点自动运行 <code>docker system prune -all --force</code></p>

            ]]></description>
            <pubDate>Tue, 18 Aug 2020 15:06:21 GMT</pubDate>
            <guid>https://blog.whe.me/post/docker-swarm-auto-prune-all-nodes.html</guid>
        </item>
        <item>
            <title>Docker Swarm 模式添加 manager node 报错</title>
            <link>https://blog.whe.me/post/docker-swarm-mode-add-manager.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-6f0">0x00. 前言</a></li>
<li><a href="#toc-17c">0x01. 问题描述</a></li>
<li><a href="#toc-c3a">0x02. 解决方案</a><ul>
<li><a href="#toc-8d8">Step 1. 打开防火墙</a></li>
<li><a href="#step-2-%E9%85%8D%E7%BD%AE-no_proxyhttpsdocsdockercomconfigdaemonsystemd%23httphttps-proxy">Step 2. <a href="https://docs.docker.com/config/daemon/systemd/#httphttps-proxy">配置 NO_PROXY</a></a></li>
</ul>
</li>
</ul>
</div><p>在 Docker Swarm 模式中发现无法 promote master 或者新的 node 无法直接 join 为 master node。这篇文章提供了解决方案。</p>
<!--more-->
<h2><a id="toc-6f0" class="anchor" href="#toc-6f0"></a>0x00. 前言</h2>
<p>我使用 Zerotier 把我在全球的十几个小鸡连起来建立了一个虚拟内网，并使用 Docker Swarm 统一管理。在单一 manager 的情况下的配置：</p>
<pre><code class="hljs lang-bash">ufw allow <span class="hljs-keyword">in</span> on &lt;ZEROTIER_NETWORK&gt;  <span class="hljs-comment"># 打开防火墙</span>

docker swarm init --advertise-addr &lt;ZEROTIER_IP&gt;  <span class="hljs-comment"># 初始化 Swarm</span>

docker swarm join-token worker  <span class="hljs-comment"># 取得 join token</span>
</code></pre>
<p>之后将取得的 join token 在所有 node 上运行来成功组建全球 swarm 。</p>
<h2><a id="toc-17c" class="anchor" href="#toc-17c"></a>0x01. 问题描述</h2>
<p>为了更好的 swarm resilience ，我决定 promote 另两个 node 为 manager 。于是在现有 master 上运行</p>
<pre><code class="hljs lang-undefined">docker node promote &lt;NEW_MASTER_NAME&gt;
</code></pre>
<p>可是一直报错</p>
<pre><code class="hljs lang-undefined">Error response from daemon: rpc error: code = DeadlineExceeded desc = context deadline exceeded
</code></pre>
<p>于是也尝试了先让新的 node 离开 swarm 再使用 manager join token 加入 swarm 亦无果。</p>
<pre><code class="hljs lang-undefined">Error response from daemon: manager stopped: can't initialize raft node: rpc error: code = Unknown desc = could not connect to prospective new cluster member using its advertised address: rpc error: code = DeadlineExceeded desc = context deadline exceeded
</code></pre>
<h2><a id="toc-c3a" class="anchor" href="#toc-c3a"></a>0x02. 解决方案</h2>
<h3><a id="toc-8d8" class="anchor" href="#toc-8d8"></a>Step 1. 打开防火墙</h3>
<p>我这里使用的是 ufw 于是</p>
<pre><code class="hljs lang-bash">ufw allow <span class="hljs-keyword">in</span> on &lt;ZEROTIER_NETWORK&gt;  <span class="hljs-comment"># 打开防火墙</span>
</code></pre>
<h3><a id="toc-abb" class="anchor" href="#toc-abb"></a>Step 2. <a href="https://docs.docker.com/config/daemon/systemd/#httphttps-proxy">配置 NO_PROXY</a></h3>
<pre><code class="hljs lang-bash">mkdir -p /etc/systemd/system/docker.service.d

cat &gt;&gt; /etc/systemd/system/docker.service.d/http-proxy.conf &lt;&lt; EOF
[Service]
Environment=<span class="hljs-string">"NO_PROXY=localhost,127.0.0.1,&lt;MANAGER_ZEROTIER_IP&gt;"</span>
EOF

systemctl daemon-reload

systemctl restart docker

<span class="hljs-comment"># 可选，检查配置是否生效</span>
<span class="hljs-comment"># systemctl show --property=Environment docker</span>
</code></pre>
<p>完成。再次 promote 或者使用 join-token 即可！</p>

            ]]></description>
            <pubDate>Fri, 04 Oct 2019 18:59:18 GMT</pubDate>
            <guid>https://blog.whe.me/post/docker-swarm-mode-add-manager.html</guid>
        </item>
        <item>
            <title>Docker Volume 存储卷数据迁移</title>
            <link>https://blog.whe.me/post/docker-volume-migration.html</link>
            <description><![CDATA[
            <div class="toc"></div><p>在使用 Docker 的过程中，经常会使用 Docker Volume 数据存储卷来保持持久数据。典型的例子就是一个 MySQL 数据库。在服务器迁移过程中，Docker 镜像的迁移重构就是几行字的事，但是如何迁移存储卷中的数据呢？</p>
<!--more-->
<p>准备工作：在原服务器上确保能够密钥连接 SSH，需要能够 <code>ssh -p &lt;PORT&gt; &lt;USERNAME&gt;@&lt;TARGET_HOST&gt;</code> 直接连接新服务器（不应提示输密码）。</p>
<p>接着在原服务器上运行如下即可：</p>
<pre>
docker run --rm -v <strong>[SOURCE_VOLUME_NAME]</strong>:/from alpine ash -c "cd /from; tar -cf - ." | \
    ssh -p <strong>[PORT]</strong> <strong>[USERNAME]</strong>@<strong>[TARGET_HOST]</strong> 'docker run --rm -i -v <strong>[TARGET_VOLUME_NAME]</strong>:/to alpine ash -c "cd /to; tar -xpvf -"'
</pre>

<table>
<thead>
<tr>
<th>名称</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>TARGET_HOST</code></td>
<td>新服务器</td>
</tr>
<tr>
<td><code>SOURCE_VOLUME_NAME</code></td>
<td>原数据卷名称</td>
</tr>
<tr>
<td><code>TARGET_VOLUME_NAME</code></td>
<td>新数据卷名称</td>
</tr>
</tbody>
</table>
<p>原理简单来说就是新建一个小的 Docker 镜像，把原数据卷加载上去，打包并通过 SSH 加密隧道传到新服务器上新建的 Docker 镜像的新数据卷内。之后新服务器上其他镜像也可使用此数据卷了。</p>

            ]]></description>
            <pubDate>Fri, 11 May 2018 04:57:45 GMT</pubDate>
            <guid>https://blog.whe.me/post/docker-volume-migration.html</guid>
        </item>
        <item>
            <title>Chrome 强制一个网站使用 HTTPS</title>
            <link>https://blog.whe.me/post/chrome-force-https.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-07e">0x00 - 准备</a></li>
<li><a href="#toc-82b">0x01 - 配置本地 HSTS 规则</a></li>
<li><a href="#toc-942">0x02 - 更多信息</a><ul>
<li><a href="#%E8%B0%B7%E6%AD%8C%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0-appdev-%E7%AD%89%E5%9F%9F%E5%90%8D%E7%9A%84%E5%BC%BA%E5%88%B6-https-%E9%87%8D%E5%AE%9A%E5%90%91">谷歌是如何实现 <code>.app</code>、<code>.dev</code> 等域名的强制 HTTPS 重定向？</a></li>
</ul>
</li>
</ul>
</div><p>有一些网站通过 HTTP 和 HTTPS 都可以访问。比如 <a href="http://www.kuaidi100.com">http://www.kuaidi100.com</a> 和 <a href="https://www.kuaidi100.com">https://www.kuaidi100.com</a> 均可以访问网站。这篇文章讲解了一个 Chrome 里最轻量的重定向强制使用 HTTPS 方法。</p>
<!--more-->
<h2><a id="toc-07e" class="anchor" href="#toc-07e"></a>0x00 - 准备</h2>
<p>首先请访问 HTTPS 的网址。如果可以正常访问，请往下看。</p>
<h2><a id="toc-82b" class="anchor" href="#toc-82b"></a>0x01 - 配置本地 HSTS 规则</h2>
<p>进入 <strong>Domain Security Policy</strong> 页面：<a href="chrome://net-internals/#hsts">chrome://net-internals/#hsts</a>（需复制前往）</p>
<p>在 HSTS/PKP 下面的 <strong>Add HSTS/PKP domain</strong> 里添加新的 HSTS 规则：</p>
<p>如果<strong>只添加单域名</strong> <em><a href="http://www.kuaidi100.com">www.kuaidi100.com</a></em></p>
<ul>
<li>Domain: <code>www.kuaidi100.com</code></li>
</ul>
<p>如果<strong>添加域名及所有子域名</strong> <em>kuaidi100.com, *.kuaidi100.com</em></p>
<ul>
<li>Domain: <code>kuaidi100.com</code></li>
<li>Include subdomains for STS: ☑️ </li>
</ul>
<p>大功告成！以后浏览器访问 HTTP 的网址会自动被重定向到 HTTPS 版本。注意：如果网站 HTTPS 版本配置错误或者无法访问，请使用网页中的 <strong>Delete domain security policies</strong> 功能移除它。</p>
<h2><a id="toc-942" class="anchor" href="#toc-942"></a>0x02 - 更多信息</h2>
<p>服务器端配置 HSTS 及 HSTS 扫盲：</p>
<ul>
<li><a href="https://imququ.com/post/sth-about-switch-to-https.html#toc-2">合理使用 HSTS | 关于启用 HTTPS 的一些经验分享（一）| JerryQu 的小站</a></li>
</ul>
<h3><a id="toc-088" class="anchor" href="#toc-088"></a>谷歌是如何实现 <code>.app</code>、<code>.dev</code> 等域名的强制 HTTPS 重定向？</h3>
<p>在 2018 年 5 月 8 日开放注册的 <code>.app</code> 域名是第一个全域强制 HTTPS 的公开注册的域名。它的实现方法便是上面链接「HSTS Preload List」内所说的添加 HSTS 进入 Chrome 及 Firefox 浏览器源码。目前强制 HSTS 的顶级域名可见<a href="https://cs.chromium.org/chromium/src/net/http/transport_security_state_static.json">这个链接</a>里的源码。</p>
<pre><code class="hljs lang-javascript">    <span class="hljs-comment">// gTLDs and eTLDs are welcome to preload if they are interested.</span>
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"android"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"app"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"bank"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"chrome"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"dev"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"foo"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"gle"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"gmail"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"google"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span>, <span class="hljs-string">"pins"</span>: <span class="hljs-string">"google"</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"hangout"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span>, <span class="hljs-string">"pins"</span>: <span class="hljs-string">"google"</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"insurance"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"meet"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"new"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"page"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"play"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"search"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
    { <span class="hljs-string">"name"</span>: <span class="hljs-string">"youtube"</span>, <span class="hljs-string">"policy"</span>: <span class="hljs-string">"public-suffix"</span>, <span class="hljs-string">"mode"</span>: <span class="hljs-string">"force-https"</span>, <span class="hljs-string">"include_subdomains"</span>: <span class="hljs-literal">true</span> },
</code></pre>
<p>也可查看浏览器 <code>chrome://net-internals/#hsts</code> 的 HSTS 部分：
<img src="https://xn--57h.whe.me/static/upload/20180508/DNPZFLMTU6C8d82Y5cXAdgYc.png" alt=".app 的 HSHS 记录"></p>

            ]]></description>
            <pubDate>Wed, 10 Jan 2018 00:18:15 GMT</pubDate>
            <guid>https://blog.whe.me/post/chrome-force-https.html</guid>
        </item>
        <item>
            <title>编辑器自动检测写入权限</title>
            <link>https://blog.whe.me/post/nano-detect-write-permission.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-898">0x01 - 演示</a></li>
<li><a href="#toc-970">0x02 - 安装</a><ul>
<li><a href="#toc-0b5">添加 nano-helper</a></li>
<li><a href="#toc-02b">添加执行权限</a></li>
<li><a href="#toc-aad">添加 nano alias</a></li>
</ul>
</li>
</ul>
</div><p>使用 <code>nano</code> 等编辑器的时候时常发现编辑完了无法保存，因为没有写入权限。其实 <code>nano</code> 打开无写入权限的文件时候会提示，但是我从来都不注意。这篇文章介绍了一个小 utility，自动在打开文件前检查并提示授权写入权限。</p>
<!--more-->
<h2><a id="toc-898" class="anchor" href="#toc-898"></a>0x01 - 演示</h2>
<pre><code class="hljs lang-undefined">$ nano /etc/ssh/sshd_config 
/etc/ssh/sshd_config is not writable. Edit with sudo? [y/n]
</code></pre>
<h2><a id="toc-970" class="anchor" href="#toc-970"></a>0x02 - 安装</h2>
<p><strong>直接复制以下代码运行</strong> 默认<code>nano</code>安装在 <code>/bin/nano</code></p>
<h3><a id="toc-0b5" class="anchor" href="#toc-0b5"></a>添加 nano-helper</h3>
<pre><code class="hljs lang-bash">cat &lt;&lt; <span class="hljs-string">'EOF'</span> | sudo tee /usr/<span class="hljs-built_in">local</span>/bin/nano-helper &gt; /dev/null
<span class="hljs-meta">#!/bin/sh</span>
<span class="hljs-comment"># Checks file permissions before opening nano.</span>

sudo=
editor=/bin/nano

<span class="hljs-keyword">if</span> <span class="hljs-built_in">test</span> -e <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span> &amp;&amp; <span class="hljs-built_in">test</span> ! -w <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>; <span class="hljs-keyword">then</span>
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">test</span> -t 0 &amp;&amp; <span class="hljs-built_in">test</span> -t 2; <span class="hljs-keyword">then</span>
        <span class="hljs-built_in">read</span> -p <span class="hljs-string">"<span class="hljs-variable">$1</span> is not writable. Edit with sudo? [y/n] "</span> yn
        <span class="hljs-keyword">case</span> <span class="hljs-variable">$yn</span> <span class="hljs-keyword">in</span>
        y|Y)
            sudo=<span class="hljs-literal">true</span>
            ;;
        n|N)
            sudo=
            ;;
        *)
            <span class="hljs-built_in">printf</span> <span class="hljs-string">"\nInvalid choice. Please try again later.\n"</span> 1&gt;&amp;2
            <span class="hljs-built_in">exit</span> 1
            ;;
        <span class="hljs-keyword">esac</span>
    <span class="hljs-keyword">else</span>
        <span class="hljs-built_in">printf</span> <span class="hljs-string">"%s is not writable. Fix the permissions or run \"cat\" instead."</span> <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span> 1&gt;&amp;2
        <span class="hljs-built_in">exit</span> 1
    <span class="hljs-keyword">fi</span>
<span class="hljs-keyword">fi</span>

<span class="hljs-variable">${sudo:+sudo}</span> <span class="hljs-string">"<span class="hljs-variable">$editor</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
EOF
</code></pre>
<h3><a id="toc-02b" class="anchor" href="#toc-02b"></a>添加执行权限</h3>
<pre><code class="hljs lang-bash">sudo chmod +x /usr/<span class="hljs-built_in">local</span>/bin/nano-helper
</code></pre>
<h3><a id="toc-aad" class="anchor" href="#toc-aad"></a>添加 nano alias</h3>
<pre><code class="hljs lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"alias nano=nano-helper"</span> &gt;&gt; ~/.bashrc
<span class="hljs-built_in">source</span> ~/.bashrc
</code></pre>
<p>使用其他编辑器的童鞋请自动把上面的 <code>nano</code> 都替换成你的编辑器。</p>
<hr>
<p>Sources: </p>
<ul>
<li><a href="https://unix.stackexchange.com/questions/11871#answer-12014">Mikel&#39;s Answer</a> from StackExchange</li>
</ul>

            ]]></description>
            <pubDate>Fri, 10 Nov 2017 08:35:59 GMT</pubDate>
            <guid>https://blog.whe.me/post/nano-detect-write-permission.html</guid>
        </item>
        <item>
            <title>开启 BBR 拥塞控制算法</title>
            <link>https://blog.whe.me/post/enable-bbr.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-966">0x00 - 检测你的 Linux Kernel 版本是否自带 BBR</a><ul>
<li><a href="#toc-d68">检测 BBR 是否已开启</a></li>
</ul>
</li>
<li><a href="#toc-5a5">0x01 - 启用 BBR</a></li>
<li><a href="#toc-a0b">0x02 - 检测 BBR</a></li>
</ul>
</div><p>BBR (&quot;<strong>B</strong>ottleneck <strong>B</strong>andwidth and <strong>R</strong>ound-trip propagation time&quot;) 是 Google 开源的新型拥塞控制算法。Google 的测试表明使用 BBR 吞吐量最高是现有方案的 2700 倍。Linux Kernel 4.9+ 已经默认包含了对 BBR 的支持。此文讲述了开启 BBR 的方法。</p>
<!--more-->
<p><a href="https://cloudplatform.googleblog.com/2017/07/TCP-BBR-congestion-control-comes-to-GCP-your-Internet-just-got-faster.html"><img src="https://xn--57h.whe.me/static/upload/20171106/vFqj_szE7B-un2kZc0CjKbGX.gif" alt="BBR - Before and after"></a></p>
<h2><a id="toc-966" class="anchor" href="#toc-966"></a>0x00 - 检测你的 Linux Kernel 版本是否自带 BBR</h2>
<pre><code class="hljs lang-bash">egrep <span class="hljs-string">'CONFIG_NET_SCH_FQ'</span> /boot/config-$(uname -r)
</code></pre>
<pre><code class="hljs lang-bash">egrep <span class="hljs-string">'CONFIG_TCP_CONG_BBR'</span> /boot/config-$(uname -r)
</code></pre>
<p>请确保以上两个命令都有输出，否则请升级 Linux kernel 到 4.9+ 再试。</p>
<h4><a id="toc-d68" class="anchor" href="#toc-d68"></a>检测 BBR 是否已开启</h4>
<pre><code class="hljs lang-undefined">sysctl net.core.default_qdisc
sysctl net.ipv4.tcp_congestion_control
</code></pre>
<h2><a id="toc-5a5" class="anchor" href="#toc-5a5"></a>0x01 - 启用 BBR</h2>
<pre><code class="hljs lang-bash">cat &lt;&lt; EOF &gt;&gt; /etc/sysctl.conf
<span class="hljs-comment"># Enable BBR</span>
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF

sysctl --system
</code></pre>
<h2><a id="toc-a0b" class="anchor" href="#toc-a0b"></a>0x02 - 检测 BBR</h2>
<pre><code class="hljs lang-undefined">sysctl net.core.default_qdisc
sysctl net.ipv4.tcp_congestion_control
</code></pre>
<p><strong>成功！</strong></p>
<hr>
<p>Sources:</p>
<ul>
<li><a href="https://github.com/google/bbr/blob/master/Documentation/bbr-quick-start.md">google/bbr | Github</a></li>
<li><a href="https://cloudplatform.googleblog.com/2017/07/TCP-BBR-congestion-control-comes-to-GCP-your-Internet-just-got-faster.html">TCP BBR congestion control comes to GCP – your Internet just got faster | Google Blog</a></li>
<li><a href="https://www.zhihu.com/question/53559433">Linux Kernel 4.9 中的 BBR 算法与之前的 TCP 拥塞控制相比有什么优势？| Zhihu</a></li>
<li><a href="http://blog.csdn.net/dog250/article/details/52830576">来自Google的TCP BBR拥塞控制算法解析 | CSDN</a></li>
</ul>

            ]]></description>
            <pubDate>Sun, 05 Nov 2017 21:03:37 GMT</pubDate>
            <guid>https://blog.whe.me/post/enable-bbr.html</guid>
        </item>
        <item>
            <title>Nginx www 重定向</title>
            <link>https://blog.whe.me/post/nginx-www-redirection.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#nginxwwwredirection">Nginx WWW Redirection</a><ul>
<li><a href="#redirectnon-wwwtowww">Redirect non-www to www</a><ul>
<li><a href="#toc-643">For Single Domain:</a></li>
<li><a href="#toc-30b">For All Domains:</a></li>
</ul>
</li>
<li><a href="#toc-8a6">Redirect www to non-www:</a><ul>
<li><a href="#toc-643">For Single Domain:</a></li>
<li><a href="#toc-30b">For All Domains:</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div><p>裸域名 <code>example.com</code> -&gt; <code>www.example.com</code> and vise-versa 的 Nginx 实现方法。</p>
<!--more-->
<p>摘自<a href="https://gist.github.com/ddhhz/287704d376befae122e0ca201b21ee15">Gist</a></p>
<h1><a id="nginxwwwredirection" class="anchor" href="#nginxwwwredirection"></a>Nginx WWW Redirection</h1>
<h2><a id="redirectnon-wwwtowww" class="anchor" href="#redirectnon-wwwtowww"></a>Redirect non-www to www</h2>
<h3><a id="toc-643" class="anchor" href="#toc-643"></a>For Single Domain:</h3>
<pre><code class="hljs lang-nginx"><span class="hljs-section">server</span> {
    <span class="hljs-attribute">server_name</span> example.com;
    <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> <span class="hljs-variable">$scheme</span>://www.example.com<span class="hljs-variable">$request_uri</span>;
}
</code></pre>
<h3><a id="toc-30b" class="anchor" href="#toc-30b"></a>For All Domains:</h3>
<pre><code class="hljs lang-nginx"><span class="hljs-section">server</span> {
    <span class="hljs-attribute">server_name</span> <span class="hljs-string">"~^(?!www\.).*"</span> ;
    <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> <span class="hljs-variable">$scheme</span>://www.<span class="hljs-variable">$host</span><span class="hljs-variable">$request_uri</span>;
}
</code></pre>
<h2><a id="toc-8a6" class="anchor" href="#toc-8a6"></a>Redirect www to non-www:</h2>
<h3><a id="toc-643" class="anchor" href="#toc-643"></a>For Single Domain:</h3>
<pre><code class="hljs lang-nginx"><span class="hljs-section">server</span> {
    <span class="hljs-attribute">server_name</span> www.example.com;
    <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> <span class="hljs-variable">$scheme</span>://example.com<span class="hljs-variable">$request_uri</span>;
}
</code></pre>
<h3><a id="toc-30b" class="anchor" href="#toc-30b"></a>For All Domains:</h3>
<pre><code class="hljs lang-nginx"><span class="hljs-section">server</span> {
    <span class="hljs-attribute">server_name</span> <span class="hljs-string">"~^www\.(.*)$"</span>;
    <span class="hljs-attribute">return</span> <span class="hljs-number">301</span> <span class="hljs-variable">$scheme</span>://<span class="hljs-variable">$1</span><span class="hljs-variable">$request_uri</span>;
}
</code></pre>

            ]]></description>
            <pubDate>Sat, 29 Jul 2017 22:05:24 GMT</pubDate>
            <guid>https://blog.whe.me/post/nginx-www-redirection.html</guid>
        </item>
        <item>
            <title>快速获取服务器公网 ip 地址（轻量、超快）</title>
            <link>https://blog.whe.me/post/get-external-ip.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-80d">查询公网 ip</a><ul>
<li><a href="#ipv4">ipv4</a></li>
<li><a href="#ipv6">ipv6</a></li>
<li><a href="#toc-720">添加为 alias</a></li>
</ul>
</li>
</ul>
</div><p>如何快速获取服务器的外部 ip 地址，这篇文章介绍了一个超快的方法。</p>
<!--more-->
<h2><a id="toc-80d" class="anchor" href="#toc-80d"></a>查询公网 ip</h2>
<p>一般查询本机的外部 ip 都是通过 http 的方法。OpenDNS 提供一个通过 dns 查询获取本机公网 ip 的方法：</p>
<h4><a id="ipv4" class="anchor" href="#ipv4"></a>ipv4</h4>
<pre><code class="hljs lang-undefined">$ dig +short myip.opendns.com @resolver1.opendns.com
</code></pre>
<h4><a id="ipv6" class="anchor" href="#ipv6"></a>ipv6</h4>
<pre><code class="hljs lang-undefined">$ dig +short AAAA myip.opendns.com @resolver1.ipv6-sandbox.opendns.com
</code></pre>
<h4><a id="toc-720" class="anchor" href="#toc-720"></a>添加为 alias</h4>
<pre><code class="hljs lang-bash"><span class="hljs-built_in">alias</span> myipv4=<span class="hljs-string">"dig +short myip.opendns.com @resolver1.opendns.com 2&gt; /dev/null"</span>
<span class="hljs-built_in">alias</span> myipv6=<span class="hljs-string">"dig +short AAAA myip.opendns.com @resolver1.ipv6-sandbox.opendns.com 2&gt; /dev/null"</span>
<span class="hljs-built_in">alias</span> myip=<span class="hljs-string">"myipv4; myipv6"</span>
</code></pre>

            ]]></description>
            <pubDate>Thu, 27 Jul 2017 10:38:56 GMT</pubDate>
            <guid>https://blog.whe.me/post/get-external-ip.html</guid>
        </item>
        <item>
            <title>Ubuntu 安装 VNC Server 和桌面环境</title>
            <link>https://blog.whe.me/post/ubuntu-install-gui.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-c73">0x00 - 准备工作</a></li>
<li><a href="#toc-6ef">0x01 - 安装桌面环境</a><ul>
<li><a href="#toc-8e6">完全安装（不推荐）</a></li>
<li><a href="#toc-4a1">精简安装</a></li>
</ul>
</li>
<li><a href="#toc-88b">0x02 - 安装并启用 VNC 服务器</a></li>
<li><a href="#toc-50b">0x03 - 测试 VNC</a></li>
<li><a href="#toc-c91">0x04 - 配置 VNC Server</a></li>
<li><a href="#toc-84d">0x05 - 设置开机自动启动（可选）</a></li>
<li><a href="#toc-c9e">0x06 - 小结</a></li>
</ul>
</div><p>十分钟给 Ubuntu Server 安装桌面环境。</p>
<!--more-->
<p>今天学校交作业视频，需要把一个 YouTube 上的高清视频下载下来再上传到学校服务器。可惜家里网速太慢，于是决定临时开了个 <a href="https://o.whe.me/do">Digital Ocaen (DO)</a> 的 <code>2CPU/4GB RAM</code> VPS 来完成这个任务。因为上传需使用浏览器登录学校网站，于是决定安装以下桌面环境和 Chromium 浏览器。</p>
<p>此教程使用 Ubuntu 16.04 LTS。</p>
<h2><a id="toc-c73" class="anchor" href="#toc-c73"></a>0x00 - 准备工作</h2>
<p>首先要有一台 Ubuntu 服务器，设置好 SSH 并登录。我使用了 DO 的 <a href="/post/cloud-init-configurations.html">Cloud-init</a> 功能自动完成所有服务器的初始化。</p>
<pre><code class="hljs lang-undefined">$ sudo apt update
</code></pre>
<h2><a id="toc-6ef" class="anchor" href="#toc-6ef"></a>0x01 - 安装桌面环境</h2>
<p>可以选择完全安装（不推荐）或精简安装。</p>
<h3><a id="toc-8e6" class="anchor" href="#toc-8e6"></a>完全安装（不推荐）</h3>
<p>包括很多额外组件：Office、浏览器等等。</p>
<pre><code class="hljs lang-undefined">$ sudo apt-get install ubuntu-desktop gnome-panel gnome-settings-daemon metacity nautilus gnome-terminal -y
</code></pre>
<h3><a id="toc-4a1" class="anchor" href="#toc-4a1"></a>精简安装</h3>
<p>仅安装核心组件。其他组件需手动安装</p>
<pre><code class="hljs lang-undefined">$ sudo apt-get install --no-install-recommends ubuntu-desktop gnome-panel gnome-settings-daemon metacity nautilus gnome-terminal -y
</code></pre>
<h2><a id="toc-88b" class="anchor" href="#toc-88b"></a>0x02 - 安装并启用 VNC 服务器</h2>
<pre><code class="hljs lang-undefined">$ sudo apt-get install vnc4server -y

$ ssh -L 5901:127.0.0.1:5901 &lt;username&gt;@&lt;host&gt; -p &lt;port&gt;

$ vncserver :1
</code></pre>
<p>这里 SSH 连接使用了<a href="/post/ssh-configurations-and-tips.html#toc-4b7">本地端口转发</a>。访问 <code>127.0.0.1:5901</code> 的本地流量会通过 SSH 加密隧道转发到远程服务器。</p>
<p>第一次运行 <code>vncserver</code> 时设置 VNC 密码。</p>
<h2><a id="toc-50b" class="anchor" href="#toc-50b"></a>0x03 - 测试 VNC</h2>
<p>本机使用任意 <a href="https://chrome.google.com/webstore/detail/vnc%C2%AE-viewer-for-google-ch/iabmpiboiopbgfabjmgeedhcmjenhbla?hl=en">VNC Client</a> 连接到 <code>localhost:5901</code>。</p>
<p>测试成功后回到 SSH 终端并输入 <code>$ vncserver -kill :1</code> 关闭 VNC 服务器。</p>
<h2><a id="toc-c91" class="anchor" href="#toc-c91"></a>0x04 - 配置 VNC Server</h2>
<p>编辑 <code>~/.vnc/xstartup</code>，在文件末新增如下四行：</p>
<pre><code class="hljs lang-undefined">gnome-panel &amp;
gnome-settings-daemon &amp;
metacity &amp;
nautilus &amp;
</code></pre>
<p>保存后运行 <code>vncserver :1</code> 启动 VNC Server。</p>
<h2><a id="toc-84d" class="anchor" href="#toc-84d"></a>0x05 - 设置开机自动启动（可选）</h2>
<p>使用 <code>crontab -e</code> 新增 <code>@reboot /usr/bin/vncserver :1</code> 任务。</p>
<h2><a id="toc-c9e" class="anchor" href="#toc-c9e"></a>0x06 - 小结</h2>
<p>建议防火墙关掉 5901 端口并永远使用 SSH 端口转发的方式安全使用 VNC。</p>
<p>因为我选了精简安装，需手动运行 <code>sudo apt-get install chromium-browser</code> 安装浏览器。安装完成后在 VNC 桌面菜单直接显示可用。</p>
<p>DO 纽约服务器很给力，半分钟下载 YouTube 视频（使用 <a href="https://rg3.github.io/youtube-dl/">youtube-dl</a>）半分钟上传。算上设置服务器和安装桌面环境一共使用了大约 20 分钟。<code>2CPU/4GB RAM</code> VPS 每小时为 <code>$0.06</code>，总花费：<code>$0.06 / 3 = $0.02</code>。</p>
<p>如果有兴趣开个 VPS 玩玩就试试 <a href="https://o.whe.me/do">DigitalOcean</a> 呗，使用此<a href="https://o.whe.me/do">链接</a>送 $10 等于免费玩两个月哈。</p>
<hr>
<p>Source: 「<a href="https://www.htcp.net/2524.html">Ubuntu 16.04 安装 VNC 及 gnome 桌面环境 | 云梦</a>」</p>

            ]]></description>
            <pubDate>Tue, 25 Jul 2017 21:27:24 GMT</pubDate>
            <guid>https://blog.whe.me/post/ubuntu-install-gui.html</guid>
        </item>
        <item>
            <title>Ubuntu 更新 hostname 主机名</title>
            <link>https://blog.whe.me/post/ubuntu-update-hostname.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#method1">Method 1</a></li>
<li><a href="#method2">Method 2</a></li>
</ul>
</div><p>系统默认的主机名太丑了！如何更新 Ubuntu 的 hostname 主机名呢？以下有两个简单的方法。</p>
<!--more-->
<h2><a id="method1" class="anchor" href="#method1"></a>Method 1</h2>
<pre><code class="hljs lang-bash">NEW_HOSTNAME=<span class="hljs-string">"Ubuntu-Test"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"127.0.1.1 <span class="hljs-variable">$NEW_HOSTNAME</span>"</span> | sudo tee --append /etc/hosts
<span class="hljs-built_in">echo</span> <span class="hljs-variable">$NEW_HOSTNAME</span> | sudo tee /etc/hostname
sudo hostname <span class="hljs-variable">$NEW_HOSTNAME</span>
</code></pre>
<h2><a id="method2" class="anchor" href="#method2"></a>Method 2</h2>
<pre><code class="hljs lang-bash">NEW_HOSTNAME=<span class="hljs-string">"Ubuntu-Test"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"127.0.1.1 <span class="hljs-variable">$NEW_HOSTNAME</span>"</span> | sudo tee --append /etc/hosts
sudo hostnamectl <span class="hljs-built_in">set</span>-hostname <span class="hljs-variable">$NEW_HOSTNAME</span>
</code></pre>
<p>两种方法效果相同。执行 <code>hostname</code> 可以看到更新后的主机名。</p>
<p>更新之后 PS1 不会立即更新，可以重新登录或者执行 <code>exec bash</code> 更新。</p>

            ]]></description>
            <pubDate>Tue, 25 Jul 2017 06:34:47 GMT</pubDate>
            <guid>https://blog.whe.me/post/ubuntu-update-hostname.html</guid>
        </item>
    </channel>
</rss>
