目录

「TrueNAS」听我说谢谢你

TrueNAS安装及配置。

TrueNAS

RAID 简介

RAID (Redundant Array of Independent/InexpensiveDisks),独立磁盘冗余阵列,简称为「磁盘阵列」,是一种将多块独立的硬盘(物理硬盘)按不同的组合方式形成一个硬盘组(逻辑硬盘),从而提供比单块硬盘更大的存储容量、更高的可靠性和 更快的读写性能等。

模式

  • RAID 0

简单地说,RAID0主要通过将多块硬盘“串联”起来,从而形成一个更大容量的逻辑硬盘。RAID0通过“条带化(striping)”将数据分成不同的 数据块,并依次将这些数据块写到不同的硬盘上。因为数据分布在不同的硬盘上,所以数据吞吐量得到大大提升。但是,很容易看出RAID0没有任何数据冗余, 因此其可靠性不高。

https://store.yirami.xyz/review/truenas/RAID0.png

  • RAID 1

如果说RAID 0是RAID中一种只注重存储容量而没有任何容错的极端形式,那么RAID1则是有充分容错而不关心存储利用率的另一种极端表现。RAID1通过“镜像 (mirroring)”,将每一份数据都同时写到多块硬盘(一般是两块)上去,从而实现了数据的完全备份。因此,RAID1 支持―“热替换”,在不断电的情况下对故障磁盘进行更换。一般情况下,RAID1 控制器在读取数据时支持负载平衡,允许数据从不同磁盘上同时读取,从而提高数据的读取速度;但是,RAID1在写数据的性能没有改善。

https://store.yirami.xyz/review/truenas/RAID1.png

  • RAID 2

RAID 2以比特(bit)为单位,将数据―“条带化(striping)”分布存储在不同硬盘上;同时,将不同硬盘上同一位置的数据位用海明码进行编码,并将这些 编码数据保存在另外一些硬盘的相同位置上,从而实现错误检查和恢复。因为技术实施上的复杂性,商业环境中很少采用RAID2。

https://store.yirami.xyz/review/truenas/RAID2.png

  • RAID 3

与RAID 2类似,不同的是:1)以字节(byte)为单位进行―条带化‖处理;2)以奇偶校验码取代海明码。RAID3的读写性能都还不错,而且存储利用率也相当高,可达到(n-1)/n。但是对于随机读写操作,奇偶盘会成为写操作的瓶颈。

https://store.yirami.xyz/review/truenas/RAID3.png

  • RAID 4

与RAID 3的分布结构类似,不同的是RAID 4以数据块(block)为单位进行奇偶校验码的计算。另外,与RAID2和RAID3不同的是,RAID4中各个磁盘是独立操作的,并不要求各个磁盘的磁头同步转动。因此,RAID4允许多个I/O请求并行处理。

https://store.yirami.xyz/review/truenas/RAID4.png

  • RAID 5

RAID 3和RAID 4都存在同一个问题,就是奇偶校验码放在同一个硬盘上,容易造成写操作的瓶颈。RAID5与RAID4基本相同,但是其将奇偶校验码分开存放到不同的硬盘上去,从而减少了写奇偶校验码带来瓶颈的可能性。

https://store.yirami.xyz/review/truenas/RAID5.png

  • RAID 6

在RAID 5的基础上,RAID 6又另外增加了一组奇偶校验码,从而获得更高的容错性,最多允许同时有两块硬盘出现故障。但是,新增加的奇偶校验计算同时也带来了写操作性能上的损耗。

https://store.yirami.xyz/review/truenas/RAID6.png

  • RAID 7

RAID 7并非公开的RAID标准,而是Storage Computer Corporation的专利硬件产品名称,RAID 7是以RAID 3及RAID 4为基础所发展,但是经过强化以解决原来的一些限制。另外,在实现中使用大量的缓冲存储器以及用以实现异步数组管理的专用即时处理器,使得RAID 7可以同时处理大量的IO要求,所以性能甚至超越了许多其他RAID标准的实现产品。但也因为如此,在价格方面非常的高昂。

  • RAID 0+1

为了获取更好的I/O吞吐率或者可靠性,将不同的RAID标准级别混合产生的组合方式叫做嵌套式RAID,或者混合RAID。RAID0+1 是先将硬盘分 为若干组,每组以RAID0的方式组成―条带化‖的硬盘阵列,然后将这些组RAID0的硬盘阵列以RAID1的方式组成一个大的硬盘阵列。

https://store.yirami.xyz/review/truenas/RAID01.png

  • RAID 10

类似于RAID 0+1, RAID 10则是先“镜像”(RAID 1)、后“条带化”(RAID0)。RAID0+1和RAID10性能上并无太大区别,但是RAID10在可靠性上要好于RAID0+1。这是因为在 RAID10中,任何一块硬盘出现故障不会影响到整个磁盘阵列,即整个系统仍将以RAID10的方式运行;而RAID0+1中,一个硬盘出现故障则会导致 其所在的RAID0子阵列全部无法正常工作,从而影响到整个RAID0+1磁盘阵列 – 在只有两组RAID0子阵列的情况下,整个系统将完全降级为RAID0级别。

https://store.yirami.xyz/review/truenas/RAID10.png

  • RAID 50

RAID 5与RAID 0的组合,先作RAID 5,再作RAID 0,也就是对多组RAID 5彼此构成Stripe访问。由于RAID 50是以RAID 5为基础,而RAID 5至少需要3颗硬盘,因此要以多组RAID 5构成RAID 50,至少需要6颗硬盘。以RAID 50最小的6颗硬盘配置为例,先把6颗硬盘分为2组,每组3颗构成RAID 5,如此就得到两组RAID 5,然后再把两组RAID 5构成RAID 0。RAID 50在底层的任一组或多组RAID 5中出现1颗硬盘损坏时,仍能维持运作,不过如果任一组RAID 5中出现2颗或2颗以上硬盘损毁,整组RAID 50就会失效。RAID 50由于在上层把多组RAID 5构成Stripe,性能比起单纯的RAID 5高,容量利用率比RAID5要低。比如同样使用9颗硬盘,由各3颗RAID 5再组成RAID 0的RAID 50,每组RAID 5浪费一颗硬盘,利用率为(1-3/9),RAID 5则为(1-1/9)。

https://store.yirami.xyz/review/truenas/RAID50.png

  • RAID 53

它拥有一个镜像条带数组,硬盘里其中一个条带就是一个是由3组以上的RAID 5组成RAID 3硬盘阵列。

  • RAID 60

RAID 6与RAID 0的组合:先作RAID 6,再作RAID 0。换句话说,就是对两组以上的RAID 6作Stripe访问。RAID 6至少需具备4颗硬盘,所以RAID 60的最小需求是8颗硬盘。 由于底层是以RAID 6组成,所以RAID 60可以容许任一组RAID 6中损毁最多2颗硬盘,而系统仍能维持运作;不过只要底层任一组RAID 6中损毁3颗硬盘,整组RAID 60就会失效,当然这种情况的概率相当低。比起单纯的RAID 6,RAID 60的上层透过结合多组RAID 6构成Stripe访问,因此性能较高。不过使用门槛高,而且容量利用率低是较大的问题。

基准

https://store.yirami.xyz/review/truenas/raid_benchmark.png

预设

端口

安装

应用

CORE

SCALE

Kubernets(k3s) Apps

SCALE-22.02.1 PASSED

add TrueCharts catalog
install official or community app
install custom app by custom-app
  • qbittorrent
clear apps and stop k3s
  1. 重置组件
    1. 进入 TrueNAS SCALEWeb UI
    2. 进入 Apps -> Settings
    3. 点击 Unset Pool
    4. 确认输出中 poolnull
      midclt call kubernetes.config
  2. 停用系统级 Kubernetes 服务以释放资源
    # systemctl stop kubelet
    # systemctl disable kubelet
    # 如果运行出错,可查看其状态是否为停止或未配置
    midclt call kubernetes.status
    
    systemctl stop k3s
    systemctl disable k3s
Docker

SCALE 24.10 开始,TrueNAS 的应用后端从 Kubernets 转为 Docker

准备

TrueNAS SCALE 23.10 PASSED;

系统默认限制 apt 包管理器的使用,可以使用 install-dev-tools 命令进入开发者模式来放开限制。该工具会自动解除对某些目录的保护并安装一些常用工具,但在版本升级后不会被保留,需要重新安装。

注意:文档警告不要使用 TrueNAS Web Shell 来进行包管理,因为中间件的更改可能导致系统无法访问。

TrueNAS Web Shell 界面的 System settings -> Services 中可以开启 SSH 访问。若需密码登录,还需配合修改 /etc/ssh/sshd_config 文件。

vim /etc/ssh/sshd_config
# edit PermitRootLogin and PasswordAuthentication
systemctl restart ssh

若是简单需求,可以直接赋予 aptdpkg 可执行权限。

chmod +x /usr/bin/apt*
chmod +x /usr/bin/dpkg

# 禁用官方源,加入国内源
vim /etc/apt/sources.list

apt update && apt install -y docker.io docker-compose
安装应用

实际部署配置备份在腾讯云存储
注意:.env 文件未备份到云端

  • ddns-go
version: "3.8"
services:
  ddns-go:
    image: jeessy/ddns-go:latest
    container_name: ddns-go
    restart: unless-stopped
    ports:
      - "19081:9876"
    volumes:
      - /path/to/your/ddnsgo:/root
    environment:
      - TZ=Asia/Shanghai
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 500M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9876"]
      interval: 30s
      timeout: 5s
      retries: 3
  • traefik

    • docker-compose.yml
    services:
      traefik:
        image: traefik:v3
        container_name: traefik
        restart: unless-stopped
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - /path/to/your/certs:/certs
          - ./config/traefik.yml:/traefik.yml
          - ./config/dynamic.yml:/dynamic.yml
        environment:
          - CF_API_EMAIL=${CF_DNS_EMAIL}
          - CLOUDFLARE_DNS_API_TOKEN=${CF_API_TOKEN}
          - CLOUDFLARE_ZONE_API_TOKEN=${CF_API_TOKEN}
        labels:
          - "traefik.enable=true"
        deploy:
          resources:
            limits:
              cpus: '0.5'
              memory: 256M
        healthcheck:
          test: ["CMD-SHELL", "wget -qO- http://localhost:8080/api/rawdata"]
          interval: 3s
          retries: 10
    • traefik.yml
    api:
      dashboard: true
      insecure: true  # 关闭该配置无法访问仪表板,原因未知,因此停止相关端口映射来替  代
    
    entryPoints:
      http:
        address: ":80"
      https:
        address: ":443"
    
    providers:
      docker:
        endpoint: "unix:///var/run/docker.sock"
        exposedByDefault: false
      file:
        filename: /dynamic.yml
        watch: true
    
    certificatesResolvers:
      le:
        acme:
          email: ${CF_DNS_EMAIL}
          storage: /certs/acme.json
          dnsChallenge:
            provider: cloudflare
            delayBeforeCheck: 30
            resolvers:
              - "1.1.1.1:53"
              - "8.8.8.8:53"
    
    global:
      sendAnonymousUsage: false
      checkNewVersion: false
    • dynamic.yml
    http:
      routers:
        traefik-dashboard:
          entryPoints:
            - http
          rule: "Host(`aio.traefik.yirami.xyz`)"
          service: dashboard@internal
    
        traefik-dashboard-api:
          entryPoints:
            - http
          rule: "Host(`aio.traefik.yirami.xyz`) && PathPrefix(`/api`)"
          service: api@internal
    
        traefik-dashboard-secure:
          entryPoints:
            - https
          rule: "Host(`traefik.aio.yirami.xyz`)"
          service: dashboard@internal
          tls:
            certResolver: le
            domains:
              - main: ${CF_DNS_DOMAIN}
                sans:
                  - ${CF_DNS_DOMAIN_LIST}
    
        traefik-dashboard-api-secure:
          entryPoints:
            - https
          rule: "Host(`traefik.aio.yirami.xyz`) && PathPrefix(`/api`)"
          service: api@internal
          tls:
            certResolver: le
    
        nas-aio:
          entryPoints:
            - https
          rule: "Host(`nas.aio.yirami.xyz`)"
          service: nas-aio-service
          tls:
            certResolver: le
    
      services:
        nas-aio-service:
          loadBalancer:
            servers:
              - url: "http://aio.nas.yirami.xyz:19080"
  • oauth2-proxy

OAUTH2_COOKIE_SECRET: $(openssl rand -base64 32 | head -c 32)

version: '3.8'
services:
  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0
    container_name: oauth2-proxy
    restart: unless-stopped
    expose:
      - "4180"
    environment:
      - OAUTH2_PROXY_PROVIDER=github
      - OAUTH2_PROXY_CLIENT_ID=${OAUTH2_CLIENT_ID}
      - OAUTH2_PROXY_CLIENT_SECRET=${OAUTH2_CLIENT_SECRET}
      - OAUTH2_PROXY_COOKIE_SECRET=${OAUTH2_COOKIE_SECRET}
      - OAUTH2_PROXY_EMAIL_DOMAINS=*
      - OAUTH2_PROXY_REDIRECT_URL=https://traefik.aio.yirami.xyz:9443/oauth2/callback
      - OAUTH2_PROXY_UPSTREAMS=http://traefik:8080/
      - OAUTH2_PROXY_HTTP_ADDRESS=0.0.0.0:4180
      - OAUTH2_PROXY_SKIP_PROVIDER_BUTTON=true
    labels:
      - "traefik.enable=false"
  • qbittorrent
version: '3.8'
services:
  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    restart: unless-stopped
    ports:
      - 19085:8080
      - 36881:36881
      - 36881:36881/udp
    volumes:
      - /path/to/your/qb:/config
      - /path/to/your/QDL:/downloads
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - WEBUI_PORT=8080
      - TORRENTING_PORT=36881
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.qb.rule=Host(`bt.aio.yirami.xyz`)"
      - "traefik.http.routers.qb.entrypoints=https"
      - "traefik.http.routers.qb.tls=true"
      - "traefik.http.routers.qb.tls.certresolver=le"
      - "traefik.http.services.qb.loadbalancer.server.port=8080"    
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 4G
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
  • aria2-pro

参考博客

version: "3.8"
services:
  aria2:
    container_name: aria2
    image: p3terx/aria2-pro
    restart: unless-stopped
    ports:
      - 6800:6800
      - 36888:6888
      - 36888:6888/udp
    environment:
      - PUID=1000
      - PGID=1000
      - UMASK_SET=022
      - RPC_SECRET=${ARIA2_RPC_SECRET}
      - RPC_PORT=6800
      - LISTEN_PORT=6888
      - DISK_CACHE=64M
      - IPV6_MODE=false
      - UPDATE_TRACKERS=true
      - CUSTOM_TRACKER_URL=
      - TZ=Asia/Shanghai
    volumes:
      - /path/to/your/aria2/config:/config
      - /path/to/your/aria2/downloads:/downloads
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
    healthcheck:
      test: ["CMD-SHELL", "wget -qO /tmp/health.json --header='Content-Type: application/json' --post-data='{\"jsonrpc\":\"2.0\",\"method\":\"aria2.getVersion\",\"id\":\"health\",\"params\":[\"token:${ARIA2_RPC_SECRET}\"]}' http://localhost:6800/jsonrpc && grep -q 'version' /tmp/health.json"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s
    logging:
      options:
        max-size: "1m"

  ariang:
    container_name: ariang
    image: p3terx/ariang
    restart: unless-stopped
    ports:
      - 36880:6880
    depends_on:
      - aria2
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    healthcheck:
      disable: true
    logging:
      driver: json-file
      options:
        max-size: 1m
  • cloudreve
version: "3.8"
services:
  cloudreve:
    container_name: cloudreve
    image: cloudreve/cloudreve:latest
    profiles: ["disable"]  # 默认不启动
    restart: unless-stopped
    ports:
      - "19086:5212"
    volumes:
      - /path/to/your/cloudreve/conf.ini:/cloudreve/conf.ini
      - /path/to/your/cloudreve/cloudreve.db:/cloudreve/cloudreve.db
      - /path/to/your/cloudreve/uploads:/cloudreve/uploads
      - /path/to/your/cloudreve/avatar:/cloudreve/avatar
      - /path/to/your/aria2/downloads:/data
    depends_on:
      - aria2
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 2G
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --spider -q -t 1 http://localhost:5212/api/v3/site/config || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
  • tinymediamanager

注意:刮削时需要注意容器指定的权限与主机文件目录的权限匹配,否则可能无法存入刮削信息!

---
version: "3.8"
services:
  tinymediamanager:
    image: tinymediamanager/tinymediamanager:latest
    container_name: tinymediamanager
    restart: unless-stopped
    environment:
      - USER_ID=1000
      - GROUP_ID=1000
      - ALLOW_DIRECT_VNC=true
      - LC_ALL=en_US.UTF-8
      - LANG=en_US.UTF-8
      - PASSWORD=${TMM_PASSWD}
    volumes:
      - /path/to/your/tmm/data:/data
      - /path/to/your/tmm/addons:/media/addons
      - /path/to/your/tmm/movies:/media/movies
      - /path/to/your/tmm/tvshows:/media/tv_shows
    ports:
      - 19087:5900 # VNC port
      - 19088:4000 # Web UI
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.tmm.rule=Host(`tmm.aio.yirami.xyz`)"
      - "traefik.http.routers.tmm.entrypoints=https"
      - "traefik.http.routers.tmm.tls=true"
      - "traefik.http.routers.tmm.tls.certresolver=le"
      - "traefik.http.services.tmm.loadbalancer.server.port=4000"    
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:4000"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s
启动服务
# 后台启动
docker-compose up -d

# 更新并后台启动
docker-compose pull && docker-compose up -d

维护

版本升级

数据集管理

迁移

问题的起因是最初使用 SCALE Apps 功能时,将 Kubernets(k3s) 存储初始化在存储池的根目录,即 ix-applications 数据集,而该存储池的根目录还同时创建了多个其它数据集。此前,为了方便都是将该存储池直接通过 SAMBA 共享,而从 SCALE 22.02 升级到 SCALE 23.10 后提示不允许将该数据集通过 SAMBA 共享,因此考虑在存储池下重新建立一个数据集,并将原来的多个数据集迁移到该新建数据集下,分享时仅分享该新建数据集。

同一存储池内的数据集迁移(路径层次更改)可以使用 rename 命令。它是一个原子操作重命名,可以直接修改数据集的路径名,且完全保留原数据集属性。

一个典型的操作流程如下:

# 1) 确认数据集结构
zfs list -r <your_pool_name>
# 2) 停止相关的共享、应用服务
# 3) 创建临时快照(可选)
zfs snapshot -r <your_pool_name>/<your_dataset>@before_migration

# 4) 迁移
zfs rename <your_pool_name>/<your_dataset> <your_pool_name>/<new_dataset>/<your_dataset>
# 5) 权限检查(NFSv4 or Unix?)
# 6) 修改依赖的共享、应用路径
# 7) 恢复共享及应用

参考