(Chinese-Only) 一个Linux用户的绿联NAS配置指南

如何在绿联NAS上配置舒适的终端,尽量避免干扰NAS本身,以及各类优化与可观测配置。

启用 SSH

没SSH就别想着用Terminal了,赶紧去开了。

去绿联NAS的Web管理界面,控制面板 -> 终端机 -> SSH ,记得关闭自动关闭功能。

ssh

配置 root 用户 SSH 登录

用你的NAS用户名SSH到NAS上ssh yourname@nas-ip

你需要先有一个SSH密钥对,如果你还没有,可以用 ssh-keygen -t rsa 生成一个。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 切root
sudo su

# 打开 root SSH 登录(仅允许密钥登录)
# 注意,别去改 /etc/ssh/sshd_config ,因为绿联NAS的系统会保护这个文件,改了重启就失效了。
echo 'PermitRootLogin prohibit-password' >> /etc/ssh/sshd_config.d/A0-root-login.conf

# 应用修改
systemctl restart ssh

# 将你的公钥添加到 /root/.ssh/authorized_keys 中
mkdir -p /root/.ssh
chmod 700 /root/.ssh
echo 'your-public-key' >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys

配置你本机的 ~/.ssh/config 文件使用对应的密钥登录NAS:

1
2
3
4
Host nas
    HostName nas-ip
    User root
    IdentityFile ~/.ssh/<private-key>

安装包管理器 Opkg

那么有人会问,你为什么要装包管理器,你怎么不用自带的 apt?UGOS Pro不是基于Debian的么?其实最主要的原因是避免干扰内置预装的package。我举个例子,你想安装git,好,那你得apt update吧,update完拉过来了最新的package index,然后刚好这个新的git依赖新版的libcurl,你装的时候给他一起升级了,但是绿联自带了不少预装的package,这些package可能是根据老版本libcurl编译的。正常的debian你upgrade一下就完事了,但是UGOS作为一个定制的Debian,不建议你这么做(其实要是你尝试去upgrade,你会发现不让你upgrade,绿联已经做了保护了,虽然你可以绕过就是)。

OK,我们解释清楚了为什么不用apt。那么用什么呢?我的建议是一个轻量级给嵌入式设备用的包管理器,用OpenWRT的可能很熟悉了,就是opkg。你想重量级一点,也可以用Homebrew/Linuxbrew(不过你得先装git才能装Homebrew,但是git又没有,现在还没包管理器也没得装git,循环依赖了属于是),也可以用Nix(我觉得Nix太重了,除非你真的需要Nix的特性,不然就算了)。

这里用 Opkg 做演示。你需要用root用户SSH到NAS上ssh root@nas-ip

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 下载 opkg 安装脚本
wget https://mirrors.nju.edu.cn/entware/x64-k3.2/installer/generic.sh -O opkg-install.sh
# 换源 1
sed -i 's|http://bin.entware.net|https://mirrors.nju.edu.cn/entware|g' opkg-install.sh
# 换源 2
sed -i 's|-O /opt/etc/opkg.conf|-O /opt/etc/opkg.conf \&\& sed -i "s,http://bin.entware.net,https://mirrors.nju.edu.cn/entware,g" /opt/etc/opkg.conf|g' opkg-install.sh
# 执行 opkg 安装脚本
/bin/sh opkg-install.sh

# 临时设置 PATH ,使得 opkg 命令可用
export PATH=/opt/bin:/opt/sbin:/opt/usr/bin:$PATH
# 设置 PATH ,使得 opkg 命令可用。如果你用 zsh,记得改成 ~/.zshrc 。
# 或者如果你用我的 dotfiles,现在可以先不做这一步,你可以看后续我配置 zsh 的时候怎么做的。
echo 'export PATH=/opt/bin:/opt/sbin:/opt/usr/bin:$PATH' >> ~/.bashrc

安装常用Package

这时有包管理器了,而且是与NAS系统隔离的,你可以随意安装了。以下是一些常用的包:

1
2
3
4
5
6
# 注意 vim 需要安装两个包,vim-full 和 vim-runtime
opkg install vim-full vim-runtime
# 终于能装 git 了,注意需要 git-http
opkg install git git-http
# 其他常见工具
opkg install htop sysstat zsh

配置 ZSH

如果你想要一个更好的终端体验,建议使用 ZSH 和比较好的配置。以下是使用我的 dotfiles 来配置 ZSH 的步骤:

注意,你需要在你以后会经常使用的用户中配置 ZSH 。 我这里都是用的 root 用户,我对 Linux 比较熟悉了所以我并不怕搞坏东西。 如果你不熟悉 Linux,建议切换回自己用户配置 ZSH 。 如果找不到 git ,记得参考 opkg 安装中最后一步重新设置 PATH 。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 先设置代理,等会要从 GitHub 上下载文件
export https_proxy=http://<your-proxy-ip>:<your-proxy-port>
# 克隆我的 dotfiles 仓库
cd
git clone https://github.com/charlie0129/dotfiles.git
cd dotfiles
./bootstrap.sh -f
zsh
# 询问 Do you need to use a proxy [y/n] 的时候选择 n (因为前面 set 过 proxy 了)
# 询问 Change login shell of root to /opt/bin/zsh 的时候选择 y (不然呢)

将 Opkg 的 bin 和 sbin 目录添加到 ZSH 的 PATH 中:

1
vim ~/dotfiles/env/custom.sh
1
2
3
4
5
6
7
8
9
# This list is inserted before PATH
PATH_BEFORE=(
    # custom bin in this repo, i.e. bin/custom
    $HOME/dotfiles/bin/custom
    # Opkg bin and sbin directories
    /opt/bin
    /opt/sbin
    /opt/usr/bin
)

效果

zsh

优化 Zram

需要使用 root 用户

绿联默认的 zram 不够激进,关了它。我们要用 zstd 算法和一半的内存来做zram,榨干内存。

zramoff

1
2
3
4
5
6
7
# 先设置代理,等会要从 GitHub 上下载文件
export https_proxy=http://<your-proxy-ip>:<your-proxy-port>
git clone --depth=1 https://github.com/foundObjects/zram-swap.git
cd zram-swap
./install.sh
cd ..
rm -rf zram-swap

配置使用 zstd 算法(因为 Debian 13 里面默认 zram 不给 lzo-rle 算法了,lz4 的压缩率又不行,所以用 zstd ):

1
2
3
4
vim /etc/default/zram-swap
# 修改以下行
# _zram_algorithm="zstd"
systemctl restart zram-swap

为 zram 配置合适的 sysctl ,较大的 swappiness 值可以不活跃的页面更快地被交换出去,让内存留给更有用的页面。

1
2
3
4
5
6
7
echo '# ZRAM BEGIN' >> /etc/sysctl.conf
echo 'vm.swappiness = 180' >> /etc/sysctl.conf
echo 'vm.watermark_boost_factor = 0' >> /etc/sysctl.conf
echo 'vm.watermark_scale_factor = 125' >> /etc/sysctl.conf
echo 'vm.page-cluster = 0' >> /etc/sysctl.conf
echo '# ZRAM END' >> /etc/sysctl.conf
sysctl --system

能看到内存大小 1.5 倍的zram swap即成功

1
2
3
$ swapon
NAME               TYPE       SIZE USED PRIO
/dev/zram0         partition 93.8G   0B   15

安装 Docker

先去应用中心安装 Docker 。默认绿联的Docker 配置比较烂,而且还会在根目录下留下一个 /daemon.json 的文件夹(WTF?),一看就是绿联的安装脚本写错了。

1
2
# 删除错误的 daemon.json 文件夹
rmdir /daemon.json

将以下json写入 /etc/docker/daemon.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "log-opts": {
    "max-size": "1m"
  },
  "experimental": true,
  "metrics-addr": "0.0.0.0:8132",
  "storage-driver": "overlay2",
  "data-root": "<your-data-root>",
  "registry-mirrors": [
    "https://xxxxxx.com/dockerhub/",
  ]
}
  • log-opts:限制日志大小,防止日志占满磁盘。
  • experimental:启用实验性功能。
  • metrics-addr:设置可观测端口。
  • storage-driver:使用 overlay2 存储驱动。
  • data-root:设置 Docker 数据目录,建议查看你之前的 /etc/docker/daemon.json 中的配置,不要动。
  • registry-mirrors:设置 Docker 镜像加速器,建议使用国内的镜像源。

可观测配置

  • Grafana: 用于可视化和监控。
  • Victoria Metrics: 用于指标存储与查询。
  • Alloy: 用于容器日志采集。
  • Cadvisor: 用于容器指标采集。
  • Loki: 用于日志存储与查询。
  • Node Exporter: 用于主机指标采集。
  • Smartctl Exporter: 用于硬盘SMART指标采集。
  • Intel PCM: 用于CPU性能监控。

效果:TODO

参考 Docker Compose (镜像版本可以适当升级,记得更换volume中数据存储位置):

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
name: o11y
services:
  grafana:
    container_name: grafana
    image: grafana/grafana:12.1.0
    restart: unless-stopped
    mem_limit: 1G
    user: 0:0
    networks:
      - o11y
    # 我使用了 Traefik 作为反向代理,所以不需要暴露端口
    # 如果你没有使用 Traefik,可以取消注释以下端口映射
    # ports:
    #   - 3000:3000
    healthcheck:
      test: wget --no-verbose --tries=1 --spider http://localhost:3000/api/health
    volumes:
      - /volume2/docker/data/grafana:/var/lib/grafana
    environment:
      TZ: Asia/Shanghai
      GF_SERVER_ENABLE_GZIP: true
    # 如果你使用 Traefik 作为反向代理,可以添加以下标签
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grafana.entrypoints=web"
      - "traefik.http.routers.grafana.rule=Host(`grafana.example.com`)"
      - "traefik.http.routers.grafana.service=grafana-secure"
      # - "traefik.http.routers.grafana.middlewares=grafana-https-redirect"
      # - "traefik.http.middlewares.grafana-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.grafana-secure.tls=true"
      - "traefik.http.routers.grafana-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.grafana-secure.entrypoints=websecure"
      - "traefik.http.routers.grafana-secure.rule=Host(`grafana.example.com`)"
      - "traefik.http.services.grafana-secure.loadbalancer.server.port=3000"

  vm:
    container_name: vm
    image: victoriametrics/victoria-metrics:v1.122.0
    restart: unless-stopped
    mem_limit: 4G
    user: 0:0
    networks:
      - o11y
    # ports:
    #   - 8428:8428
    healthcheck:
      test: wget --no-verbose --tries=1 --spider http://localhost:8428/ || exit 1
    command:
      - -httpListenAddr=0.0.0.0:8428
      - -promscrape.config=/etc/victoriametrics/scrape.yml
      - -storageDataPath=/var/victoriametrics
      - -retentionPeriod=20y
      - -inmemoryDataFlushInterval=300s # 减少写盘
    extra_hosts:
      - 'host.docker.internal:host-gateway'
      - 'd48t:host-gateway'
    volumes:
      - ./vm:/etc/victoriametrics # Scrape 配置
      - /volume2/docker/data/vm:/var/victoriametrics

  alloy:
    container_name: alloy
    image: grafana/alloy:v1.10.0
    restart: unless-stopped
    user: 0:0
    networks:
      - o11y
    mem_limit: 512M
    # ports:
    #   - 12345:12345
    command:
      - run
      - --disable-reporting
      - --server.http.listen-addr=0.0.0.0:12345
      - --storage.path=/var/lib/alloy/data
      - /etc/alloy/config.alloy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /tmp/alloy:/var/lib/alloy/data # 这玩意巨能写,SSD会写爆,给他放内存里去。绿联的 /tmp 是内存盘。
      - ./alloy/config.alloy:/etc/alloy/config.alloy

  cadvisor:
    container_name: cadvisor
    image: gcr.io/cadvisor/cadvisor:v0.53.0
    mem_limit: 256M
    restart: unless-stopped
    user: 0:0
    # ports:
    #   - 8080:8080
    networks:
      - o11y
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    privileged: true
    devices:
      - /dev/kmsg
    command:
      - --store_container_labels=false
      - --docker_only=true
      - --housekeeping_interval=30s

  loki:
    container_name: loki
    image: grafana/loki:3.5.3
    restart: unless-stopped
    user: 0:0
    networks:
      o11y:
        ipv4_address: "172.26.195.254" # 固定 IP,方便 Docker 的 Loki plugin 用(虽然我们这里用的 Alloy 其实并不需要)
    # 如果你没有使用 Traefik 作为反向代理,可以取消注释以下端口映射。
    # ports:
    #   - 3100:3100
    mem_limit: 4G
    command:
      - "-config.file=/etc/loki/config.yml"
    volumes:
      - ./loki:/etc/loki:ro
      - /volume2/docker/data/loki:/loki

  node-exporter:
    container_name: node-exporter
    image: prom/node-exporter:v1.9.1
    restart: unless-stopped
    mem_limit: 256M
    user: 0:0
    privileged: true
    # ports:
    #   - 9100:9100
    command:
      - --path.rootfs=/host
      - --web.listen-address=172.17.0.1:9100
    network_mode: host
    # networks:
    #   - o11y
    pid: host
    volumes:
      - '/:/host:ro,rslave'

  smartctl-exporter:
    container_name: smartctl-exporter
    image: prometheuscommunity/smartctl-exporter:v0.14.0
    restart: unless-stopped
    mem_limit: 512M
    user: 0:0
    # ports:
    #   - 192.168.91.1:9633:9633
    networks:
      - o11y
    privileged: true

  pcm:
    container_name: pcm
    image: opcm/pcm
    mem_limit: 256M
    restart: unless-stopped
    user: 0:0
    privileged: true
    # ports:
    #   - 192.168.91.1:9738:9738
    networks:
      - o11y

networks:
  o11y:
    name: o11y
    driver: bridge
    ipam:
      config:
        - subnet: "172.26.195.0/24"

参考 Victoria Metrics 的 Scrape 配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# ./vm/scrape.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: victoria-metrics
    static_configs:
      - targets:
          - vm:8428
# 如果你有 Traefik
#   - job_name: traefik
#     static_configs:
#       - targets:
#           - traefik:8082
  - job_name: node-exporter
    static_configs:
      - targets:
          - d48t:9100
  - job_name: cadvisor
    static_configs:
      - targets:
          - cadvisor:8080
  - job_name: loki
    static_configs:
      - targets:
          - loki:3100
  - job_name: grafana
    static_configs:
      - targets:
          - grafana:3000
  - job_name: smartctl-exporter
    static_configs:
      - targets:
          - smartctl-exporter:9633
  - job_name: pcm
    static_configs:
      - targets:
          - pcm:9738

参考 Loki 配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# ./loki/config.yml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  instance_addr: 127.0.0.1
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

ingester:
  chunk_encoding: lz4 # 比默认的snappy压缩率高
  chunk_target_size: 8388608 # 8M
  max_chunk_age: 48h
  chunk_idle_period: 12h

limits_config:
  max_query_lookback: 672h # 28 days
  retention_period: 672h   # 28 days

schema_config:
  configs:
    - from: 2020-10-24
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093

参考 Grafana Alloy 配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# ./alloy/config.alloy
logging {
  level  = "info"
  format = "logfmt"
}

// Discover Docker containers and extract metadata.
discovery.docker "logs_integrations_docker" {
    host = "unix:///var/run/docker.sock"
    refresh_interval = "5s"
}

// Define a relabeling rule to create a service name from the container name.
discovery.relabel "logs_integrations_docker" {
    targets = []


    rule {
        target_label = "job"
        replacement = "integrations/docker"
    }


    rule {
        target_label = "instance"
        replacement = "d48t" // constants.hostname
    }


    rule {
        source_labels = ["__meta_docker_container_name"]
        regex = "/(.*)"
        target_label = "container"
    }


    rule {
        source_labels = ["__meta_docker_container_log_stream"]
        target_label = "stream"
    }
}


// Configure a loki.source.docker component to collect logs from Docker containers.
loki.source.docker "logs_integrations_docker" {
  host       = "unix:///var/run/docker.sock"
  targets    = discovery.docker.logs_integrations_docker.targets
  relabel_rules = discovery.relabel.logs_integrations_docker.rules
  forward_to = [loki.write.local.receiver]
  refresh_interval = "15s"
}

loki.write "local" {
    endpoint {
        url = "http://loki:3100/loki/api/v1/push"
    }
}
Built with Hugo
Theme Stack designed by Jimmy