更好的 Aria2 容器化使用方案

本文使用「署名 4.0 國際 (CC BY 4.0)」許可協議,歡迎轉載、或從新修改使用,但須要註明來源。 署名 4.0 國際 (CC BY 4.0)javascript

本文做者: 蘇洋css

建立時間: 2019年04月04日 統計字數: 6010字 閱讀時間: 12分鐘閱讀 本文連接: soulteary.com/2019/04/04/…html


更好的 Aria2 容器化使用方案

平常偶爾會下載百度網盤的資源,可是又嫌棄官方客戶端「限速」和「笨重」,十天前趁着整理 HomeLab 的機會,把 Aria2 封裝成了容器鏡像。前端

本文將介紹如何搭配 Traefik 快速使用 Aria2 ,以及如何調整不兼容 Traefik 的應用與之兼容。java

鏡像介紹

在開始講解如何作以前,須要先簡單介紹一下鏡像的構成。node

對於長期運行的應用/服務來講,代碼是否透明公開很重要,我已經將代碼上傳至 GitHub:代碼倉庫地址git

使用到的技術棧/工具主要有如下內容:github

改造過程

網上盛行使用一個容器同時提供 HTTP + ARIA2 服務,可是這種胖容器其實不符合「單一進程單一容器」的原則,更通俗的說,這樣使用容器,和使用 VM 虛擬機沒有差異。web

因此在使用的過程當中,咱們須要單獨運行這兩個部分,使用 docker-compose.yml 定義的話,或許最簡單的示例就是下面這樣了:docker

version: '3'

services:

  web:
    container_name: web
    image: ${WEBUI_IMAGE}
    expose:
      - 8888

  aria2:
    container_name: aria2
    image: ${ARIA2_IMAGE}
    volumes:
      - ./downloads:/downloads
    expose:
      - 6800
複製代碼

但若是你將 Aria2 WebUI 直接使用在容器裏,你會發現這個 Web 管理界面將 RPC 端口寫死爲了 6800 ,由於項目設計的時候,只考慮了同機部署,而且是 Aria2 在默認配置執行的狀況。

之因此這樣處理,是由於項目自己是一套使用 Angular 編寫的 SPA 程序,不具有服務端接口處理能力,可是實際上,項目在生產環境運行的時候,做者有提供一個簡單的 Node Server,是能夠支持必定程度的接口定製的。

而咱們使用容器將 Web UI 和 Aria2 進行隔離,至關於在兩臺不一樣的服務器中執行這兩個應用,因此這裏咱們要進行一些改造。

WebUI 支持和 Aria2 分離部署

影響 WebUI 使用 Aria2 RPC 接口的主要有兩處引用,分別在:

/app/src/js/services/configuration.js
/app/src/js/services/rpc/rpc.js
複製代碼

先處理 configuration.js 的內容:

host: location.protocol.startsWith("http") ? location.hostname : "localhost",
    path: "/jsonrpc",
    port: 6800,
    encrypt: false,
複製代碼

找到程序中上面的代碼部分,將端口 6800 修改成 80

接着處理 rpc.js 的內容:

if (["http", "https"].indexOf(uri.protocol()) != -1 && uri.host() != "localhost") {
                configurations.push(
                    {
                        host: uri.host(),
                        path: "/jsonrpc",
                        port: 6800,
                        encrypt: false
                    },
複製代碼

一樣找到這段代碼,將端口 6800 修改成 80

將代碼改造完畢,你會發現WebUI中全部的請求都被定位到了當前域名下的 80 端口,前面提過,這個界面是一套前端 SPA 應用,是缺少接口處理能力的。

因此,接下來咱們要對服務端進行處理,我使用 ES6 重寫了做者的 Node Server,主要支持了 HTTP/WS 協議轉發至 aria2 容器對應的端口。

const { createServer } = require("http");
const url = require("url");
const { join, extname } = require("path");
const { exists, statSync, readFile } = require("fs");

const port = parseInt(process.argv[2], 10) || 8888;
const baseDir = join(process.cwd(), "docs");

const httpProxy = require('http-proxy');

const { WEB_PROXY, WS_PROXY } = process.env;
const webProxy = httpProxy.createProxyServer({ target: WEB_PROXY });
const wsProxy = httpProxy.createServer({ target: WS_PROXY, ws: true });

createServer(function (request, response) {
    const uri = url.parse(request.url).pathname;
    let filename = join(baseDir, uri);

    let contentType = "text/html";
    switch (extname(filename)) {
        case ".js":
            contentType = "text/javascript";
            break;
        case ".css":
            contentType = "text/css";
            break;
        case ".ico":
            contentType = "image/x-icon";
            break;
        case ".svg":
            contentType = "image/svg+xml";
            break;
    }

    if (filename.endsWith("jsonrpc")) {
        webProxy.web(request, response);
        return;
    }

    exists(filename, function (exists) {
        if (!exists) {
            response.writeHead(404, { "Content-Type": "text/plain" });
            response.write("404 Not Found\n");
            response.end();
            return;
        }

        if (statSync(filename).isDirectory()) filename += "/index.html";

        readFile(filename, "binary", function (err, file) {
            if (err) {
                response.writeHead(500, { "Content-Type": "text/plain" });
                response.write(`${err}\n`);
                response.end();
                return;
            }
            response.writeHead(200, { "Content-Type": contentType });
            response.write(file, "binary");
            response.end();
        });
    });
}).on('upgrade', function (req, socket, head) {
    wsProxy.ws(req, socket, head);
}).listen(port);

console.log(`WebUI Aria2 Server is running on http://localhost:${port}`);
複製代碼

當用戶訪問管理界面的時候,靜態資源會直接使用 Node.js 的 HTTP 模塊提供服務。而當用戶進行下載狀態查詢,新增下載任務的時候,將經過 Node.js 調用 Aria2 的接口進行處理,並將結果使用 HTTPWS 反饋給用戶。

構建鏡像

爲了方便部署和維護,咱們須要將上面的改動記錄在 Dockerfile 中。

FROM node:11.12-alpine
LABEL AUTHOR soulteary

ADD webui-aria2/package.json /app/package.json
ADD webui-aria2/package-lock.json /app/package-lock.json

WORKDIR /app

RUN npm install -g yarn && yarn

ADD webui-aria2/ /app

ADD ./patches/configuration.js /app/src/js/services/configuration.js
ADD ./patches/rpc.js /app/src/js/services/rpc/rpc.js

RUN yarn build

RUN yarn add http-proxy
ADD ./patches/node-server.js /app/node-server.js

CMD [ "node", "./node-server.js" ]
複製代碼

執行 docker build -t soulteary/traefik-aria2-with-webui . ,稍等片刻你將獲得封裝好的容器鏡像。

接下來即是使用容器編排工具,正式使用啦。

編排應用

Aria2 的鏡像,可使用 ndthuan/aria2-alpine,無須從新封裝,固然,若是你須要一些新的特性,也能夠參考鏡像提供的 Dockerfile,進行從新構建。

建立一個 docker-compose.yml,使用下面的示例代碼:

version: '3'

services:

  web:
    container_name: web
    image: ${WEBUI_IMAGE}
    expose:
      - 8888
    networks:
      - traefik
    environment:
      - WEB_PROXY=http://aria2:6800
      - WS_PROXY=ws://aria2:6800
    labels:
      - "traefik.enable=true"
      - "traefik.port=8888"
      - "traefik.frontend.rule=Host:${BIND_HOSTS}"
      - "traefik.frontend.entryPoints=http,https"

  aria2:
    container_name: aria2
    image: ${ARIA2_IMAGE}
    volumes:
      - ./downloads:/downloads
    expose:
      - 6800
    networks:
      - traefik
    labels:
      - "traefik.enable=false"

networks:
  traefik:
    external: true
複製代碼

而後再建立一個 .env 文件,在其中填入:

WEBUI_IMAGE=soulteary/traefik-aria2-with-webui
ARIA2_IMAGE=ndthuan/aria2-alpine

BIND_HOSTS=download.lab.com,download.lab.io
複製代碼

使用 docker-compose up,你將看到相似下面的日誌:

Creating web   ... done
Creating aria2 ... done
Attaching to aria2, web
aria2    | 2019-04-04T04:28:41Z 48a00033e530 confd[7]: INFO Backend set to env
aria2    | 2019-04-04T04:28:41Z 48a00033e530 confd[7]: INFO Starting confd
aria2    | 2019-04-04T04:28:41Z 48a00033e530 confd[7]: INFO Backend nodes set to
aria2    | 2019-04-04T04:28:41Z 48a00033e530 confd[7]: INFO Target config /etc/aria2.conf out of sync
aria2    | 2019-04-04T04:28:41Z 48a00033e530 confd[7]: INFO Target config /etc/aria2.conf has been updated
aria2    |
aria2    | 04/04 04:28:41 [WARN] Neither --rpc-secret nor a combination of --rpc-user and --rpc-passwd is set. This is insecure. It is extremely recommended to specify --rpc-secret with the adequate secrecy or now deprecated --rpc-user and --rpc-passwd.
aria2    |
aria2    | 04/04 04:28:41 [NOTICE] IPv4 RPC: listening on TCP port 6800
aria2    |
aria2    | 04/04 04:28:41 [NOTICE] IPv6 RPC: listening on TCP port 6800
web      | WebUI Aria2 Server is running on http://localhost:8888
複製代碼

在瀏覽器中打開你在 .env 中定義的域名,不出意外,你將看到屬於你的下載應用。

搭建好的 Aria Web UI 界面

最後

改變習慣不易,尤爲是你周圍的環境都在使用其餘看起來更「標準成熟」的方案時。

可是一旦當你用慣了 Traefik + Docker 以後,你會發現你的服務搭建效率遠比使用 Nginx 加 vhost 高的多。

最近快忙暈了,臨近小長假,補全了這篇拖了十天的文章,倉促成文,不免有紕漏,歡迎留言指正。


我如今有一個小小的折騰羣,裏面彙集了一些喜歡折騰的小夥伴。

在不發廣告的狀況下,咱們在裏面會一塊兒聊聊軟件、HomeLab、編程上的一些問題,也會在羣裏不按期的分享一些技術沙龍的資料。

喜歡折騰的小夥伴歡迎掃碼添加好友。(請註明來源和目的,不然不會經過審覈)

關於折騰羣入羣的那些事

相關文章
相關標籤/搜索