frp+docker | 通过内网穿透部署博客项目

前言

最近买了mac mini m4 32G,性能很强劲啊!干活更流畅了,于是打算作为家庭服务器去运行一些项目,最好还能把目前在云服务器上运行的项目转移到本地去跑。

而这样一来亟待解决的是公网IP的问题,以往我的云端项目,比如网站,通过云服务器厂商提供的公网IP进行域名解析,就可以通过公网访问到我的域名,再包括一些web项目也需要通过公网IP去进行访问,所以说公网IP还是必要的。参考过很多方案,比如向通信运营商申请公网IP或租赁费用更划算带宽更高的云服务器,最后发现依然是「内网穿透」最能满足我的需求,整个配置流程也很直接很迅速。

提供内网穿透服务的平台有很多,我用的是:www.natfrp.com,目前配置的方案是10元/月的套餐,带宽限速24 Mbps,隧道数量10条,每月流量5 GiB + 108 GiB,非常划算了!

image-20250619154728722

FRP与内网穿透

FRP(Fast Reverse Proxy)是一款开源的内网穿透工具,核心功能是通过反向代理技术,让位于内网或防火墙后的设备(如个人服务器、路由器、智能家居等)被公网访问。

内网穿透,乍一听好像是一门很高端很深奥的黑客技术。但其实可以理解它带来的作用就一个,就是让你可以通过公网来访问到你本地局域网里的东西。比如我在家里电脑的一份文件,我希望在公司通过某种方式来访问到这份文件,那内网穿透服务就可以达到这种效果。

总体来说,整套frp服务分为「服务端」和「客户端」,客户端就是你的本地环境。

实际上如果你有一台云服务器,也可以在上面自己搭建一套frp服务端,不难,可能就是配置信息上面的通信需要花点时间去调通,同时访问速度也受限于云服务器的带宽速度。

frp服务端

使用frp服务在「服务端」所需要的配置过程如下:

  1. 创建隧道

    以我使用的付费frp服务为例,在隧道配置页面,点击创建隧道,

    image-20250619160713920

    比如我现在需要给我的网站配置一个隧道,实现公网通过域名访问到我的网站,那么在配置时可以选「https建站隧道」,后面的配置根据自己的情况来填写域名,

    配置好了之后点开配置查看,这个配置信息就是等一下需要在客户端填写的内容。

    image-20250619162804618

frp客户端

使用frp服务在「客户端」所需要的配置过程如下:

  1. 下载frp客户端

    本地项目所有应用我使用的是docker compose容器编排部署的方案,包括frp客户端,这一步需要将frp项目的文件下载到本地,项目线上地址:https://github.com/fatedier/frp/releases, 下载到本地之后项目目录中的frpc.ini文件就是需要编辑的配置文件,将上一步获取的配置信息填写进来:

    image-20250619163856363

    这一步的[server]部分的配置,首先[server]对应的是创建隧道时的隧道名,必须要对应上。

    然后local_iplocal_port,由于我要进行内网穿透的web服务是通过nginx容器代理转发,并且容器之间通过局域网互相通信,所以这里我写的server_nginx对应的是nginx容器名称,如果是本地服务,那么一般填写localhost,根据具体情况来。

    一般这一步都需要一些时间去耐心调整配置信息,才能顺利地跟服务端通信。

  2. 制作frp镜像

    这一步是可选的,镜像用于下一步的yml文件的镜像配置,我的方案是自己制作一个轻量的frp镜像。

    我的目录层级关系如下:

    image-20250619164349750

    在Dockerfile中填写:

    FROM busybox:latest
    
    MAINTAINER Billy_Chen
    
    COPY ./frp /bin/frp
    
    ENV TZ=Asia/Shanghai \
        PATH="/bin/frp:$PATH"
    

    主要的构建步骤就是把本地的frp项目文件复制进去,其中比较重要的两个文件,一个是客户端配置文件「frpc.ini」,一个是客户端执行文件「frpc」,执行文件就是用来在容器中执行整套frp客户端应用的。

    使用build命令制作镜像:docker build -t chenxuefan/server_frp:v1 --platform linux/arm64 . ,由于我本地运行的机器是Mac m4处理器,因此架构是linux/arm64

    更多使用操作参考:使用-dockerfile-构建项目

  3. 配置docker-compose.yml文件

      server_frp:
        image: chenxuefan/server_frp:v1
        container_name: server_frp
        restart: always
        environment:
          TZ: ${TZ}
        volumes:
          - ./server_frp/frp:/bin/frp
          - ./server_frp/logs:/var/log
        ports:
          - "8010-8050:8010-8050"
        command: [ "frpc", "-c", "/bin/frp/frpc.ini" ]
        networks:
          - server
        healthcheck:
          test: [ "CMD", "ps", "-ef", "|", "grep", "frpc", "|", "grep", "-v", "grep" ]
          interval: 30s
          timeout: 10s
          retries: 3
          start_period: 40s
    

    server_frp服务连同我的其他服务都配置在yml文件中,最后通过命令docker-compose up起来,整个部署流程就完成了。通过server.chenxuefan.cn域名即可访问到我本地电脑启动的服务。

所有服务都在容器中隔离运行,我的mac mini也设置成永不休眠,所以理论上只要家里不断网,就可以一直访问到,很稳定!

一些好用的应用 & yml文件配置

minio

  server_minio:
    image: minio/minio:latest
    container_name: server_minio
    restart: unless-stopped
    volumes:
      - ./server_minio/data:/data
      - ./server_minio/config:/root/.minio
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
      TZ: ${TZ}
    ports:
      - "9005:9000"
      - "9006:9001"
    command: [ "server", "/data", "--console-address", ":9001" ]
    healthcheck:
      test: [ "CMD", "curl", "--silent", "--fail", "http://localhost:9000/minio/health/live" ]
      interval: 30s
      retries: 3
      start_period: 40s
      timeout: 10s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "7"
    networks:
      - server

umami

  server_umami:
    image: ghcr.io/umami-software/umami:postgresql-latest
    container_name: server_umami
    ports:
      - "8009:3000"
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@server_postgres:${POSTGRES_PORT}/${POSTGRES_DB_UMAMI}
      DATABASE_TYPE: postgresql
      HASH_SALT: replace-me-with-a-random-string
    depends_on:
      - server_postgres
    restart: always
    networks:
      - server
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

joplin

  server_joplin:
    image: joplin/server:latest
    container_name: server_joplin
    restart: unless-stopped
    user: root
    ports:
      - "8007:22300"
    volumes:
      - ./server_joplin/data:/var/lib/joplin
      - ./server_joplin/config:/config
    environment:
      APP_PORT: ${JOPLIN_APP_PORT}
      DISABLE_CORS: ${JOPLIN_DISABLE_CORS}
      CORS_ALLOWED_ORIGINS: ${JOPLIN_CORS_ALLOWED_ORIGINS}
      APP_BASE_URL: ${JOPLIN_APP_BASE_URL}
      APP_DB: ${JOPLIN_APP_DB}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_DATABASE: ${POSTGRES_DB_JOPLIN}
      POSTGRES_PORT: ${POSTGRES_PORT}
      TZ: ${TZ}
      YARN_CACHE_FOLDER: ${JOPLIN_YARN_CACHE_FOLDER}
    command: sh -c "mkdir -p /tmp/yarn-cache && yarn start"
    depends_on:
      - server_postgres
    networks:
      - server
    healthcheck:
      test: ["CMD", "curl", "--silent", "--fail", "http://localhost:22300"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

code-server

  server_code-server:
    image: codercom/code-server:latest
    container_name: server_code-server
    restart: unless-stopped
    user: root
    ports:
      - "8006:8080"
    volumes:
      - ./server_code_server/data:/home/coder/project
    environment:
      PASSWORD: ${CODE_SERVER_PASSWORD}
      TZ: ${TZ}
    networks:
      - server
    healthcheck:
      test: ["CMD", "curl", "--silent", "--fail", "http://localhost:8080"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

portainer

  server_portainer:
    image: portainer/portainer-ce:latest
    container_name: server_portainer
    restart: unless-stopped
    ports:
      - "8005:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./server_portainer/data:/data
    environment:
      TZ: ${TZ}
    networks:
      - server
    healthcheck:
      test: ["CMD", "curl", "--silent", "--fail", "http://localhost:9000"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

sun-panel

  server_index_panel:
    image: "hslr/sun-panel:latest"
    container_name: server_index_panel
    environment:
      TZ: ${TZ}
    volumes:
      - ./server_sun-panel/conf:/app/conf
      - /var/run/docker.sock:/var/run/docker.sock
      - ./server_sun-panel/runtime:/app/runtime
    ports:
      - "8008:3002"
    restart: always
    networks:
      - server
    healthcheck:
      test: ["CMD", "curl", "--silent", "--fail", "http://localhost:3002"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

certd

这个项目可以自动生成供域名所使用的SSL证书,保证域名可以通过https进行请求。到期后台自动重新申请,这样可保证网站的长期正常运行。

项目地址:https://github.com/certd/certd

  server_certd:
    image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
    container_name: server_certd # 容器名
    restart: unless-stopped # 自动重启
    volumes:
      - ./server_certd/data/certd:/app/data
      - ./server_nginx/ssl:/ssl
    ports: # 端口映射
      - "7001:7001"
      - "7002:7002"
    labels:
      com.centurylinklabs.watchtower.enable: "true"
    environment:
     - certd_system_resetAdminPasswd=false
    networks:
      - server


2224 字