备份 Docker 镜像容器和数据

本意上想要了解一下 Docker 容器中 Volume 的备份,毕竟重要的数据都在 Volume 中。然后顺带看了一下 Docker 镜像,容器的备份,不过镜像和容器托管到 Docker Hub 上也算是备份了。

Volume 可以叫做 数据卷,可供一个或者多个容器使用:

  • 数据卷 可以在容器之间共享和重用
  • 对 数据卷 的修改会立马生效
  • 对 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除

镜像备份

这里说的备份指的是直接从本地备份镜像文件,可以使用 docker save 命令将镜像打包成 tar 文件,之后可以使用 docker load 命令来恢复。

容器备份

备份容器有不同的方法:

  • 通过 [docker commit] 命令来提交一个基于当前容器状态的新镜像
  • 使用 [docker export] 命令来将容器导出到系统文件并压缩成 tar,之后可以根据该 tar 文件使用 docker import 来创建新的镜像

需要注意的是所有的命令都只会备份容器 layered file system ,不包括 挂载的数据卷 Volumes

数据卷操作

Docker user guide 中有非常详细的知道,如何备份数据卷,这样就可以在新容器启动时使用备份好的数据。当备份 data volume 时,需要先关闭容器。

docker volume create my-vol          # 创建数据卷
docker volume ls                     # 查看所有数据卷
docker volume inspect my-vol         # 查看指定数据卷内容
docker run -d -P \
    --name web \
    # -v my-vol:/wepapp \
    --mount source=my-vol,target=/webapp \
    training/webapp \
    python app.py                   # 启动并挂载一个数据卷 使用 `--mount`
docker inspect web                  # 查看容器中 mount 信息
docker volume rm my-vol             # 移除数据卷

数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。

无主(dangling)的数据卷可能会占据很多空间,要清理请使用以下命令

docker volume prune

数据卷备份

比如在 docker compose 中定义了 volumes

volumes:
  db_data:

那么在启动 docker compose 之后会生成一个 DOCKER_COMPOSE_NAME 加上 VOLUME_NAME 的容器卷

[DOCKER_COMPOSE_NAME]_[VOLUME_NAME]

那么可以使用下面的命令来备份数据卷:

docker run --rm \ 
  --volume [DOCKER_COMPOSE_PREFIX]_[VOLUME_NAME]:/[TEMPORARY_DIRECTORY_TO_STORE_VOLUME_DATA] \
  --volume $(pwd):/[TEMPORARY_DIRECTORY_TO_STORE_BACKUP_FILE] \
  alpine \
  tar cvf /[TEMPORARY_DIRECTORY_TO_STORE_BACKUP_FILE]/[BACKUP_FILENAME].tar /[TEMPORARY_DIRECTORY_TO_STORE_VOLUME_DATA]

看清楚其中的临时 DATA 目录和 临时备份目录,执行该命令之后,在当前文件夹下就会产生 BACKUP_FILENAME.tar 这样的文件,里面包含数据卷中的内容。

那么就能够使用该命令来恢复数据卷数据

docker run --rm \ 
  --volume [DOCKER_COMPOSE_PREFIX]_[VOLUME_NAME]:/[TEMPORARY_DIRECTORY_STORING_EXTRACTED_BACKUP] \
  --volume $(pwd):/[TEMPORARY_DIRECTORY_TO_STORE_BACKUP_FILE] \
  alpine \
  tar xvf /[TEMPORARY_DIRECTORY_TO_STORE_BACKUP_FILE]/[BACKUP_FILENAME].tar -C /[TEMPORARY_DIRECTORY_STORING_EXTRACTED_BACKUP] --strip 1

如果是数据库容器,比如 mysql 容器,备份数据可以使用如下方式

docker exec [CONTAINER_NAME] /usr/bin/mysqldump -u root --password=root [DATABASE] > backup.sql

然后使用下面的命令来恢复

cat backup.sql | docker exec -i [CONTAINER_NAME] /usr/bin/mysql -u root --password=root [DATABASE]

对于 docker compose 启动的多个容器,可能因为宿主机器变化而导致 docker 容器的id有变化,可能在回复数据之后,还需要对数据库连接的地址进行修改才能完整的恢复。

reference


2018-03-16 linux , docker , container , image , volume

树莓派中安装 Docker 及 docker compose

仅仅作为记录,为了不让树莓派吃灰。主要参考官网这篇文章

Docker 的好用程度已经不比多说,经过这两年的发展已经非常成熟,还记得一年前买的书已经跟不上Docker的发展了,所以这里还是推荐 Docker 的官方文档,要比市面上存在所有书籍要详细。不过要是想要了解 Docker 的内部技术还是有不少好书可以参考。跑偏了,回到正题。

安装

Docker 官方已经支持 Raspbian Jessie,所以可以直接安装:

curl -sSL https://get.docker.com | sh

Docker client 只能被 root 或者 docekr group 中的用户使用,所以将 pi 用户添加到 docker 用户组:

sudo usermod -aG docker pi

使用

如果拉取了 busybox 镜像,可能会发现工作不正常,这只是因为有些镜像是为了 PC 或者 x86_64 架构设计的,可能未来版本会修复,所以应该使用那些设计为了在 ARM 上运行的镜像,目前 Docker 团队维护了一小部分镜像,可以在arm32v6这个用户下找到。

可以使用 arm32v6/alpine 作为基础镜像,同样也可以使用 Resin.io 制作的镜像,该镜像被用在了当前 Docker 中,这是一个轻量版本的 Raspberry Jessie。

制作镜像

比如说想要制作一个在树莓派上能够跑的镜像,可以以 resion/rpi-raspbian 作为基础镜像

FROM resin/rpi-raspbian:latest
ENTRYPOINT []

RUN apt-get update && \
    apt-get -qy install curl ca-certificates

CMD ["curl", "https://docker.com"]

或者也可以

FROM arm32v6/alpine:3.5

RUN apk add --no-cache curl ca-certificates

CMD ["curl", "https://docker.com"]

build 命令

docker build -t curl_docker .
docker run curl_docker

如果不怎么使用 Raspberry Pi 连接显示器,或者不怎么使用 GPU,可以限制 gpu 内存的占用,修改 /boot/config.txt 添加下面一行:

gpu_mem=16

reference


2018-03-15 linux , respberry-pi , docker , docker-compose

VPS 云服务器能够做什么

很早以前买一台VPS主要的功能就是翻墙,然后常年也仅仅是跑一个SS,后来渐渐的发现其实有一台服务器即使只有单核1G,也能够用来做很多事情。以前我也看过一些文章讲述如何充分利用起VPS,但大部分除了说自建网站,挂机刷YouTube赚钱外也都没有什么实质性的内容。而自从开始接触Docker,我渐渐的发现了很多服务,因此我自己搜罗了一些。当然其实 GitHub 上有一个 Awesome Selfhosted 这里面列举了成百上千种可以自己托管部署的服务,几乎可以代替掉日常生活中用到的80%的服务,可以自建 nextcloud 代替 Dropbox,可以用 WordPress 代替 Blogger,可以用 GitLab,gogs 代替 GitHub,你甚至可以构建自己的即时通讯,邮件服务,社交媒体1 等等。

当然这一切都无法离开一台可用稳定的VPS,并且借助Docker几乎可以做到无痛搭建,备份,迁移,不用在花费时间在环境和部署中。下面就分享一些我觉得很好用的服务,可以用来充分利用你的机器。

wordpress

自建博客

linx server

文件共享服务,使用 Go 书写,可以用来快速分享本地文件给别人。支持 Web UI,也支持 API 上传。

h5ai

一款文件夹浏览,一般情况下开启 Nginx 允许浏览目录的设置也足够了,h5ai提供了一些额外的功能,比如二维码,搜索等等

gogs

一款较 GitLab 轻便的 Git 托管服务。

Drone

持续集成 CI

frp

内网穿透

qiandao

签到服务,binux 写的自动签到服务

pyspider

爬虫

自建DNS

无污染 DNS 服务器

jumpserver

作为跳板机 ssh 连接其他电脑用

aria2

aria2 挂机下载


2018-03-14 linux , vps , cloud , server , docker

docker volumes 中 -v 和 -mount 区别

Docker Volumes 机制通常用来给 Docker 容器保存持久化数据,使用 Volumes 有很多优势

  • 更容易备份和迁移
  • 使用 Docker CLI 命令或者 Docker API 来管理
  • 可以在 Linux 和 Windows 上使用
  • 可以更安全得在多个容器中共享
  • Volume drivers 允许容器将内容保存到远端,云服务提供商,或者加密volume内容,或者增加其他功能
  • 新 Volume 的内容可以被容器预先填充

Volumes 通常也优于容器的可写层,使用 Volumes 不会增加容器的体积,并且 Volumes 的内容存储在外部独立于容器的生命周期。如果容器不产生持久化数据,可以考虑使用 tmpfs mount来避免数据存储在其他可能的地方,避免增加容器的体积。

-v 和 -mount 选项

最开始 -v 或者 --volume 选项是给单独容器使用, --mount 选项是给集群服务使用。但是从 Docker 17.06 开始,也可以在单独容器上使用 --mount。通常来讲 --mount 选项也更加具体(explicit)和”啰嗦”(verbose),最大的区别是

  • -v 选项将所有选项集中到一个值
  • --mount 选项将可选项分开

如果需要指定 volume driver 选项,那么必须使用 --mount

  • -v--volume: 包含三个 field,使用 : 来分割,所有值需要按照正确的顺序。第一个 field 是 volume 的名字,并且在宿主机上唯一,对于匿名 volume,第一个field通常被省略;第二个field是宿主机上将要被挂载到容器的path或者文件;第三个field可选,比如说 ro
  • --mount: 包含多个 key-value 对,使用逗号分割。--mount 选项更加复杂,但是各个值之间无需考虑顺序。

    • type,可以为 bind, volume, tmpfs, 通常为 volume
    • source 也可以写成 src,对于 named volumes,可以设置 volume 的名字,对于匿名 volume,可以省略
    • destination 可以写成 dst或者 target 该值会挂载到容器
    • readonly 可选,如果使用,表示只读
    • volume-opt 可选,可以使用多次

两个例子

docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html \
  nginx:latest

docker run -d \
  --name=nginxtest \
  -v nginx-vol:/usr/share/nginx/html \
  nginx:latest

reference


2018-03-13 docker , dockerfile , command , docker-compose , linux

使用 Docker 安装 gogs

Gogs 是一个能够自建Git托管服务的开源项目,用 Go 语言实现。因为较之 GitLab 轻量化一些,所以受到一定欢迎。

使用 Docker 来搭建 Gogs 服务时,需要额外依赖 MySQL,网上一般的教程都是先启动一个 MySQL 容器,开放端口,然后在启动 Gogs 容器配置。其实可以使用 docker-compose 一次性启动好。

version: '3.3'
services: 
  gogsdb:
    image: mysql:5.7
    container_name: gogsdb
    restart: always
    environment:
      MYSQL_DATABASE: gogs
      MYSQL_ROOT_PASSWORD: gogs
      MYSQL_USER: gogs
      MYSQL_PASSWORD: gogs
    volumes:
      - db_data:/var/lib/mysql_gogs
    ports:
      - "13306:3306"
    networks:
      - gogs-network

  gogsapp:
    depends_on:
      - gogsdb
    image: gogs/gogs
    container_name: gogsapp
    restart: always
    ports:
      - "322:22"
      - "3000:3000"
    volumes:
      - app_data:/data
    networks:
      - gogs-network
volumes:
  db_data:
  app_data:
networks:
  gogs-network:
    driver: bridge

在第一次安装的时候你可能遇到下面的错误:

数据库设置不正确:dial tcp 127.0.0.1:13306: getsockopt: connection refused

或者

Database setting is not correct: dial tcp 127.0.0.1:13306: getsockopt: connection refused

这个错误的意思就是他表达的意思,gogs 想要在 127.0.0.1:13306 这个地址和端口连接 MySQL 失败了,我找了一圈之后发现,这里的地址需要填写 MySQL 容器的网关地址。

sudo docker inspect gogsdb

查看输出中的 Gateway 字段,填写这个地址。

最新的内容可以参考: https://github.com/einverne/dockerfile/tree/master/gogs


2018-03-12 docker , gogs , git , github

docker-compose 中 links 和 depends_on 区别

以下的内容适用于 docker-compose 版本 version 2version 3。先来看 Docker 官方文档中关于 Docker Compose and Django的例子,可以使用 depends_on 来访问容器中的数据

version: '2'
services:
  db:
    image: postgres
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

而比如下面使用

web:
  links:
   - db

则表示当启动 db 容器时会随机分配一个本地端口比如32777来连接容器3306端口,每一次修改或者重启容器都会改变该端口,使用 links 来保证每一次都能够连接数据库,而不需要知道具体端口是什么。比如说启动了一个 mysql 容器

docker run -d --name=my-mysql --env="MYSQL_ROOT_PASSWORD=mypassword" -P mysql
docker inspect <container-id> | grep HostPort

会显示该容器的本地端口。

docker-compose 执行 V2 文件时会自动在容器间创建一个网络,每一个容器都能够立即通过名字来访问到另外的容器。 因此,不再需要 links,links 过去通常用来开始db容器和web server容器网络之间的通讯,但是这一步已经被 docker-compose 做了。1

当使用 depends_on 来定义服务之间的依赖关系时会造成下面的影响2

  • docker-compose up 会依据依赖顺序启动服务
  • docker-compose up 启动时 SERVICE 会自动包括 SERVICE 的依赖

看这个例子:

version: '2'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

这个例子中 db ,redis 容器启动顺序要优先于 web 容器;当启动 web 容器时会自动创建 redis 和 db 容器。

不过需要注意的是, depends_on 不会等到 db 和 redis 容器 ready 再启动,web 容器仅仅等到 redis 和 db 容器启动就开始启动。具体可参考官网启动顺序了解。

reference


2018-03-12 docker , docker-compose , linux

使用 privoxy 转发 socks 到 http

Privoxy 是一款不进行网页缓存且自带过滤功能的代理服务器,本文主要使用其 socks 转 http 代理的功能。Privoxy 也能够过滤网页内容,管理 Cookie,控制访问,去广告、横幅、弹窗等等,因此可以作为广告过滤。

Privoxy is a non-caching web proxy with advanced filtering capabilities for enhancing privacy, modifying web page data and HTTP headers, controlling access, and removing ads and other obnoxious Internet junk. GNU GPLv2 开源

因为 shadowsocks,v2ray 都是将代理转为本地 socks5 代理,所以如果需要使用 http 代理,就需要借助 Privoxy 。如果只需要在本地启用 http 代理,也可以使用 proxychains

安装及配置

在 Linux 下安装非常简单

sudo apt install privoxy

默认的配置文件地址在 /etc/privoxy/config 目录下。假设本地 1080 端口已经启动(不管是本地 sslocal 还是 v2ray 本地都需要启动)然后要将本地 1080 socks5 代理转成 http 代理,重要的配置只有两行

# 把本地HTTP流量转发到本地1080 SOCKS5代理
forward-socks5t / 127.0.0.1:1080 .
# 可选,默认监听本地连接
listen-address 127.0.0.1:8118

如果想要将 http 代理非常到局域网中,可以使用 listen-address 0.0.0.0:8118。 Privoxy 默认的端口为 8118,可以自行修改。修改完成保存之后使用如下命令启动

sudo /etc/init.d/privoxy start
sudo /etc/init.d/privoxy reload   # 不重启服务的情况下重新加载配置

可以在终端进行测试 export http_proxy=http://127.0.0.1:8118 && curl ip.gs 应该显示代理的IP地址。如果监听 0.0.0.0:8118 ,那么局域网中,使用 ip:8118 也能够使用该 HTTP 代理,并且所有的流量都经由 HTTP 转发到 SOCKS5 代理,并走 shadowsocks 或者 v2ray 到墙外。

使用浏览器配置HTTP代理,然后访问 http://p.p 如果看到 Privoxy 启动成功表示一切OK。

无法启动或启动错误

当启动 sudo /etc/init.d/privoxy start 时出现如下错误:

systemctl status privoxy.service
● privoxy.service - Privacy enhancing HTTP Proxy
Loaded: loaded (/lib/systemd/system/privoxy.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Sun 2018-03-11 17:49:40 CST; 4s ago
Process: 23666 ExecStopPost=/bin/rm -f $PIDFILE (code=exited, status=0/SUCCESS)
Process: 23668 ExecStart=/usr/sbin/privoxy --pidfile $PIDFILE --user $OWNER $CONFIGFILE (code=exited, status=1/FAILURE)
Main PID: 21029 (code=exited, status=15)

Mar 11 17:49:39 VM-145-149-ubuntu systemd[1]: Stopped Privacy enhancing HTTP Proxy.
Mar 11 17:49:39 VM-145-149-ubuntu systemd[1]: Starting Privacy enhancing HTTP Proxy...
Mar 11 17:49:40 VM-145-149-ubuntu systemd[1]: privoxy.service: Control process exited, code=exited status=1
Mar 11 17:49:40 VM-145-149-ubuntu systemd[1]: Failed to start Privacy enhancing HTTP Proxy.
Mar 11 17:49:40 VM-145-149-ubuntu systemd[1]: privoxy.service: Unit entered failed state.
Mar 11 17:49:40 VM-145-149-ubuntu systemd[1]: privoxy.service: Failed with result 'exit-code'.

绝大部分情况下是配置文件错误,仔细检查 /etc/privoxy/config 文件,是否有重复配置,或者输入错误。

Privoxy 配置

其核心配置文件在 /etc/privoxy/config 文件中,之前配置过转发 socks 流量就在该文件中,不过通常情况下会需要修改另外两类文件:

  • action 文件,其中又包括 match-all.actiondefault.actionuser.action
  • filter 文件,其中包括 default.filteruser.filter

match-all.actiondefault.actiondefault.filter 建议不要修改, Privoxy 升级时会覆盖掉,自定义内容可以放入 user.actionuser.filter 文件中。

action 文件定义 Privoxy 动作,比如

{+block{禁止访问垃圾百度}}
.baidu.com

{+block} 是一个动作,block 后面的 {} 注释,可省略; .baidu.com 是上述动作对象,分为两个部分,host 和 path, host 部分支持通配符,path 部分指的是 / 后部分网址,支持 POSIX 1003.2 正则表达式。更加具体的可以参考官网文档。上述配置生效之后 baidu.com 的任何请求都会返回 403 。

filter 文件定义过滤响应的规则,比如

FILTER: replaceText 替换文本
s|网易|Google|g 

FILTER 大写表示定义过滤规则, replaceText 表示规则名称,后面接注释;第二行定义具体规则,如果使用过 vi 或者 sed 工具,那么一定很熟悉这个 s 替换命令。

定义了 user.filter 过滤规则之后,需要在 user.action 文件中应用规则

{+filter{replaceText}}
.163.com

这样访问 163.com 网站中任何带有“网易”的字都会被替换为 Google,当然如果网页启用了 HTTPS,那么 Privoxy 也无能为力。Privoxy 唯一能够对HTTPS网站做的就是 block 了。这也就意味着屏蔽HTTPS网站页面内广告的能力下降了。

当前 Privoxy 配置的 action 和 filter 文件可以在代理情况下访问 http://config.privoxy.org/show-status 这个网址查看到。

广告屏蔽

前面也提到过 Privoxy 的广告过滤,不过需要注意的是使用去广告功能可能丢失一定的匿名性1

下载 user.actionuser.filter 两个文件分别替换 /etc/privoxy/ 目录下的默认文件,重启 Privoxy 。

reference


2018-03-11 linux , proxy , socks , http , proxychains

查看Docker容器的日志

系统运行一段时间之后难免容器会出现问题,出现问题并不可怕,可怕的是不知道问题出现在哪里,这个时候查看当前容器运行的日志就能够排查出一些问题。

在之前的文章中,学会了如何创建,查看,移除等等管理容器的方法,其实查看日志也和这些方法类似。

比如要查看容器所有运行的日志可以使用

docker logs [containerId]

如果要持续观察容器的日志输出,可以使用 -f 或者 --follow 参数

docker logs -f [containerId]

但是这个命令时灵时不灵,在不同系统上,有的时候会打印出全部的日志,就和没加 -f 参数一样,所以有的时候要查看日志最末尾几行可以使用 --tail

docker logs --tail 100 [containerId]

如果想要查看某个时间之后的日志,可以使用 --since

docker logs --since 2018-05-01 [containerId]

同理如果要查看直到某个时间之前的日志也可以使用 --until

docker logs --until 2018-05-01 [containerId]

2018-03-10 docker , logs , linux

挂载腾讯云对象存储COS

腾讯云对象存储 Cloud Object Storage ,简称 COS,是腾讯云为企业和个人开发者提供的存储海量数据的分布式存储服务。

基本使用

控制面板申请对象存储基本信息,创建存储桶,输入名字,选择地域,选择访问权限,然后访问秘钥,可以得到如下信息:

bucket: backup-1251234567 (格式为 bucketname-appid)
SecretId: SecretId
SecretKey: SecretKey
region: ap-beijing
appid: 123456789

将腾讯云 COS 挂载到腾讯云服务器中

安装必要的应用

wget https://github.com/tencentyun/cosfs/releases/download/v1.0.2/cosfs_1.0.2-ubuntu16.04_amd64.deb
sudo apt update && sudo apt install gdebi-core
sudo gdebi release-cosfs-package

配置文件,设置 bucket name, access key/id 等信息,将其存放在 /etc/passwd-cosfs 文件中,文件权限设置为 640

echo my-bucket:my-access-key-id:my-access-key-secret > /etc/passwd-cosfs
chmod 640 /etc/passwd-cosfs

然后将 cos bucket mount 到指定目录

cosfs appid:bucket-name mount-point -ourl=my-cos-endpoint -odbglevel=info -oallow_other

这里的 cos-endpoint 不同地区不一样,比如北京是 http://cos.ap-beijing.myqcloud.com,其他地区根据 region 不同设置不同值

项目可参考官方项目

COSFS 工具使用


2018-03-09 linux , cos , tencent , vps , cloud-storage

Flask 使用模板渲染

Flask 使用 Jinja2 模板引擎。

Flask 会在 templates 文件夹里寻找模板。所以,如果你的应用是个模块,这个文件夹应该与模块同级;如果它是一个包,那么这个文件夹作为包的子目录:

情况 1: 模块:

/application.py
/templates
    /hello.html

情况 2: 包:

/application
    /__init__.py
    /templates
        /hello.html

使用 Jinja2 只需要使用 render_template() 方法

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

模板支持继承

继承模式具体可参考: http://docs.jinkan.org/docs/flask/patterns/templateinheritance.html#template-inheritance

reference


2018-03-07 linux , flask , template , jinja2

电子书

Google+

最近文章

  • 威联通折腾篇五:安装 Transmission 下载 BT 这一篇讲在威联通上安装和使用下载工具 – Transmission。
  • 威联通折腾篇六:文件同步 文件同步应该算是 NAS 最最基本的一个服务了,但是为什么直到篇六才提到他呢,是因为威联通自带的 QSync ,嗯,虽然能用,但是,没有 Linux 客户端,虽然其他平台客户端 OK,但是作为我主力工作的平台没有同步客户端,只能 smb 挂载。而之前搞 zerotier 同局域网速度不佳,其他 frp 内网穿透 也最多拉一些小文件,完全做不到 Dropbox 那样无缝,无痛。
  • 威联通折腾篇四:Container Station 运行 Docker 容器 威联通上有一个 Container Station 的应用,可以直接用官方的 App Center 中下载安装,这其实就是一个 Docker 本地环境,如果熟悉 Docker 使用,那么其实都直接可以 ssh 登录 NAS 然后完全使用命令行来操作。
  • 威联通折腾篇一:使用命令行安装威联通 QNAP 的 qpkg 安装包 如果想要给威联通安装一个 qpkg 的安装包时,最直观界面方式就是在 App Center 中,右上角,将本地的 .qpkg 文件上传到 NAS 并安装。
  • 威联通折腾篇二:使用 frp 内网穿透 这是 QNAP NAS 折腾第二篇,使用 frp 来从外网访问 NAS。威联通自带的 qlink.to 实在是太慢几乎到了无法使用的地步,用 Zerotier 也依然很慢,所以无奈还是用回了 frp.