docker 部署 sharp-admin + 1Panel 管理面板

购买云服务器

登录阿里云购买ECS,操作系统我选择 Debian 12.10 64位,选择 Alibaba Cloud linux 安装 1Panel 的时候会出现错误。

docker 部署 sharp-admin

创建项目目录

mkdir -p /usr/local/projects/sharp-admin

sharp-admin 下依次创建目录:data、deploy、init

root@iZbp114oy5r2mjdc1yuspoZ:/usr/local/projects/sharp-admin# ls -al
total 28
drwxr-xr-x 5 root root 4096 Apr 25 19:59 .
drwxr-xr-x 3 root root 4096 Apr 25 19:52 ..
drwxr-xr-x 8  999 root 4096 Apr 25 21:01 data # 挂载 mysql 的数据卷
drwxr-xr-x 2 root root 4096 Apr 25 19:54 deploy # 上传可执行的 jar 文件
-rw-r--r-- 1 root root 1190 Apr 25 19:57 docker-compose.yaml
-rw-r--r-- 1 root root  409 Apr 25 19:58 Dockerfile
drwxr-xr-x 2 root root 4096 Apr 25 19:59 init # 初始化的 mysql 脚本

拷贝文件:sharp-admin-2.0-SNAPSHOT.jar、docker-compose.yaml、Dockerfile、init-sql-2025-01-11.sql 到对应的目录下
树形结构展示

├── data
├── deploy
│   └── sharp-admin-2.0-SNAPSHOT.jar
├── docker-compose.yaml
├── Dockerfile
└── init
    └── init-sql-2025-01-11.sql

Dockerfile

# For Java 8, try this
# FROM openjdk:8-jdk-alpine

# For Java 8, try this
FROM openjdk:8-jdk-alpine

# Refer to Maven build -> finalName
ARG JAR_FILE=deploy/sharp-admin-2.0-SNAPSHOT.jar

# cd /opt/app
WORKDIR /opt/app

# cp target/sharp-admin-2.0-SNAPSHOT.jar /opt/app/app.jar
COPY ${JAR_FILE} app.jar

# java -jar /opt/app/app.jar
ENTRYPOINT ["java","-Dspring.profiles.active=docker-prod","-jar","app.jar"]

docker-compose.yaml

version: '3'

services:
  db:
    container_name: mysql-sharp-admin
    image: mysql:latest
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: sharp-admin
    volumes:
      - /usr/local/projects/sharp-admin/data:/var/lib/mysql # 数据挂载
      - ./init:/docker-entrypoint-initdb.d # 数据挂载目录下没有数据,那么执行 init 下的 sql
#    ports:
#      - "3306:3306"
#      - "33060:33060"
    expose: # 不会真的开放端口到宿主机或外部网络
      - 3306
      - 33060
    healthcheck: # 给 db 添加健康检查,要真正做到“等数据库准备好再启动”
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p123456"]
      interval: 10s
      timeout: 5s
      retries: 5

  sharp-admin:
    container_name: sharp-admin
    image: sharp-admin:2.0
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    volumes:
      - /Users/rick/Space/tmp/admin/log:/opt/app/log
    depends_on:
      db:
        condition: service_healthy

1Panel

安装

参考https://1panel.cn/docs/installation/online_installation/#__tabbed_1_3

curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && bash quick_start.sh

安装完成后,控制台会打外网访问链接,用户名,密码。如果无法访问,查看一下默认端口 22367 是否开启允许访问。

环境配置

网站

【网站】中安装:OpenResty 服务后,【创建】网站,选择【反向代理】填写域名【sharp-admin.xhope.top】,【代理地址】写【127.0.0.1:8080】,我们的应用就运行在8080端口。不要忘记域名解析到【sharp-admin.xhope.top】。配置完成后,通过地址sharp-admin.xhope.top就能访问应用。

证书

  • 创建 Acme 账户,填写邮箱和类型后确认。
  • 创建 DNS 账户,填写名称,类型选择【阿里云】,Access key 和 Secret key,通过 – RAM 访问控制创建用户获取,同时给权限【AliyunDNSFullAccess】
  • 申请证书,从网站中获取选择网站【sharp-admin.xhope.top】
  • 网站配置,启用 HTTPS

mvn 自动上传 jar 部署重启容器运行

  • sharp-admin 目录下添加 文件 config.sh、deploy-docker.yml
  • 配置 ssh 免密登陆,配置别名 node,参考https://xhope.top/?p=1722
  • 运行 mvn.sh

config.sh

server_ip="47.97.7.98"
dir="/usr/local/projects"
project_name="sharp-admin"
port="8080"
project_path="$dir/$project_name"

deploy-docker.yml

#!/bin/bash
source ./config.sh

docker-compose stop $project_name
docker-compose rm $project_name -f
docker rmi $project_name:2.0
docker build -t $project_name:2.0 .
docker-compose create $project_name
docker-compose start $project_name

mvn.sh

#!/bin/bash
source ./config.sh

# 获取当前工作目录的绝对路径
current_dir=$(pwd)

mvn clean package -Dmaven.test.skip=true
scp $current_dir/target/$project_name-2.0-SNAPSHOT.jar root@$server_ip:$dir/$project_name/deploy

# 服务器免密登录 ssh node
# 执行服务器 deploy-docker.sh
ssh node "cd $dir/$project_name && bash -s < $dir/$project_name/deploy-docker.sh"

对象存储minio使用指南

安装

Docker Hub: https://hub.docker.com/r/minio/minio
拉取镜像

docker pull minio/minio

启动容器

  docker run -d \
  -p 9000:9000 \
  -p 9001:9001 \
  --name minio \
  -e "MINIO_ROOT_USER=admin" \
  -e "MINIO_ROOT_PASSWORD=admin123" \
  -v /Users/rick/Space/tmp/minio/data:/data \
  minio/minio:latest server /data --console-address ":9001"

登录管理台

http://localhost:9001/

用户名:admin 密码:admin123

集成sharp-fileupload

  1. 创建 access key
  2. 创建 Bucket 默认访问 Access Policy 是 private,如果想通过 url 地址访问,需要修改成 public

application.yml

fileupload:  
  tmp: /Users/rick/Space/tmp/fastdfs/tmp # 下载的临时目录  
  local: # cd /Users/rick/Space/tmp/fileupload && http-server -p 7892  
    root-path: /Users/rick/Space/tmp/fileupload  
    server-url: http://localhost:7892/ # 映射到tmp目录  
  oss:  
    endpoint: http://localhost:9000  
    accessKeyId: sZVg9wFYsFYZZnF1n2mm  
    accessKeySecret: go4HPFTPjT1S4TmO3ySzkTOut6jsaQah5JvP3C9g  
    bucketName: test

Config.java

@Bean  
public InputStreamStore minioInputStreamStore(OSSProperties ossProperties) {  
    MinioClient minioClient =  
            MinioClient.builder()  
                    .endpoint(ossProperties.getEndpoint())  
                    .credentials(ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret())  
                    .build();  
    return new MinioInputStreamStore(minioClient, ossProperties);  
}

Test.java

@Autowired  
private InputStreamStore inputStreamStore;  

private static String path;  

@Test  
@Order(1)  
public void testPropertyStore() throws IOException {  
    StoreResponse response = inputStreamStore.store("hello", "jpeg",  
            new FileInputStream("/Users/rick/Space/tmp/fileupload/demo/1.jpg"));  
    System.out.println(response.getGroupName());  
    System.out.println(response.getPath());  
    System.out.println(response.getFullPath());  
    System.out.println(response.getUrl());  // Bucket 如果是 public 的,可以通过 url 访问
    path = response.getPath();  
}  

@Test  
@Order(2)  
public void getInputStream() throws IOException {  
    InputStream is = inputStreamStore.getInputStream("hello", path);  
    FileUtils.copyInputStreamToFile(is, new File("/Users/rick/Space/tmp/fileupload/download/minio-1.png"));  
    is.close();  
}  

@Test  
@Order(3)  
public void delete() throws IOException {  
    inputStreamStore.delete("hello", path);  
}

docusaurus 部署到 GitHub Pages 自定义域名

在 iCloud Drive 下创建目录 docusaurus, 命令行进入 docusaurus

npm init docusaurus@latest

What should we name this site? docs-test
…默认选择向下执行

托管 github

新建仓库 docs-test

git init
git add .
git commit -m 'project int'
git remote add origin https://github.com/jkxyx205/docs-test.git
git push -u origin main
  • 开启 github pages
    docs-test/Settings/Pages/, Source 设置 Github Actions
  • 创建部署文件.github/workflows/deploy.yml
    提示词:「docusaurus 自动部署到 GitHub Pages, 写一下部署的yml」,下面的配置是通过 Claude 3.5 sonnet 生成的
name: Deploy Docusaurus to GitHub Pages

on:
  push:
    branches: ["main"]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Build website
        run: npm run build

      - name: Setup Pages
        uses: actions/configure-pages@v4

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: build

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

提交 push ,如果成功部署可以访问: https://jkxyx205.github.io/docs-test/

绑定域名

  • 添加域名解析docs-test CNAME jkxyx205.github.io
  • docs-test/Settings/Pages/Custom domain 填写域名 docs-test.xxx.com 并保存
  • Enforce HTTPS 勾选,http 会自动跳转到 https

SpringBoot application.yml 配置参数,不上传到GitHub

需求:

application.yml 配置的数据库信息采用变量的形式,这样提交到GitHub的时候就不会泄露敏感信息

  • 项目目录下添加.env配置信息
URL=jdbc:postgresql://localhost:5432/postgres
USERNAME=root
PASSWORD=123456
  • application.yml导入 .env
spring:
    config:
        import:
        - optional:file:.env[.properties]
  • application.yml 使用变量
datasource:
    url: ${URL}
    username: ${USERNAME}
    password: ${PASSWORD}
  • java 代码使用变量
@Value("${USERNAME}")
private String username;

@Value("${spring.datasource.username}")
private String username2;

@Value("${PASSWORD}")
private String password;
  • .gitignore 忽略 .env
.env*

SpringBoot 集成 sharp-database

添加依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.9</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.24</version>
    </dependency>

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
    </dependency>

    <dependency>
        <groupId>com.rick.db</groupId>
        <artifactId>sharp-database</artifactId>
        <version>2.0-SNAPSHOT</version>
    </dependency>
</dependencies>

数据库配置文件:

server:
  port: 8080
spring:
  datasource:
    name: druidDataSource
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
      username: root
      password: root
      initialSize: 1
      minIdle: 5
      maxActive: 10
      maxWait: 60000

sharp:
  database: # 多个包扫描用 , 隔开
    entity-base-package: com.rick.sharp.**.entity

测试

@SpringBootApplication
@DependsOn("entityDAOSupport")
@EnableScheduling
public class DemoApplication {

    @Autowired
    private EntityDAO<User, Long> userDAO;

    @Autowired
    private EntityDAO<CodeDescription, Long> codeDescriptionDAO;


    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);

        String ss = JsonUtils.toJson("ss");
        System.out.println(ss);
        System.out.println("running.....");
    }

    @Scheduled(fixedRate = 5000)
    public void find() {
        User user = userDAO.selectById(1L).get();
        System.out.println(user.getName());

        codeDescriptionDAO.insert(CodeDescription.builder()
                        .code("test-" + System.currentTimeMillis())
                        .description("hello")
                .category(CodeDescription.CategoryEnum.MATERIAL)
                .build());
    }

}