LOADING

加载过慢请开启缓存 浏览器默认开启

峰言峰语

数据库

记录 2025/2/24

外键

  1. 企业级项目一般不建立外键,因为外键会导致数据库的耦合性增加,而且外键存在级联约束,会导致数据库的性能下降。主要影响写入操作,对子表进行写入的时候会对父表加共享锁,在高并发的情况下会导致数据库的性能下降。
  2. 阿里巴巴Java开源手册——不得使用外键与级联,一切外键关联一律在应用层处理。

事务

  1. 数据库事务是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。
  2. 事务的四个特性:原子性A、一致性C、隔离性I、持久性D。
    • 原子性:事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
    • 一致性:事务执行前后,数据库的完整性约束没有被破坏。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
    • 隔离性:多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
    • 持久性:事务一旦提交,对数据库的改变是永久性的。
阅读全文

跳表

数据结构 2025/2/23

结构

跳表的期望空间复杂度为O(n),跳表的查询,插入和删除操作的期望时间复杂度均为O(logn)。跳表实际为一种多层的有序链表,跳表的每一层都为一个有序链表,且满足每个位于第i层的节点有p的概率出现在第i+1层,其中p为常数。

查询

从跳表的当前的最大层数level层开始查找,在当前层水平地逐个比较直至当前节点的下一个节点大于等于目标节点,然后移动至下一层进行查找,重复这个过程直至到达第1层。此时,若第1层的下一个节点的值等于target,则返回true;反之,则返回false。如图所示:

添加

从跳表的当前的最大层数level层开始查找,在当前层水平地逐个比较直至当前节点的下一个节点大于等于目标节点,然后移动至下一层进行查找,重复这个过程直至到达第1层。设新加入的节点为newNode,我们需要计算出此次节点插入的层数lv,如果level小于lv,则同时需要更新level。我们用数组update保存每一层查找的最后一个节点,第i层最后的节点为update[i]。我们将newNode的后续节点指向update[i]下一个节点,同时更新update[i]的后续节点为newNode。如图所示:

删除

首先我们需要查找当前元素是否存在跳表中。从跳表的当前的最大层数level层开始查找,在当前层水平地逐个比较直至当前节点的下一个节点大于等于目标节点,然后移动至下一层进行查找,重复这个过程直至到达第1层。如果第1层的下一个节点不等于num时,则表示当前元素不存在直接返回。我们用数组update保存每一层查找的最后一个节点,第i层最后的节点为update[i]。此时第i层的下一个节点的值为num,则我们需要将其从跳表中将其删除。由于第i层的以p的概率出现在第i+1层,因此我们应当从第1层开始往上进行更新,将numupdate[i]下一跳中删除,同时更新update[i]的后续节点,直到当前层的链表中没有出现num的节点为止。最后我们还需要更新跳表中当前的最大层数level。如图所示:

空间复杂度分析

每次添加节点时,节点出现在第i层的概率为\((1-p)×{p}^{i-1}\),跳表插入时的期望层数为:
\[ E(L)=\sum_{i=1}^{\infin}i×(1-p)×{p}^{i-1}=\frac{1}{1-p} \]

如果节点的目标层数为L,则此时需要的空间为O(L),因此总的空间复杂度为\(O(n×E(L))=O(n×\frac{1}{1-p})=O(n)\)

时间复杂度分析

在含有n个节点的跳表中,当前最大层数L(n)包含的元素个数期望为\(\frac{1}{p}\),根据跳表的定义可以知道第1层的每个元素出现在L(n)的概率为\({p}^{L(n)-1}\),则此时我们可以推出如下:
\[ \frac{1}{p}=n{p}^{L(n)-1} \]
根据以上结论可以知道在含有n个节点的跳表中,当前最大层数期望\(L(n)={log}_{p}\frac{1}{n}\)

C(i)为在一个无限长度的跳表中向上爬i层的期望代价,最小代价即为往左上角爬,根据定义,则知道:
\[ \begin{gather} C(0)=0 \\ C(i)=(1-p)(1+C(i))+p(1+C(i-1)) \\ C(i)=\frac{i}{p} \end{gather} \]
在含有n个元素的跳表中,从第1层爬到第L(n)层的期望步数存在上界\(\frac{L(n)-1}{p}\),当达到第L(n)层后,我们需要向左走。我们已知L(n)层的节点总数的期望存在上界为\(\frac{1}{p}\),所以平均查询时间复杂度为\(\frac{L(n)-1}{p}+\frac{1}{p}=\frac{log_{\frac{1}{p}}n}{p}=O(log\ n)\)

实现细节

每个跳表节点保存一个值以及下一列的地址

class Skiplist {
    
    // 最大层数
    static final int MAX_LEVEL = 32;
    // 下一层留存概率
    static final double P = 0.5;
    // 哨兵节点
    private SkiplistNode head;
    // 当前最大层数
    private int level;
    // 用于插入时随机分配层数
    private Random random;

    public Skiplist() {
        this.head = new SkiplistNode(-1, MAX_LEVEL);
        this.level = 0;
        this.random = new Random();
    }
    
    public boolean search(int target) {
        SkiplistNode cur = this.head;
        // 从最高层开始向下查找
        for (int i = this.level - 1; i >= 0; i--) {
            // 在当前层从左往右查找不大于target的最大节点
            while (cur.forward[i] != null && cur.forward[i].val < target) {
                cur = cur.forward[i];
            }
        }
        // 下一节点
        cur = cur.forward[0];
        if (cur != null && cur.val == target) {
            return true;
        }
        return false;
    }
    
    public void add(int num) {
        // 保存每层需要更新下一节点的列地址
        SkiplistNode[] update = new SkiplistNode[MAX_LEVEL];
        Arrays.fill(update, this.head);
        SkiplistNode cur = this.head;
        for (int i = this.level - 1; i >= 0; i--) {
            while (cur.forward[i] != null && cur.forward[i].val < num) {
                cur = cur.forward[i];
            }
            update[i] = cur;
        }
        int lv = this.randomLevel();
        // 更新层数
        this.level = Math.max(this.level, lv);
        SkiplistNode newNode = new SkiplistNode(num, lv);
        // 更新列
        for (int i = 0; i < lv; i++) {
            newNode.forward[i] = update[i].forward[i];
            update[i].forward[i] = newNode;
        }
    }
    
    public boolean erase(int num) {
        SkiplistNode[] update = new SkiplistNode[MAX_LEVEL];
        SkiplistNode cur = this.head;
        for (int i = this.level - 1; i >= 0; i--) {
            while (cur.forward[i] != null && cur.forward[i].val < num) {
                cur = cur.forward[i];
            }
            update[i] = cur;
        }
        cur = cur.forward[0];
        // 不在跳表里
        if (cur == null || cur.val != num) {
            return false;
        }
        for (int i = 0; i < this.level; i++) {
            if (update[i].forward[i] != cur) {
                break;
            }
            update[i].forward[i] = cur.forward[i];
        }
        // 更新层数
        while (this.level > 1 && this.head.forward[this.level - 1] == null) {
            this.level--;
        }
        return true;
    }

    private int randomLevel() {
        int lv = 1;
        while (random.nextDouble() < P && lv < MAX_LEVEL) {
            lv++;
        }
        return lv;
    }
    
}

class SkiplistNode {
    
    // 节点值
    int val;
    // 下一列的地址
    SkiplistNode[] forward;

    public SkiplistNode(int val, int maxLevel) {
        this.val = val;
        this.forward = new SkiplistNode[maxLevel];
    }
    
}
阅读全文

简历优化

求职 2024/12/28

优化目标——让HR在短时间内看出你和岗位的匹配度

核心

  1. 我是什么人/角色(who),我做了什么工作(what),做出了什么结果(how)
  2. 2-3行一段,使用短句
  3. 量化所作的工作细节以及取得成果
  4. 围绕岗位关键词展开

内容


个人信息

  • 姓名
  • 性别
  • 年龄(视情况)
  • 联系方式
  • 电子邮箱(正式邮箱,不要使用QQ邮箱

教育背景

  • 时间
  • 院校
  • 专业
  • 学历
  • 成绩(视情况)

大学及以后教育经历,时间由近到远列出


项目经历

STAR法则

Situation:什么场景/背景

Task:什么任务/目标

Action:做了什么措施

Result:取得什么结果


工作经历

项目经历,应用STAR法则


在校经历(视情况)


个人技能


获奖情况


自我评价(视情况)

要写就要博人眼球,突出自己的特点

阅读全文

组建NAS(五)

记录 2024/10/8

使用TailScale

虽然NAS已经可以正常使用,但是ZeroTier对手机端的支持并没有想象中好,于是选择更换组网框架——TailScale

安装TailScale

  1. 停止并删除ZeroTier相关容器,删除相关数据卷和数据文件,删除相关镜像

  2. 到官网Tailscale · Best VPN Service for Secure Networks创建一个网络,并生成auth-key

  3. 拉取TailScale镜像

    docker pull tailscale/tailscale:latest
  4. 运行容器

    sudo docker run -d --name tailscale --restart always --cap-add NET_ADMIN --cap-add SYS_MODULE -v /dev/net/tun:/dev/net/tun -v tailscale-state:/var/lib/tailscale -e TS_AUTHKEY=<auth-key> -e TS_STATE_DIR=/var/lib/tailscale -e TS_USERSPACE=false -e TS_ACCEPT_DNS=true --hostname <hostname> --network host tailscale/tailscale:latest
  5. 访问网络控制面板,解除对设备的有期限授权

  6. 在控制面板关闭MagicDNS,添加自定义DNS服务器

  7. 在DNS服务器添加本地DNS记录

  8. 其他客户端只需下载相应操作系统客户端并使用同一账户登录即可

使用LDAP

Nextcloud本身在图片和影音上并不出色,只是集成方便,于是我打算使用专门的软件对图片、影音进行管理,因此就需要用到统一验证

安装OpenLDAP、Authelia

  • 拉取镜像

    sudo docker pull osixia/openldap
  • 运行容器

    sudo docker run -d \
        -e LDAP_DOMAIN=<域名> \
        -e LDAP_ORGANISATION=nas \
        -e LDAP_ADMIN_PASSWORD=<password> \
        --name openldap \
        --restart always \
        osixia/openldap
  • 拉取UI镜像

    sudo docker pull osixia/phpldapadmin
  • 运行UI,登录用户cn=admin,dc=二级域名,dc=顶级域名

    sudo docker run -d \
        -e PHPLDAPADMIN_LDAP_HOSTS=<ldap-server-ip> \
        -e PHPLDAPADMIN_HTTPS=false \
        -e PHPLDAPADMIN_TRUST_PROXY_SSL=true \
        -v pla-data:/var/www/phpldapadmin \
        --name pla \
        --restart always \
        osixia/phpldapadmin
  • 拉取镜像

    sudo docker pull authelia/authelia
  • 拉取postgres

    sudo docker pull postgres
  • 编写配置configuration.yml

    default_2fa_method: 'totp'
    server:
      address: 'tcp://:9091/'
    totp:
      issuer: '域名'
    identity_validation:
      reset_password:
        jwt_secret: '密钥'
    authentication_backend:
      ldap:
        address: 'ldap://openldap:389'
        base_dn: 'dc=二级域名,dc=顶级域名'
        user: 'cn=admin,dc=二级域名,dc=顶级域名'
        password: 'admin-password'
        users_filter: '(&({username_attribute}={input})(objectClass=inetOrgPerson))'
        groups_filter: '(&(member={dn})(objectClass=groupOfNames))'
    access_control:
      rules:
      - domain: 'auth.域名'
        policy: 'bypass'
    session:
      secret: '密钥'
      cookies:
        - domain: '域名'
          authelia_url: 'https://auth.域名'
    storage:
      encryption_key: '密钥>32'
      postgres:
        address: 'tcp://authelia_postgres:5432'
        database: 'authelia'
        username: 'authelia'
        password: 'database-password'
    notifier:
      filesystem:
        filename: '/config/notification.txt'
    identity_providers:
      oidc:
        hmac_secret: '密钥64'
        jwks:
          - key: '密钥'
        clients:
          - client_id: 'immich'
            client_name: 'immich'
            client_secret: '密钥'
            public: false
            authorization_policy: 'one_factor'
            redirect_uris:
              - 'https://immich.域名/auth/login'
              - 'https://immich.域名/user-settings'
              - 'app.immich:///oauth-callback'
            scopes:
              - 'openid'
              - 'profile'
              - 'email'
            userinfo_signed_response_alg: 'none'
          - client_id: 'jellyfin'
            client_name: 'jellyfin'
            client_secret: ''
            public: false
            authorization_policy: 'one_factor'
            require_pkce: true
            pkce_challenge_method: 'S256'
            redirect_uris:
              - 'https://jellyfin.域名/sso/OID/redirect/Authelia'
              - 'https://jellyfin.域名/sso/OID/r/Authelia'
            scopes:
              - 'openid'
              - 'profile'
              - 'groups'
            userinfo_signed_response_alg: 'none'
            token_endpoint_auth_method: 'client_secret_post'
          - client_id: 'nextcloud'
            client_name: 'nextcloud'
            client_secret: ''
            public: false
            authorization_policy: 'one_factor'
            require_pkce: true
            pkce_challenge_method: 'S256'
            redirect_uris:
              - 'https://nextcloud.域名/apps/user_oidc/code'
            scopes:
              - 'openid'
              - 'profile'
              - 'email'
              - 'groups'
            userinfo_signed_response_alg: 'none'
            token_endpoint_auth_method: 'client_secret_post'
  • 编写docker-compose.yml

    services:
      authelia:
        container_name: authelia
        image: authelia/authelia:latest
        restart: always
        environment:
          TZ: Asia/Shanghai
        volumes:
          - /path/to/authelia/config:/config
        depends_on:
          - database
          - ldap
    
      database:
        container_name: authelia_postgres
        image: postgres:latest
        restart: always
        environment:
          POSTGRES_PASSWORD: <password>
          POSTGRES_USER: authelia
          POSTGRES_DB: authelia
        volumes:
          - ./postgres:/var/lib/postgresql/data
    
      ldap:
        container_name: openldap
        image: osixia/openldap:latest
        restart: always
        environment:
          LDAP_DOMAIN: <域名>
          LDAP_ORGANISATION: nas
          LDAP_ADMIN_PASSWORD: <password>
        volumes:
          - ./ldap/data:/var/lib/ldap
          - ./ldap/config:/etc/ldap/slapd.d
    
      pla:
        container_name: pla
        image: osixia/phpldapadmin:latest
        restart: always
        environment:
          PHPLDAPADMIN_LDAP_HOSTS: openldap
          PHPLDAPADMIN_HTTPS: false
          PHPLDAPADMIN_TRUST_PROXY_SSL: true
        volumes:
          - ./pla:/var/www/phpldapadmin
        depends_on:
          - ldap
  • 启动编排

    sudo docker compose up -d
  • 配置反向代理

    https://ldap.域名:443 {
      reverse_proxy http://pla-ip:80
    }
    https://auth.域名:443 {
        reverse_proxy http://authelia-ip:9091
    }

NextCloud配置

  • 安装OpenID Connect user backend应用

  • 管理设置里的OpenID Connect启用OpenID

  • 添加Authelia

    • Identifier: Authelia
    • Client ID: nextcloud
    • Client secret: insecure_secret
    • Discovery endpoint: https://auth.example.com/.well-known/openid-configuration
    • Scope: openid email profile

安装jellyfin

  • 拉取镜像

    sudo docker pull jellyfin/jellyfin
  • 运行镜像

    # 查询render组id,用于硬件加速
    getent group render | cut -d: -f3
    getent group video | cut -d: -f3
    sudo docker run -d \
        --name jellyfin \
        --user 33:33 \
        -v /path/to/jellyfin/config:/config \
        -v /path/to/jellyfin/cache:/cache \
        -v /path/to/media:/media \
        --restart always \
        --net host \
        --group-add="render-group-id" \
        --device /dev/dri/renderD128:/dev/dri/renderD128 \
        jellyfin/jellyfin
    https://jellyfin.域名:443 {
      reverse_proxy jellyfin.域名:8096
    }
  • 在控制面板-常规-品牌添加以下内容

    <form action="https://jellyfin.域名/sso/OID/start/服务提供商id">
      <button class="raised block emby-button button-submit">Login with 服务提供商id</button>
    </form>
    #loginPage .readOnlyContent {
      display: flex;
      flex-direction: column-reverse;
    }
    
    .loginDisclaimerContainer {
      margin-top: 0;
      margin-bottom: 1em;
    }
    
    .loginDisclaimer {
      width: 100%;
      height: 100%;
    }
  • 安装jellyfin sso插件

  • 填写配置

    1. Visit the Jellyfin Administration Dashboard.
    2. Visit the Plugins section.
    3. Visit the Repositories tab.
    4. Click the + to add a repository.
    5. Enter the following details:
      1. Repository Name: Jellyfin SSO
      2. Repository URL: https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json
    6. Click Save.
    7. Click Ok to confirm the repository installation.
    8. Visit the Catalog tab.
    9. Select SSO Authentication from the Authentication section.
    10. Click Install.
    11. Click Ok to confirm the plugin installation.
    12. Once installed restart Jellyfin.
    13. Complete steps 1 and 2 again.
    14. Click the SSO-Auth plugin.
    15. Add a provider with the following settings:
      1. Name of the OID Provider: Authelia
      2. OID Endpoint: https://auth.example.com
      3. OpenID Client ID: jellyfin
      4. OID Secret: insecure_secret
      5. Enabled: Checked
      6. Enable Authorization by Plugin: Checked
      7. Enable All Folders: Checked
      8. Roles: jellyfin-users
      9. Admin Roles: jellyfin-admins
      10. Role Claim: groups
      11. Request Additional Scopes: groups
      12. Set default username claim: preferred_username
    16. All other options may remain unchecked or unconfigured.
    17. Click Save.

安装immich

  • 获取docker-compose.yml和example.env

    wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
    wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
    # 可选,获取硬件转码配置
    wget -O hwaccel.transcoding.yml https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
  • 编辑.env

    # You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables
    
    # The location where your uploaded files are stored
    UPLOAD_LOCATION=/custom/path/immich/
    # The location where your database files are stored
    DB_DATA_LOCATION=数据库数据位置
    
    # To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
    TZ=Asia/Shanghai
    
    # The Immich version to use. You can pin this to a specific version like "v1.71.0"
    IMMICH_VERSION=release
    
    # Connection secret for postgres. You should change it to a random password
    # Please use only the characters `A-Za-z0-9`, without special characters or spaces
    DB_PASSWORD=密码
    
    # The values below this line do not need to be changed
    ###################################################################################
    DB_USERNAME=postgres
    DB_DATABASE_NAME=immich
  • 编辑docker-compose.yml

    #
    # WARNING: Make sure to use the docker-compose.yml of the current release:
    #
    # https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
    #
    # The compose file on main may not be compatible with the latest release.
    #
    
    name: immich
    
    services:
      immich-server:
        container_name: immich_server
        image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
        extends:
          file: hwaccel.transcoding.yml
          service: 加速后端 # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
        volumes:
          # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
          - ${UPLOAD_LOCATION}:/usr/src/app/upload/upload
          - /etc/localtime:/etc/localtime:ro
        env_file:
          - .env
        ports:
          - 2283:3001
        depends_on:
          - redis
          - database
        restart: always
        healthcheck:
          disable: false
    
      immich-machine-learning:
        container_name: immich_machine_learning
        # For hardware acceleration, add one of -[armnn, cuda, openvino] to the image tag.
        # Example tag: ${IMMICH_VERSION:-release}-cuda
        image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
        # extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration
        #   file: hwaccel.ml.yml
        #   service: cpu # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference - use the `-wsl` version for WSL2 where applicable
        volumes:
          - model-cache:/cache
        env_file:
          - .env
        restart: always
        healthcheck:
          disable: false
    
      redis:
        container_name: immich_redis
        image: docker.io/redis:6.2-alpine@sha256:2d1463258f2764328496376f5d965f20c6a67f66ea2b06dc42af351f75248792
        healthcheck:
          test: redis-cli ping || exit 1
        restart: always
    
      database:
        container_name: immich_postgres
        image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
        environment:
          POSTGRES_PASSWORD: ${DB_PASSWORD}
          POSTGRES_USER: ${DB_USERNAME}
          POSTGRES_DB: ${DB_DATABASE_NAME}
          POSTGRES_INITDB_ARGS: '--data-checksums'
        volumes:
          # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
          - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
        healthcheck:
          test: pg_isready --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
          interval: 5m
          start_interval: 30s
          start_period: 5m
        command: ["postgres", "-c", "shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"]
        restart: always
    
    volumes:
      model-cache:
  • 启动compose

    sudo docker compose up -d
  • 配置反向代理

    sudo docker network connect immich_default caddy
    https://immich.域名:443 {
        reverse_proxy http://immich-server-ip:3001 # 或http://localhost:2283
    }
  • 进入服务器设置-Oauth设置

    • Issuer URL: https://auth.example.com/.well-known/openid-configuration.
    • Client ID: immich.
    • Client Secret: insecure_secret.
    • Scope: openid profile email.
    • Button Text: Login with Authelia.
    • Auto Register: Enable if desired.

使用Authentik替换Authelia

  • 获取docker-compose配置

    wget https://goauthentik.io/docker-compose.yml
  • 编辑.env

    PG_PASS=密码
    AUTHENTIK_SECRET_KEY=密钥
    
    AUTHENTIK_ERROR_REPORTING__ENABLED=false
    
    # SMTP Host Emails are sent to
    #AUTHENTIK_EMAIL__HOST=localhost
    #AUTHENTIK_EMAIL__PORT=25
    # Optionally authenticate (don't add quotation marks to your password)
    #AUTHENTIK_EMAIL__USERNAME=
    #AUTHENTIK_EMAIL__PASSWORD=
    # Use StartTLS
    #AUTHENTIK_EMAIL__USE_TLS=false
    # Use SSL
    #AUTHENTIK_EMAIL__USE_SSL=false
    #AUTHENTIK_EMAIL__TIMEOUT=10
    # Email address authentik will send from, should have a correct @domain
    #AUTHENTIK_EMAIL__FROM=authentik@localhost
    
    #COMPOSE_PORT_HTTP=80
    #COMPOSE_PORT_HTTPS=443
    docker compose pull
    docker compose up -d
  • 访问http://server-ip:9000/if/flow/initial-setup/开启初始化流程

系统监控

安装Portainer

  • 拉取镜像

    docker pull portainer/portainer-ce
  • 启动容器

    docker run -d --name portainer --restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest

安装Netdata

  • 拉取镜像

    docker pull netdata/netdata
  • 运行容器

    docker run -d --name=netdata \
      --pid=host \
      --network=host \
      -v netdataconfig:/etc/netdata \
      -v netdatalib:/var/lib/netdata \
      -v netdatacache:/var/cache/netdata \
      -v /:/host/root:ro,rslave \
      -v /etc/passwd:/host/etc/passwd:ro \
      -v /etc/group:/host/etc/group:ro \
      -v /etc/localtime:/etc/localtime:ro \
      -v /proc:/host/proc:ro \
      -v /sys:/host/sys:ro \
      -v /etc/os-release:/host/etc/os-release:ro \
      -v /var/log:/host/var/log:ro \
      -v /var/run/docker.sock:/var/run/docker.sock:ro \
      -v /run/dbus:/run/dbus:ro \
      --restart unless-stopped \
      --cap-add SYS_PTRACE \
      --cap-add SYS_ADMIN \
      --security-opt apparmor=unconfined \
      netdata/netdata

使用UIforFreedom替换clash

安装

docker pull ui4freedom/uif:latest # 拉取最新镜像
docker run --network host --name uif --privileged --restart unless-stopped -d ui4freedom/uif:latest

配置

docker logs -f uif
# Password: 92c204a9-3934-4976-96f2-7bbcb338ccf0
# Web Address: 0.0.0.0:9527
# API Address: 0.0.0.0:9413

打开网址ip:9527配置api后端为ip:9413

在入站规则中关闭系统代理,根据自己的需要配置入站规则,即连接协议和端口等

在出站规则中添加订阅链接,启用节点,完成

使用coredns做docker服务发现

安装

docker pull kevinjqiu/coredns-dockerdiscovery:latest
docker run -d --name coredns --restart=always -v /path/to/Corefile:/etc/Corefile -v /var/run/docker.sock:/var/run/docker.sock -p 8053:53/udp kevinjqiu/coredns-dockerdiscovery -conf /etc/Corefile

配置

.:53 { # 监听53端口
    docker { # 使用docker服务发现模块
        domain docker.loc # 服务域名
    }
    log # 使用日志模块
    errors # 使用错误流模块
}

如果启用dae,需要在dae中配置dns和拦截规则

阅读全文

组建NAS(四)

记录 2024/9/25

前言

之前捣鼓了很久,NAS基本运行起来了,但是还存在证书信任等问题,非常烦。问了GPT,提供了一个ZeroTier + Pi-Hole + Caddy的网络方案(不得不说,科技改变生活)

重置服务器

  • 删除证书

    mkcert -uninstall
    
    cd /etc/ssl/certs
    rm xxxx.pem # 删除空的根证书软链接
    rm xxxx.0 # 删除空的根证书hash软链接
    
    rm -r ~/.local/share/mkcert
  • 清理docker

    sudo docker stop xxx # 停止所有容器(除了dae)
    sudo docker container prune # 删除停止的容器
    sudo docker network rm nextcloud-aio # 删除虚拟网卡
    sudo docker volume prune --filter all=1 # 删除空挂载卷
    sudo docker image prune -a # 删除无活动镜像
  • 清理文件

    sudo rm -r /mnt/ncdata/ftp
    sudo rm -r /mnt/ncdata/nextcloud
    
    sudo rm -r ~/xxxx # 清理home目录下相关文件

安装ZeroTier

  • 介绍

    ZeroTier是P2P VPN,利用在互联网中构建一个虚拟局域网来实现组网。

    一般情况下ZeroTier使用UDP进行打洞(先利用共同的通信服务器交换两台设备的链路信息,然后两台设备利用信息实现直连),实现内网直连;UDP打洞失败情况下会使用TCP中继。

    ZeroTier除了官方实现外,还有很多第三方控制器,用以解除对设备数量的限制。这里我选择的是imashen/zerotier-aio,集成了ZeroTier 1.14.0、ztnui和planet构建工具

  • 安装

  • 拉取镜像

    sudo docker pull imashen/zerotier-aio
  • 运行容器

    sudo docker run -d -p 9993:9993/udp -p 3443:3443 -p 3180:3180 \
        -v zerotier-one:/var/lib/zerotier-one \
        -v zerotier-webui:/www/zerotier-webui/etc \
        -v zerotier-logs:/logs \
        -e ZEROTIER-WEBUI_PASSWD=<password> \
        -e MYADDR=<outter-network-ip> \
        -e MYDOMAIN=site.home \
        --name zerotier-aio \
        --restart always \
        imashen/zerotier-aio
    # 9993是ZeroTier打洞端口,3443是面板https端口,3180是http服务器端口
    # 该容器需要有公网ip,作为整个局域网的控制器和根节点(可选)
    # 迁移只需复制三个卷
  • 访问https://本机ip:3443

    默认用户名admin,密码为设置的密码,如果密码太弱会被自动更换,需要进入容器日志查看

    sudo docker exec -it zerotier-aio /bin/bash
    cd /logs
    cat zerotier-webui.log

    新建一个网络->easy setup,生成网络IP池

    进入IP池,创建一个/80的ipv6 IP池

    进入ipv6选项,勾选自动分配及6plane

    进入路由选项,填入ipv6子网,路由留空

  • 在其他ZeroTier客户端,加入该网络,启用DNS,全局ip和默认路由

    zerotier-cli join <networkID>
    zerotier-cli set <networkID> allowDNS=1
    zerotier-cli set <networkID> allowGlobal=1
    zerotier-cli set <networkID> allowDefault=1
  • 加速

  • 进入zerotier-zio容器,配置planet

    sudo docker exec -it zerotier-aio /bin/bash
    mkplanet -b2j
    # 在roots中配置,配置项为{"identity": "zerotier-id", "stableEndpoints": [ipv4/zerotier-udp-or-tcp-port, ipv6/zerotier-udp-or-tcp-port]},最多四个
    vim planet.json
    mkplanet -j2b

    将生成的planet文件替换客户端下planet文件,重启zerotier服务,可以将planet文件放在/www/zerotier-webui/etc/myfs/下供客户端下载

    Linux:/var/lib/zerotier-one

    Windows:C:

    MacOS:/Library/Application Support/ZeroTier/One

安装Pi-hole

  • 安装

  • 拉取镜像

    sudo docker pull pihole/pihole
  • 停止系统默认域名解析服务

    sudo systemctl stop systemd-resolved
    vim /etc/systemd/resolved.conf # 解除注释DNSStubListener,并改为no
    sudo systemctl restart systemd-resolved
    # 用/run/systemd/resolve/resolv.conf替换软链接/run/systemd/resolve/stub-resolv.conf
    sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
  • 运行容器

    sudo docker run -d \
        --name pihole \
        --cap-add=NET_ADMIN \
        --restart always \
        -p 53:53/tcp -p 53:53/udp -p 8000:80/tcp \
        -e TZ=Asia/Shanghai -e WEBPASSWORD=<password> \
        -v etc-pihole:/etc/pihole -v etc-dnsmasq_d:/etc/dnsmasq.d \
        pihole/pihole
    # 80是面板入口,需要修改映射以防端口冲突
  • 访问http://宿主机ip或zerotier虚拟ip:8000/admin,登录面板

    在Local DNS->DNS Records中添加自定义域名(节点名称.域名)及对应的zerotier虚拟ip4和ipv6(dae需要)

    在Settings->DNS->Interface settings中,选择Permit all origins

  • 在ZeroTier自建控制器中设置网络的dns,域名填写和pihole中的一致,ip填写pihole所在节点的虚拟ip

  • 在dae中配置代理自建DNS

    # 添加合并内容
    dns {
      upstream {
          piholedns: 'tcp+udp://zerotier虚拟ip:53'
      }
      routing {
          request {
              qname(suffix: 你的域名) -> piholedns
          }
          response {
              upstream(piholedns) -> accept
          }
      }
    }
    routing {
      sip(dns源填的ip) && l4proto(udp) && port(53) -> must_direct
    }

获取域名

  • 免费域名

  • 访问getlocalcert,使用GitHub登录注册

  • 注册一个免费域名,记得选择.localcert.net,.localhostcert.net只能用于本机测试

  • 生成API Key,保存json文件

  • zerotier网络配置的域名需要与该域名一致,否则可能需要额外dns配置才能实现访问

安装Caddy

  • 安装

  • 拉取镜像

    sudo docker pull caddy:<version>-builder
    sudo docker pull caddy:<version>
  • 构建DNS质询caddy镜像

    sudo vim Dockerfile
    
    # 内容
    # FROM caddy:<version>-builder AS builder
    #
    # RUN xcaddy build --with github.com/caddy-dns/acmedns
    #
    # FROM caddy:<version>
    #
    # COPY --from=builder /usr/bin/caddy /usr/bin/caddy
    
    sudo docker build -t caddy:<tag> .
  • 运行容器

    sudo docker run -d \
        --restart always \
        --name caddy \
        -p 80:80 -p 443:443 -p 443:443/udp -p 8443:8443 \
        -v caddy-data:/data \
        -v caddy-config:/config \
        -v /path/to/Caddyfile/Dir:/etc/caddy \
        caddy
  • Caddyfile配置

    <yourSubdomain>.localcert.net {
      tls {
        # ca https://acme-staging-v02.api.letsencrypt.org/directory
        dns acmedns <creds.json>
      }
      respond "Hello from Caddy"
    }

    可以先解除注释,尝试获取证书,确认无误后再使用正式环境获取证书

安装NextCloud AIO

  • 安装

  • 拉取镜像

    sudo docker pull nextcloud/all-in-one
  • 配置反向代理

    # Caddyfile
    https://<yourSubdomain>.localcert.net:443 {
      reverse_proxy nextcloud-aio网卡的ip:11000
    }
    https://<your-nc-domain>:8443 {
        reverse_proxy https://nextcloud-aio网卡的ip:8080 {
            transport http {
                tls_insecure_skip_verify
            }
        }
    }
  • 运行容器

    sudo docker run -d \
                             --init \
                             --sig-proxy=false \
                             --name nextcloud-aio-mastercontainer \
                             --restart always \
                             --publish 8080:8080 \
                             --env APACHE_PORT=11000 \
                             --env APACHE_IP_BINDING=0.0.0.0 \
                             --env NEXTCLOUD_DATADIR="data-dir" \
                             --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
                             --volume /var/run/docker.sock:/var/run/docker.sock:ro \
                             nextcloud/all-in-one:latest
  • 给caddy添加nextcloud-aio网卡,dae代理nextcloud-aio网卡

后续

  • 内网DNS太容易断线了,把pihole服务器放到了有公网ip的云服务器上
阅读全文

组建NAS(三)

记录 2024/9/23

使用dae做网卡级代理

  1. 删除docker关于代理的配置

  2. 停止所有容器

  3. 删除所有容器

  4. 删除nextcloud-aio网络

  5. 删除所有卷

  6. 拉取dae镜像

    sudo docker pull daeuniverse/dae
  7. 启动容器

    sudo docker run -d \
        --restart always \
        --network host \
        --pid host \
        --privileged \
        -v /sys:/sys \
        -v /etc/dae:/etc/dae \
        --name dae \
        daeuniverse/dae:latest
  8. 在/etc/dae目录下创建config.dae文件,修改权限为640

    global {
        log_level: info
        lan_interface: docker0
        wan_interface: auto
        auto_config_kernel_parameter: true
    }
    node {
        sock: 'socks5://localhost:7890'
        http: 'http://localhost:7890'
    }
    dns {
        upstream {
            alidns: 'udp://dns.alidns.com:53'
            googledns: 'tcp+udp://dns.google.com:53'
        }
        routing {
            request {
                qname(geosite:cn) -> alidns
                fallback: googledns
            }
            response {
                upstream(googledns) -> accept
                ip(geoip:private) && !qname(geosite:cn) -> googledns
                fallback: accept
            }
        }
    }
    group {
        my_group {
        policy: min_moving_avg
    }
    }
    routing {
        pname(clash) -> must_direct
        dip(geoip:private) -> direct
        dip(geoip:cn) -> direct
        domain(geosite:cn) -> direct
        domain(home.arpa) -> direct
        fallback: my_group
    }
  9. 运行zerotier容器、zeronsd容器

  10. 运行nginx proxy manager容器

  11. 运行nextcloud-aio容器,将nextcloud-aio网卡加入dae

安装ftp服务器

  1. 拉取镜像

    sudo docker pull kibatic/proftpd
  2. 运行镜像

    sudo docker run -d --net host \
        -e FTP_LIST="admin:<password>" \
        -e MASQUERADE_ADDRESS=<serverIP> \
        -v /path_to_ftp_dir:/home/admin \
        --name proftpd \
        kibatic/proftpd:latest

切换zerotier-aio

  1. 拉取镜像

    sudo docker pull imashen/zerotier-aio
  2. 运行容器

    sudo docker run -d -p 9993:9993/udp -p 3443:3443 -p 3180:3180 \
        -v zerotier-one:/var/lib/zerotier-one \
        -v zerotier-webui:/www/zerotier-webui/etc \
        -v zerotier-logs:/logs \
        -e NODE_ENV=production \
        -e ZEROTIER-WEBUI_PASSWD=<password> \
        -e HTTPS_PORT=3443 \
        --name zerotier-aio \
        imashen/zerotier-aio
  3. 额,由于该镜像不支持zeronsd做DNS,所有最后只是取了容器中mkplanet工具生成planet文件,分发给各个客户端做加速

阅读全文

组建NAS(二)

记录 2024/9/14

安装Docker

  1. 清理旧版本

    for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
  2. 添加Docker的apt仓库

    # Add Docker's official GPG key:
    sudo apt-get update
    sudo apt-get install ca-certificates curl
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    
    # Add the repository to Apt sources:
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt-get update
  3. 安装Docker

    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  4. 开机自启动

    sudo systemctl enable docker
  5. 编辑~/.docker/config.json添加代理,会被加入容器

    {
     "proxies": {
            "default": {
                "httpProxy": "http://dockerIP:vpnPort",
                "httpsProxy": "http://dockerIP:vpnPort",
                "noPorxy": "localhost,127.0.0.1,zeronsd域名"
            }
        }
    }

安装ZeroTier

Web端

  1. 注册ZeroTier账号、登录
  2. 创建一个网络
  3. 创建账户的API Access Token
  4. 等待客户端接入
  5. 对接入的客户端进行授权和命名

NAS

  1. 拉取或从Dockerfile构建ZeroTier镜像

    sudo docker pull zerotier/zerotier
  2. 运行容器

    sudo docker run -d \
                             --name zerotier \
                             --device=/dev/net/tun \
                             --net=host \
                             --cap-add=NET_ADMIN \
                             --cap-add=SYS_ADMIN \
                             --restart=always \
                             -v ~/zerotier-one/:/var/lib/zerotier-one/ \
                             zerotier/zerotier:latest
  3. 进入容器,加入网络,开启DNS

    sudo docker exec -it zerotier /bin/bash
    # 容器内
    zerotier-cli join networkID
    zerotier-cli set networkID allowDNS=1
  4. 拉取或者构建ZeroNSD镜像,用来搭建ZeroTier的DNS

    sudo docker pull zerotier/zeronsd
  5. 运行容器

    sudo docker run -d \
                             --net=host \
                             -v ~/zerotier-one/authtoken.secret:/authtoken.secret \
                             --restart=always \
                             -e ZEROTIER_CENTRAL_TOKEN=<token> \
                             --name zeronsd \
                             zerotier/zeronsd:latest start -s /authtoken.secret \
                             <networkID>

云服务器

  1. 安装ZeroTier

    curl -s https://install.zerotier.com | sudo bash
  2. 加入网络,开启DNS

    zerotier-cli join networkID
    zerotier-cli set networkID allowDNS=1
  3. 进入ZeroTier配置文件夹,配置Moon服务器

    cd /var/lib/zerotier-one
    zerotier-idtool initmoon identity.public >> moon.json
  4. 查看moon配置,并在roots.stableEndpoints填入"IP/端口",可以使用公网IP,端口默认9993

        {
          "id": "deadbeef00",
          "objtype": "world",
          "roots": [
            {
              "identity": "deadbeef00:0:34031483094...",
              "stableEndpoints": ["IP/PORT"]
            }
          ],
          "signingKey": "b324d84cec708d1b51d5ac03e75afba501a12e2124705ec34a614bf8f9b2c800f44d9824ad3ab2e3da1ac52ecb39ac052ce3f54e58d8944b52632eb6d671d0e0",
          "signingKey_SECRET": "ffc5dd0b2baf1c9b220d1c9cb39633f9e2151cf350a6d0e67c913f8952bafaf3671d2226388e1406e7670dc645851bf7d3643da701fd4599fedb9914c3918db3",
          "updatesMustBeSignedBy": "b324d84cec708d1b51d5ac03e75afba501a12e2124705ec34a614bf8f9b2c800f44d9824ad3ab2e3da1ac52ecb39ac052ce3f54e58d8944b52632eb6d671d0e0",
          "worldType": "moon"
        }
  5. 生成moon,重启服务

    zerotier-idtool genmoon moon.json
    mkdir moons.d
    mv 0000worldID.moon moons.d/0000worldID.moon
    /etc/init.d/zerotier-one restart
  6. 其他客户端订阅moon服务器

    zerotier-cli orbit worldID rootID

安装Nignx Proxy Manager

  1. 拉取镜像

    sudo docker pull jc21/nginx-proxy-manager
  2. 运行容器

    sudo docker run -d \
                             --restart=always \
                             -p 80:80 -p 443:443 -p 81:81 \
                             -v ~/nignx-proxy-manager/data:/data -v ~/nignx-proxy-manager/letsencrypt:/etc/letsencrypt \
                             --name nignx-proxy-manager \
                             jc21/nginx-proxy-manager:latest

安装硬盘

  1. 将两块机械硬盘插入机箱

  2. 查看硬盘

    sudo fdisk -l
    Disk /dev/nvme0n1:238.47 GiB,256060514304 字节,500118192 个扇区
    Disk model: aigo NVMe SSD P3500 256GB 
    单元:扇区 / 1 * 512 = 512 字节
    扇区大小(逻辑/物理):512 字节 / 512 字节
    I/O 大小(最小/最佳):512 字节 / 512 字节
    磁盘标签类型:gpt
    磁盘标识符:89E7ED5D-5DDD-4A92-8808-CAC413B4C5EE
    
    设备              起点      末尾      扇区   大小 类型
    /dev/nvme0n1p1    2048   2203647   2201600     1G EFI 系统
    /dev/nvme0n1p2 2203648   6397951   4194304     2G Linux 文件系统
    /dev/nvme0n1p3 6397952 500115455 493717504 235.4G Linux 文件系统
    
    
    Disk /dev/mapper/ubuntu--vg-ubuntu--lv:235.42 GiB,252782313472 字节,493715456 个扇区
    单元:扇区 / 1 * 512 = 512 字节
    扇区大小(逻辑/物理):512 字节 / 512 字节
    I/O 大小(最小/最佳):512 字节 / 512 字节
    
    
    Disk /dev/sda:1.82 TiB,2000398934016 字节,3907029168 个扇区
    Disk model: ST2000VN004-2E41
    单元:扇区 / 1 * 512 = 512 字节
    扇区大小(逻辑/物理):512 字节 / 4096 字节
    I/O 大小(最小/最佳):4096 字节 / 4096 字节
    
    
    Disk /dev/sdb:1.82 TiB,2000398934016 字节,3907029168 个扇区
    Disk model: ST2000VN004-2E41
    单元:扇区 / 1 * 512 = 512 字节
    扇区大小(逻辑/物理):512 字节 / 4096 字节
    I/O 大小(最小/最佳):4096 字节 / 4096 字节
  3. 对硬盘进行分区、更改分区类型、保存

    sudo fdisk /dev/sda # /dev/sdb同理
    命令(输入 m 获取帮助): n
    # 后续使用默认选项即可
    
    # 分区完成后
    命令(输入 m 获取帮助): t
    Hex code or alias (type L to list all): L
    Hex code or alias (type L to list all): 8E # 改成LVM
    
    # 写入磁盘保存
    命令(输入 m 获取帮助): w
    sudo fdisk -l
    Disk /dev/sda:1.82 TiB,2000398934016 字节,3907029168 个扇区
    Disk model: ST2000VN004-2E41
    单元:扇区 / 1 * 512 = 512 字节
    扇区大小(逻辑/物理):512 字节 / 4096 字节
    I/O 大小(最小/最佳):4096 字节 / 4096 字节
    磁盘标签类型:dos
    磁盘标识符:0x18312fd8
    
    设备       启动  起点       末尾       扇区  大小 Id 类型
    /dev/sda1        2048 3907029167 3907027120  1.8T 8e Linux LVM
    
    
    Disk /dev/sdb:1.82 TiB,2000398934016 字节,3907029168 个扇区
    Disk model: ST2000VN004-2E41
    单元:扇区 / 1 * 512 = 512 字节
    扇区大小(逻辑/物理):512 字节 / 4096 字节
    I/O 大小(最小/最佳):4096 字节 / 4096 字节
    磁盘标签类型:dos
    磁盘标识符:0xbbb3186f
    
    设备       启动  起点       末尾       扇区  大小 Id 类型
    /dev/sdb1        2048 3907029167 3907027120  1.8T 8e Linux LVM
  4. 创建PV

    sudo pvcreate /dev/sda1 # 另一块同理
  5. 创建VG

    sudo vgcreate nas-storage /dev/sda1 # 用一块PV创建VG,建一个就好
    sudo vgextend nas-storage /dev/sdb1 # 扩容VG
  6. 创建LV

    sudo lvcreate -l 100%FREE --name nas-storage-lv nas-storage
  7. 格式化LV

    sudo mkfs --type=ext4 /dev/nas-storage/nas-storage-lv
  8. 挂载LV

    mkdir /mnt/ncdata
    sudo mount /dev/nas-storage/nas-storage-lv /mnt/ncdata
    sudo df -Th
    文件系统                                  类型   大小  已用  可用 已用% 挂载点
    tmpfs                                     tmpfs  3.1G  1.7M  3.1G    1% /run
    /dev/mapper/ubuntu--vg-ubuntu--lv         ext4   232G   14G  207G    7% /
    tmpfs                                     tmpfs   16G     0   16G    0% /dev/shm
    tmpfs                                     tmpfs  5.0M     0  5.0M    0% /run/lock
    /dev/nvme0n1p2                            ext4   2.0G  253M  1.6G   14% /boot
    /dev/nvme0n1p1                            vfat   1.1G  6.1M  1.1G    1% /boot/efi
    tmpfs                                     tmpfs  3.1G     0  3.1G    0% /run/user/1000
    /dev/mapper/nas--storage-nas--storage--lv ext4   3.6T   28K  3.4T    1% /mnt/ncdata
  9. 配置开机自动挂载

    # 查看文件系统UUID
    sudo blkid
    
    sudo vim /etc/fstab
    
    # 添加一行
    UUID=8aba1ccb-750e-4d00-9566-a786537bee30 /mnt/ncdata ext4 defaults 0 0
    
    # 重新挂载(读取/etc/fstab)
    sudo mount -a
    sudo df -h

安装NextCloud AIO

  1. 拉取镜像

    sudo docker pull nextcloud/all-in-one
  2. 运行容器

    sudo docker run -d \
                             --init \
                             --sig-proxy=false \
                             --name nextcloud-aio-mastercontainer \
                             --restart always \
                             --publish 8080:8080 \
                             --env APACHE_PORT=11000 \
                             --env APACHE_IP_BINDING=0.0.0.0 \
                             --env NEXTCLOUD_DATADIR="data-dir" \
                             --add-host zeronsd域名:zerotierIP \
                             --env NEXTCLOUD_TRUSTED_CACERTS_DIR=ca-certificates-dir \
                             --volume /etc/ssl/certs:/etc/ssl/certs \
                             --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
                             --volume /var/run/docker.sock:/var/run/docker.sock:ro \
                             nextcloud/all-in-one:latest

获取证书

  1. 安装mkcert

    sudo apt install libnss3-tools
    curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
    chmod +x mkcert-v*-linux-amd64
    sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert
  2. 生成本地CA证书

    mkcert -install
  3. 生成服务器证书

    mkcert "*.home.arpa"
  4. 容器信任证书,将主机/etc/ssl/certs映射到容器

进行配置

  1. 访问Nignx Proxy Manager管理网站http://nas服务器ip:81,连接了ZeroTier可以使用ZeroTier内网ip

    ,密码changeme

  2. 填写好基本信息后回到首页,选择Proxy Hosts->Add Proxy Host

    # 给nignx proxy manager添加nextcloud aio的网卡
    sudo docker network connect nextcloud-aio nignx-proxy-manager

    填写ZeroNSD生成的域名,ip填写网卡nextcloud-aio的ip,端口填写aio启动参数apache_port端口

  3. 转到Advanced,填写下面配置,保存

    client_body_buffer_size 512k;
    proxy_read_timeout 86400s;
    client_max_body_size 0;
  4. 转到SSL Certificates,添加mkcert生成的证书

  5. 回到Proxy Hosts,编辑,选择SSL,选择已有证书

  6. 访问https://nas服务器ip:8080到达NextCloud网站,默认密码在下方,记得复制

  7. 填写nignx中配置的域名

  8. 选择所需容器,等待安装完成

阅读全文

组建NAS(一)

记录 2024/9/13

硬件

  • 机箱:见方L 8盘位机箱
  • 主板:华硕 PRIME A520M-K
  • CPU:AMD RYZEN 5 5600G
  • 显卡:核显
  • CPU散热:利民 Silver Soul 110 BLACK
  • 机箱风扇:
    • 利民 C12C×3
    • 利民 C12015B×2
    • 利民 P9×1
  • 内存:光威 天策 32GB(16GB×2) DDR4 3600MHz
  • 系统硬盘:爱国者 SSD P3500 256GB
  • 电源:安钛克 NE650W GOLD 全模组
  • NAS硬盘:希捷 IronWolf ST2000VN004×2
  • 配件:
    • 利民 FAN HUB X4 集线器×1
    • 利民 M.2 2280 固态散热马甲×1
    • 乐扩 PCIe Gen3 x1 转 4 SATA Gen3 扩展卡×1
    • SATA线×8
    • 安钛克 6pin 转 4 大4pin 全模组定制线×1

组装

  1. 拆除机箱前置风扇面板和电源面板、移除顶盖、拆下硬盘笼
  2. 将前置风扇利民 C12015B×2安装到前置风扇位置,风向前进后出
  3. 将IO挡板安装到机箱后部
  4. 将利民 C12C×1安装到机箱侧面风扇处,用于出风
  5. 往主板上依次安装CPU、CPU散热(从内存方向进风从IO接口方向出风)、内存、系统硬盘、固态散热马甲,并将CPU散热电源线插到主板CPU_FAN
  6. 将主板安装到机箱底部
  7. 安装PCIe扩展卡
  8. 将利民 P9×1安装到机箱后部风扇处,用于出风
  9. 将全模组电源线按位置插到主板电源、CPU供电口,将集线器插到主板CHA_FAN,将机箱供电线插到电源供电口
  10. 安装电源到机箱后部电源处
  11. 将主板供电线、CPU供电线插入电源
  12. 将SATA线×8、6pin 转 大4pin电源线插到硬盘笼背板
  13. 安装硬盘笼到机箱前部
  14. 将SATA线接入主板,硬盘笼供电线插入电源
  15. 将利民 C12C×2安装到顶盖风扇槽
  16. 将机箱风扇供电线插入集线器,并将集线器贴在机箱内侧
  17. 将机箱前置电源面板的电源按钮和前置USB插针接到主板
  18. 安装前置面板和顶盖,插入电源和硬盘

系统和软件

  • 安装系统

    1. 将键盘、鼠标、显示器和引导U盘(存放系统镜像,这里是Ubuntu22.04)插入机箱
    2. 启动主机,进入BIOS(注意检查CPU温度),选择从引导U盘启动
    3. 按指引安装系统
    4. 拔出引导U盘、重启系统
  • 移除snap

    •   sudo apt autoremove --purge snapd
  • 系统盘扩容

    •   lsblk
      NAME                      MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
      nvme0n1                   259:0    0 238.5G  0 disk 
      ├─nvme0n1p1               259:1    0     1G  0 part /boot/efi
      ├─nvme0n1p2               259:2    0     2G  0 part /boot
      └─nvme0n1p3               259:3    0 235.4G  0 part 
        └─ubuntu--vg-ubuntu--lv 253:0    0   100G  0 lvm  /
    • 可以看到系统根目录只分配了100G

    •   sudo vgdisplay
        --- Volume group ---
        VG Name               ubuntu-vg
        System ID             
        Format                lvm2
        Metadata Areas        1
        Metadata Sequence No  2
        VG Access             read/write
        VG Status             resizable
        MAX LV                0
        Cur LV                1
        Open LV               1
        Max PV                0
        Cur PV                1
        Act PV                1
        VG Size               235.42 GiB
        PE Size               4.00 MiB
        Total PE              60268
        Alloc PE / Size       25600 / 100.00 GiB
        Free  PE / Size       34668 / 135.42 GiB
        VG UUID               aoRfz0-qgEh-A7rr-FJmB-s5MW-MJfW-rnfvfX
    • 卷组VG已经占满硬盘空间PV,说明逻辑卷LV空间没占满

    •   sudo lvdisplay
        --- Logical volume ---
        LV Path                /dev/ubuntu-vg/ubuntu-lv
        LV Name                ubuntu-lv
        VG Name                ubuntu-vg
        LV UUID                JGyBIQ-2alO-XCXr-gCft-VBlS-xxlz-MwRmlf
        LV Write Access        read/write
        LV Creation host, time ubuntu-server, 2024-09-13 20:55:48 +0000
        LV Status              available
        # open                 1
        LV Size                100.00 GiB
        Current LE             25600
        Segments               1
        Allocation             inherit
        Read ahead sectors     auto
        - currently set to     256
        Block device           253:0
    •   sudo lvextend -l +100%FREE -r /dev/ubuntu-vg/ubuntu-lv
    • 再次查看硬盘情况

      NAME                      MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
      nvme0n1                   259:0    0 238.5G  0 disk 
      ├─nvme0n1p1               259:1    0     1G  0 part /boot/efi
      ├─nvme0n1p2               259:2    0     2G  0 part /boot
      └─nvme0n1p3               259:3    0 235.4G  0 part 
        └─ubuntu--vg-ubuntu--lv 253:0    0 235.4G  0 lvm  /
  • 支持中文

    •   sudo apt update
        sudo apt install language-pack-zh-hans
        localectl set-locale LANG=zh_CN.UTF-8
        reboot
  • 安装代理

    • 解压clash并赋予可执行权限

    • 下载Country.mmdb文件移动至~/.config/clash文件夹

    • 用自己的配置修改~/.config/clash/config.yaml

    • 打开系统代理(添加代理环境变量)

    • 启动clash

    • 通过在配置中填写external-controller开启外部控制

      认证

      • 外部控制器接受Bearer Token作为认证方式
        • 使用Authorization: Bearer <Your Secret>添加到请求头以进行验证

      API

      • /logs

        • 方法:GET

          • 描述:获取实时日志

          •   curl -X GET localhost:9090/logs
      • /traffic

        • 方法:GET

          • 描述:获取实时流量

          •   curl -X GET localhost:9090/traffic
      • /version

        • 方法:GET

          • 描述:获取Clash版本

          •   curl -X GET localhost:9090/version
      • /configs

        • 方法:GET

          • 描述:获取基础配置

          •   curl -X GET localhost:9090/configs
        • 方法:PATCH

          • 描述:增量修改配置

          •   curl -X PATCH -d '{}' localhost:9090/configs
      • /proxies

        • 方法:GET

          • 描述:获取所有节点/选择器信息

          •   curl -X GET localhost:9090/proxies
      • /proxies/:name

        • 方法:GET

          • 描述:获取指定节点/选择器信息

          •   curl -X GET localhost:9090/proxies/nodeOrSelectorName
        • 方法:PUT

          • 描述:切换选择器中选中节点

          •   curl -X PUT -d '{"name": "nodeName"}' localhost:9090/proxies/SelectorName
      • /proxies/:name/delay

        • 方法:GET

          • 描述:获取指定节点延迟

          •   curl -X GET "localhost:9090/proxies/nodeOrSelectorName/delay?url=http://xxxx&timeout=xxxx"
      • /rules

        • 方法:GET

          • 描述:获取规则信息

          •   curl -X GET localhost:9090/rules
      • /connections

        • 方法:GET

          • 描述:获取连接信息

          •   curl -X GET localhost:9090/connections
        • 方法:DELETE

          • 描述:关闭所有连接

          •   curl -X DELETE localhost:9090/connections
      • /connections/:id

        • 方法:DELETE

          • 描述:关闭指定连接

          •   curl -X DELETE localhost:9090/connections/connectionID
      • /providers/proxies

        • 方法:GET

          • 描述:获取所有代理集信息

          •   curl -X GET localhost:9090/providers/proxies
      • /providers/proxies/:name

        • 方法:GET

          • 描述:获取指定代理集信息

          •   curl -X GET localhost:9090/providers/proxies/proxyName
阅读全文

往者不可谏,来者犹可追

随笔 2024/8/25

为什么要写这篇文章

谨以此篇,送葬华年。

费劲巴拉地写下这篇文字,也算是对我前面四年烂泥一样的大学生活作个总结吧。待我未来某天回看这篇文章时,希望有的只是轻松和欣慰,而不是痛苦和惭愧。

过往

疫情结束之后,我从一个平平无奇的小地方考入了武汉大学,在选择专业的时候,其实完全没有概念,只是听着网上众说纷纭,选择了比较火的计算机专业。但我当时对这个专业要学什么、做什么、怎么找工作等等都没有概念。

作为家里第一个大学生,进入大学,一切体验都是全新的,不曾出现在老师同学口中,也不曾出现在哥哥姐姐亲戚朋友的经验之谈中。另外,由于性格比较孤僻,不善社交,周围并没有什么朋友,也不怎么逛社区,不敢表达,对自己没有一点自信,总觉得见识浅,叫人说教,甚至在网上也束手束脚,不想留下只言片语。于是乎整个大学生活,我都是在跌跌撞撞、懵懵懂懂中度过的。

其实在大学过程中,我有很多次机会可以将生活导向正轨或者说大多数人选择的路,比如看见同学参加竞赛可以去了解、实验室发来了招募可以去咨询、看到秋招的广告可以去问问……但最终都因为害怕无法完成项目、害怕技术实力不够、害怕面试被怼……等等害怕而不了了之。

最后关于学习呢,没有明确的规划,在纷繁的技术丛林中拾花摘叶,没有明确的学习路线,没有扎实的技术基础,最后只会简单使用springboot、vue框架做点皮毛开发。

这便是我的过往,没有计划、信息闭塞、不敢表达、自卑焦虑……失败buff满满的四年。

未来

往好点说,未来还有许多可能;往坏了看,我对未来还是充满迷茫,但不知怎的,没那么焦虑了,或许是看到过去的自己如此不堪,有了些前进的方向和动力吧。突然想起高中的好哥们对我说,把握好自己的学习节奏,努力总是会有回报的。既然未来充满了不确定,那这就不写太多了,好好努力,朝着自己的小目标前进吧。

阅读全文
1 ... 2
avatar
周文峰

疯疯癫癫,一笑一天
(笑不出来版)