首页 > 其他分享 >开源模型应用落地-从源代码构建和运行vLLM-以满足您更高的需求

开源模型应用落地-从源代码构建和运行vLLM-以满足您更高的需求

时间:2024-10-11 15:18:25浏览次数:11  
标签:engine None 源代码 -- py vLLM 开源 usr vllm

一、前言

    通过vLLM源码构建Docker镜像有诸多显著的好处。首先,源码构建能够确保我们使用的是最新的功能,避免版本不一致带来的问题。其次,自定义构建过程使能够根据特定需求优化镜像,比如去除不必要的依赖或者添加自定义配置,从而提高镜像的性能和安全性。此外,Docker容器化的特性使得部署更加灵活,简化了环境隔离与管理,开发人员可以在任何支持Docker的平台上快速复现相同的运行环境,确保开发和生产的一致性。最后,借助Docker的版本控制和可复制性,可以更轻松地追踪和回滚到先前的镜像版本,这在进行调试和测试时尤为重要。


二、术语

2.1. Docker

    是一个开源的容器化平台,允许开发者将应用及其依赖打包成轻量级、可移植的容器。这些容器可以在任何支持 Docker 的环境中运行,从而确保应用在不同环境中的一致性。Docker 提供了简化的开发、测试和部署流程,使得应用的交付更加高效和灵活,同时也支持微服务架构的实现。通过隔离和资源管理,Docker 使得应用的扩展和维护变得更加便捷。

2.2. hub.docker.com

    是 Docker 官方的容器镜像库和共享平台,用户可以在此存储、分享和管理 Docker 镜像。它提供了一个简单易用的界面,开发者可以通过该平台上传自己的镜像,或从中拉取公共镜像以用于项目开发。

2.3. vLLM

    vLLM是一个开源的大模型推理加速框架,通过PagedAttention高效地管理attention中缓存的张量,实现了比HuggingFace Transformers高14-24倍的吞吐量。


三、前提条件 

3.1. 基础环境及前置条件

 1)操作系统:centos7

 2)Tesla V100-SXM2-32GB  CUDA Version: 12.2

3.2. Docker安装

1. 更新系统,确保你的系统是最新的:

sudo yum update -y

执行结果:
    Last metadata expiration check: 2:32:22 ago on Sun 06 Oct 2024 09:48:43 AM CST.
    Dependencies resolved.
    Nothing to do.
    Complete!

2. 安装必要的依赖:

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

执行结果:
    Installed:
      device-mapper-event-8:1.02.177-10.el8.x86_64  device-mapper-event-libs-8:1.02.177-10.el8.x86_64  device-mapper-persistent-data-0.9.0-4.el8.x86_64  libaio-0.3.112-1.el8.x86_64  lvm2-8:2.03.12-10.el8.x86_64
      lvm2-libs-8:2.03.12-10.el8.x86_64            

    Complete!
    
3. 设置Docker仓库:

sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

执行结果:   

     Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
    
4. 安装 Docker:

sudo yum install -y docker-ce docker-ce-cli containerd.io

5. 启动Docker服务,并设置为开机自启:

sudo systemctl start docker
sudo systemctl enable docker

6. 验证 Docker 安装:

sudo docker run hello-world

   如果看到 "Hello from Docker!" 的消息,说明 Docker 安装成功。

7. (可选)将用户添加到Docker组:
   如果你希望以非 root 用户身份运行 Docker 命令,可以将用户添加到 Docker 组:

sudo usermod -aG docker $USER

   添加后,注销并重新登录,或者运行 `newgrp docker` 使更改生效。

3.3. 下载vLLM源码

Tags · vllm-project/vllm · GitHubA high-throughput and memory-efficient inference and serving engine for LLMs - Tags · vllm-project/vllmicon-default.png?t=O83Ahttps://github.com/vllm-project/vllm/tags

(base) [root@gpu tmp]# git clone --branch v0.6.2 --single-branch https://github.com/vllm-project/vllm.git
Cloning into 'vllm'...
remote: Enumerating objects: 28985, done.
remote: Total 28985 (delta 0), reused 0 (delta 0), pack-reused 28985 (from 1)
Receiving objects: 100% (28985/28985), 17.32 MiB | 6.84 MiB/s, done.
Resolving deltas: 100% (21960/21960), done.
Note: switching to '7193774b1ff8603ad5bf4598e5efba0d9a39b436'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

四、技术实现

4.1. 需求

背景:使用目前最新vLLM开源镜像,无法正常运行Qwen2-VL-7B-Instruct模型,错误描述如下:

(base) [root@gpu ~]# docker run --runtime nvidia --gpus all \
>     -p 9000:9000 \
>     --ipc=host \
> -v /data/model/qwen2-vl-7b-instruct:/qwen2-vl-7b-instruct \
> -it --rm \
>     vllm/vllm-openai:latest \
>     --model /qwen2-vl-7b-instruct --dtype float16 --max-parallel-loading-workers 1  --max-model-len 8192 --enforce-eager --host 0.0.0.0 --port 9000
INFO 10-09 22:34:49 api_server.py:526] vLLM API server version 0.6.1.dev238+ge2c6e0a82
INFO 10-09 22:34:49 api_server.py:527] args: Namespace(host='0.0.0.0', port=9000, uvicorn_log_level='info', allow_credentials=False, allowed_origins=['*'], allowed_methods=['*'], allowed_headers=['*'], api_key=None, lora_modules=None, prompt_adapters=None, chat_template=None, response_role='assistant', ssl_keyfile=None, ssl_certfile=None, ssl_ca_certs=None, ssl_cert_reqs=0, root_path=None, middleware=[], return_tokens_as_token_ids=False, disable_frontend_multiprocessing=False, enable_auto_tool_choice=False, tool_call_parser=None, model='/qwen2-vl-7b-instruct', tokenizer=None, skip_tokenizer_init=False, revision=None, code_revision=None, tokenizer_revision=None, tokenizer_mode='auto', trust_remote_code=False, download_dir=None, load_format='auto', config_format='auto', dtype='float16', kv_cache_dtype='auto', quantization_param_path=None, max_model_len=8192, guided_decoding_backend='outlines', distributed_executor_backend=None, worker_use_ray=False, pipeline_parallel_size=1, tensor_parallel_size=1, max_parallel_loading_workers=1, ray_workers_use_nsight=False, block_size=16, enable_prefix_caching=False, disable_sliding_window=False, use_v2_block_manager=False, num_lookahead_slots=0, seed=0, swap_space=4, cpu_offload_gb=0, gpu_memory_utilization=0.9, num_gpu_blocks_override=None, max_num_batched_tokens=None, max_num_seqs=256, max_logprobs=20, disable_log_stats=False, quantization=None, rope_scaling=None, rope_theta=None, enforce_eager=True, max_context_len_to_capture=None, max_seq_len_to_capture=8192, disable_custom_all_reduce=False, tokenizer_pool_size=0, tokenizer_pool_type='ray', tokenizer_pool_extra_config=None, limit_mm_per_prompt=None, mm_processor_kwargs=None, enable_lora=False, max_loras=1, max_lora_rank=16, lora_extra_vocab_size=256, lora_dtype='auto', long_lora_scaling_factors=None, max_cpu_loras=None, fully_sharded_loras=False, enable_prompt_adapter=False, max_prompt_adapters=1, max_prompt_adapter_token=0, device='auto', num_scheduler_steps=1, multi_step_stream_outputs=False, scheduler_delay_factor=0.0, enable_chunked_prefill=None, speculative_model=None, speculative_model_quantization=None, num_speculative_tokens=None, speculative_draft_tensor_parallel_size=None, speculative_max_model_len=None, speculative_disable_by_batch_size=None, ngram_prompt_lookup_max=None, ngram_prompt_lookup_min=None, spec_decoding_acceptance_method='rejection_sampler', typical_acceptance_sampler_posterior_threshold=None, typical_acceptance_sampler_posterior_alpha=None, disable_logprobs_during_spec_decoding=None, model_loader_extra_config=None, ignore_patterns=[], preemption_mode=None, served_model_name=None, qlora_adapter_name_or_path=None, otlp_traces_endpoint=None, collect_detailed_traces=None, disable_async_output_proc=False, override_neuron_config=None, disable_log_requests=False, max_log_len=None, disable_fastapi_docs=False)
INFO 10-09 22:34:49 api_server.py:164] Multiprocessing frontend to use ipc:///tmp/11e808d4-b43b-40e9-9201-01436c4f6469 for IPC Path.
INFO 10-09 22:34:49 api_server.py:177] Started engine process with PID 22
Unrecognized keys in `rope_scaling` for 'rope_type'='default': {'mrope_section'}
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/usr/local/lib/python3.12/dist-packages/vllm/entrypoints/openai/api_server.py", line 571, in <module>
    uvloop.run(run_server(args))
  File "/usr/local/lib/python3.12/dist-packages/uvloop/__init__.py", line 109, in run
    return __asyncio.run(
           ^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "uvloop/loop.pyx", line 1517, in uvloop.loop.Loop.run_until_complete
  File "/usr/local/lib/python3.12/dist-packages/uvloop/__init__.py", line 61, in wrapper
    return await main
           ^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/entrypoints/openai/api_server.py", line 538, in run_server
    async with build_async_engine_client(args) as engine_client:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/contextlib.py", line 210, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/entrypoints/openai/api_server.py", line 105, in build_async_engine_client
    async with build_async_engine_client_from_engine_args(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/contextlib.py", line 210, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/entrypoints/openai/api_server.py", line 182, in build_async_engine_client_from_engine_args
    engine_config = engine_args.create_engine_config()
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/engine/arg_utils.py", line 874, in create_engine_config
    model_config = self.create_model_config()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/engine/arg_utils.py", line 811, in create_model_config
    return ModelConfig(
           ^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/config.py", line 207, in __init__
    self.max_model_len = _get_and_verify_max_len(
                         ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/config.py", line 1746, in _get_and_verify_max_len
    assert "factor" in rope_scaling
           ^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
Unrecognized keys in `rope_scaling` for 'rope_type'='default': {'mrope_section'}
Process SpawnProcess-1:
Traceback (most recent call last):
  File "/usr/lib/python3.12/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/usr/lib/python3.12/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.12/dist-packages/vllm/engine/multiprocessing/engine.py", line 388, in run_mp_engine
    engine = MQLLMEngine.from_engine_args(engine_args=engine_args,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/engine/multiprocessing/engine.py", line 134, in from_engine_args
    engine_config = engine_args.create_engine_config()
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/engine/arg_utils.py", line 874, in create_engine_config
    model_config = self.create_model_config()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/engine/arg_utils.py", line 811, in create_model_config
    return ModelConfig(
           ^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/config.py", line 207, in __init__
    self.max_model_len = _get_and_verify_max_len(
                         ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/vllm/config.py", line 1746, in _get_and_verify_max_len
    assert "factor" in rope_scaling
           ^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

目标:构建自定义vLLM镜像,可以通过docker容器快速体验Qwen2-VL-7B-Instruct效果

4.2. 添加自定义需求

4.2.1.修改transformers版本

默认vLLM源码中,指定安装的transformers版本为≥4.45.0,为了后续安装Qwen2-VL-7B-Instruct,需要将transformers指定为特定版本,示例如下:

修改requirements-common.txt文件

删掉transformers依赖:

修改Dockerfile文件,指定transformers通过源码安装

RUN --mount=type=cache,target=/root/.cache/pip python3 -m pip install git+https://github.com/huggingface/transformers@21fac7abba2a37fae86106f87fcf9974fd1e3830

4.2.2.增加qwen-vl-utils依赖

修改requirements-common.txt文件,添加qwen-vl-utils依赖

4.2.3.问题修复

问题一:ModuleNotFoundError: No module named 'distutils'

具体描述:

832.4     from pip._internal.cli.main import main as _main  # isort:skip # noqa
832.4     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
832.4   File "/usr/lib/python3/dist-packages/pip/_internal/cli/main.py", line 10, in <module>
832.4     from pip._internal.cli.autocompletion import autocomplete
832.4   File "/usr/lib/python3/dist-packages/pip/_internal/cli/autocompletion.py", line 9, in <module>
832.4     from pip._internal.cli.main_parser import create_main_parser
832.4   File "/usr/lib/python3/dist-packages/pip/_internal/cli/main_parser.py", line 7, in <module>
832.4     from pip._internal.cli import cmdoptions
832.4   File "/usr/lib/python3/dist-packages/pip/_internal/cli/cmdoptions.py", line 19, in <module>
832.4     from distutils.util import strtobool
832.4 ModuleNotFoundError: No module named 'distutils'
------
Dockerfile:148
--------------------
 147 |     # Install Python and other dependencies
 148 | >>> RUN echo 'tzdata tzdata/Areas select America' | debconf-set-selections \
 149 | >>>     && echo 'tzdata tzdata/Zones/America select Los_Angeles' | debconf-set-selections \
 150 | >>>     && apt-get update -y \
 151 | >>>     && apt-get install -y ccache software-properties-common git curl sudo vim python3-pip \
 152 | >>>     && apt-get install -y ffmpeg libsm6 libxext6 libgl1 \
 153 | >>>     && add-apt-repository ppa:deadsnakes/ppa \
 154 | >>>     && apt-get update -y \
 155 | >>>     && apt-get install -y python${PYTHON_VERSION} python${PYTHON_VERSION}-dev python${PYTHON_VERSION}-venv libibverbs-dev \
 156 | >>>     && update-alternatives --install /usr/bin/python3 python3 /usr/bin/python${PYTHON_VERSION} 1 \
 157 | >>>     && update-alternatives --set python3 /usr/bin/python${PYTHON_VERSION} \
 158 | >>>     && ln -sf /usr/bin/python${PYTHON_VERSION}-config /usr/bin/python3-config \
 159 | >>>     && curl -sS https://bootstrap.pypa.io/get-pip.py | python${PYTHON_VERSION} \
 160 | >>>     && python3 --version && python3 -m pip --version
 161 |     
--------------------
ERROR: failed to solve: process "/bin/sh -c echo 'tzdata tzdata/Areas select America' | debconf-set-selections     && echo 'tzdata tzdata/Zones/America select Los_Angeles' | debconf-set-selections     && apt-get update -y     && apt-get install -y ccache software-properties-common git curl sudo vim python3-pip     && apt-get install -y ffmpeg libsm6 libxext6 libgl1     && add-apt-repository ppa:deadsnakes/ppa     && apt-get update -y     && apt-get install -y python${PYTHON_VERSION} python${PYTHON_VERSION}-dev python${PYTHON_VERSION}-venv libibverbs-dev     && update-alternatives --install /usr/bin/python3 python3 /usr/bin/python${PYTHON_VERSION} 1     && update-alternatives --set python3 /usr/bin/python${PYTHON_VERSION}     && ln -sf /usr/bin/python${PYTHON_VERSION}-config /usr/bin/python3-config     && curl -sS https://bootstrap.pypa.io/get-pip.py | python${PYTHON_VERSION}     && python3 --version && python3 -m pip --version" did not complete successfully: exit code: 1

修改Dockerfile文件,增加python3-distutils依赖

4.3. 构建镜像

DOCKER_BUILDKIT=1 docker build . --target vllm-openai --tag vllm/vllm-qwen2-vl --build-arg max_jobs=8 --build-arg nvcc_threads=2

参数解释:

1. `DOCKER_BUILDKIT=1`:
   - 这个环境变量设置了 Docker BuildKit,为 Docker 构建过程启用 BuildKit。一旦启用,BuildKit 可以提供更快的构建速度、更好的缓存机制和更强的功能。

2. `docker build`:
   - 这是 Docker 命令,用于构建 Docker 镜像。

3. `.`:
   - 这个点表示当前目录,Docker 会在当前目录中查找 Dockerfile 和上下文文件。上下文文件包括 Dockerfile 所需的其他文件和目录。

4. `--target vllm-openai`:
   - 指定了构建的目标(stage)。在多阶段构建中,可能会有多个构建阶段。通过这个参数,Docker 会只构建名为 `vllm-openai` 的阶段。

5. `--tag vllm/vllm-qwen2-vl`:
   - 指定了构建完成的镜像的名称和标签。格式是 `repository:tag`,在这里 `vllm` 是命名空间(或仓库名),`vllm-qwen2-vl` 是镜像名称。可以用于推送到 Docker Hub 或其他镜像仓库。

6. `--build-arg max_jobs=8`:
   - 这个参数用于设置构建时的变量 `max_jobs`,值为 `8`。在 Dockerfile 中可以通过 `ARG max_jobs` 来访问这个变量。通常用于定制构建过程,例如并行构建的作业数。

7. `--build-arg nvcc_threads=2`:
   - 这个参数类似于前一个,用于设置另一个构建时变量 `nvcc_threads`,值为 `2`。这可能用于指定与 NVIDIA CUDA 编译相关的线程数量。

漫长的构建过程:


五、附带说明

5.1.清理Docker临时文件

在 Docker 中,如果构建失败,可能会留下临时文件和中间层,这些文件可能会占用磁盘空间。

1. 清理未使用的构建缓存
docker builder prune

使用 `-f` 选项来跳过确认提示:
docker builder prune -f


2. 全面地清理 Docker,包括未使用的容器、网络、镜像和构建缓存
docker system prune

使用 `-f` 选项来跳过确认提示:
docker system prune -f


要删除所有未使用的镜像(包括未被任何容器使用的镜像),可以加上 `--all` 选项:
docker system prune -a -f


3. 检查磁盘使用情况,包括镜像、容器、数据卷和构建缓存的详细信息。
docker system df

标签:engine,None,源代码,--,py,vLLM,开源,usr,vllm
From: https://blog.csdn.net/qq839019311/article/details/142820217

相关文章

  • 【JAVA开源】基于Vue和SpringBoot卫生健康系统
    本文项目编号T076,文末自助获取源码\color{red}{T076,文末自助获取源码}......
  • 23k star,推荐一款超强的开源工具--trivy
    今天一件一款开源的镜像扫描工具,下面是扫描某个python镜像的示例:Trivy:全面的安全扫描工具软件简介Trivy是由AquaSecurity开发的开源安全扫描工具,旨在帮助开发者和运维人员识别和修复各种环境中的安全问题。Trivy的名字来源于“Trigger”和“Envy”的组合,寓意其强大的......
  • 重磅推荐 40.2K star,一个开源免费的低代码开发平台,功能太强!
    项目介绍JeecgBoot是一款企业级的低代码平台!前后端分离架构SpringBoot2.x,SpringCloud,AntDesign&Vue3,Mybatis-plus,Shiro,JWT支持微服务。强大的代码生成器让前后端代码一键生成!JeecgBoot引领低代码开发模式(OnlineCoding->代码生成->手工MERGE),帮助解决Java项目70%......
  • android开发编译openssl源代码生成libcrypto.so和libssl.so两个动态库用于android ndk
    openssl编译本篇文章的操作是在Linux环境之下,在虚拟机ubuntu20版本上操作的步骤1.openssl下载解压tar包openssl下载地址:https://openssl-library.org/source/下载完解压:tar-zxvfopenssl-3.3.2.tar.gz//我这里下载openssl-3.3.2.tar.gz版本2.编译openssl库,得......
  • 开源项目更新|WPF/Uno Platform/WinUI 3三个版本的《英雄联盟客户端》
    ​哈喽大家好!我们是中韩MicrosoftMVP夫妇Vicky&James^^很高兴能加入博客园和大家分享我们的技术!自2008年以来,我们一直深耕于WPF技术,积累了丰富的经验。这些年来,随着Xamarin、MAUI、Uno-Platform、AvaloniaUI和OpenSilver等跨平台技术的不断发展,我们也将在WPF中积累的技能成功......
  • OOOPS:零样本实现360度开放全景分割,已开源 | ECCV'24
    全景图像捕捉360°的视场(FoV),包含了对场景理解至关重要的全向空间信息。然而,获取足够的训练用密集标注全景图不仅成本高昂,而且在封闭词汇设置下训练模型时也受到应用限制。为了解决这个问题,论文定义了一个新任务,称为开放全景分割(OpenPanoramicSegmentation,OPS)。在该任务中,模型在......
  • OCR+PDF解析配套前端工具开源详解!
    面对日常生活和工作中常见的OCR识别、PDF解析、翻译、校对等场景,配套的可视化工具能够极大地提升我们的使用体验和工作效率。通过可视化界面,我们可以直观地看到文本识别、解析和翻译的结果,便捷评估产品效果。今天来跟大家分享一个非常棒的开源项目——TextInParseX-Frontend,帮......
  • 【MATLAB代码】基于RSSI的蓝牙定位程序,N个锚点、二维平面(源代码,可直接复制)
    文章目录介绍主要功能技术细节适用场景:源代码运行结果结语介绍这款基于接收信号强度指示(RSSIRSSIRSSI)原理的蓝牙定位程序专为二维平面定位设计,通过N个蓝牙锚点实现对未知位置的精准定位。程序利用信号强度衰减模型,模拟测量误差&#x......
  • JAVA 毕设 环保小程序 开源
    项目介绍项目是帮别人设计的毕设项目,主要分为前台和后台,前台是微信小程序,后台是通过若依框架快速开发的管理平台,项目主要分为题库、活动、社区、视频、基础信息、会员等几大模块。开发技术:前端:Vue2.0+ElementUi+微信小程序开发语言后端:SpringBoot+Redis+MySQL其他框架:......
  • OpenSource - License 开源项目 TrueLicense
    文章目录官网集成Demo官网https://truelicense.namespace.global/https://github.com/christian-schlichtherle/truelicense集成Demohttps://github.com/christian-schlichtherle/truelicense-maven-archetypehttps://github.com/zifangsky/LicenseDemohttp......