「TrueNAS」听我说谢谢你
TrueNAS安装及配置。
TrueNAS
RAID 简介
RAID (Redundant Array of Independent/InexpensiveDisks),独立磁盘冗余阵列,简称为「磁盘阵列」,是一种将多块独立的硬盘(物理硬盘)按不同的组合方式形成一个硬盘组(逻辑硬盘),从而提供比单块硬盘更大的存储容量、更高的可靠性和 更快的读写性能等。
模式
- RAID 0
简单地说,RAID0主要通过将多块硬盘“串联”起来,从而形成一个更大容量的逻辑硬盘。RAID0通过“条带化(striping)”将数据分成不同的 数据块,并依次将这些数据块写到不同的硬盘上。因为数据分布在不同的硬盘上,所以数据吞吐量得到大大提升。但是,很容易看出RAID0没有任何数据冗余, 因此其可靠性不高。

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

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

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

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

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

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

- 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的方式组成一个大的硬盘阵列。

- 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级别。

- 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)。

- 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访问,因此性能较高。不过使用门槛高,而且容量利用率低是较大的问题。
基准

预设
端口
安装
应用
CORE
SCALE
Kubernets(k3s) Apps
SCALE-22.02.1 PASSED
add TrueCharts catalog
- 若能正常访问
GitHub,可根据参考配置 - 使用中国区镜像
- Catalog Name: TruechartsGitee
- Repository: https://gitee.com/truecharts/dh_catalog
install official or community app
install custom app by custom-app
- qbittorrent
clear apps and stop k3s
- 重置组件
- 进入
TrueNAS SCALE的Web UI - 进入
Apps->Settings - 点击
Unset Pool - 确认输出中
pool是nullmidclt call kubernetes.config
- 进入
- 停用系统级
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若是简单需求,可以直接赋予 apt 和 dpkg 可执行权限。
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: 3traefik
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: 10traefik.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: falsedynamic.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) 恢复共享及应用