
[{"content":"除了所有默认 Hugo 简码 之外，Blowfish 还添加了一些额外的功能。\n参数 功能 icon 可选 显示在左侧的图标。\n默认： exclaimation triangle icon (查看图标简码，了解有关使用图标的更多详细信息。) iconColor 可选 基本 CSS 样式中图标的颜色。\n可以是十六进制值 (#FFFFFF) 或颜色名称 (white)\n默认情况下由当前配色方案决定。 cardColor 可选 基本 CSS 样式中卡片背景的颜色。\n可以是十六进制值 (#FFFFFF) 或颜色名称 (white)\n默认情况下由当前配色方案决定。 textColor 可选 基本 CSS 样式中文本的颜色。\n可以是十六进制值 (#FFFFFF) 或颜色名称 (white)\n默认情况下由当前配色方案决定。 输入内容是用 Markdown 语言编写的，因此您可以根据需要设置其格式。\n例1: 无参数\n{{\u0026lt; alert \u0026gt;}} **警告！**此操作具有破坏性！ {{\u0026lt; /alert \u0026gt;}} **警告！**此操作具有破坏性！ 例2: 未命名参数\n{{\u0026lt; alert \u0026#34;twitter\u0026#34; \u0026gt;}} Don\u0026#39;t forget to [follow me](https://twitter.com/nunocoracao) on Twitter. {{\u0026lt; /alert \u0026gt;}} Don\u0026rsquo;t forget to follow me on Twitter. 例3: 命名参数\n{{\u0026lt; alert icon=\u0026#34;fire\u0026#34; cardColor=\u0026#34;#e63946\u0026#34; iconColor=\u0026#34;#1d3557\u0026#34; textColor=\u0026#34;#f1faee\u0026#34; \u0026gt;}} This is an error! {{\u0026lt; /alert \u0026gt;}} This is an error! Admonition # Admonition 用于在内容中插入醒目提示。\nAdmonition 的用途与 alert shortcode 类似，但其实现方式是通过 Hugo 的 render hooks。两者的关键区别在于语法：admonition 使用 Markdown 语法，因此在不同平台之间具有更好的可移植性；而 shortcode 是 Hugo 专有的。其语法类似 GitHub 的 alerts：\n\u0026gt; [!NOTE] \u0026gt; 一个 NOTE 类型的提示块。 \u0026gt; [!TIP]+ 自定义标题 \u0026gt; 一个带有自定义标题的可折叠提示块。 笔记 一个 NOTE 类型的提示块。\n自定义标题 一个带有自定义标题的可折叠提示块。\n提示符号（+ 或 -）是可选的，用于控制提示块是否默认折叠。请注意，该提示符号仅在 Obsidian 中兼容。\n支持的类型 可用的 admonition 类型包括 GitHub alert 类型 和 Obsidian callout 类型。类型名称不区分大小写。\nGitHub 类型： NOTE, TIP, IMPORTANT, WARNING, CAUTION\nObsidian 类型： note, abstract, info, todo, tip, success, question, warning, failure, danger, bug, example, quote\nArticle # Article 将把一篇文章嵌入到一个 markdown 文件中。 参数中的 link应该是要嵌入的文件的 .RelPermalink。请注意，如果简码引用其父级文件，则它不会显示任何内容。 注意：如果您在 Blowfish（即 /blowfish/）等子文件夹中运行网站，请在链接中包含该路径。\n参数 功能 link 必填 要嵌入文章的 .RelPermalink showSummary 可选 布尔值，指示是否显示文章摘要。如果未设置，将使用站点的默认配置。 compactSummary 可选 布尔值，指示是否以紧凑模式显示摘要。默认为 false。 例如：\n{{\u0026lt; article link=\u0026#34;/zh-cn/docs/welcome/\u0026#34; showSummary=true compactSummary=true \u0026gt;}} Badge # badge 输出一个美观的徽章组件，该组件对于显示元数据很有用。\n例如：\n{{\u0026lt; badge \u0026gt;}} New article! {{\u0026lt; /badge \u0026gt;}} New article! Button # button 输出一个样式化的按钮组件，可用于突出显示主要操作。它有三个可选变量 href、target 和 rel，可用于指定链接的 URL、目标和关系。\n例如：\n{{\u0026lt; button href=\u0026#34;#button\u0026#34; target=\u0026#34;_self\u0026#34; \u0026gt;}} Call to action {{\u0026lt; /button \u0026gt;}} Call to action Carousel # carousel 用于生成可交互且具有视觉吸引力的方式展示多个图像的画廊。这允许用户滑动浏览多个图像，同时仅占用单个图像的垂直空间。 所有图像均使用父组件的完整宽度并使用预定义的宽高比 16:9 、 21:9 或 32:9 之一显示。\n参数 功能 images 必填 用于匹配图像名称的正则表达式或 URL。 aspectRatio 可选 画廊的纵横比。16-9 、21-9 或32-9 。默认设置为16-9 。 interval 可选 自动滚动的时间间隔，以毫秒为单位指定。默认为2000（2 秒）。 例1: 16:9 宽高比和 URL 图像列表\n{{\u0026lt; carousel images=\u0026#34;{https://cdn.pixabay.com/photo/2016/12/11/12/02/mountains-1899264_960_720.jpg, gallery/03.jpg, gallery/01.jpg, gallery/02.jpg, gallery/04.jpg}\u0026#34; \u0026gt;}} Previous Next 例2: 21:9 宽高比和正则表达式图像列表\n{{\u0026lt; carousel images=\u0026#34;gallery/*\u0026#34; aspectRatio=\u0026#34;21-9\u0026#34; interval=\u0026#34;2500\u0026#34; \u0026gt;}} Previous Next Chart # chart 使用 Chart.js 库将图表嵌入到使用简单结构化数据的文章中。它支持多种不同的图表样式，并且所有内容都可以在简码中进行配置。只需在简码中提供图表参数，Chart.js 将完成剩下的工作。\n有关语法和支持的图表类型的详细信息，请参阅 Chart.js 官方文档。\n例如：\n{{\u0026lt; chart \u0026gt;}} type: \u0026#39;bar\u0026#39;, data: { labels: [\u0026#39;Tomato\u0026#39;, \u0026#39;Blueberry\u0026#39;, \u0026#39;Banana\u0026#39;, \u0026#39;Lime\u0026#39;, \u0026#39;Orange\u0026#39;], datasets: [{ label: \u0026#39;# of votes\u0026#39;, data: [12, 19, 3, 5, 3], }] } {{\u0026lt; /chart \u0026gt;}} 您可以在页面上查看一些更多 Chart.js 示例。\nCode Importer # 此短代码用于轻松从外部源导入代码，无需复制和粘贴\nParameter Description url 必需的 外部托管代码文件的 URL. type 用于语法突出显示的代码类型. startLine 可选 从代码文件中导入的起始行. endLine 可选 从代码文件中导入的结束行. Example:\n{{\u0026lt; codeimporter url=\u0026#34;https://raw.githubusercontent.com/nunocoracao/blowfish/main/layouts/shortcodes/mdimporter.html\u0026#34; type=\u0026#34;go\u0026#34; \u0026gt;}} {{\u0026lt; codeimporter url=\u0026#34;https://raw.githubusercontent.com/nunocoracao/blowfish/main/config/_default/hugo.toml\u0026#34; type=\u0026#34;toml\u0026#34; startLine=\u0026#34;11\u0026#34; endLine=\u0026#34;18\u0026#34; \u0026gt;}} Codeberg Card # codeberg 允许您通过 Codeberg API 快速链接 Codeberg 存储库，提供星号和分叉等统计信息的实时更新.\nParameter Description repo [String] Codeberg 存储库的格式为 username/repo Example 1:\n{{\u0026lt; codeberg repo=\u0026#34;forgejo/forgejo\u0026#34; \u0026gt;}} \u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e forgejo/forgejo Beyond coding. We forge. Go 4854 834 Figure # Blowfish 包含一个 figure 简码，用于将图像添加到内容中。该简码取代了基本的 Hugo 功能，且性能更好。\n当提供的图像是页面资源时，将使用 Hugo Pipes 对其进行优化并缩放，以提供适合不同设备分辨率的图像。如果提供了静态资产或外部图像的 URL，它将按原样包含在内，而无需 Hugo 进行任何图像处理。\nfigure 简码接受六个参数：\n参数 功能 src 必填 图像的本地路径/文件名或 URL。当提供路径和文件名时，主题将尝试使用以下查找顺序来查找图像：首先，作为与页面绑定的页面资源；然后是 assets/ 目录中的文件；最后是，static/目录中的文件。 alt 图像的替代文本描述。 caption Markdown 格式的图像标题，将显示在图像下方。 class 应用于图像的其他 CSS 类。 href 图像应链接到的 URL。 target href URL 的目标属性。 nozoom nozoom=true 会禁用图像缩放功能。与 href 结合使用十分有用。 default 用于恢复默认 Hugo figure 行为的特殊参数。只需提供default=true，然后使用正常的 Hugo 简码语法。 Blowfish 还支持使用标准 Markdown 语法自动转换图像。只需使用以下格式，主题将自动处理：\n![Alt text](image.jpg \u0026#34;Image caption\u0026#34;) Forgejo Card # forgejo allows you to quickly link a Forgejo repository via the forgejo API, providing real-time updates on stats such as stars and forks.\nParameter Description repo [String] forgejo repo in the format of username/repo server [String] server URL like https://v11.next.forgejo.org Example 1:\n{{\u0026lt; forgejo server=\u0026#34;https://v11.next.forgejo.org\u0026#34; repo=\u0026#34;a/mastodon\u0026#34; \u0026gt;}} 例如：\n{{\u0026lt; figure src=\u0026#34;abstract.jpg\u0026#34; alt=\u0026#34;Abstract purple artwork\u0026#34; caption=\u0026#34;Photo by [Jr Korpa](https://unsplash.com/@jrkorpa) on [Unsplash](https://unsplash.com/)\u0026#34; \u0026gt;}} \u0026lt;!-- OR --\u0026gt; ![Abstract purple artwork](abstract.jpg \u0026#34;Photo by [Jr Korpa](https://unsplash.com/@jrkorpa) on [Unsplash](https://unsplash.com/)\u0026#34;) Photo by Jr Korpa on Unsplash Gallery # gallery 允许您以响应式一次展示多个图像，并具有更加多样化和有趣的布局的图库。\n为了将图像添加到图库中，请为每个图像使用img标签并添加class =\u0026quot;grid-wXX\u0026quot;，以便图库能够识别每个图像的列宽。默认情况下可用的宽度从 10% 开始，以 5% 的增量一直达到 100%。例如，要将宽度设置为 65%，请将类设置为grid-w65。此外，还可以使用 33% 和 66% 的宽度来构建 3 列的画廊。您还可以利用 Tailwind 的响应指示器来构建响应网格。\n例1: 普通图库\n{{\u0026lt; gallery \u0026gt;}} \u0026lt;img src=\u0026#34;gallery/01.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/02.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/03.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/04.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/05.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/06.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/07.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; {{\u0026lt; /gallery \u0026gt;}} 例2: 响应式图库\n{{\u0026lt; gallery \u0026gt;}} \u0026lt;img src=\u0026#34;gallery/01.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33 xl:grid-w25\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/02.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33 xl:grid-w25\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/03.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33 xl:grid-w25\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/04.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33 xl:grid-w25\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/05.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33 xl:grid-w25\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/06.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33 xl:grid-w25\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/07.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33 xl:grid-w25\u0026#34; /\u0026gt; {{\u0026lt; /gallery \u0026gt;}} Gist # gist 短代码允许你通过指定 Gist 用户名、ID 以及可选的特定文件，直接将 GitHub Gist 嵌入到内容中。\n参数 描述 [0] [字符串] GitHub 用户名 [1] [字符串] Gist ID [2]（可选） [字符串] Gist 中要嵌入的文件名（可选） 示例 1：嵌入整个 Gist\n{{\u0026lt; gist \u0026#34;octocat\u0026#34; \u0026#34;6cad326836d38bd3a7ae\u0026#34; \u0026gt;}} 示例 2：嵌入 Gist 中的特定文件\n{{\u0026lt; gist \u0026#34;rauchg\u0026#34; \u0026#34;2052694\u0026#34; \u0026#34;README.md\u0026#34; \u0026gt;}} Gitea 卡片 # gitea 允许你通过 gitea API 快速链接一个 Gitea 仓库，提供诸如 stars 和 forks 等统计数据的实时更新。\n参数 描述 repo [字符串] 以 用户名/仓库名 格式表示的 gitea 仓库 server [字符串] 服务器 URL，如 https://git.fsfe.org 示例 1：\n{{\u0026lt; gitea server=\u0026#34;https://git.fsfe.org\u0026#34; repo=\u0026#34;FSFE/fsfe-website\u0026#34; \u0026gt;}} GitHub 卡片 # github 允许您快速链接到 github Repo，同时显示和更新有关它的实时统计信息，例如它的 star 和 fork 数。\n参数 功能 repo [字符串] 格式为 username/repo 的 github repo showThumbnail 可选 [布尔值] 是否显示缩略图，默认为 true 例1:\n{{\u0026lt; github repo=\u0026#34;nunocoracao/blowfish\u0026#34; \u0026gt;}} nunocoracao/blowfish Personal Website \u0026amp; Blog Theme for Hugo HTML 2798 728 GitLab 卡片 # gitlab 允许您快速链接 GitLab 项目（GitLab 的 Repo）。 显示有关的实时统计数据，例如它拥有的 star 和 fork 的数量。 与 github 不同，它无法显示项目的主要编程语言。 最后，只要 api/v4/projects/ 可用，就可以提供自定义 GitLab 实例 URL，从而使此简码能够显示大多数自托管/企业组织。\n参数 功能 projectID [String] gitlab 数字项目ID baseURL [String] 可选 gitlab 实例 URL，默认为 https://gitlab.com/ 例1:\n{{\u0026lt; gitlab projectID=\u0026#34;278964\u0026#34; \u0026gt;}} GitLab.org / GitLab GitLab is an open source end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more. Self-host GitLab on your own servers, in a container, or on a cloud provider. 6033 12156 Hugging Face 卡片 # huggingface 让您能够快速链接 Hugging Face 模型或数据集，显示实时信息如点赞数和下载量，以及类型和描述。\n参数 描述 model [字符串] 格式为 用户名/模型名 的 Hugging Face 模型 dataset [字符串] 格式为 用户名/数据集名 的 Hugging Face 数据集 注意： 使用 model 或 dataset 参数中的一个，不要同时使用。\n示例1（模型）：\n{{\u0026lt; huggingface model=\u0026#34;google-bert/bert-base-uncased\u0026#34; \u0026gt;}} {{}}\n示例2（数据集）：\n{{\u0026lt; huggingface dataset=\u0026#34;stanfordnlp/imdb\u0026#34; \u0026gt;}} stanfordnlp/imdb Large Movie Review Dataset. This is a dataset for binary sentiment classification containing substantially more data than previous benchmark datasets. We provide a set of 25,000 highly polar movie reviews for training, and 25,000 for testing. There is additional unlabeled data for use as well. Supported Tasks and Leaderboards More Information Needed Languages More Information Needed… See the full description on the dataset page: https://huggingface.co/datasets/stanfordnlp/imdb. dataset 379 268674 图标 # icon 输出一个 SVG 图标并以图标名称作为其唯一参数。图标会自动缩放以匹配当前文本大小。\n例如：\n{{\u0026lt; icon \u0026#34;github\u0026#34; \u0026gt;}} Output: 图标使用 Hugo Pipeline 填充，这使得它们非常灵活。 Blowfish 包含许多用于社交、链接和其他内置图标。参考 [图标示例]({{/\u0026lt; ref \u0026ldquo;samples/icons\u0026rdquo; \u0026gt;/}}) 页面以获取支持的图标的完整列表。\n可以通过在项目的 assets/icons/ 目录中提供您自己的图标来添加自定义图标。然后可以使用不带 .svg 扩展名的 SVG 文件名在简码中引用该图标。\n还可以通过调用 [iconpartial]({{/ref \u0026ldquo;partials#icon\u0026rdquo; \u0026gt;/ }}) 在 partials 中使用图标。\nKaTeX # katex 简码可用于使用 KaTeX 包向文章内容添加数学表达式。有关可用语法，请参阅支持的 TeX 函数 的在线参考。\n要在文章中加入数学表达式，只需将简码放在任意位置即可。每篇文章只需加入一次，KaTeX 将自动呈现该页面上的任何标记。支持内联和块表示法。\n可以通过将表达式包装在 \\( 和 \\) 分隔符中来生成内联表示法。或者，可以使用 $$ 分隔符生成块符号。\n例如：\n{{\u0026lt; katex \u0026gt;}} \\(f(a,b,c) = (a^2+b^2+c^2)^3\\) (f(a,b,c) = (a^2+b^2+c^2)^3)\n重点突出 # keyword 组件可用于在视觉上突出显示某些重要的单词或短语，例如专业技能等。 keywordList 简码可用于将多个 keyword 组合在一起。每个组件可以具有以下参数。\n参数 功能 icon 可选 关键字中使用的图标 输入内容是用 Markdown 编写的，因此您可以根据需要设置其格式。\n例1 :\n{{\u0026lt; keyword \u0026gt;}} Super skill {{\u0026lt; /keyword \u0026gt;}} Standalone skill 例2 :\n{{\u0026lt; keywordList \u0026gt;}} {{\u0026lt; keyword icon=\u0026#34;github\u0026#34; \u0026gt;}} Lorem ipsum dolor. {{\u0026lt; /keyword \u0026gt;}} {{\u0026lt; keyword icon=\u0026#34;code\u0026#34; \u0026gt;}} **Important** skill {{\u0026lt; /keyword \u0026gt;}} {{\u0026lt; /keywordList \u0026gt;}} {{\u0026lt; keyword \u0026gt;}} *Standalone* skill {{\u0026lt; /keyword \u0026gt;}} Lorem ipsum dolor Important skill Standalone skill Lead # lead 用于强调文章的开头。它可以用来设计介绍的样式，或者指出一条重要的信息。只需将任何 Markdown 内容包装在 lead 简码中即可。\n例如：\n{{\u0026lt; lead \u0026gt;}} When life gives you lemons, make lemonade. {{\u0026lt; /lead \u0026gt;}} When life gives you lemons, make lemonade. 列表 # List 将显示最近文章的列表。此简码需要一个限制值来约束列表。此外，它还支持输入 where 和 value ，以便按参数过滤文章。请注意，此简码不会显示其父页面，但会计入限制值。\n参数 功能 limit 必填 要显示的最近文章数量。 title 可选 列表标题，默认为 Recent cardView 可选 列表启用卡片视图，默认为 false where 用于筛选文章的变量，例如 Type value 需要与 where 中定义的参数匹配的值，以进行文章查询，例如对于 where == Type，可以找到文章 sample where 和 value 值用于简码中进行以下格式的查询 where .Site.RegularPages $where $value 。检查 Hugo 文档 以了解有关可用参数的更多信息。 例 1:\n{{\u0026lt; list limit=2 \u0026gt;}} 最近的文章 价格行为系列教程（00入门篇） 977 字\u0026middot;2 分钟 价格行为 投资理财 交易生涯 例 2:\n{{\u0026lt; list title=\u0026#34;Samples\u0026#34; cardView=true limit=6 where=\u0026#34;Type\u0026#34; value=\u0026#34;sample\u0026#34; \u0026gt;}} Samples 文字书写方向 # ltr 和 rtl 允许您混排内容。许多从左往右书写语言的用户希望在文章中包含部分从右往左的书写内容。使用此简码可以让您做到这一点，并利用 % 作为简码中最外层的标识符 Hugo Shortcodes，其中任何 markdown 内容都会正常渲染。\n例如：\n- This is an markdown list. - Its per default a LTR direction {{% rtl %}} - هذه القائمة باللغة العربية - من اليمين الى اليسار {{% /rtl %}} This is an markdown list. Its per default a LTR direction هذه القائمة باللغة العربية من اليمين الى اليسار Markdown 导入 # 此简码允许您从外部源导入 Markdown 文件。这对于包含来自其他仓库或网站的内容非常有用，而无需复制和粘贴内容。\n参数 功能 url 必填 外部托管 Markdown 文件的 URL。 例如：\n{{\u0026lt; mdimporter url=\u0026#34;https://raw.githubusercontent.com/nunocoracao/nunocoracao/master/README.md\u0026#34; \u0026gt;}} Mermaid # mermaid 允许您使用文本绘制可视化的图表。底层使用 Mermaid，并支持各种图表、图表和其他输出格式。\n只需在 mermaid 简码中编写您的 Mermaid 语法，然后让插件完成其余的工作。\n有关语法和支持的图表类型的详细信息，请参阅官方 Mermaid 文档。\n例如：\n{{\u0026lt; mermaid \u0026gt;}} graph LR; A[Lemons]--\u0026gt;B[Lemonade]; B--\u0026gt;C[Profit] {{\u0026lt; /mermaid \u0026gt;}} graph LR; A[Lemons]--\u003eB[Lemonade]; B--\u003eC[Profit] 色板 # swatches 输出一组最多三种不同的颜色来展示颜色元素的调色板。该简码采用每种颜色的 HEX 码并为每种颜色创建预览。\n例\n{{\u0026lt; swatches \u0026#34;#64748b\u0026#34; \u0026#34;#3b82f6\u0026#34; \u0026#34;#06b6d4\u0026#34; \u0026gt;}} 输出 Tabs # tabs 简码常用于呈现某个步骤的不同变体。例如，可用于展示在不同平台上安装 VS Code 的方式。\n示例\n{{\u0026lt; tabs \u0026gt;}} {{\u0026lt; tab label=\u0026#34;Windows\u0026#34; \u0026gt;}} 使用 Chocolatey 安装: ```pwsh choco install vscode.install ``` 或使用 WinGet 安装 ```pwsh winget install -e --id Microsoft.VisualStudioCode ``` {{\u0026lt; /tab \u0026gt;}} {{\u0026lt; tab label=\u0026#34;macOS\u0026#34; \u0026gt;}} ```bash brew install --cask visual-studio-code ``` {{\u0026lt; /tab \u0026gt;}} {{\u0026lt; tab label=\u0026#34;Linux\u0026#34; \u0026gt;}} 参见[文档](https://code.visualstudio.com/docs/setup/linux#_install-vs-code-on-linux)。 {{\u0026lt; /tab \u0026gt;}} {{\u0026lt; /tabs \u0026gt;}} 输出\nWindows macOS Linux 使用 Chocolatey 安装:\nchoco install vscode.install 或使用 WinGet 安装\nwinget install -e --id Microsoft.VisualStudioCode brew install --cask visual-studio-code 参见文档。 时间线 # timeline 创建了一个可视化时间线，用于展示专业经验、项目成就等。 timeline 简码依赖于 timelineItem 子简码来定义主时间线中的每个项目。每个项目可以具有以下属性。\n参数 功能 md 将内容渲染为 Markdown (true/false) icon 要在时间线中使用的图标 header 每个条目的标题 badge 放置在右上角徽章内的文本 subheader 每个条目的副标题 例如：\n{{\u0026lt; timeline \u0026gt;}} {{\u0026lt; timelineItem icon=\u0026#34;github\u0026#34; header=\u0026#34;header\u0026#34; badge=\u0026#34;badge test\u0026#34; subheader=\u0026#34;subheader\u0026#34; \u0026gt;}} Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus non magna ex. Donec sollicitudin ut lorem quis lobortis. Nam ac ipsum libero. Sed a ex eget ipsum tincidunt venenatis quis sed nisl. Pellentesque sed urna vel odio consequat tincidunt id ut purus. Nam sollicitudin est sed dui interdum rhoncus. {{\u0026lt; /timelineItem \u0026gt;}} {{\u0026lt; timelineItem icon=\u0026#34;code\u0026#34; header=\u0026#34;Another Awesome Header\u0026#34; badge=\u0026#34;date - present\u0026#34; subheader=\u0026#34;Awesome Subheader\u0026#34; \u0026gt;}} With html code \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;Coffee\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Tea\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;Milk\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; {{\u0026lt; /timelineItem \u0026gt;}} {{\u0026lt; timelineItem icon=\u0026#34;star\u0026#34; header=\u0026#34;Shortcodes\u0026#34; badge=\u0026#34;AWESOME\u0026#34; \u0026gt;}} With other shortcodes {{\u0026lt; gallery \u0026gt;}} \u0026lt;img src=\u0026#34;gallery/01.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/02.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/03.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/04.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/05.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/06.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;gallery/07.jpg\u0026#34; class=\u0026#34;grid-w33\u0026#34; /\u0026gt; {{\u0026lt; /gallery \u0026gt;}} {{\u0026lt; /timelineItem \u0026gt;}} {{\u0026lt; timelineItem icon=\u0026#34;code\u0026#34; header=\u0026#34;Another Awesome Header\u0026#34;\u0026gt;}} {{\u0026lt; github repo=\u0026#34;nunocoracao/blowfish\u0026#34; \u0026gt;}} {{\u0026lt; /timelineItem \u0026gt;}} {{\u0026lt; /timeline \u0026gt;}} header badge test subheader Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus non magna ex. Donec sollicitudin ut lorem quis lobortis. Nam ac ipsum libero. Sed a ex eget ipsum tincidunt venenatis quis sed nisl. Pellentesque sed urna vel odio consequat tincidunt id ut purus. Nam sollicitudin est sed dui interdum rhoncus. Another Awesome Header date - present Awesome Subheader With html code Coffee Tea Milk Shortcodes AWESOME With other shortcodes Another Awesome Header nunocoracao/blowfish Personal Website \u0026amp; Blog Theme for Hugo HTML 2798 728 TypeIt # TypeIt 是用于创建打字机效果的最通用的 JavaScript 工具。通过简单的配置，它允许您键入单个或多个断行、删除和相互替换的字符串，甚至可以处理包含复杂 HTML 的字符串。\nBlowfish 使用简码实现 TypeIt 功能的子集。在 typeit 简码中编写文本，并使用以下参数来配置您想要的行为。\n参数 功能 tag [String] 将用于呈现字符串的 html 标签。 classList [String] 应用于 html 元素的 css 类列表。 initialString [String] 将显示为先写入并将被替换的初始字符串。 speed [number] 每步之间的打字速度，以毫秒为单位。 lifeLike [boolean] 使打字速度不规律，就像真人在打字一样。 startDelay [number] 插件在初始化后到开始输入的延迟时间。 breakLines [boolean] 将多个字符串换行输出 (true)，或者将它们删除并替换 (false)。 waitUntilVisible [boolean] 决定脚本在网站加载时启动还是在目标元素可见时启动。默认为 true loop [boolean] 字符串动画是否会循环 例1:\n{{\u0026lt; typeit \u0026gt;}} Lorem ipsum dolor sit amet {{\u0026lt; /typeit \u0026gt;}} 例2:\n{{\u0026lt; typeit tag=h1 lifeLike=true \u0026gt;}} Lorem ipsum dolor sit amet, consectetur adipiscing elit. {{\u0026lt; /typeit \u0026gt;}} 例3:\n{{\u0026lt; typeit tag=h3 speed=50 breakLines=false loop=true \u0026gt;}} Lorem ipsum dolor sit amet, consectetur adipiscing elit. {{\u0026lt; /typeit \u0026gt;}} Youtube 嵌入播放器 # 使用 lite-youtube-embed 库嵌入 YouTube 视频的简码。该库是 YouTube 嵌入播放器的轻量级替代品，其设计速度更快、更高效。\n参数 功能 id [String] 要嵌入的 YouTube 视频 ID。 label [String] 视频的标签 例1:\n{{\u0026lt; youtubeLite id=\u0026#34;SgXhGb-7QbU\u0026#34; label=\u0026#34;Blowfish-tools demo\u0026#34; \u0026gt;}} \u0026ndash;\u0026gt;\n","date":"2025/03/09","externalUrl":null,"permalink":"/posts/ohters/shortcodes/","section":"吾生有涯，而知无涯","summary":"","title":"简码","type":"posts"},{"content":"","date":"2026/02/25","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"2026/02/25","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2026/02/25","externalUrl":null,"permalink":"/series/%E4%BB%B7%E6%A0%BC%E8%A1%8C%E4%B8%BA/","section":"Series","summary":"","title":"价格行为","type":"series"},{"content":"","date":"2026/02/25","externalUrl":null,"permalink":"/tags/%E4%BB%B7%E6%A0%BC%E8%A1%8C%E4%B8%BA/","section":"Tags","summary":"","title":"价格行为","type":"tags"},{"content":"阅读YTC价格行为交易读书笔记 第一章20260411 # 正确认识市场 # 价格背后是决策，价格运动源自于供需失衡，失衡不一定是数量上的，有可能是紧迫感失衡。可以视为: $$\\text{Price} = {f}(\\text{Supply}, \\text{Demand}, \\text{time})$$ graph LR %% 定义节点 (全部使用直线框) A([市场价格]) B[市场预期与分析] C[交易活动] D[供大于求\\n买方市场] E[供小于求\\n卖方市场] %% 定义流程和关系 (全部使用直线连接) A --\u003e B; B --\u003e C; C -- 导致 --\u003e D \u0026 E; D --\u003e F; E --\u003e G; subgraph one[价格变动] F([价格下降]) G([价格上涨]) end one -- 形成新的 --\u003e A; 在市场当中，最有效的分析不是对价格的分析，而是对交易者决策的分析。 在处于紧张状态时，人类的决策制定和行为的可预测性大大提高，一般会表现得更加不顾一切，更加充满紧迫感。这恰好是我们正在探寻的东西。因此，稳定的获利源于捕捉其他交易者在最大紧张点时的止损操作。。 作者此时认为：交易是零和博弈。 有效市场策略的基础是对市场内供需力量的分析，以及供需力量将对其他交易者们的决策产生怎样影响的评估。知道价格运动是交易者决策的结果，有效交易策略致力于辨识图表上交易者们正在制定的决策是净看涨或看跌的那些区域。\n市场分析 # 首要目标和核心在于培养情景意识和保持专注，明白价格已经处于什么位置，评估当前价格运动的强弱，培养未来价格很可能向哪里运动的感觉。（AL强调的背景或者脉络Context）\n简言之：（1） 价格已经在什么位置？（2）价格很可能向哪个位置运动？\n支撑和阻力\n支撑和阻力不像是一堵砖墙。这是一个价格区域，会引起交易者们的一些情绪变化，是先前供需失衡的结果，预期交易者们未来还会在这一区域内制定决策。情绪影响区域的边界不能被准确地定义。**备选和来源：波段高点和波段低点、缺口、\n多重时间框架\n类型 描述 主要 备用 较高时间框架 辨识一个支撑和阻力的框架，为市场提供结构。 30min 60min 交易时间框架 当较高时间框架市场结构内运动时，分析趋势和判断未来运动路径。 3min 5min 较低时间框架 提供更多交易细节，微调交易框架的分析，以及确定交易入场和出场时间。 1min / 时间框架表现一致性，主要是小的时间框架合并构成大时间框架。 我们在交易时间框架交易。但是，我们是把这种价格行为放在较高时间框架结构的背景内的。\n市场结构\n规则 1——价格在一个由支撑和阻力构成的结构性框架内运动。 规则 2——对结构性框架支撑或阻力的突破将引出价格在下一框架区域 内的运动。 Previous Next 初始分析过程 ","date":"2026/02/25","externalUrl":null,"permalink":"/posts/priceaction/00-basic/","section":"吾生有涯，而知无涯","summary":"","title":"价格行为系列教程（00入门篇）","type":"posts"},{"content":"","date":"2026/02/25","externalUrl":null,"permalink":"/tags/%E4%BA%A4%E6%98%93%E7%94%9F%E6%B6%AF/","section":"Tags","summary":"","title":"交易生涯","type":"tags"},{"content":"","date":"2026/02/25","externalUrl":null,"permalink":"/tags/%E6%8A%95%E8%B5%84%E7%90%86%E8%B4%A2/","section":"Tags","summary":"","title":"投资理财","type":"tags"},{"content":" 序言 # 很多入职开发前的伙伴, 可能或多或少接触过 git,实际用的最多的命令也就是 git clone,git push, 以为 git 的流程也就这样, 实则不然, 实际上企业中的 git 流程, 由五大分支构成\n理论 # fI6dAXt_82424×1476 111 KB 提示\n只有 main 主分支和 develop 开发分支是贯穿项目生命周期本身, 其他分支都会中途离场\n所以也说 main 和 develop 是上二分支, 其他分支是下三分支\n主分支 (main/master): 项目的生产发布分支, 始终保持稳定可发布状态 热修复分支 (hotfix/*): 从 main 分支创建, 用于紧急修复线上 BUG, 修复后同时合并回 main 和 develop 开发分支 (develop): 日常开发的主分支, 所有功能开发的集成分支 特性分支 (feature/*): 从 develop 创建, 开发新功能, 完成后合并回 develop 预发布分支 (release/*): 从 develop 创建, 用于发布前的测试和最终调整, 测试通过后合并回 main(打版本标签) 和 develop 分支合并关系 # feature/* → develop develop → release/* release/* → main + develop hotfix/* → main + develop\n主分支 # 作用: 存放 稳定, 可随时部署到生产环境的代码 特点:\n分支上每一个提交对都应一个正式发布的版本 不允许在此分支直接开发 通常被打上版本标签 (如: v1.0.0 或 v20260101) 开发分支 # 作用: 存放 最新开发成果 的集成分支, 是功能开发的集线器 特点:\n当 develop 分支上的代码到达稳定状态并准备发布时, 会合并到 main 分支 (如果有 release 分支, 则合并到此分支) 所有功能的分支、发布分支都从 develop 分支拉取 功能分支 # 来源: develop 合并目标: develop 命令惯例:feature/user-authentication,feature/payment-integration 作用: 开发新功能 生命周期:\n从 develop 分支拉取 开发完成后, 合并回 develop 合并后, 该功能分支通常被删除 发布分支 # 来源: develop 合并目标:develop 和 main 命名惯例:release/1.2.0,release/2026-spring 作用: 为发布新版本做准备. 在此分支上, 只做 BUG 修复,生成版本号,整理文档 等发布准备工作, 不再添加新功能 生命周期:\n当 develop 分支的功能足够进行一次发布时, 从 develop 拉出 release 分支 在此分支上进行最后的测试和修复 准备就绪后, 将 release 分支合并到 main 分支 并打上版本标签 同时, 必须合并回 develop 分支, 因为 release 分支上修复的 BUG 可能 develop 分支上还没修复 热修复分支 # 来源:main 合并目标: main 和 develop 命名惯例:hotfix/critical-security-patch,hotfix/1.2.1 作用:快速修复生产环境(main分支)上的紧急BUG 生命周期:\n从 main 分支上出现BUG的提交点(通常是最近的标签)拉取 修复完成后,合并回main分支并打上新的版本标签(如:v1.0.1) 同时,必须合并回 develop 分支,确保修复在后续开发中也生效 人话 # 恭喜你能看到这里, 理论看起来确实是枯燥的, 感谢你自己的坚持,\n希望评论区就 gitflow 能展开相关讨论, 而不是一味的刷 感谢分享 感谢大佬,\n我分享文章也是希望有思想和经验上的交流碰撞 实际上,普通的开发入职后,如果有开发需求,一般都是从develop分支拉取代码后,本地新建feature/xxx 功能分支,在feature/xxx分支上进行开发的。功能开发到一定阶段后,比如一阶段完成了核心需求(因为产品可能在开发内提出新需求,这很常见), 就可以合并到 develop 分支, develop 在积累了多个功能分支合并后, 就可以发布到 release 发布分支, 这一分支不再接受新功能合并, 只允许修补 BUG 这一过程就是提测, 让测试工程师核验功能完整性, 如果有 BUG 要及时修复 修复的时候拉取的是 release 分支,然后提交的时候也是推送到这个分支 在release 分支准备完善 (修复了测试工程师检测到的 BUG),就可以准备往主分支 (main) 发布了, 这就是一次完整的 git flow 发布流程, 当然还有线上有 BUG, 需要做紧急修复 这个时候, 就是直接拉取 main 分支并在本地创建热修复分支 hotfix, 在 BUG 被修复后, 就可以合并回 main 分支 和 develop 分支了 是的, 这里要注意, hotfix 要合并到 2 个分支里, 而不是 hotfix-\u0026gt;main-\u0026gt;develop 这是错误的做法, 因为造成 develop 被 main 污染 (额外的标签记录以及其他)\n","date":"2026/01/04","externalUrl":null,"permalink":"/posts/tricks/git_flow/","section":"吾生有涯，而知无涯","summary":"示例摘要","title":"Git_flow","type":"posts"},{"content":"","date":"2026/01/04","externalUrl":null,"permalink":"/series/%E5%BB%BA%E7%AB%99%E6%8A%80%E6%9C%AF/","section":"Series","summary":"","title":"建站技术","type":"series"},{"content":"","date":"2026/01/04","externalUrl":null,"permalink":"/tags/%E8%87%AA%E6%88%91%E6%8A%98%E8%85%BE/","section":"Tags","summary":"","title":"自我折腾","type":"tags"},{"content":"","date":"2025/12/14","externalUrl":null,"permalink":"/series/%E5%AE%89%E5%8D%93rom%E7%B3%BB%E5%88%97/","section":"Series","summary":"","title":"安卓ROM系列","type":"series"},{"content":"","date":"2025/12/14","externalUrl":null,"permalink":"/tags/%E5%AE%89%E5%8D%93%E5%88%B7%E6%9C%BA/","section":"Tags","summary":"","title":"安卓刷机","type":"tags"},{"content":" 前言 # 本文将系统介绍电视刷机包的完整刷机流程，以家中创维电视为例，演示如何对电视系统进行精简和升级。通过DIY固件，可以实现以下功能：\n🚀 优化系统性能：去除冗余功能，提升系统响应速度 🔓 解锁隐藏功能：支持更多视频格式，优化画质参数 🎨 深度定制体验：自定义开机动画、预装应用 💪 刷入第三方ROM：彻底改变使用体验 虽然本文以特定的创维型号为例，但只要是基于 Mstar（晨星）芯片 的电视，其底层原理基本通用。如果你也厌倦了系统的种种限制，不妨跟着教程一起探索！😎\n警告 风险提示：刷机涉及修改系统底层文件，存在导致设备无法开机（变砖）的可能风险。请在操作前确保已备份重要数据，并具备一定的动手能力。本教程仅供学习交流使用，刷机可能导致保修失效，操作不当可能造成设备变砖，作者不对任何损失负责。\n实战流程 # 零、系统配置说明及前期准备 # 设备信息 # 品牌型号：创维（SkyWorth）7S77/43G31XX-S010339 生产日期：2021年04月19日 主控芯片：Mstar 系统架构：ARMv7（32位），出厂预制安卓8.0系统。 提前检查 # 开启电视ADB调试后使用开心助手进行连接，adb shell的不具有root用户权限，因此只能采取从刷机包提取boot.img的方法，如果具有root权限，可以直接提取所有镜像文件，不需要解包打包的过程。\n预先准备:刷机包和U盘 # 1. 刷机包获取:建议在淘宝搜索\u0026quot;[品牌型号]+刷机包\u0026quot;，向客服提供电视铭牌照片（型号、SN等信息），价格通常5-20元。目标文件：本例获取的刷机包名为 MstarUpgrade.bin，大小约 1.66GB。\n2. U盘准备：容量：建议8GB以下（部分老设备不识别大容量U盘）。格式：FAT32格式，刷机前务必格式化，避免文件碎片导致刷写失败。\n一、刷机包解包 # 安装Python 3.x环境,建议使用uv或者Anaconda。\n下载解包工具mstar-bin-tool,并解压到本地目录（如E:\\mstar-bin-tool-master），具体用法可见doc目录。\n解包流程为：在mstar-bin-tool当前目录下运行uv run unpack.py或者python unpack.py MstarUpgrade.bin(需配置后python环境变量)，如果运行解包命令没有提供解压路径参数[extarct_path]，默认解压路径为当前目录下的unpacked文件夹。\nPS E:\\mstar-bin-tool-master\u0026gt; uv run .\\unpack.py E:\\MstarUpgrade.bin mstar-bin-tool unpack.py v.1.2_sha-man [i] Analizing header ... [i] Saving header script to unpacked\\~header_script.sh ... [i] Parsing script ... [i] Partition: sboot Offset: 0x4000 Size 0x24000 (144.0 KB) -\u0026gt; unpacked\\sboot.img [i] Partition: MBOOT Offset: 0x28000 Size 0x263200 (2.39 MB) -\u0026gt; unpacked\\MBOOT.img [i] Partition: recovery Offset: 0x28c000 Size 0x13cc800 (19.8 MB) -\u0026gt; unpacked\\recovery.img [i] Parsing setenv recoverycmd -\u0026gt; mmc read.p 0x23000000 dtb 0x00100000\\; mmc read.p 0x25000000 recovery 0x02000000\\; bootm 0x25000000 [i] Partition: boot Offset: 0x1659000 Size 0x10c4800 (16.77 MB) -\u0026gt; unpacked\\boot.img [i] Parsing setenv bootcmd -\u0026gt; mmc read.p 0x23000000 dtb 0x00100000\\; mmc read.p 0x25000000 boot 0x01400000\\; bootm 0x25000000 [i] Partition: optee Offset: 0x271e000 Size 0x3d0ac0 (3.82 MB) -\u0026gt; unpacked\\optee.bin [i] Partition: armfw Offset: 0x2aef000 Size 0x1c900 (114.25 KB) -\u0026gt; unpacked\\armfw.bin [i] Partition: RTPM Offset: 0x2b0c000 Size 0x10000 (64.0 KB) -\u0026gt; unpacked\\RTPM.img [i] Partition: dtb Offset: 0x2b1c000 Size 0x39e8 (14.48 KB) -\u0026gt; unpacked\\dtb.img [i] Partition: frc Offset: 0x2b20000 Size 0x81010 (516.02 KB) -\u0026gt; unpacked\\frc.img [i] Partition: cm4 Offset: 0x2ba2000 Size 0x20d18 (131.27 KB) -\u0026gt; unpacked\\cm4.img [i] Partition: system Offset: 0x2bc3000 Size 0x95c7d18 (149.78 MB) -\u0026gt; unpacked\\system_sparse.0 [i] Partition: system Offset: 0xc18b000 Size 0x8e88668 (142.53 MB) -\u0026gt; unpacked\\system_sparse.1 [i] Partition: system Offset: 0x15014000 Size 0x95af0a8 (149.68 MB) -\u0026gt; unpacked\\system_sparse.2 [i] Partition: system Offset: 0x1e5c4000 Size 0x95ffe58 (150.0 MB) -\u0026gt; unpacked\\system_sparse.3 [i] Partition: system Offset: 0x27bc4000 Size 0x1de400 (1.87 MB) -\u0026gt; unpacked\\system_sparse.4 [i] Partition: cache Offset: 0x27da3000 Size 0x1050190 (16.31 MB) -\u0026gt; unpacked\\cache_sparse.0 [i] Partition: vendor Offset: 0x28df4000 Size 0x95ec88c (149.92 MB) -\u0026gt; unpacked\\vendor_sparse.0 [i] Partition: vendor Offset: 0x323e1000 Size 0x8fd24a8 (143.82 MB) -\u0026gt; unpacked\\vendor_sparse.1 [i] Partition: vendor Offset: 0x3b3b4000 Size 0x7b803f8 (123.5 MB) -\u0026gt; unpacked\\vendor_sparse.2 [i] Partition: tvservice Offset: 0x42f35000 Size 0xa000000 (160.0 MB) -\u0026gt; unpacked\\tvservice.img [i] Partition: tvconfig Offset: 0x4cf35000 Size 0xb00000 (11.0 MB) -\u0026gt; unpacked\\tvconfig.img [i] Partition: tvdatabase Offset: 0x4da35000 Size 0x800000 (8.0 MB) -\u0026gt; unpacked\\tvdatabase.img [i] Partition: tvcustomer Offset: 0x4e235000 Size 0x1000000 (16.0 MB) -\u0026gt; unpacked\\tvcustomer.img [i] Partition: tvcertificate Offset: 0x4f235000 Size 0x800000 (8.0 MB) -\u0026gt; unpacked\\tvcertificate.img [i] Partition: atv Offset: 0x4fa35000 Size 0x1800000 (24.0 MB) -\u0026gt; unpacked\\atv.img [i] Partition: factory Offset: 0x51235000 Size 0x6c00000 (108.0 MB) -\u0026gt; unpacked\\factory.img [i] Partition: skyworth Offset: 0x57e35000 Size 0x9600000 (150.0 MB) -\u0026gt; unpacked\\skyworth.img [i] Partition: skyworth Offset: 0x61435000 Size 0x3a00000 (58.0 MB) append to unpacked\\skyworth.img [i] Partition: userdata Offset: 0x64e35000 Size 0x56ec568 (86.92 MB) -\u0026gt; unpacked\\userdata_sparse.0 [i] Parsing setenv bootargs -\u0026gt; console [i] Parsing setenv bootargs -\u0026gt; console=ttyS0,115200 androidboot.console=ttyS0 root=/dev/ram rw rootwait init=/init CORE_DUMP_PATH=/data/core_dump.%e_%p.gz KDebug=1 delaylogo=true androidboot.selinux=permissive security=selinux platform=mi tee_mode=optee str_ignore_wakelock pm_path=/tvconfig/config/PM.bin loglevel=0 [i] Parsing setenv bootlogo_gopidx -\u0026gt; 2 [i] Parsing setenv bootlogo_buffer -\u0026gt; E_MMAP_ID_BOOTLOGO_BUFFER [i] Parsing setenv OSD_BufferAddr -\u0026gt; E_MMAP_ID_JPD_WRITE_ADR [i] Parsing setenv upgrade_mode -\u0026gt; null [i] Parsing setenv factory_poweron_mode -\u0026gt; memory [i] Parsing setenv Customer_Rest -\u0026gt; true [i] Parsing setenv Customer_Rest_AIStandby -\u0026gt; null [i] Parsing setenv str_crc -\u0026gt; 2 [i] Parsing setenv db_table -\u0026gt; 0 [i] Parsing setenv verify -\u0026gt; n [i] Parsing setenv sync_mmap -\u0026gt; 1 [i] Parsing setenv CONFIG_PATH -\u0026gt; /tvconfig/config [i] Parsing setenv mboot_default_env -\u0026gt; 0 [i] Parsing setenv music -\u0026gt; 1 [i] Parsing setenv music_vol -\u0026gt; 35 [i] Parsing setenv BootVideoPlayBySrv -\u0026gt; 1 [i] Parsing setenv BootVideoPlaySourceConfig -\u0026gt; file:///system/bootvideo.mp4?looptime=1\u0026amp;playtime=10 [i] Parsing setenv close_log -\u0026gt; yes [i] Parsing setenv MstarUpgrade_complete -\u0026gt; 1 [i] Parsing setenv sync_mmap -\u0026gt; 1 [i] Parsing setenv db_table -\u0026gt; 0 [i] Sparse: converting system_sparse.*to system.img [i] Sparse: remain cache_sparse.* to cache_sparse.* [i] Sparse: converting vendor_sparse.* to vendor.img [i] Sparse: remain userdata_sparse.*to userdata_sparse.* [i] Done. 二、刷机镜像解析 # MstarUpgrade.bin通常被成为固件（Firmware），即电视操作系统的软件，包含了所有控制电视硬件的基本代码，以及电视中运行的各种功能和服务。驱动程序：为电视的各种硬件组件编写的软件，如显示驱动、声音驱动等，确保硬件能正常工作。配置文件：可能包括用于设置电视特定功能的配置文件，如语言、地区设置、网络配置等。解包后会得到多个.img 或 .bin 文件，以下是关键文件的说明：\nPS E:\\mstar-bin-tool-master\\unpacked\u0026gt; ls Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2025/12/28 10:43 116992 armfw.bin -a---- 2025/12/28 10:44 25165824 atv.img -a---- 2025/12/28 10:43 17582080 boot.img #重要文件，Magisk Patch对象 -a---- 2025/12/28 10:43 17105296 cache_sparse.0 -a---- 2025/12/28 10:43 134424 cm4.img -a---- 2025/12/28 10:43 14824 dtb.img -a---- 2025/12/28 10:44 113246208 factory.img -a---- 2025/12/28 10:43 528400 frc.img -a---- 2025/12/28 10:43 2503168 MBOOT.img #重要文件，负责电视启动操作 -a---- 2025/12/28 10:43 4000448 optee.bin -a---- 2025/12/28 10:43 20760576 recovery.img #重要文件 -a---- 2025/12/28 10:43 65536 RTPM.img -a---- 2025/12/28 10:43 147456 sboot.img -a---- 2025/12/28 10:44 218103808 skyworth.img #厂商自定义文件 -a---- 2025/12/28 10:44 1006632960 system.img #安卓主系统，调整变更主要内容 -a---- 2025/12/28 10:44 8388608 tvcertificate.img -a---- 2025/12/28 10:44 11534336 tvconfig.img #厂商自定义文件 -a---- 2025/12/28 10:44 16777216 tvcustomer.img #厂商自定义文件 -a---- 2025/12/28 10:44 8388608 tvdatabase.img #厂商自定义文件，数据库 -a---- 2025/12/28 10:44 167772160 tvservice.img -a---- 2025/12/28 10:44 91145576 userdata_sparse.0 -a---- 2025/12/28 10:44 704643072 vendor.img #厂商专有代码文件，数据库 -a---- 2025/12/28 10:43 16384 ~header # 刷机包文件头，记录刷机包的刷写代码，容量为16K -a---- 2025/12/28 10:43 6349 ~header_script.sh # 刷机包文件头的提取内容，记录刷机包的刷写代码 文件名 大小 功能说明 修改难度 ~header 16KB 刷机包文件头，包含分区表和刷写脚本 ⚠️ 谨慎修改，自动生成 ~header_script.sh 6KB 文件头提取内容（可读） - sboot.img 147KB 安全启动镜像 🔒 不建议修改 MBOOT.img 2.5MB 主引导程序（类似电脑BIOS） ⚠️ 重要文件 boot.img ⭐ 17MB Android内核及根文件系统 ✅ Magisk修改对象 recovery.img 20MB 恢复模式镜像 ⚠️ 重要文件 system.img ⭐ 1006MB Android主系统（应用、框架层） ✅ 主要修改对象 vendor.img 704MB 厂商专有代码和驱动 ⚠️ 谨慎修改 tvservice.img 167MB 电视专用服务 - tvconfig.img 11MB 厂商配置文件 ✅ 可精简 skyworth.img 218MB 创维专属分区 ✅ 可精简 userdata_sparse.0 91MB 用户数据（稀疏镜像） - Tips：带⭐标记的是主要修改对象，其他文件建议保持原样。\n三、刷机镜像修改 # 经搜索归纳可知，解包通常有以下两种方法:ROM助手和Linux挂载。两者均不推荐，ROM助手没有找到特别合适的软件，修改方式固定单一；Linux挂载小白还需要一定Linux使用基础，且实战尝试无法挂载安装稀疏镜像，提示超级块损坏。\n推荐使用的方法是MIK镜像工具，优势为Windows下图形化操作，支持拖拽解压/封装，无需Linux环境。\nsystem镜像 # 修改system镜像可以实现去除广告、预制安装应用、调整设置等功能，更多涉及安卓系统的优化和调试。 建议直接使用上面的软件进行解压和封装镜像，此处仍然提供网络其他解压方法进行参考。\n镜像挂载修改流程 # 建立目录镜像挂载 挂载的目录可以随意进行选取，通常/mnt/system\ntest@ubuntu:/mnt$ mkdir -p system test@ubuntu:/mnt$ sudo mount -rw -t ext4 system.img system/ 查看并修改system.img内容\ntest@ubuntu:/mnt/system/$ ls -l # ll 如果存在alias命令 total 60 drwxr-xr-x 13 root root 4096 Jan 1 1970 ./ drwxrwxr-x 4 biren biren 4096 Jun 9 20:41 ../ drwxr-xr-x 2 root root 4096 Dec 16 2012 app/ drwxr-xr-x 2 root 2000 4096 Dec 16 2012 bin/ -rw-r--r-- 1 root root 1979 Dec 16 2012 build.prop drwxr-xr-x 9 root root 4096 Dec 16 2012 etc/ drwxr-xr-x 2 root root 4096 Dec 16 2012 fonts/ drwxr-xr-x 2 root root 4096 Dec 16 2012 framework/ drwxr-xr-x 8 root root 8192 Dec 16 2012 lib/ drwxr-xr-x 3 root root 4096 Dec 16 2012 media/ drwxr-xr-x 3 root root 4096 Dec 16 2012 tts/ drwxr-xr-x 8 root root 4096 Dec 16 2012 usr/ drwxr-xr-x 3 root 2000 4096 Dec 16 2012 vendor/ drwxr-xr-x 2 root 2000 4096 Dec 16 2012 xbin/ 退出挂载的命令为test@ubuntu:/mnt$ sudo umount system，注意当前命令的执行目录。\n内置APK删除修改 # apk通常放置在/app或/priv-app目录：等目录当中，直接替换或者添加其他需要的应用即可。预装的应用可能在preload文件夹，所有APKS可以使用Apk-Info工具查看apks的具体信息。\nHost文件实现去广告 # 编辑/etc/hosts文件，添加广告域名拦截规则：\n# 创维广告域名示例 127.0.0.1 ads.skyworth.com 127.0.0.1 upgrade.skyworth.com boot镜像（主要用于获取Root权限） # 主要操作为使用magisk app进行patch，网络上面参考资料已经足够多。 此处记录一个小问题，提取到boot.img镜像后直接使用手机上的Magisk进行Ptach刷入后电视无法启动，主要原因可能有两点：一是CPU系统架构不匹配，手机CPU架构为armv8,电视CPU架构为armv7，两者就是64位和32位的区别；二是疑似DM-verification问题，涉及到系统镜像的检验等等问题暂不明确，与dm-verity、AVB等有关。 上述问题的解决方案是直接在电视上面安装MagiskAPP，然后进行Patch操作重新刷入后即可成功开机。\n其他镜像（可选操作） # 修改其他相应的镜像可以调整开机动画、首屏、开机logo内容，此处不再进行详述，方法和system.img大同小异。\n镜像文件 可修改内容 备注 tvconfig.img 开机动画、系统音效 - recovery.img Recovery界面美化 - vendor.img 驱动优化（高级） ⚠️ 需专业知识 四、刷机包封装 # 这个过程是解包的逆过程，即将所有镜像重新打包成 MstarUpgrade.bin,需要正确配置 header_script.sh 和 config.ini。简单来说刷机包的文件结构布局如下图，通常将涉及的镜像进行二进制合并，在MBOOT模式下对内置存储芯片进行格式化和内容刷写。文件头主要是实现镜像容量尺寸定位和刷写，以及部分启动变量的写入。\n理解文件头 (Header) # 解包时生成的 ~header_script.sh 极其重要，它记录了分区的内存布局和刷写命令（如 mmc create, mmc write）。其中的内容直接询问大模型可以得到更多的信息。**提示：无需手动进行编写，直接复用解包时生成的内容，或在此基础上修改。**就当前获取的header_script.sh，内容为:\n#-------------USB Upgrade Bin Info---------------- # Device : sky848_9s60 # Build PATH : /work_ssd3/workspace/Release/build_system/Mstar/7S77_G51/Android # Build TIME : 2020-11-23 20:32:01 # File Partition: set_partition mmc slc 0 1 mmc rmgpt mmc create misc 0x00080000 mmc create param 0x100000 mmc create recovery 0x02000000 mmc create boot 0x01400000 mmc create optee 0x00600000 mmc create armfw 0x00010000 mmc create RTPM 0x00040000 mmc create dtb 0x00100000 mmc create frc 0x00100000 mmc create cm4 0x00080000 mmc create system 0x3C000000 mmc create cache 0x38000000 mmc create vendor 0x2A000000 mmc create tvservice 0x0A000000 mmc create tvconfig 0x01400000 mmc create tvdatabase 0x00800000 mmc create tvcustomer 0x01000000 mmc create tvcertificate 0x00800000 mmc create atv 0x1800000 mmc create factory 0x6C00000 mmc create skyworth 0xD000000 mmc create demura 0x300000 mmc create userdata 0x106800000 # File Partition: mboot filepartload 0x28A00000 $(UpgradeImage) 0x4000 0x24000 mmc write.boot 1 0x28A00000 0 0x24000 filepartload 0x28A00000 $(UpgradeImage) 0x28000 0x263200 mmc write.p 0x28A00000 MBOOT 0x263200 # File Partition: recovery filepartload 0x28A00000 $(UpgradeImage) 0x28c000 0x13cc800 mmc erase.p misc mmc erase.p recovery mmc write.p 0x28A00000 recovery 0x13CC800 1 setenv recoverycmd mmc read.p 0x23000000 dtb 0x00100000\\; mmc read.p 0x25000000 recovery 0x02000000\\; bootm 0x25000000 saveenv # File Partition: boot filepartload 0x28A00000 $(UpgradeImage) 0x1659000 0x10c4800 mmc erase.p boot mmc write.p 0x28A00000 boot 0x10C4800 1 setenv bootcmd mmc read.p 0x23000000 dtb 0x00100000\\; mmc read.p 0x25000000 boot 0x01400000\\; bootm 0x25000000 saveenv # File Partition: optee filepartload 0x28A00000 $(UpgradeImage) 0x271e000 0x3d0ac0 multi2optee 0x28A00000 optee $(filesize) # File Partition: armfw filepartload 0x28A00000 $(UpgradeImage) 0x2aef000 0x1c900 multi2optee 0x28A00000 armfw $(filesize) # File Partition: RT_PM filepartload 0x28A00000 $(UpgradeImage) 0x2b0c000 0x10000 mmc erase.p RTPM mmc write.p 0x28A00000 RTPM 0x10000 1 # File Partition: dtb filepartload 0x28A00000 $(UpgradeImage) 0x2b1c000 0x39e8 mmc erase.p dtb mmc write.p 0x28A00000 dtb 0x39E8 1 # File Partition: frc filepartload 0x28A00000 $(UpgradeImage) 0x2b20000 0x81010 mmc erase.p frc mmc write.p 0x28A00000 frc 0x81010 # File Partition: cm4 filepartload 0x28A00000 $(UpgradeImage) 0x2ba2000 0x20d18 mmc erase.p cm4 mmc write.p 0x28A00000 cm4 0x20D18 # File Partition: system mmc erase.p system filepartload 0x28A00000 $(UpgradeImage) 0x2bc3000 0x95c7d18 sparse_write mmc 0x28A00000 system $(filesize) filepartload 0x28A00000 $(UpgradeImage) 0xc18b000 0x8e88668 sparse_write mmc 0x28A00000 system $(filesize) filepartload 0x28A00000 $(UpgradeImage) 0x15014000 0x95af0a8 sparse_write mmc 0x28A00000 system $(filesize) filepartload 0x28A00000 $(UpgradeImage) 0x1e5c4000 0x95ffe58 sparse_write mmc 0x28A00000 system $(filesize) filepartload 0x28A00000 $(UpgradeImage) 0x27bc4000 0x1de400 sparse_write mmc 0x28A00000 system $(filesize) # File Partition: cache filepartload 0x28A00000 $(UpgradeImage) 0x27da3000 0x1050190 mmc erase.p cache sparse_write mmc 0x28A00000 cache $(filesize) # File Partition: vendor mmc erase.p vendor filepartload 0x28A00000 $(UpgradeImage) 0x28df4000 0x95ec88c sparse_write mmc 0x28A00000 vendor $(filesize) filepartload 0x28A00000 $(UpgradeImage) 0x323e1000 0x8fd24a8 sparse_write mmc 0x28A00000 vendor $(filesize) filepartload 0x28A00000 $(UpgradeImage) 0x3b3b4000 0x7b803f8 sparse_write mmc 0x28A00000 vendor $(filesize) # File Partition: tvservice filepartload 0x28A00000 $(UpgradeImage) 0x42f35000 0xa000000 mmc erase.p tvservice mmc write.p 0x28A00000 tvservice 0xA000000 1 # File Partition: tvconfig filepartload 0x28A00000 $(UpgradeImage) 0x4cf35000 0xb00000 mmc erase.p tvconfig mmc write.p 0x28A00000 tvconfig 0xB00000 1 # File Partition: tvdatabase filepartload 0x28A00000 $(UpgradeImage) 0x4da35000 0x800000 mmc erase.p tvdatabase mmc write.p 0x28A00000 tvdatabase 0x800000 1 # File Partition: tvcustomer filepartload 0x28A00000 $(UpgradeImage) 0x4e235000 0x1000000 mmc erase.p tvcustomer mmc write.p 0x28A00000 tvcustomer 0x1000000 1 # File Partition: tvcertificate filepartload 0x28A00000 $(UpgradeImage) 0x4f235000 0x800000 mmc erase.p tvcertificate mmc write.p 0x28A00000 tvcertificate 0x800000 1 # File Partition: atv filepartload 0x28A00000 $(UpgradeImage) 0x4fa35000 0x1800000 mmc erase.p atv mmc write.p 0x28A00000 atv 0x1800000 1 # File Partition: factory filepartload 0x28A00000 $(UpgradeImage) 0x51235000 0x6c00000 mmc erase.p factory mmc write.p 0x28A00000 factory 0x6C00000 1 # File Partition: skyworth mmc erase.p skyworth filepartload 0x28A00000 $(UpgradeImage) 0x57e35000 0x9600000 mmc write.p 0x28A00000 skyworth 0x9600000 1 filepartload 0x28A00000 $(UpgradeImage) 0x61435000 0x3a00000 mmc write.p.continue 0x28A00000 skyworth 0x4B000 0x3A00000 1 # File Partition: demura mmc erase.p demura # File Partition: userdata filepartload 0x28A00000 $(UpgradeImage) 0x64e35000 0x56ec568 mmc erase.p userdata sparse_write mmc 0x28A00000 userdata $(filesize) # File Partition: set_config setenv bootargs console saveenv setenv bootargs console=ttyS0,115200 androidboot.console=ttyS0 root=/dev/ram rw rootwait init=/init CORE_DUMP_PATH=/data/core_dump.%e_%p.gz KDebug=1 delaylogo=true androidboot.selinux=permissive security=selinux platform=mi tee_mode=optee str_ignore_wakelock pm_path=/tvconfig/config/PM.bin loglevel=0 setenv bootlogo_gopidx 2 setenv bootlogo_buffer E_MMAP_ID_BOOTLOGO_BUFFER setenv OSD_BufferAddr E_MMAP_ID_JPD_WRITE_ADR setenv upgrade_mode null setenv factory_poweron_mode memory setenv Customer_Rest true setenv Customer_Rest_AIStandby null setenv str_crc 2 setenv db_table 0 setenv verify n setenv sync_mmap 1 setenv CONFIG_PATH /tvconfig/config setenv mboot_default_env 0 setenv music 1 setenv music_vol 35 setenv BootVideoPlayBySrv 1 setenv BootVideoPlaySourceConfig file:///system/bootvideo.mp4?looptime=1\u0026amp;playtime=10 setenv close_log yes saveenv setenv MstarUpgrade_complete 1 setenv sync_mmap 1 setenv db_table 0 saveenv printenv 编写config.ini文件并调试 # mstar-bin-tool源代码目录configs当中已经包含较多的示例代码，其过程也比较简单，该部分需要不间断进行调试或者修改，该部分内容可以直接联系我或者期待下一期分享。扩展内容：只刷写boot.img、recovery.img、system.img单一或者组合镜像也是可以的。\n在此提供测试的config文件\n# # Pack Configuration File for sky848_9s60 (MStar 7S77_G51 Build) # Build Time: 2020-11-23 20:32:01 # [Main] # Output file name derived from general usage, adjust if needed FirmwareFileName=MstarUpgrade.bin ProjectFolder=./unpacked useHexValuesPrefix=true SCRIPT_FIRMWARE_FILE_NAME=$$(UpgradeImage) DRAM_BUF_ADDR=28A00000 MAGIC_FOOTER=12345678 HEADER_SIZE=16KB [HeaderScript] Label: \\#-------------USB Upgrade Bin Info---------------- \\# Device : sky848_9s60 \\# Build PATH : /work_ssd3/workspace/Release/build_system/Mstar/7S77_G51/Android \\# Build TIME : 2020-11-23 20:32:01 Prefix: mmc slc 0 1 mmc rmgpt mmc create misc 0x00080000 mmc create param 0x100000 mmc create recovery 0x02000000 mmc create boot 0x01400000 mmc create optee 0x00600000 mmc create armfw 0x00010000 mmc create RTPM 0x00040000 mmc create dtb 0x00100000 mmc create frc 0x00100000 mmc create cm4 0x00080000 mmc create system 0x3C000000 mmc create cache 0x38000000 mmc create vendor 0x2A000000 mmc create tvservice 0x0A000000 mmc create tvconfig 0x01400000 mmc create tvdatabase 0x00800000 mmc create tvcustomer 0x01000000 mmc create tvcertificate 0x00800000 mmc create atv 0x1800000 mmc create factory 0x6C00000 mmc create skyworth 0xD000000 mmc create demura 0x300000 mmc create userdata 0x106800000 # Custom directives at the end of the script (Derived from \u0026#34;File Partition: set_config\u0026#34; and partition commands) Suffix: setenv bootargs console saveenv setenv bootargs console=ttyS0,115200 androidboot.console=ttyS0 root=/dev/ram rw rootwait init=/init CORE_DUMP_PATH=/data/core_dump.%e_%p.gz KDebug=1 delaylogo=true androidboot.selinux=permissive security=selinux platform=mi tee_mode=optee str_ignore_wakelock pm_path=/tvconfig/config/PM.bin loglevel=0 setenv bootlogo_gopidx 2 setenv bootlogo_buffer E_MMAP_ID_BOOTLOGO_BUFFER setenv OSD_BufferAddr E_MMAP_ID_JPD_WRITE_ADR setenv upgrade_mode null setenv factory_poweron_mode memory setenv Customer_Rest true setenv Customer_Rest_AIStandby null setenv str_crc 2 setenv db_table 0 setenv verify n setenv sync_mmap 1 setenv CONFIG_PATH /tvconfig/config setenv mboot_default_env 0 setenv music 1 setenv music_vol 35 setenv BootVideoPlayBySrv 1 setenv BootVideoPlaySourceConfig file:///system/bootvideo.mp4?looptime=1\u0026amp;playtime=10 setenv close_log yes # avbab set_device_state 0 setenv devicestate unlock saveenv # avbab disable-verity setenv MstarUpgrade_complete 1 setenv sync_mmap 1 setenv db_table 0 saveenv printenv # List of partitions to pack # Sizes are derived from \u0026#34;mmc create\u0026#34; or explicit sizes in the source script. # List of partitions to pack # [partition_name] - Name of partition. Shold begin with \u0026#34;part/\u0026#34; # create - flag to generate \u0026#34;mmc create\u0026#34; directive. It requires \u0026#34;size\u0026#34; parameter # size - Required parameter, if create flag sets to True. Partition size to create [hex] # erase - flag to generate \u0026#34;mmc erase.p\u0026#34; directive. # imageFile - Path to image file to pack # type - partition type: # partitionImage - Plain partition image. It generates \u0026#34;filepartload\u0026#34; and \u0026#34;mmc write.p\u0026#34; directives # secureInfo - signature file. Uses \u0026#34;store_secure_info\u0026#34; directive # nuttxConfig - Nuttx config file. Uses \u0026#34;store_nuttx_config\u0026#34; directive # lzo - pack partition/chunk to lzo. Uses \u0026#34;mmc unlzo\u0026#34; directive # chunkSize - chunk size to split partition. A single chunk uses, if chunkSize is not set. Units: B, KB, MB, GB [part/sboot] imageFile=${Main:ProjectFolder}/sboot.img type=sboot emptySkip=True [part/MBOOT] imageFile=${Main:ProjectFolder}/MBOOT.img type=partitionImage emptySkip=False # File Partition: misc [part/misc] erase=True # File Partition: param [part/param] erase=True imageFile=${Main:ProjectFolder}/param.img # File Partition: recovery [part/recovery] erase=True imageFile=${Main:ProjectFolder}/recovery.img type=partitionImage command=setenv recoverycmd mmc read.p 0x23000000 dtb 0x00100000\\; mmc read.p 0x25000000 recovery 0x02000000\\; bootm 0x25000000 saveenv # File Partition: boot [part/boot] erase=True imageFile=${Main:ProjectFolder}/boot.img command=setenv bootcmd mmc read.p 0x23000000 dtb 0x00100000\\; mmc read.p 0x25000000 boot 0x01400000\\; bootm 0x25000000 saveenv type=partitionImage [part/optee] imageFile=${Main:ProjectFolder}/optee.bin type=multi2optee emptySkip=False [part/armfw] imageFile=${Main:ProjectFolder}/armfw.bin type=multi2optee emptySkip=False # File Partition: RT_PM [part/RTPM] erase=True imageFile=${Main:ProjectFolder}/RTPM.img type=partitionImage [part/dtb] erase=True imageFile=${Main:ProjectFolder}/dtb.img type=partitionImage emptySkip=True [part/frc] erase=True imageFile=${Main:ProjectFolder}/frc.img type=partitionImage emptySkip=False [part/cm4] erase=True imageFile=${Main:ProjectFolder}/cm4.img type=partitionImage emptySkip=False # File Partition: system [part/system] erase=True imageFile=${Main:ProjectFolder}/system.img type=partitionImage sparse=True chunkSize=150MB # File Partition: cache [part/cache] erase=True imageFile=${Main:ProjectFolder}/cache.img type=partitionImage sparse=True # File Partition: vendor [part/vendor] erase=True imageFile=${Main:ProjectFolder}/vendor.img type=partitionImage sparse=True chunkSize=200MB # File Partition: tvservice [part/tvservice] erase=True imageFile=${Main:ProjectFolder}/tvservice.img type=partitionImage # File Partition: tvconfig [part/tvconfig] erase=True imageFile=${Main:ProjectFolder}/tvconfig.img type=partitionImage # File Partition: tvdatabase [part/tvdatabase] erase=True imageFile=${Main:ProjectFolder}/tvdatabase.img type=partitionImage # File Partition: tvcustomer [part/tvcustomer] erase=True imageFile=${Main:ProjectFolder}/tvcustomer.img type=partitionImage # File Partition: tvcertificate [part/tvcertificate] erase=True imageFile=${Main:ProjectFolder}/tvcertificate.img type=partitionImage # File Partition: atv [part/atv] erase=True imageFile=${Main:ProjectFolder}/atv.img type=partitionImage [part/factory] erase=True imageFile=${Main:ProjectFolder}/factory.img type=partitionImage # File Partition: skyworth [part/skyworth] erase=True imageFile=${Main:ProjectFolder}/skyworth.img type=partitionImage # File Partition: demura [part/demura] erase=True # File Partition: userdata [part/userdata] erase=True imageFile=${Main:ProjectFolder}/userdata.img type=partitionImage sparse=True 打包刷机镜像 # 在mstart-bin-tool目录下启动终端，运行命令为uv run .\\pack.py .\\configs\\XXXXX.ini，生成的 MstarUpgrade.bin 即为最终的刷机包。\n五、U盘烧录与刷机 # 以下内容仅限于前述特定电视。\n拷贝文件：将新生成的 MstarUpgrade.bin 复制到 FAT32 格式 U 盘的根目录。注意：部分海信或老款设备可能要求放入 /Target/His$xxx$Upgrade.bin其中$xxx$为特定型号，请根据具体机型确认。 进入强刷模式： 断开电视电源。 插入 U 盘到 USB 2.0 接口。 按住电视机身（非遥控器）的“待机/确定（开关机）”键不松手。 接通电源，直到屏幕出现蓝色/绿色的升级进度条再松手。 等待重启：刷机过程约 5-10 分钟，完成后电视会自动重启。期间严禁断电！断电可能会导致刷写过程失败，直接影响底层芯片存储，导致系统无法开机。 界面提示示例： ┌─────────────────────────┐ │ System Upgrading... │ │ ████████░░░░ 65% │ │ Do NOT Power Off! │ └─────────────────────────┘ 问题集锦 # 刷机后系统后台更新怎么办？ 如果电视厂商下发的OTA刷机包包含新的boot镜像，重新刷写升级过程会导致Maigisk Patch后的boot镜像被覆盖，进而导致root权限消失。主要有以下方法解决：\n禁用系统更新，一劳永逸。~~~有一些系统更新就是为了增加广告，越更新越卡~~~ 找到OTA包提取boot镜像进行按照步骤重新刷入。简易流程为ADB系统登录，使用命令su获取root权限，运行du -h获取分区存储情况，手动更新电视系统，期间不断运行du -h观察系统分区占用情况，最后找到不断增多的分区目录，找到*.zip之类的OTA升级包。将OTA升级包当作zip或者rar格式的刷机包对待即可。 电视无法识别 U 盘？ 确认 U 盘文件系统为 FAT32（不支持 NTFS/ExFAT）。 分区表需为 MBR 格式（不支持 GPT）。 尝试更换 USB 2.0 接口或更小容量（\u0026lt;8GB）的 U 盘。 系统无法正常启动或者刷机错误 可能是刷机失败或固件不兼容，使用官方固件或联系售后。 Boot 校验失败：Root 后的 boot 镜像未通过校验。尝试刷回原厂 boot.img 确认是否恢复。 System 权限错误：Linux 下修改文件后未修正文件权限（通常需 0644 或 0755）。 救砖：准备好官方原版固件，按上述“强刷模式”重新刷入即可修复。 参考资料 # 精简system.img和tvconfig.img让电视迅捷如风 康佳LED37R5200PDF电视精简升级 Magisk官方文档 mstar-bin-tool GitHub ","date":"2025/12/14","externalUrl":null,"permalink":"/posts/android_rom/05-example-tv/","section":"吾生有涯，而知无涯","summary":"","title":"安卓刷机教程-05创维电视刷机实战","type":"posts"},{"content":"","date":"2025/12/14","externalUrl":null,"permalink":"/tags/%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"基础教程","type":"tags"},{"content":"","date":"2025/12/14","externalUrl":null,"permalink":"/tags/%E7%B3%BB%E7%BB%9F%E4%BF%AE%E6%94%B9/","section":"Tags","summary":"","title":"系统修改","type":"tags"},{"content":" 概述 # 电视（包括各类型盒子）芯片通常有两种Amlogic和Mstar，需要适用不同的工具。\n实战流程 # 中兴盒子参数 # 固件包搜索及修改 # 需要待添加的功能\n刷机步骤流程 # ","date":"2025/12/14","externalUrl":null,"permalink":"/posts/android_rom/04-example-tvbox/","section":"吾生有涯，而知无涯","summary":"","title":"安卓刷机教程-04中兴盒子实战","type":"posts"},{"content":"介绍安卓系统刷机的基础知识和一些常用的工具\n安卓系统刷机教程01-基础篇 # 基本概念 # Root权限、安卓刷机包镜像，刷机包，刷机流程（解包、打包、刷入），ADB，系统校验\n使用到工具分类 # 工欲善其事必先利其器，安卓系统刷机修改需要使用到一些工具软件，大多数软件通常在window操作系统下运行，\n0. 基础环境配置 # Python环境配置 如果涉及到使用python环境，建议使用uv管理器直接安装运行。以下给出建议的代码，代码通常在windows系统的powershell运行，linux系统可自行查询对应的配置代码。\npowershell -ExecutionPolicy ByPass -c \u0026#34;irm https://astral.sh/uv/install.ps1 | iex\u0026#34; cd XXX #对应的源码文件包 uv python install uv run xxxx.py xxx # 运行代码 ADB及fastboot工具 Android SDK Platform-Tools 是 Android SDK 的一个组件。它包含与 Android 平台进行交互的工具，主要是 adb 和 fastboot。虽然 adb 和 fastboot 中的某些新功能仅适用于较新的 Android 版本，但它们是向后兼容的，因此您只需使用最新版本的 SDK 平台工具即可。下载地址为https://developer.android.google.cn/tools/releases/platform-tools?hl=zh-cn。\n1. 固件打包解包工具 # 序号 名称 开发语言 运行平台 适用SOC 功能描述 来源地址 1 Mstar-bin-tool1 Python Win或Linux，Python 晨星Mstar 刷机固件包解包合成解密等功能 mstar-bin-tool 2 TIK Win或Linux 通用 通用类型的解包打包软件 3 payload-dumper-go Go 全平台支持 通用 An android OTA payload dumper written in Go. https://github.com/ssut/payload-dumper-go 2. 系统修改工具 # 大多数是基础的镜像修改，主要使用的工具软件有HxD Hex Editor（二进制编辑调整）、WinMergeU（二进制或其他格式文件对比）、Beyond Compare（文件对比）等等\n3. 连接工具 # 使用列表格式，说明清楚内容和下载地址 主要工具有 谷歌官方的platform-tools adb，开心连接助手，\n4. 其他可能需要软件工具 # 系统软件优化类\nMagisk KernelSu 增强管理工具\n小白文件管理器 影视小软件\n星火电视海外版，直播观看电视频道， Uvvidio影视 各式类型的TVBox，优点是开源空壳，只需要视频源即可进行观看视频内容。 网络调试类\n系统抓包软件，如PorxyPin、Reqable等等。 这是一个脚注的内容。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025/12/08","externalUrl":null,"permalink":"/posts/android_rom/01-basic/","section":"吾生有涯，而知无涯","summary":"","title":"安卓刷机教程-01基础篇","type":"posts"},{"content":"","date":"2025/07/30","externalUrl":null,"permalink":"/series/linux%E6%95%99%E7%A8%8B/","section":"Series","summary":"","title":"Linux教程","type":"series"},{"content":"","date":"2025/07/30","externalUrl":null,"permalink":"/tags/linux%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"Linux教程","type":"tags"},{"content":" 信创电脑修改Root密码教程 # 如果在使用银河麒麟操作系统V10时不慎忘记了root密码，可以按照以下步骤重置：\n重启系统并进入GRUB菜单：首先，需要重启银河麒麟V10操作系统。在系统启动过程中，屏幕会显示GRUB菜单。请注意时机，在GRUB菜单出现后迅速按下e键。 输入GRUB账户密码：银河麒麟V10服务器版操作系统在GRUB模式下可能需要输入账户密码才能进一步操作。默认情况下，账户名为root，密码为Kylin123123。\n修改启动参数：在GRUB编辑界面中，使用上下箭头键将光标移至以linux开头的行。在该行末尾添加以下参数：rw init=/bin/bash console=tty0。这些参数会将系统引导至单用户模式，允许你无需密码即可执行系统命令。修改完成后，按下Ctrl+X或根据屏幕提示的快捷键启动系统。 修改root密码 系统启动进入单用户模式后，你会看到一个命令行界面。此时，输入passwd root命令并按回车键。系统会提示你输入并确认新的root密码。直接输入两遍你想要设置的新密码即可，无需输入原密码。\n重启系统： 密码修改完成后，需要重启系统以使更改生效。请注意，在单用户模式下，直接使用reboot命令可能无效。建议使用完整路径/usr/sbin/reboot来重启系统，并可加上-f参数以强制重启。 ","date":"2025/07/30","externalUrl":null,"permalink":"/posts/selfhost/keylin-change-pwd/","section":"吾生有涯，而知无涯","summary":"","title":"信创电脑修改Root密码教程","type":"posts"},{"content":" 读《Python高手之路》笔记 # 涵盖的主题较为宽泛，主要是给出如何成为一名经验丰富的Python开发者提供的建议。可以算的上是Python开发的实践知识。\n主题一 # 涉及主题：系统库开发与使用\n主题二：开发自己的Python库 # 良好的项目工程代码 API优化修改 import warnings class Car(object): def __init__(self,name): self.name = name def turn_left(self): ``` Older API ``` warnings.warn(\u0026#34;the function `turn_left` has been ,Please use `self.turn(direction=\u0026#34;left\u0026#34;) instead.`\u0026#34;) return self.turn(direction=\u0026#34;left\u0026#34;) def turn(self,direction:str): ``` New APi ``` pass 主题三 # 推荐实践操作 ","date":"2025/07/30","externalUrl":null,"permalink":"/posts/learn_record/python/guide/","section":"吾生有涯，而知无涯","summary":"","title":"Python读书笔记","type":"posts"},{"content":"","date":"2025/07/30","externalUrl":null,"permalink":"/tags/python%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"Python教程","type":"tags"},{"content":"","date":"2025/07/30","externalUrl":null,"permalink":"/series/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/","section":"Series","summary":"","title":"读书笔记","type":"series"},{"content":"","date":"2025/07/30","externalUrl":null,"permalink":"/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/","section":"Tags","summary":"","title":"读书笔记","type":"tags"},{"content":" Docker基础篇 # 基础篇主要包含\nDocker常用的命令集合 curl -sSL https://get.docker.com | bash usermod -aG docker $USER newgrp docker docker attach 和 docker exec的区别\nDockerfile编写技巧及镜像优化方法\nCOPY 和 ADD区别 使用COPY复制指定目录下的所有文件到容器，不包括本级目录。 ADD 使用COPY和ADD时，可以使用--chown=\u0026lt;user\u0026gt;:\u0026lt;group\u0026gt;直接进行文件权限的更改，这样就不用使用RUN在容器里面执行chown，不仅方便，还可以缩小镜像的体积。\nCMD 和 ENTRYPOINT的区别 CMD在使用docker run时可以被直接覆盖，比如我们使用docker run启动centos:env-cmd镜像，并在后面指定启动命令，此时CMD就会被覆盖；在ENTRYPOINT指定的不能被直接覆盖，后置的命令会被当作ENTRYPOINT的参数。ENTRYPOINT也是可以被覆盖的，需要指定\u0026ndash;entrypoint参数，比如我们指定entrypoint为ls，后置命令为/tmp，就相当于ENTRYPOINT是ls，CMD是/tmp.\n多阶段构建的含义，方法和具体操作\n通俗来讲，多阶段构建就是在Dockerfile中定义多个FROM，每个FROM下有多个不同的指令，一般可简单分为构建步骤和生成业务应用镜像步骤，也就是说前面一个或多个阶段用于构建，产生业务应用的包或其他产物，之后的阶段将上述阶段产生的包或其他产物再次构建成镜像。这样一来，最后一步就没有了构建时产生的缓存文件，也起到了优化镜像体积的作用。\n对于Docker多阶段构建的例子，提供配置文件并进行详细说明\nK8S 基础篇 # K8s适用的场景，解决的主要问题，优缺点 K8s架构解析、主要组成及各部分功能特性 涉及的基础概念： Pod：Pod可被建模为一组具有共享命名空间、卷、IP地址和端口的容器。 Service Pod镜像拉取策略和重启策略 给出一个创建Pod的标准格式（YAML格式） K8S服务部署调度篇 # 该部分章节主要讲述\nReplication Controller和ReplicaSet 简单总结和回顾 Replication Controller可以确保Pod副本数达到期望值，也就是RC定义的数量。换句话说，Replication Controller可以确保一个Pod或一组同类Pod总是可用的。 ReplicaSet是支持基于集合的标签选择器的下一代ReplicationController，它主要用作Deployment协调创建、删除和更新Pod，和ReplicationController唯一的区别是，ReplicaSet支持标签选择器。 无状态应用管理Deployment 有状态应用管理StatefulSet 守护进程集DaemonSet CronJob K8S服务发布+配置管理篇 # 服务发布Service和Ingress # 配置管理ConfigMap和Secret # 两者的区别 相对于Secret，ConfigMap更倾向于存储和共享非敏感、未加密的配置信息，假如是在集群中使用敏感信息，最好使用Secret。 ","date":"2025/07/02","externalUrl":null,"permalink":"/posts/learn_record/k8s-and-docker/","section":"吾生有涯，而知无涯","summary":"","title":"K8S\u0026Docker 100问题","type":"posts"},{"content":"","date":"2025/07/02","externalUrl":null,"permalink":"/series/k8s%E6%95%99%E7%A8%8B/","section":"Series","summary":"","title":"K8s教程","type":"series"},{"content":"","date":"2025/06/16","externalUrl":null,"permalink":"/tags/mysql/","section":"Tags","summary":"","title":"MySQL","type":"tags"},{"content":"在掌握MySQL基础语法之后，通过实战练习是巩固知识的最佳途径。本文精选了50道经典的MySQL基础练习题，涵盖了数据查询、多表联结、聚合函数、子查询、窗口函数和日期函数等多个核心概念。通过这些练习，您不仅能够检验自己的SQL技能，还能深入理解数据库查询的各种应用场景。\n一、环境准备 # 为了方便练习，请先创建以下四张表及插入测试数据。 表格说明：\nStudent：学生信息表（学生ID, 学生姓名, 出生日期, 性别） Course：课程信息表（课程ID, 课程名称, 教师ID） Teacher：教师信息表（教师ID, 教师姓名） SC：学生选课及成绩表（学生ID, 课程ID, 成绩） -- 创建学生表 CREATE TABLE Student( sid VARCHAR(10) PRIMARY KEY, sname VARCHAR(10), sage DATETIME, ssex NVARCHAR(10) ); -- 插入学生数据 INSERT INTO Student VALUES(\u0026#39;01\u0026#39; , \u0026#39;赵雷\u0026#39; , \u0026#39;1990-01-01\u0026#39; , \u0026#39;男\u0026#39;); INSERT INTO Student VALUES(\u0026#39;02\u0026#39; , \u0026#39;钱电\u0026#39; , \u0026#39;1990-12-21\u0026#39; , \u0026#39;男\u0026#39;); INSERT INTO Student VALUES(\u0026#39;03\u0026#39; , \u0026#39;孙风\u0026#39; , \u0026#39;1990-05-20\u0026#39; , \u0026#39;男\u0026#39;); INSERT INTO Student VALUES(\u0026#39;04\u0026#39; , \u0026#39;李云\u0026#39; , \u0026#39;1990-08-06\u0026#39; , \u0026#39;男\u0026#39;); INSERT INTO Student VALUES(\u0026#39;05\u0026#39; , \u0026#39;周梅\u0026#39; , \u0026#39;1991-12-01\u0026#39; , \u0026#39;女\u0026#39;); INSERT INTO Student VALUES(\u0026#39;06\u0026#39; , \u0026#39;吴兰\u0026#39; , \u0026#39;1992-03-01\u0026#39; , \u0026#39;女\u0026#39;); INSERT INTO Student VALUES(\u0026#39;07\u0026#39; , \u0026#39;郑竹\u0026#39; , \u0026#39;1989-07-01\u0026#39; , \u0026#39;女\u0026#39;); INSERT INTO Student VALUES(\u0026#39;08\u0026#39; , \u0026#39;王菊\u0026#39; , \u0026#39;1990-01-20\u0026#39; , \u0026#39;女\u0026#39;); -- 创建课程表 CREATE TABLE Course( cid VARCHAR(10) PRIMARY KEY, cname VARCHAR(10), tid VARCHAR(10) ); -- 插入课程数据 INSERT INTO Course VALUES(\u0026#39;01\u0026#39; , \u0026#39;语文\u0026#39; , \u0026#39;02\u0026#39;); INSERT INTO Course VALUES(\u0026#39;02\u0026#39; , \u0026#39;数学\u0026#39; , \u0026#39;01\u0026#39;); INSERT INTO Course VALUES(\u0026#39;03\u0026#39; , \u0026#39;英语\u0026#39; , \u0026#39;03\u0026#39;); -- 创建教师表 CREATE TABLE Teacher( tid VARCHAR(10) PRIMARY KEY, tname VARCHAR(10) ); -- 插入教师数据 INSERT INTO Teacher VALUES(\u0026#39;01\u0026#39; , \u0026#39;张三\u0026#39;); INSERT INTO Teacher VALUES(\u0026#39;02\u0026#39; , \u0026#39;李四\u0026#39;); INSERT INTO Teacher VALUES(\u0026#39;03\u0026#39; , \u0026#39;王五\u0026#39;); -- 创建学生选课成绩表 CREATE TABLE SC( sid VARCHAR(10), cid VARCHAR(10), score DECIMAL(18,1), PRIMARY KEY (sid, cid) -- 联合主键，确保一个学生一门课只有一条成绩 ); -- 插入成绩数据 INSERT INTO SC VALUES(\u0026#39;01\u0026#39; , \u0026#39;01\u0026#39; , 80); INSERT INTO SC VALUES(\u0026#39;01\u0026#39; , \u0026#39;02\u0026#39; , 90); INSERT INTO SC VALUES(\u0026#39;01\u0026#39; , \u0026#39;03\u0026#39; , 99); INSERT INTO SC VALUES(\u0026#39;02\u0026#39; , \u0026#39;01\u0026#39; , 70); INSERT INTO SC VALUES(\u0026#39;02\u0026#39; , \u0026#39;02\u0026#39; , 60); INSERT INTO SC VALUES(\u0026#39;02\u0026#39; , \u0026#39;03\u0026#39; , 80); INSERT INTO SC VALUES(\u0026#39;03\u0026#39; , \u0026#39;01\u0026#39; , 80); INSERT INTO SC VALUES(\u0026#39;03\u0026#39; , \u0026#39;02\u0026#39; , 80); INSERT INTO SC VALUES(\u0026#39;03\u0026#39; , \u0026#39;03\u0026#39; , 80); INSERT INTO SC VALUES(\u0026#39;04\u0026#39; , \u0026#39;01\u0026#39; , 50); INSERT INTO SC VALUES(\u0026#39;04\u0026#39; , \u0026#39;02\u0026#39; , 30); INSERT INTO SC VALUES(\u0026#39;04\u0026#39; , \u0026#39;03\u0026#39; , 20); INSERT INTO SC VALUES(\u0026#39;05\u0026#39; , \u0026#39;01\u0026#39; , 76); INSERT INTO SC VALUES(\u0026#39;05\u0026#39; , \u0026#39;02\u0026#39; , 87); INSERT INTO SC VALUES(\u0026#39;06\u0026#39; , \u0026#39;01\u0026#39; , 31); INSERT INTO SC VALUES(\u0026#39;06\u0026#39; , \u0026#39;03\u0026#39; , 34); INSERT INTO SC VALUES(\u0026#39;07\u0026#39; , \u0026#39;02\u0026#39; , 89); INSERT INTO SC VALUES(\u0026#39;07\u0026#39; , \u0026#39;03\u0026#39; , 98); 二、正文部分 # 2.1 复杂条件查询与联结 # 1. 查询\u0026quot;01\u0026quot;课程比\u0026quot;02\u0026quot;课程成绩高的学生的信息及课程分数 # SELECT s.sid, s.sname, s.sage, s.ssex, sc1.score AS \u0026#39;01_score\u0026#39;, sc2.score AS \u0026#39;02_score\u0026#39; FROM Student s JOIN SC sc1 ON s.sid = sc1.sid AND sc1.cid = \u0026#39;01\u0026#39; JOIN SC sc2 ON s.sid = sc2.sid AND sc2.cid = \u0026#39;02\u0026#39; WHERE sc1.score \u0026gt; sc2.score; 结果：\n+-----+-------+---------------------+------+----------+----------+ | sid | sname | sage | ssex | 01_score | 02_score | +-----+-------+---------------------+------+----------+----------+ | 02 | 钱电 | 1990-12-21 00:00:00 | 男 | 70.0 | 60.0 | | 04 | 李云 | 1990-08-06 00:00:00 | 男 | 50.0 | 30.0 | +-----+-------+---------------------+------+----------+----------+ 2 rows in set 解析： 这道题需要比较同一个学生在不同课程上的成绩。\n自连接SC表： 我们需要两次引用SC表，一次用于获取'01\u0026rsquo;课程的成绩（设为sc1），另一次用于获取'02\u0026rsquo;课程的成绩（设为sc2）。 联结条件： s.sid = sc1.sid AND sc1.cid = '01'：将学生表与第一次引用的SC表联结，并筛选出'01\u0026rsquo;课程的成绩。 s.sid = sc2.sid AND sc2.cid = '02'：将学生表与第二次引用的SC表联结，并筛选出'02\u0026rsquo;课程的成绩。 筛选条件： WHERE sc1.score \u0026gt; sc2.score：筛选出'01\u0026rsquo;课程成绩高于'02\u0026rsquo;课程成绩的学生。 选择字段： 最后从Student表和两次联结中选择所需的学生信息和两门课程的成绩。 2. 查询学生选课存在\u0026quot;01\u0026quot;课程但可能不存在\u0026quot;02\u0026quot;课程的情况（不存在时显示为 null） # SELECT sc1.sid, sc1.cid AS \u0026#39;01_cid\u0026#39;, sc1.score AS \u0026#39;01_score\u0026#39;, sc2.cid AS \u0026#39;02_cid\u0026#39;, sc2.score AS \u0026#39;02_score\u0026#39; FROM SC sc1 LEFT JOIN SC sc2 ON sc1.sid = sc2.sid AND sc2.cid = \u0026#39;02\u0026#39; WHERE sc1.cid = \u0026#39;01\u0026#39;; 结果：\n+-----+--------+----------+--------+----------+ | sid | 01_cid | 01_score | 02_cid | 02_score | +-----+--------+----------+--------+----------+ | 01 | 01 | 80.0 | 02 | 90.0 | | 02 | 01 | 70.0 | 02 | 60.0 | | 03 | 01 | 80.0 | 02 | 80.0 | | 04 | 01 | 50.0 | 02 | 30.0 | | 05 | 01 | 76.0 | 02 | 87.0 | | 06 | 01 | 31.0 | NULL | NULL | +-----+--------+----------+--------+----------+ 6 rows in set 解析： 这道题要求我们找到所有选修了课程'01\u0026rsquo;的学生，并尝试匹配他们是否也选修了课程'02\u0026rsquo;。如果选了'02\u0026rsquo;，显示成绩；如果没选，则'02\u0026rsquo;课程的信息显示为NULL。\n左连接(LEFT JOIN)： LEFT JOIN非常适合这种“左边存在，右边可能不存在”的场景。我们以选修'01\u0026rsquo;课程的记录作为左表。 构建左表： 使用 SC sc1 WHERE sc1.cid = '01' 筛选出所有'01\u0026rsquo;课程的成绩记录作为左表。 构建右表并联结： 使用 SC sc2 ON sc1.sid = sc2.sid AND sc2.cid = '02' 将左表与另一份SC表（作为sc2）联结，条件是学生ID相同且课程ID为'02\u0026rsquo;。 LEFT JOIN 的特性： 如果sc2中找不到匹配的记录，sc2的相关字段将显示为NULL，这正是题目要求的效果。 3. 查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩 # SQL (推荐显式JOIN):\nSELECT s.sid, s.sname, AVG(sc.score) AS average_score FROM Student s JOIN SC sc ON s.sid = sc.sid GROUP BY s.sid, s.sname HAVING AVG(sc.score) \u0026gt;= 60; 结果：\n+-----+-------+---------------+ | sid | sname | average_score | +-----+-------+---------------+ | 01 | 赵雷 | 89.66667 | | 02 | 钱电 | 70.00000 | | 03 | 孙风 | 80.00000 | | 05 | 周梅 | 81.50000 | | 07 | 郑竹 | 93.50000 | +-----+-------+---------------+ 5 rows in set 解析： 这道题需要计算每个学生的平均成绩，并基于平均成绩进行筛选。\n多表联结： 需要Student表获取学生姓名，SC表获取学生成绩。通过Student s JOIN SC sc ON s.sid = sc.sid 联结这两张表。 分组(GROUP BY)： 为了计算每个学生的平均成绩，需要根据sid和sname对结果进行分组。GROUP BY s.sid, s.sname。 聚合函数(AVG)： AVG(sc.score)用于计算每个分组的平均成绩。 筛选分组(HAVING)： HAVING AVG(sc.score) \u0026gt;= 60用于过滤分组，只保留平均成绩大于或等于60分的学生。HAVING子句用于过滤GROUP BY后的结果，而WHERE子句是在GROUP BY之前过滤行。 4. 查询在 SC 表存在成绩的学生信息 # SQL (推荐使用EXISTS或INNER JOIN):\n-- 使用 INNER JOIN (更直接且常用) SELECT DISTINCT s.* FROM Student s JOIN SC sc ON s.sid = sc.sid; -- 或者使用 EXISTS (更注重是否有匹配，效率有时更高) -- SELECT s.* -- FROM Student s -- WHERE EXISTS (SELECT 1 FROM SC sc WHERE s.sid = sc.sid); 结果：\n+-----+-------+---------------------+------+ | sid | sname | sage | ssex | +-----+-------+---------------------+------+ | 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | | 02 | 钱电 | 1990-12-21 00:00:00 | 男 | | 03 | 孙风 | 1990-05-20 00:00:00 | 男 | | 04 | 李云 | 1990-08-06 00:00:00 | 男 | | 05 | 周梅 | 1991-12-01 00:00:00 | 女 | | 06 | 吴兰 | 1992-03-01 00:00:00 | 女 | | 07 | 郑竹 | 1989-07-01 00:00:00 | 女 | +-----+-------+---------------------+------+ 7 rows in set 解析： 这道题要求找出所有有成绩记录的学生信息。\n明确目标： 只需要Student表中的信息，但条件是其sid必须存在于SC表中。 INNER JOIN 方法： Student s JOIN SC sc ON s.sid = sc.sid：通过学生ID联结Student和SC表。INNER JOIN的特性是只返回两个表中都存在匹配项的行。 SELECT DISTINCT s.*：因为一个学生可能有多门课程成绩，直接SELECT s.*会返回重复的学生信息，所以使用DISTINCT去重。 EXISTS 子查询方法 (效率优势)： WHERE EXISTS (SELECT 1 FROM SC sc WHERE s.sid = sc.sid)：EXISTS子句会检查子查询是否返回行。如果子查询至少返回一行，EXISTS条件就为真。这种方法通常在只需要判断存在性时比IN或JOIN更高效，因为它在找到第一个匹配后就会停止扫描。 5. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的成绩总和 # SELECT s.sid AS 学生编号, s.sname AS 学生姓名, COUNT(sc.cid) AS 选课总数, SUM(sc.score) AS 课程成绩总和 FROM Student s LEFT JOIN SC sc ON s.sid = sc.sid GROUP BY s.sid, s.sname; 结果：\n+----------+----------+----------+--------------+ | 学生编号 | 学生姓名 | 选课总数 | 课程成绩总和 | +----------+----------+----------+--------------+ | 01 | 赵雷 | 3 | 269.0 | | 02 | 钱电 | 3 | 210.0 | | 03 | 孙风 | 3 | 240.0 | | 04 | 李云 | 3 | 100.0 | | 05 | 周梅 | 2 | 163.0 | | 06 | 吴兰 | 2 | 65.0 | | 07 | 郑竹 | 2 | 187.0 | | 08 | 王菊 | 0 | NULL | +----------+----------+----------+--------------+ 8 rows in set 解析： 这道题需要统计每个学生的选课数量和总成绩，包括那些可能没有选课的学生。\n左连接(LEFT JOIN)： 为了包含所有学生（即使他们没有选课或成绩），需要使用Student s LEFT JOIN SC sc ON s.sid = sc.sid。这样，Student表中的所有学生都会被保留，即使他们在SC表中没有匹配项，SC表的字段也会显示为NULL。 分组(GROUP BY)： 同样，为了统计每个学生的数据，需要根据学生ID和姓名进行分组 GROUP BY s.sid, s.sname。 聚合函数(COUNT, SUM)： COUNT(sc.cid)：统计每个学生选修的课程数量。COUNT(列名)只会统计非NULL的值，所以没有选课的学生COUNT(sc.cid)会是0。 SUM(sc.score)：计算每个学生所有课程的成绩总和。对于没有选课的学生，SUM将返回NULL。 6. 查询「李」姓老师的数量 # SELECT COUNT(tid) AS \u0026#39;李姓老师数量\u0026#39; FROM Teacher WHERE tname LIKE \u0026#39;李%\u0026#39;; 结果：\n+--------------+ | 李姓老师数量 | +--------------+ | 1 | +--------------+ 1 row in set 解析： 这道题考察基本的WHERE子句和LIKE操作符。\n筛选条件： WHERE tname LIKE '李%'：使用LIKE操作符进行模式匹配。'李%'表示以“李”字开头的任何字符串。 聚合函数(COUNT)： COUNT(tid)用于统计符合条件的教师数量。 7. 查询学过「张三」老师授课的同学的信息 # SELECT DISTINCT s.* FROM Student s JOIN SC sc ON s.sid = sc.sid JOIN Course c ON sc.cid = c.cid JOIN Teacher t ON c.tid = t.tid WHERE t.tname = \u0026#39;张三\u0026#39;; 结果：\n+-----+-------+---------------------+------+ | sid | sname | sage | ssex | +-----+-------+---------------------+------+ | 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | | 02 | 钱电 | 1990-12-21 00:00:00 | 男 | | 03 | 孙风 | 1990-05-20 00:00:00 | 男 | | 04 | 李云 | 1990-08-06 00:00:00 | 男 | | 05 | 周梅 | 1991-12-01 00:00:00 | 女 | | 07 | 郑竹 | 1989-07-01 00:00:00 | 女 | +-----+-------+---------------------+------+ 6 rows in set 解析： 这道题需要通过学生成绩 -\u0026gt; 课程 -\u0026gt; 教师的链式关系查找。\n多表联结： 涉及Student、SC、Course和Teacher四张表。 Student s JOIN SC sc ON s.sid = sc.sid：学生与成绩关联。 JOIN Course c ON sc.cid = c.cid：成绩与课程关联。 JOIN Teacher t ON c.tid = t.tid：课程与教师关联。 筛选条件： WHERE t.tname = '张三'：筛选出由“张三”老师授课的课程。 去重(DISTINCT)： 因为一个学生可能学了“张三”老师的多门课程，所以需要DISTINCT s.*来确保每个学生只出现一次。 8. 查询没有学全所有课程的同学的信息 # SELECT s.sid, s.sname, s.sage, s.ssex, COUNT(sc.cid) AS 所学课程数 FROM Student s LEFT JOIN SC sc ON s.sid = sc.sid GROUP BY s.sid, s.sname HAVING COUNT(sc.cid) \u0026lt; (SELECT COUNT(cid) FROM Course); 结果：\n+-----+-------+---------------------+------+------------+ | sid | sname | sage | ssex | 所学课程数 | +-----+-------+---------------------+------+------------+ | 05 | 周梅 | 1991-12-01 00:00:00 | 女 | 2 | | 06 | 吴兰 | 1992-03-01 00:00:00 | 女 | 2 | | 07 | 郑竹 | 1989-07-01 00:00:00 | 女 | 2 | | 08 | 王菊 | 1990-01-20 00:00:00 | 女 | 0 | +-----+-------+---------------------+------+------------+ 4 rows in set 解析： 这道题需要比较每个学生选修的课程数量与总课程数量。\n获取总课程数： (SELECT COUNT(cid) FROM Course) 子查询用于获取所有课程的总数。 联结与分组： Student s LEFT JOIN SC sc ON s.sid = sc.sid：为了包含所有学生（包括未选课的），使用LEFT JOIN。 GROUP BY s.sid, s.sname：按学生分组，以便统计每位学生的选课数量。 统计选课数量： COUNT(sc.cid) 统计每个学生选修的非NULL课程ID数量。 筛选分组(HAVING)： HAVING COUNT(sc.cid) \u0026lt; (SELECT COUNT(cid) FROM Course)：筛选出选课数量少于总课程数量的学生。 9. 查询至少有一门课与学号为\u0026quot;01\u0026quot;的同学所学相同的同学的信息 # SELECT DISTINCT s.* FROM Student s JOIN SC sc ON s.sid = sc.sid WHERE sc.cid IN (SELECT cid FROM SC WHERE sid = \u0026#39;01\u0026#39;) AND s.sid != \u0026#39;01\u0026#39;; -- 排除 \u0026#39;01\u0026#39; 学生本人 结果：\n+-----+-------+---------------------+------+ | sid | sname | sage | ssex | +-----+-------+---------------------+------+ | 02 | 钱电 | 1990-12-21 00:00:00 | 男 | | 03 | 孙风 | 1990-05-20 00:00:00 | 男 | | 04 | 李云 | 1990-08-06 00:00:00 | 男 | | 05 | 周梅 | 1991-12-01 00:00:00 | 女 | | 06 | 吴兰 | 1992-03-01 00:00:00 | 女 | | 07 | 郑竹 | 1989-07-01 00:00:00 | 女 | +-----+-------+---------------------+------+ 6 rows in set 解析： 这道题需要找出与学生'01\u0026rsquo;有共同选课的其他学生。\n子查询获取'01\u0026rsquo;学生选课： (SELECT cid FROM SC WHERE sid = '01') 获取学生'01\u0026rsquo;选修的所有课程ID列表。 联结与筛选： Student s JOIN SC sc ON s.sid = sc.sid：联结学生表和成绩表。 WHERE sc.cid IN (...)：筛选出那些其选修的课程ID在学生'01\u0026rsquo;选课列表中的记录。 AND s.sid != '01'：排除学生'01\u0026rsquo;本人，因为题目要求是“其他同学”。 去重(DISTINCT)： 因为一个学生可能与'01\u0026rsquo;学生有多个共同课程，所以需要DISTINCT s.*防止重复。 10. 查询和\u0026quot;01\u0026quot;号的同学学习的课程完全相同的其他同学的信息 # SELECT s.sid, s.sname, s.sage, s.ssex, COUNT(sc.cid) AS \u0026#39;共同课程数\u0026#39; FROM Student s JOIN SC sc ON s.sid = sc.sid WHERE sc.cid IN (SELECT sc_01.cid FROM SC sc_01 WHERE sc_01.sid = \u0026#39;01\u0026#39;) -- 首先确保课程在01同学所学范围内 AND s.sid != \u0026#39;01\u0026#39; -- 排除01同学 GROUP BY s.sid, s.sname, s.sage, s.ssex HAVING COUNT(sc.cid) = (SELECT COUNT(cid) FROM SC WHERE sid = \u0026#39;01\u0026#39;); -- 共同课程数必须和01同学的总课程数相同 结果：\n+-----+-------+---------------------+------+--------------+ | sid | sname | sage | ssex | 共同课程数 | +-----+-------+---------------------+------+--------------+ | 02 | 钱电 | 1990-12-21 00:00:00 | 男 | 3 | | 03 | 孙风 | 1990-05-20 00:00:00 | 男 | 3 | | 04 | 李云 | 1990-08-06 00:00:00 | 男 | 3 | +-----+-------+---------------------+------+--------------+ 3 rows in set 解析： 这道题的难度在于“完全相同”，这意味着不仅要选修共同的课程，而且选修的课程总数也要和参照学生一致。\n获取学生'01\u0026rsquo;的总课程数： (SELECT COUNT(cid) FROM SC WHERE sid = '01') 作为HAVING子句的比较基准。 获取学生'01\u0026rsquo;选修的课程列表： (SELECT sc_01.cid FROM SC sc_01 WHERE sc_01.sid = '01') 作为WHERE子句中IN的条件。 联结与初步筛选： Student s JOIN SC sc ON s.sid = sc.sid：联结学生和成绩表。 WHERE sc.cid IN (...)：首先筛选出所有选修了学生'01\u0026rsquo;所学课程的记录。 AND s.sid != '01'：排除学生'01\u0026rsquo;本人。 分组与最终筛选： GROUP BY s.sid, s.sname, s.sage, s.ssex：按学生分组。 HAVING COUNT(sc.cid) = (SELECT COUNT(cid) FROM SC WHERE sid = '01')：在分组后，检查每个学生选修的（与'01\u0026rsquo;共同的）课程数量是否恰好等于学生'01\u0026rsquo;所选的全部课程数量。这样确保了“完全相同”。 11. 查询没学过\u0026quot;张三\u0026quot;老师讲授的任一门课程的学生姓名 # SELECT s.sname FROM Student s WHERE s.sid NOT IN ( SELECT sc.sid FROM SC sc JOIN Course c ON sc.cid = c.cid JOIN Teacher t ON c.tid = t.tid WHERE t.tname = \u0026#39;张三\u0026#39; ); 结果：\n+-------+ | sname | +-------+ | 吴兰 | | 王菊 | +-------+ 2 rows in set 解析： 这道题需要找出那些没有接触过“张三”老师的学生。\n子查询获取学过“张三”老师课程的学生ID： 内部的SELECT sc.sid FROM SC sc JOIN Course c ON sc.cid = c.cid JOIN Teacher t ON c.tid = t.tid WHERE t.tname = '张三' 会联结SC、Course和Teacher表，找出所有学了“张三”老师课的学生ID。 NOT IN 条件： WHERE s.sid NOT IN (...)：主查询从Student表中选择学生姓名，条件是其sid不在子查询返回的学生ID列表中。这有效地排除了所有学过“张三”老师课程的学生。 12. 查询两门及其以上不及格课程的同学的学号，姓名及其平均成绩 # SELECT s.sid, s.sname, AVG(sc.score) AS average_score FROM Student s JOIN SC sc ON s.sid = sc.sid WHERE s.sid IN ( SELECT sid FROM SC WHERE score \u0026lt; 60 GROUP BY sid HAVING COUNT(cid) \u0026gt;= 2 ) GROUP BY s.sid, s.sname; 结果：\n+-----+-------+---------------+ | sid | sname | average_score | +-----+-------+---------------+ | 04 | 李云 | 33.33333 | | 06 | 吴兰 | 32.50000 | +-----+-------+---------------+ 2 rows in set 解析： 这道题需要两个步骤：首先找出不及格课程数量满足条件的学生，然后计算这些学生的平均成绩。\n子查询找出不及格课程数量 \u0026gt;=2 的学生ID： 内部的SELECT sid FROM SC WHERE score \u0026lt; 60 GROUP BY sid HAVING COUNT(cid) \u0026gt;= 2： WHERE score \u0026lt; 60：筛选出所有不及格的成绩记录。 GROUP BY sid：按学生分组。 HAVING COUNT(cid) \u0026gt;= 2：筛选出不及格课程数大于等于2的学生ID。 主查询获取学生信息和总平均成绩： Student s JOIN SC sc ON s.sid = sc.sid：联结学生表和成绩表。 WHERE s.sid IN (...)：将主查询的结果限制在子查询返回的学生ID集合内。 GROUP BY s.sid, s.sname：再次按学生分组，这次是为了计算这些学生的所有课程的平均成绩（而不是仅仅不及格课程的平均）。 AVG(sc.score)：计算每个学生的总平均成绩。 13. 查询\u0026quot;01\u0026quot;课程分数小于 60，按分数降序排列的学生信息 # SELECT s.*, sc.score FROM Student s JOIN SC sc ON s.sid = sc.sid WHERE sc.cid = \u0026#39;01\u0026#39; AND sc.score \u0026lt; 60 ORDER BY sc.score DESC; 结果：\n+-----+-------+---------------------+------+-------+ | sid | sname | sage | ssex | score | +-----+-------+---------------------+------+-------+ | 04 | 李云 | 1990-08-06 00:00:00 | 男 | 50.0 | | 06 | 吴兰 | 1992-03-01 00:00:00 | 女 | 31.0 | +-----+-------+---------------------+------+-------+ 2 rows in set 解析： 这道题是直接的多表查询和筛选排序。\n联结表： Student s JOIN SC sc ON s.sid = sc.sid 联结学生表和成绩表。 筛选条件： WHERE sc.cid = '01' AND sc.score \u0026lt; 60 筛选出课程ID为'01\u0026rsquo;且分数低于60分的记录。 排序： ORDER BY sc.score DESC 按分数降序排列结果。 14. 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩 # SELECT s.sid, s.sname, sc.cid, sc.score, (SELECT AVG(score) FROM SC WHERE sid = s.sid) AS average_score_per_student FROM Student s JOIN SC sc ON s.sid = sc.sid ORDER BY average_score_per_student DESC, s.sid ASC, sc.score DESC; 结果：\n+-----+-------+-----+-------+---------------------------+ | sid | sname | cid | score | average_score_per_student | +-----+-------+-----+-------+---------------------------+ | 07 | 郑竹 | 03 | 98.0 | 93.50000 | | 07 | 郑竹 | 02 | 89.0 | 93.50000 | | 01 | 赵雷 | 03 | 99.0 | 89.66667 | | 01 | 赵雷 | 02 | 90.0 | 89.66667 | | 01 | 赵雷 | 01 | 80.0 | 89.66667 | | 05 | 周梅 | 02 | 87.0 | 81.50000 | | 05 | 周梅 | 01 | 76.0 | 81.50000 | | 03 | 孙风 | 01 | 80.0 | 80.00000 | | 03 | 孙风 | 02 | 80.0 | 80.00000 | | 03 | 03 | 80.0 | 80.00000 | -- 这里应是 \u0026#39;孙风\u0026#39;，数据有截断 | 02 | 钱电 | 03 | 80.0 | 70.00000 | | 02 | 钱电 | 01 | 70.0 | 70.00000 | | 02 | 钱电 | 02 | 60.0 | 70.00000 | | 06 | 吴兰 | 03 | 34.0 | 32.50000 | | 06 | 吴兰 | 01 | 31.0 | 32.50000 | | 04 | 李云 | 01 | 50.0 | 33.33333 | | 04 | 李云 | 02 | 30.0 | 33.33333 | | 04 | 李云 | 03 | 20.0 | 33.33333 | +-----+-------+-----+-------+---------------------------+ 18 rows in set 解析： 这道题要求显示每个学生的每门课程成绩，同时显示该学生的总平均成绩，并根据总平均成绩进行排序。\n联结表： Student s JOIN SC sc ON s.sid = sc.sid 联结学生表和成绩表，这样可以获取每位学生的每门课程成绩。 子查询计算每个学生的平均成绩： (SELECT AVG(score) FROM SC WHERE sid = s.sid)：这是一个相关子查询。对于主查询的每一行（也就是每个学生和每门课的组合），这个子查询都会执行一次，计算当前s.sid对应的学生的平均成绩。 将子查询结果作为新列 average_score_per_student。 排序(ORDER BY)： ORDER BY average_score_per_student DESC, s.sid ASC, sc.score DESC。首先按学生的平均成绩降序排列，如果平均成绩相同，则按学生ID升序排列，如果学生ID也相同，则按单科成绩降序排列。 2.2 聚合、分组与条件统计 # 15. 查询各科成绩最高分、最低分和平均分 # 以如下形式显示： 课程 id，最高分，最低分，平均分，及格率，中等率（[70,80)），优良率（[80-90)），优秀率（\u0026gt;=90） 及格为\u0026gt;=60，中等为：[70,80)，优良为：[80-90)，优秀为：\u0026gt;=90 要求输出课程号和选修人数，查询结果按人数降序排列，若人数相同，按课程号升序 (题目原要求有冲突，此处按实际情况调整)\nSELECT cid AS 课程ID, COUNT(sid) AS 选修人数, -- 补充选修人数 MAX(score) AS 最高分, MIN(score) AS 最低分, AVG(score) AS 平均分, SUM(CASE WHEN score \u0026gt;= 60 THEN 1 ELSE 0 END) / COUNT(sid) AS 及格率, SUM(CASE WHEN score \u0026gt;= 70 AND score \u0026lt; 80 THEN 1 ELSE 0 END) / COUNT(sid) AS 中等率, SUM(CASE WHEN score \u0026gt;= 80 AND score \u0026lt; 90 THEN 1 ELSE 0 END) / COUNT(sid) AS 优良率, SUM(CASE WHEN score \u0026gt;= 90 THEN 1 ELSE 0 END) / COUNT(sid) AS 优秀率 FROM SC GROUP BY cid ORDER BY 选修人数 DESC, 课程ID ASC; -- 按人数降序，人数相同按课程号升序 结果：\n+--------+----------+--------+--------+----------+--------+--------+--------+--------+ | 课程ID | 选修人数 | 最高分 | 最低分 | 平均分 | 及格率 | 中等率 | 优良率 | 优秀率 | +--------+----------+--------+--------+----------+--------+--------+--------+--------+ | 01 | 6 | 80.0 | 31.0 | 64.50000 | 0.6667 | 0.1667 | 0.3333 | 0.0000 | | 02 | 6 | 90.0 | 30.0 | 72.66667 | 0.8333 | 0.1667 | 0.3333 | 0.1667 | | 03 | 6 | 99.0 | 20.0 | 68.50000 | 0.6667 | 0.0000 | 0.3333 | 0.3333 | +--------+----------+--------+--------+----------+--------+--------+--------+--------+ 3 rows in set 解析： 这道题需要对每门课程进行多维度的统计分析。\n分组(GROUP BY)： GROUP BY cid 将成绩记录按课程ID分组，这样我们就可以对每门课程进行单独的统计。 聚合函数： MAX(score), MIN(score), AVG(score) 分别计算每门课程的最高分、最低分和平均分。 COUNT(sid) 计算每门课程的选修学生人数。 条件聚合(CASE WHEN)： 这是本题的关键。CASE WHEN ... THEN 1 ELSE 0 END 结构用于将符合条件的记录标记为1，不符合的标记为0。然后通过SUM(...)对这些1进行求和，再除以COUNT(sid)（总人数）就可以得到百分比。 及格率: SUM(CASE WHEN score \u0026gt;= 60 THEN 1 ELSE 0 END) / COUNT(sid) 中等率: SUM(CASE WHEN score \u0026gt;= 70 AND score \u0026lt; 80 THEN 1 ELSE 0 END) / COUNT(sid) 优良率: SUM(CASE WHEN score \u0026gt;= 80 AND score \u0026lt; 90 THEN 1 ELSE 0 END) / COUNT(sid) 优秀率: SUM(CASE WHEN score \u0026gt;= 90 THEN 1 ELSE 0 END) / COUNT(sid) 排序(ORDER BY)： ORDER BY 选修人数 DESC, 课程ID ASC 首先按选修人数降序排列，如果人数相同，则按课程ID升序排列。 16. 按各科成绩进行排序，并显示排名， Score 重复时保留名次空缺 # SELECT sid, cid, score, RANK() OVER (PARTITION BY cid ORDER BY score DESC) AS ranked FROM SC; 结果：\n+-----+-----+-------+--------+ | sid | cid | score | ranked | +-----+-----+-------+--------+ | 01 | 01 | 80.0 | 1 | | 03 | 01 | 80.0 | 1 | | 05 | 01 | 76.0 | 3 | | 02 | 01 | 70.0 | 4 | | 04 | 01 | 50.0 | 5 | | 06 | 01 | 31.0 | 6 | | 01 | 02 | 90.0 | 1 | | 07 | 02 | 89.0 | 2 | | 05 | 02 | 87.0 | 3 | | 03 | 02 | 80.0 | 4 | | 02 | 02 | 60.0 | 5 | | 04 | 02 | 30.0 | 6 | | 01 | 03 | 99.0 | 1 | | 07 | 03 | 98.0 | 2 | | 02 | 03 | 80.0 | 3 | | 03 | 03 | 80.0 | 3 | | 06 | 03 | 34.0 | 5 | | 04 | 03 | 20.0 | 6 | +-----+-----+-------+--------+ 18 rows in set 解析： 这道题需要使用窗口函数来实现排名功能。\n窗口函数(RANK() OVER())： RANK()是一个排名函数，它为分区内的每一行分配一个排名。 PARTITION BY cid：表示根据cid（课程ID）进行分区。这意味着排名会在每个课程内独立进行。 ORDER BY score DESC：在每个分区内部，根据score（成绩）按降序进行排序。 RANK()的特性： RANK()函数在遇到相同值时会分配相同的排名，并且会跳过下一个排名（即存在名次空缺）。例如，1, 1, 3, 4。 17. 查询学生的总成绩，并进行排名，总分重复时保留名次空缺 # SELECT sid, SUM(score) AS total_score, RANK() OVER (ORDER BY SUM(score) DESC) AS ranked FROM SC GROUP BY sid; 结果：\n+-----+-------------+--------+ | sid | total_score | ranked | +-----+-------------+--------+ | 01 | 269.0 | 1 | | 03 | 240.0 | 2 | | 02 | 210.0 | 3 | | 07 | 187.0 | 4 | | 05 | 163.0 | 5 | | 04 | 100.0 | 6 | | 06 | 65.0 | 7 | +-----+-------------+--------+ 7 rows in set 解析： 这道题同样使用窗口函数，但需要在计算总成绩之后再进行排名。\n计算总成绩并分组： SUM(score) AS total_score FROM SC GROUP BY sid 首先计算每个学生的总成绩，并按学生分组。 窗口函数(RANK() OVER())： ORDER BY SUM(score) DESC：因为不需要按课程分区，所以PARTITION BY被省略，排名直接对所有学生总成绩进行，按总成绩降序排列。 RANK() 的特性在这里同样适用，如果总分相同，会分配相同的排名并跳过下一个排名。 18. 查询学生的总成绩，并进行排名，总分重复时不保留名次空缺 # SELECT sid, SUM(score) AS total_score, DENSE_RANK() OVER (ORDER BY SUM(score) DESC) AS ranked FROM SC GROUP BY sid; 结果：\n+-----+-------------+--------+ | sid | total_score | ranked | +-----+-------------+--------+ | 01 | 269.0 | 1 | | 03 | 240.0 | 2 | | 02 | 210.0 | 3 | | 07 | 187.0 | 4 | | 05 | 163.0 | 5 | | 04 | 100.0 | 6 | | 06 | 65.0 | 7 | +-----+-------------+--------+ 7 rows in set 解析： 这道题与上题类似，但要求在分数重复时不保留名次空缺。\n计算总成绩并分组： 步骤与上题相同。 窗口函数(DENSE_RANK() OVER())： DENSE_RANK() 与 RANK() 的主要区别在于，当存在相同排名时，DENSE_RANK() 不会跳过下一个排名。例如，1, 1, 2, 3。 由于我们的示例数据中没有学生的总成绩完全相同，所以RANK()和DENSE_RANK()的结果在这里看起来是一样的。但理解两者区别是关键。 总结三种排名函数： ROW_NUMBER(): 为分区内每一行分配一个唯一的序列号，不考虑值是否相同。(1, 2, 3, 4) RANK(): 相同值分配相同排名，跳过下一个排名。(1, 1, 3, 4) DENSE_RANK(): 相同值分配相同排名，不跳过下一个排名。(1, 1, 2, 3) 19. 统计各科成绩各分数段人数：课程编号，[100-85)，[85-70)，[70-60)，[60-0] 及所占百分比 # SELECT cid AS 课程ID, SUM(CASE WHEN score \u0026gt;= 85 AND score \u0026lt;= 100 THEN 1 ELSE 0 END) AS \u0026#39;100-85(人数)\u0026#39;, SUM(CASE WHEN score \u0026gt;= 70 AND score \u0026lt; 85 THEN 1 ELSE 0 END) AS \u0026#39;85-70(人数)\u0026#39;, SUM(CASE WHEN score \u0026gt;= 60 AND score \u0026lt; 70 THEN 1 ELSE 0 END) AS \u0026#39;70-60(人数)\u0026#39;, SUM(CASE WHEN score \u0026gt;= 0 AND score \u0026lt; 60 THEN 1 ELSE 0 END) AS \u0026#39;60-0(人数)\u0026#39;, -- 百分比（如果需要，将人数除以总人数） ROUND(SUM(CASE WHEN score \u0026gt;= 85 AND score \u0026lt;= 100 THEN 1 ELSE 0 END) * 100.0 / COUNT(sid), 2) AS \u0026#39;100-85(%)\u0026#39;, ROUND(SUM(CASE WHEN score \u0026gt;= 70 AND score \u0026lt; 85 THEN 1 ELSE 0 END) * 100.0 / COUNT(sid), 2) AS \u0026#39;85-70(%)\u0026#39;, ROUND(SUM(CASE WHEN score \u0026gt;= 60 AND score \u0026lt; 70 THEN 1 ELSE 0 END) * 100.0 / COUNT(sid), 2) AS \u0026#39;70-60(%)\u0026#39;, ROUND(SUM(CASE WHEN score \u0026gt;= 0 AND score \u0026lt; 60 THEN 1 ELSE 0 END) * 100.0 / COUNT(sid), 2) AS \u0026#39;60-0(%)\u0026#39; FROM SC GROUP BY cid ORDER BY cid; 结果：\n+--------+-------------+------------+------------+-----------+-----------+----------+----------+----------+ | 课程ID | 100-85(人数)| 85-70(人数)| 70-60(人数)| 60-0(人数)| 100-85(%) | 85-70(%) | 70-60(%) | 60-0(%) | +--------+-------------+------------+------------+-----------+-----------+----------+----------+----------+ | 01 | 2 | 1 | 1 | 2 | 33.33 | 16.67 | 16.67 | 33.33 | | 02 | 3 | 1 | 1 | 1 | 50.00 | 16.67 | 16.67 | 16.67 | | 03 | 3 | 1 | 0 | 2 | 50.00 | 16.67 | 0.00 | 33.33 | +--------+-------------+------------+------------+-----------+-----------+----------+----------+----------+ 3 rows in set 解析： 这道题需要为每个课程计算不同分数段的人数及百分比，这是典型的条件聚合问题。\n分组(GROUP BY)： GROUP BY cid 按课程ID分组。 条件聚合(SUM(CASE WHEN \u0026hellip;))： 使用CASE WHEN结构定义每个分数段的条件。例如，CASE WHEN score \u0026gt;= 85 AND score \u0026lt;= 100 THEN 1 ELSE 0 END，如果分数落在[85, 100]区间，则该行计为1，否则为0。 SUM(...) 对CASE WHEN返回的1和0求和，即可得到该分数段的人数。 计算百分比： 将每个分数段的人数除以COUNT(sid)（该课程的总人数），再乘以100，并使用ROUND()函数保留两位小数。注意，COUNT(sid)应使用浮点数（如100.0）进行计算以避免整数除法截断。 20. 查询各科成绩前三名的记录 # SELECT sid, cid, score, ranked FROM ( SELECT sid, cid, score, RANK() OVER (PARTITION BY cid ORDER BY score DESC) AS ranked FROM SC ) AS subquery WHERE ranked \u0026lt;= 3; 结果：\n+-----+-----+-------+--------+ | sid | cid | score | ranked | +-----+-----+-------+--------+ | 01 | 01 | 80.0 | 1 | | 03 | 01 | 80.0 | 1 | | 05 | 01 | 76.0 | 3 | | 01 | 02 | 90.0 | 1 | | 07 | 02 | 89.0 | 2 | | 05 | 02 | 87.0 | 3 | | 01 | 03 | 99.0 | 1 | | 07 | 03 | 98.0 | 2 | | 02 | 03 | 80.0 | 3 | | 03 | 03 | 80.0 | 3 | +-----+-----+-------+--------+ 10 rows in set 解析： 这道题是排名函数的常见应用，但需要注意WHERE子句不能直接引用窗口函数的结果。\n子查询进行排名： 内层子查询 (SELECT sid, cid, score, RANK() OVER (PARTITION BY cid ORDER BY score DESC) AS ranked FROM SC) 与第16题相同，先计算出每门课程内学生的排名。 将子查询的结果作为一个派生表（或称临时表），起别名为subquery。 外层查询筛选： WHERE ranked \u0026lt;= 3：在外层查询中，对subquery中的ranked列进行筛选，只保留排名在前三名的记录。 这里使用RANK()是因为题目中“前三名”可能意味着有并列第一、第二的情况，RANK()会保留名次空缺，如果想要连续的排名，可以使用DENSE_RANK()。 21. 查询每门课程被选修的学生数 # SELECT c.cid AS 课程ID, c.cname AS 课程名称, COUNT(sc.sid) AS 选修学生数 FROM Course c LEFT JOIN SC sc ON c.cid = sc.cid GROUP BY c.cid, c.cname ORDER BY c.cid; 结果：\n+--------+----------+--------------+ | 课程ID | 课程名称 | 选修学生数 | +--------+----------+--------------+ | 01 | 语文 | 6 | | 02 | 数学 | 6 | | 03 | 英语 | 6 | +--------+----------+--------------+ 3 rows in set 解析： 这道题统计每门课程有多少学生选修。\n左连接(LEFT JOIN)： Course c LEFT JOIN SC sc ON c.cid = sc.cid。使用LEFT JOIN是为了确保即使某门课程没有学生选修，它也能出现在结果中（此时选修学生数为0）。在这个数据集里，所有课程都有学生选修，所以INNER JOIN也会得到相同行数的结果，但LEFT JOIN更通用。 分组(GROUP BY)： GROUP BY c.cid, c.cname 按课程ID和课程名称分组。 聚合函数(COUNT)： COUNT(sc.sid) 统计每个课程下的学生数量。COUNT(列名)只会统计非NULL的值，因此如果一门课没有学生选修，sc.sid将为NULL，COUNT会正确地返回0。 22. 查询出只选修两门课程的学生学号和姓名 # SELECT s.sid, s.sname, COUNT(sc.cid) AS 选修课程数 FROM Student s JOIN SC sc ON s.sid = sc.sid GROUP BY s.sid, s.sname HAVING COUNT(sc.cid) = 2; 结果：\n+-----+-------+------------+ | sid | sname | 选修课程数 | +-----+-------+------------+ | 05 | 周梅 | 2 | | 06 | 吴兰 | 2 | | 07 | 郑竹 | 2 | +-----+-------+------------+ 3 rows in set 解析： 这道题需要统计每个学生的选课数量，并筛选出恰好选修了两门课程的学生。\n联结表： Student s JOIN SC sc ON s.sid = sc.sid 联结学生表和成绩表。 分组(GROUP BY)： GROUP BY s.sid, s.sname 按学生ID和姓名分组。 聚合函数(COUNT)： COUNT(sc.cid) 统计每个学生选修的课程数量。 筛选分组(HAVING)： HAVING COUNT(sc.cid) = 2 筛选出选修课程数等于2的学生。 关于 SELECT 语句中别名在 HAVING 子句中的使用： 在MySQL中，HAVING子句确实可以在一些情况下引用SELECT列表中定义的别名。这是MySQL的一个特色扩展，在标准SQL中，HAVING子句通常不允许引用SELECT列表中定义的别名，因为HAVING的逻辑处理阶段通常在SELECT之前。所以，在其他数据库系统或严格遵守标准SQL的场景下，为了保证兼容性，应直接使用聚合表达式（例如 HAVING AVG(score) \u0026gt; 60 而不是 HAVING average_score \u0026gt; 60）。但对于MySQL，您的代码是有效的。\n23. 查询男生、女生人数 # SELECT ssex, COUNT(sid) AS 人数 FROM Student GROUP BY ssex; 结果：\n+------+------+ | ssex | 人数 | +------+------+ | 男 | 4 | | 女 | 4 | +------+------+ 2 rows in set 解析： 这道题是简单分组统计。\n分组(GROUP BY)： GROUP BY ssex 按性别分组。 聚合函数(COUNT)： COUNT(sid) 统计每个性别分类下的学生ID数量，即人数。 24. 查询名字中含有「风」字的学生信息 # SELECT * FROM Student WHERE sname LIKE \u0026#39;%风%\u0026#39;; 结果：\n+-----+-------+---------------------+------+ | sid | sname | sage | ssex | +-----+-------+---------------------+------+ | 03 | 孙风 | 1990-05-20 00:00:00 | 男 | +-----+-------+---------------------+------+ 1 row in set 解析： 这道题考察LIKE操作符与通配符的使用。\nLIKE操作符： 用于在WHERE子句中进行模式匹配。 通配符 %： 表示零个、一个或多个字符。 '%风%' 匹配任何包含“风”字的字符串（“风”字前后可以有任意字符）。 '风%' 匹配以“风”字开头的字符串。 '%风' 匹配以“风”字结尾的字符串。 25. 查询同名同性学生名单，并统计同名人数 # SELECT sname, ssex, COUNT(sid) AS 同名同性人数 FROM Student GROUP BY sname, ssex HAVING COUNT(sid) \u0026gt;= 2; 结果：\nEmpty set (只有两条insert into Student values(\u0026#39;01\u0026#39; , \u0026#39;赵雷\u0026#39; , \u0026#39;1990-01-01\u0026#39; , \u0026#39;男\u0026#39;); insert into Student values(\u0026#39;02\u0026#39; , \u0026#39;钱电\u0026#39; , \u0026#39;1990-12-21\u0026#39; , \u0026#39;男\u0026#39;); insert into Student values(\u0026#39;03\u0026#39; , \u0026#39;孙风\u0026#39; , \u0026#39;1990-05-20\u0026#39; , \u0026#39;男\u0026#39;); insert into Student values(\u0026#39;04\u0026#39; , \u0026#39;李云\u0026#39; , \u0026#39;1990-08-06\u0026#39; , \u0026#39;男\u0026#39;); insert into Student values(\u0026#39;05\u0026#39; , \u0026#39;周梅\u0026#39; , \u0026#39;1991-12-01\u0026#39; , \u0026#39;女\u0026#39;); insert into Student values(\u0026#39;06\u0026#39; , \u0026#39;吴兰\u0026#39; , \u0026#39;1992-03-01\u0026#39; , \u0026#39;女\u0026#39;); insert into Student values(\u0026#39;07\u0026#39; , \u0026#39;郑竹\u0026#39; , \u0026#39;1989-07-01\u0026#39; , \u0026#39;女\u0026#39;); insert into Student values(\u0026#39;08\u0026#39; , \u0026#39;王菊\u0026#39; , \u0026#39;1990-01-20\u0026#39; , \u0026#39;女\u0026#39;);，根据提供的测试数据，没有同名同性学生。) 解析： 这道题需要找出姓名和性别都相同的学生，并统计其数量。\n分组(GROUP BY)： GROUP BY sname, ssex 按姓名和性别进行分组。只有姓名和性别都相同的学生才会分到同一组。 聚合函数(COUNT)： COUNT(sid) 统计每个分组中的学生数量。 筛选分组(HAVING)： HAVING COUNT(sid) \u0026gt;= 2 筛选出那些人数大于或等于2的分组，意味着存在同名同性别的学生。 2.3 日期与时间函数 # 26. 查询 1990 年出生的学生名单 # SELECT * FROM Student WHERE YEAR(sage) = 1990; 结果：\n+-----+-------+---------------------+------+ | sid | sname | sage | ssex | +-----+-------+---------------------+------+ | 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | | 02 | 钱电 | 1990-12-21 00:00:00 | 男 | | 03 | 孙风 | 1990-05-20 00:00:00 | 男 | | 04 | 李云 | 1990-08-06 00:00:00 | 男 | | 08 | 王菊 | 1990-01-20 00:00:00 | 女 | +-----+-------+---------------------+------+ 5 rows in set 解析： 这道题考察日期函数YEAR()的使用。\nYEAR() 函数： YEAR(datetime_expression) 从一个日期时间表达式中提取年份。 筛选条件： WHERE YEAR(sage) = 1990 筛选出出生年份为1990年的学生。 27. 查询每门课程的平均成绩，结果按平均成绩降序排列，平均成绩相同时，按课程编号升序排列 # SELECT cid, AVG(score) AS average_score FROM SC GROUP BY cid ORDER BY average_score DESC, cid ASC; 结果：\n+-----+---------------+ | cid | average_score | +-----+---------------+ | 02 | 72.66667 | | 03 | 68.50000 | | 01 | 64.50000 | +-----+---------------+ 3 rows in set 解析： 这道题涉及聚合、分组和多级排序。\n分组(GROUP BY)： GROUP BY cid 按课程ID分组。 聚合函数(AVG)： AVG(score) 计算每个课程的平均成绩。 多级排序(ORDER BY)： ORDER BY average_score DESC, cid ASC 首先按average_score降序排列。如果average_score相同，则进一步按cid升序排列。这将确保结果满足题目对平均成绩相同情况下的排序要求。 28. 查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩 # SELECT s.sid, s.sname, AVG(sc.score) AS average_score FROM Student s JOIN SC sc ON s.sid = sc.sid GROUP BY s.sid, s.sname HAVING AVG(sc.score) \u0026gt;= 85; 结果：\n+-----+-------+---------------+ | sid | sname | average_score | +-----+-------+---------------+ | 01 | 赵雷 | 89.66667 | | 07 | 郑竹 | 93.50000 | +-----+-------+---------------+ 2 rows in set 解析： 这道题与第3题类似，再次巩固了JOIN、GROUP BY和HAVING的组合使用。\n联结表： Student s JOIN SC sc ON s.sid = sc.sid 联结学生表和成绩表。 分组(GROUP BY)： GROUP BY s.sid, s.sname 按学生分组。 聚合函数(AVG)： AVG(sc.score) 计算每个学生的平均成绩。 筛选分组(HAVING)： HAVING AVG(sc.score) \u0026gt;= 85 筛选出平均成绩大于或等于85分的学生。 29. 查询课程名称为「数学」，且分数低于 60 的学生姓名和分数 # SELECT s.sname, sc.score, c.cname FROM Student s JOIN SC sc ON s.sid = sc.sid JOIN Course c ON sc.cid = c.cid WHERE c.cname = \u0026#39;数学\u0026#39; AND sc.score \u0026lt; 60; 结果：\n+-------+-------+-------+ | sname | score | cname | +-------+-------+-------+ | 李云 | 30.0 | 数学 | +-------+-------+-------+ 1 row in set 解析： 这道题涉及了三表联结和多个筛选条件。\n三表联结： Student s JOIN SC sc ON s.sid = sc.sid：获取学生姓名和成绩之间的关系。 JOIN Course c ON sc.cid = c.cid：将成绩与课程名称关联起来。 筛选条件： WHERE c.cname = '数学' AND sc.score \u0026lt; 60 同时满足课程名称为“数学”且分数低于60分这两个条件。 30. 查询所有学生的课程及分数情况（存在学生没成绩，没选课的情况） # SELECT s.sname AS 学生姓名, c.cname AS 课程名称, sc.score AS 成绩 FROM Student s LEFT JOIN SC sc ON s.sid = sc.sid LEFT JOIN Course c ON sc.cid = c.cid ORDER BY s.sid, c.cid; 结果 (部分截取，因为结果行数较多且包含NULL):\n+----------+----------+-------+ | 学生姓名 | 课程名称 | 成绩 | +----------+----------+-------+ | 赵雷 | 语文 | 80.0 | | 赵雷 | 数学 | 90.0 | | 赵雷 | 英语 | 99.0 | | 钱电 | 语文 | 70.0 | | 钱电 | 数学 | 60.0 | | 钱电 | 英语 | 80.0 | | 孙风 | 语文 | 80.0 | | 孙风 | 数学 | 80.0 | | 孙风 | 英语 | 80.0 | | 李云 | 语文 | 50.0 | | 李云 | 数学 | 30.0 | | 李云 | 英语 | 20.0 | | 周梅 | 语文 | 76.0 | | 周梅 | 数学 | 87.0 | | 吴兰 | 语文 | 31.0 | | 吴兰 | 英语 | 34.0 | | 郑竹 | 数学 | 89.0 | | 郑竹 | 英语 | 98.0 | | 王菊 | NULL | NULL | +----------+----------+-------+ 19 rows in set 解析： 这道题强调了“存在学生没成绩，没选课的情况”，这明确指出了需要使用LEFT JOIN。\n第一个左连接(Student 与 SC)： Student s LEFT JOIN SC sc ON s.sid = sc.sid。 以Student表为主，保留所有学生。如果学生没有选课记录，sc表相关的字段将为NULL。 第二个左连接(SC 与 Course)： LEFT JOIN Course c ON sc.cid = c.cid。 以第一个连接的结果作为左表，继续与Course表连接。如果sc.cid为NULL（ 如果sc.cid为NULL（表示学生未选课），那么Course表中的字段也将为NULL。 选择字段： SELECT s.sname, c.cname, sc.score 选择学生姓名、课程名称和成绩。对于未选课的学生，cname和score将显示为NULL。 排序 (ORDER BY): ORDER BY s.sid, c.cid 使得结果更易读，按学生ID和课程ID进行排序。 31. 查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数 # SELECT s.sname AS 姓名, c.cname AS 课程名称, sc.score AS 分数 FROM Student s JOIN SC sc ON s.sid = sc.sid JOIN Course c ON sc.cid = c.cid WHERE sc.score \u0026gt; 70 ORDER BY s.sname, c.cname; 结果：\n+----------+----------+-------+ | 姓名 | 课程名称 | 分数 | +----------+----------+-------+ | 孙风 | 英语 | 80.0 | | 孙风 | 数学 | 80.0 | | 孙风 | 语文 | 80.0 | | 周梅 | 数学 | 87.0 | | 周梅 | 语文 | 76.0 | | 赵雷 | 英语 | 99.0 | | 赵雷 | 数学 | 90.0 | | 赵雷 | 语文 | 80.0 | | 钱电 | 英语 | 80.0 | | 郑竹 | 英语 | 98.0 | | 郑竹 | 数学 | 89.0 | +----------+----------+-------+ 11 rows in set 解析： 这道题是多表联结和简单的条件筛选。\n多表联结： Student s JOIN SC sc ON s.sid = sc.sid：将学生与他们的成绩联结。 JOIN Course c ON sc.cid = c.cid：将成绩与课程信息联结，以便获取课程名称。 筛选条件： WHERE sc.score \u0026gt; 70 筛选出所有成绩大于70分的记录。 选择字段： SELECT s.sname, c.cname, sc.score 返回所需的姓名、课程名称和分数。 排序 (ORDER BY): ORDER BY s.sname, c.cname 使输出结果更具可读性。 32. 查询不及格的课程 # SELECT DISTINCT c.cname AS 课程名称 FROM Course c JOIN SC sc ON c.cid = sc.cid WHERE sc.score \u0026lt; 60; 结果：\n+----------+ | 课程名称 | +----------+ | 语文 | | 数学 | | 英语 | +----------+ 3 rows in set 解析： 这道题要求列出所有有不及格成绩的课程的名称。\n联结表： Course c JOIN SC sc ON c.cid = sc.cid 联结课程表和成绩表。 筛选条件： WHERE sc.score \u0026lt; 60 找出所有有不及格成绩的记录。 去重(DISTINCT)： SELECT DISTINCT c.cname 因为一门课程可能有多名学生不及格，或者一个学生在一门课上可能有多个不及格记录（虽然根据我们的设计，一个学生一门课只有一个记录），DISTINCT确保每个不及格的课程名称只出现一次。 33. 查询课程编号为 01 且课程成绩在 60 分以上的学生的学号和姓名 # SELECT s.sid AS 学号, s.sname AS 姓名, sc.score AS 成绩 FROM Student s JOIN SC sc ON s.sid = sc.sid WHERE sc.cid = \u0026#39;01\u0026#39; AND sc.score \u0026gt; 60; 结果：\n+----+------+-------+ | 学号 | 姓名 | 成绩 | +----+------+-------+ | 01 | 赵雷 | 80.0 | | 02 | 钱电 | 70.0 | | 03 | 孙风 | 80.0 | | 05 | 周梅 | 76.0 | +----+------+-------+ 4 rows in set 解析： 这道题涉及两表联结和复合条件筛选。\n联结表： Student s JOIN SC sc ON s.sid = sc.sid 联结学生表和成绩表。 筛选条件： WHERE sc.cid = '01' AND sc.score \u0026gt; 60 同时满足课程ID为'01\u0026rsquo;和成绩大于60分这两个条件。 选择字段： SELECT s.sid, s.sname, sc.score 返回学号、姓名和成绩。 34. 求每门课程的学生人数 # SELECT c.cid AS 课程ID, c.cname AS 课程名称, COUNT(sc.sid) AS 学生人数 FROM Course c LEFT JOIN SC sc ON c.cid = sc.cid GROUP BY c.cid, c.cname ORDER BY c.cid; 结果：\n+--------+----------+----------+ | 课程ID | 课程名称 | 学生人数 | +--------+----------+----------+ | 01 | 语文 | 6 | | 02 | 数学 | 6 | | 03 | 英语 | 6 | +--------+----------+----------+ 3 rows in set 解析： 这道题与第21题基本相同，旨在统计每门课程的选修学生数量。\n左连接(LEFT JOIN)： Course c LEFT JOIN SC sc ON c.cid = sc.cid。使用LEFT JOIN是为了确保所有课程都包含在结果中，即使它们没有学生选修（在这种情况下，学生人数将为0）。 分组(GROUP BY)： GROUP BY c.cid, c.cname 按课程ID和课程名称分组。 聚合函数(COUNT)： COUNT(sc.sid) 统计每个课程下的非NULL学生ID数量。 35. 成绩没有重复的情况下，查询选修「张三」老师所授课程的学生中，成绩最高的学生信息及其成绩 # SELECT s.sid, s.sname, s.sage, s.ssex, sc.score AS 成绩, c.cname AS 课程名称, t.tname AS 教师姓名 FROM Student s JOIN SC sc ON s.sid = sc.sid JOIN Course c ON sc.cid = c.cid JOIN Teacher t ON c.tid = t.tid WHERE t.tname = \u0026#39;张三\u0026#39; ORDER BY sc.score DESC LIMIT 1; 结果：\n+-----+-------+---------------------+------+-------+----------+----------+ | sid | sname | sage | ssex | 成绩 | 课程名称 | 教师姓名 | +-----+-------+---------------------+------+-------+----------+----------+ | 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | 90.0 | 数学 | 张三 | +-----+-------+---------------------+------+-------+----------+----------+ 1 row in set 解析： 这道题需要找出「张三」老师所教授课程中的最高分。\n多表联结： 联结Student, SC, Course, Teacher四张表，以便获取所有相关信息。 筛选条件： WHERE t.tname = '张三' 筛选出「张三」老师教授的课程。 排序 (ORDER BY)： ORDER BY sc.score DESC 将结果按成绩降序排列，最高分会在第一行。 限制结果 (LIMIT)： LIMIT 1 仅返回最高分的那一行。题目假设“成绩没有重复”，即只有一个最高分学生。 36. 成绩有重复的情况下，查询选修「张三」老师所授课程的学生中，成绩最高的学生信息及其成绩 # SELECT s.sid, s.sname, s.sage, s.ssex, sc.score AS 成绩, c.cname AS 课程名称, t.tname AS 教师姓名 FROM Student s JOIN SC sc ON s.sid = sc.sid JOIN Course c ON sc.cid = c.cid JOIN Teacher t ON c.tid = t.tid WHERE t.tname = \u0026#39;张三\u0026#39; AND sc.score = ( SELECT MAX(sc_inner.score) FROM SC sc_inner JOIN Course c_inner ON sc_inner.cid = c_inner.cid JOIN Teacher t_inner ON c_inner.tid = t_inner.tid WHERE t_inner.tname = \u0026#39;张三\u0026#39; ); 结果：\n+-----+-------+---------------------+------+-------+----------+----------+ | sid | sname | sage | ssex | 成绩 | 课程名称 | 教师姓名 | +-----+-------+---------------------+------+-------+----------+----------+ | 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | 90.0 | 数学 | 张三 | +-----+-------+---------------------+------+-------+----------+----------+ 1 row in set 解析： 在成绩有重复的情况下，LIMIT 1可能只能返回一个最高分者，而无法返回所有并列最高分者。此时需要使用子查询来确定最高分值。\n子查询获取「张三」老师课程的最高分： SELECT MAX(sc_inner.score) FROM SC sc_inner JOIN Course c_inner ON sc_inner.cid = c_inner.cid JOIN Teacher t_inner ON c_inner.tid = t_inner.tid WHERE t_inner.tname = '张三'：这个子查询与主查询的联结逻辑类似，目的是找出「张三」老师所授课程中的最高分数。 主查询联结与筛选： 主查询 Student s JOIN SC sc ... JOIN Teacher t 联结所有相关表。 WHERE t.tname = '张三' AND sc.score = (...)：首先筛选出「张三」老师教授的课程，然后使用AND条件，将这些课程的成绩与子查询返回的最高分数进行比较。这样，所有取得最高分（包括并列最高分）的学生都会被返回。 37. 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 # SELECT DISTINCT sc1.sid AS 学生编号, sc1.cid AS 课程编号, sc1.score AS 学生成绩 FROM SC sc1 JOIN SC sc2 ON sc1.sid = sc2.sid AND sc1.score = sc2.score AND sc1.cid != sc2.cid ORDER BY sc1.sid, sc1.cid; 结果：\n+----------+----------+----------+ | 学生编号 | 课程编号 | 学生成绩 | +----------+----------+----------+ | 02 | 03 | 80.0 | | 03 | 01 | 80.0 | | 03 | 02 | 80.0 | | 03 | 03 | 80.0 | +----------+----------+----------+ 4 rows in set 解析： 这道题需要比较同一个学生在不同课程上的成绩。\n自连接(SELF JOIN)： SC sc1 JOIN SC sc2：将SC表与自身联结，通常用于比较同一表中的不同行。 联结条件： sc1.sid = sc2.sid：确保是同一个学生。 sc1.score = sc2.score：学生在两门课程中的成绩相同。 sc1.cid != sc2.cid：确保是不同的课程（如果cid相同，那只是同一门课程的成绩，没有比较意义）。 去重(DISTINCT)： SELECT DISTINCT sc1.sid, sc1.cid, sc1.score：因为自连接可能会产生重复的组合（例如，如果学生A在课B和课C都得了80分，那么(A, B, 80)和(A, C, 80)都会匹配，并且以sc1或sc2身份出现，需要去重来显示唯一的课程成绩对）。 38. 查询每门功成绩最好的前两名 # SELECT sid, cid, score, ranked FROM ( SELECT sid, cid, score, DENSE_RANK() OVER (PARTITION BY cid ORDER BY score DESC) AS ranked FROM SC ) AS subquery WHERE ranked \u0026lt;= 2; 结果：\n+-----+-----+-------+--------+ | sid | cid | score | ranked | +-----+-----+-------+--------+ | 01 | 01 | 80.0 | 1 | | 03 | 01 | 80.0 | 1 | | 05 | 01 | 76.0 | 2 | | 01 | 02 | 90.0 | 1 | | 07 | 02 | 89.0 | 2 | | 05 | 02 | 87.0 | 3 | -- 这个结果是原始输出, 05 sid 应该被排除 | 01 | 03 | 99.0 | 1 | | 07 | 03 | 98.0 | 2 | +-----+-----+-------+--------+ 8 rows in set (注意：原答案中05-02-87.0-3被错误包含，已修正) 更正后的结果（按 DENSE_RANK() OVER (PARTITION BY cid ORDER BY score DESC) 且 ranked \u0026lt;= 2 的预期）：\n+-----+-----+-------+--------+ | sid | cid | score | ranked | +-----+-----+-------+--------+ | 01 | 01 | 80.0 | 1 | | 03 | 01 | 80.0 | 1 | | 05 | 01 | 76.0 | 2 | | 01 | 02 | 90.0 | 1 | | 07 | 02 | 89.0 | 2 | | 01 | 03 | 99.0 | 1 | | 07 | 03 | 98.0 | 2 | +-----+-----+-------+--------+ 7 rows in set 解析： 这道题与第20题类似，也是排名函数的应用。关键在于理解“前两名”的定义。\n子查询进行排名： 在子查询中，DENSE_RANK() OVER (PARTITION BY cid ORDER BY score DESC) 为每门课程的学生成绩进行排名。DENSE_RANK() 的特性是相同分数获得相同名次，且后续名次是连续的，不会跳过。 外层查询筛选： WHERE ranked \u0026lt;= 2 筛选出排名在前两名的记录。 关于 RANK() 和 DENSE_RANK() 的选择： 如果“前两名”指的是名次为1和2（即使有并列），使用 DENSE_RANK() 更符合直觉。例如，1人第一、1人第二，或者2人并列第一、1人第二，都会被包含。 如果“前两名”指的是名次为1和2，且如果1是两个学生，则2是跳过的（变成3），那么使用 RANK()。 根据本题的语境，“最好的前两名”通常倾向于包含所有并列的顶尖成绩，DENSE_RANK()是更合适的选择。原结果中包含 05 | 02 | 87.0 | 3 是不正确的，因为它排名为3，不符合 \u0026lt;=2 的条件。 39. 统计每门课程的学生选修人数（超过 5 人的课程才统计） # SELECT c.cid AS 课程ID, c.cname AS 课程名称, COUNT(sc.sid) AS 选修人数 FROM Course c LEFT JOIN SC sc ON c.cid = sc.cid GROUP BY c.cid, c.cname HAVING COUNT(sc.sid) \u0026gt; 5 ORDER BY c.cid; 结果：\n+--------+----------+----------+ | 课程ID | 课程名称 | 选修人数 | +--------+----------+----------+ | 01 | 语文 | 6 | | 02 | 数学 | 6 | | 03 | 英语 | 6 | +--------+----------+----------+ 3 rows in set 解析： 这道题结合了分组统计和HAVING子句。\n左连接(LEFT JOIN)： Course c LEFT JOIN SC sc ON c.cid = sc.cid，目的是获取所有课程及其对应的选课信息。 分组(GROUP BY)： GROUP BY c.cid, c.cname 按课程ID和名称分组，以便对每门课程的学生数进行统计。 聚合函数(COUNT)： COUNT(sc.sid) 统计每门课程的选修人数。 筛选分组(HAVING)： HAVING COUNT(sc.sid) \u0026gt; 5 筛选出选修人数超过5人的课程。HAVING 用于过滤GROUP BY后的分组。 40. 检索至少选修两门课程的学生学号 # SELECT s.sid AS 学号, s.sname AS 姓名, COUNT(sc.cid) AS 选修课程总数 FROM Student s JOIN SC sc ON s.sid = sc.sid GROUP BY s.sid, s.sname HAVING COUNT(sc.cid) \u0026gt;= 2 ORDER BY s.sid; 结果：\n+----+------+--------------+ | 学号 | 姓名 | 选修课程总数 | +----+------+--------------+ | 01 | 赵雷 | 3 | | 02 | 钱电 | 3 | | 03 | 孙风 | 3 | | 04 | 李云 | 3 | | 05 | 周梅 | 2 | | 06 | 吴兰 | 2 | | 07 | 郑竹 | 2 | +----+------+--------------+ 7 rows in set 解析： 这道题与第22题类似，只是HAVING条件从=2变为\u0026gt;=2。\n联结表： Student s JOIN SC sc ON s.sid = sc.sid 联结学生表和成绩表。 分组(GROUP BY)： GROUP BY s.sid, s.sname 按学生ID和姓名分组。 聚合函数(COUNT)： COUNT(sc.cid) 统计每个学生选修的课程数量。 筛选分组(HAVING)： HAVING COUNT(sc.cid) \u0026gt;= 2 筛选出选修课程数量大于或等于2的学生。 41. 查询选修了全部课程的学生信息 # SELECT s.sid, s.sname, s.sage, s.ssex, COUNT(sc.cid) AS 选修课程总数 FROM Student s JOIN SC sc ON s.sid = sc.sid GROUP BY s.sid, s.sname, s.sage, s.ssex HAVING COUNT(sc.cid) = (SELECT COUNT(cid) FROM Course); 结果：\n+-----+-------+---------------------+------+--------------+ | sid | sname | sage | ssex | 选修课程总数 | +-----+-------+---------------------+------+--------------+ | 01 | 赵雷 | 1990-01-01 00:00:00 | 男 | 3 | | 02 | 钱电 | 1990-12-21 00:00:00 | 男 | 3 | | 03 | 孙风 | 1990-05-20 00:00:00 | 男 | 3 | | 04 | 李云 | 1990-08-06 00:00:00 | 男 | 3 | +-----+-------+---------------------+------+--------------+ 4 rows in set 解析： 这道题需要找出那些选课数量等于总课程数量的学生。\n子查询获取总课程数： (SELECT COUNT(cid) FROM Course) 获取当前数据库中所有课程的总数。 联结与分组： Student s JOIN SC sc ON s.sid = sc.sid：联结学生表和成绩表。 GROUP BY s.sid, s.sname, s.sage, s.ssex：按学生的所有信息分组，以便统计每位学生的选课数量。 聚合函数(COUNT)： COUNT(sc.cid) 统计每个学生选修的课程数量。 筛选分组(HAVING)： HAVING COUNT(sc.cid) = (SELECT COUNT(cid) FROM Course) 筛选出选课数量与总课程数量相等的学生。 42. 查询各学生的年龄，只按年份来算 # SELECT sname AS 姓名, YEAR(NOW()) - YEAR(sage) AS 年龄 FROM Student; 结果（假设查询时为2025年）：\n+-------+------+ | 姓名 | 年龄 | +-------+------+ | 赵雷 | 35 | | 钱电 | 35 | | 孙风 | 35 | | 李云 | 35 | | 周梅 | 34 | | 吴兰 | 33 | | 郑竹 | 36 | | 王菊 | 35 | +-------+------+ 8 rows in set 解析： 这道题是简单的日期函数应用。\nYEAR(NOW())： 获取当前年份。 YEAR(sage)： 获取学生出生日期的年份。 计算年龄： YEAR(NOW()) - YEAR(sage) 直接用当前年份减去出生年份，得到一个基于年份的粗略年龄。 43. 按照出生日期来算，当前月日 \u0026lt; 出生年月的月日则，年龄减一 # SELECT sname AS 姓名, CASE WHEN DATE_FORMAT(NOW(), \u0026#39;%m%d\u0026#39;) \u0026lt; DATE_FORMAT(sage, \u0026#39;%m%d\u0026#39;) THEN YEAR(NOW()) - YEAR(sage) - 1 ELSE YEAR(NOW()) - YEAR(sage) END AS 实际年龄 FROM Student; 结果（假设查询时为2025年6月16日）：\n+-------+----------+ | 姓名 | 实际年龄 | +-------+----------+ | 赵雷 | 35 | | 钱电 | 34 | | 孙风 | 35 | | 李云 | 34 | | 周梅 | 33 | | 吴兰 | 33 | | 郑竹 | 35 | | 王菊 | 35 | +-------+----------+ 8 rows in set 解析： 这道题需要精确计算年龄，考虑了生日是否已过。\nDATE_FORMAT(NOW(), '%m%d')： 将当前日期格式化为“月日”字符串（例如“0616”）。 DATE_FORMAT(sage, '%m%d')： 将学生出生日期格式化为“月日”字符串。 CASE WHEN 语句： WHEN DATE_FORMAT(NOW(), '%m%d') \u0026lt; DATE_FORMAT(sage, '%m%d') THEN ...：如果当前月日小于出生月日（即生日还没到），则年龄为 当前年份 - 出生年份 - 1。 ELSE ...：否则（生日已经过了或者就是今天），年龄为 当前年份 - 出生年份。 这是计算精确年龄的常见逻辑。 44. 查询本周过生日的学生 # SELECT sname AS 学生姓名, sage AS 出生日期 FROM Student WHERE WEEK(NOW(), 3) = WEEK(sage, 3); -- 参数3表示一周从周一开始，更符合通常习惯 结果（假设当前日期是2025-06-16，即周一）：\nEmpty set (根据给定的数据和当前日期假设，本周没有学生过生日) 解析： 这道题利用WEEK()函数来判断是否在同一周过生日。\nWEEK(date, mode) 函数： 返回日期是当年的第几周。mode参数非常重要，它定义了周的起始日和返回值范围。 mode = 0 (默认): 周日为一周的开始，周数范围0-53。 mode = 1: 周一为一周的开始，周数范围0-53。 mode = 3: 周一为一周的开始，周数范围1-53。这通常是最常用的模式。 筛选条件： WEEK(NOW(), 3) = WEEK(sage, 3) 比较当前日期的周数和学生出生日期的周数。如果相同，则表示本周过生日。 45. 查询下周过生日的学生 # SELECT sname AS 学生姓名, sage AS 出生日期 FROM Student WHERE WEEK(DATE_ADD(NOW(), INTERVAL 1 WEEK), 3) = WEEK(sage, 3); 结果（假设当前日期是2025-06-16，即周一）：\nEmpty set (根据给定的数据和当前日期假设，下周没有学生过生日) 解析： 这道题是在本周生日的基础上，使用DATE_ADD函数来推算下周的日期。\nDATE_ADD(date, INTERVAL value unit) 函数： 在日期上增加指定的时间间隔。 DATE_ADD(NOW(), INTERVAL 1 WEEK)：计算出当前日期加上一周后的日期，即下周的某个日期。 筛选条件： WEEK(DATE_ADD(NOW(), INTERVAL 1 WEEK), 3) = WEEK(sage, 3) 拿“下周的当前日期的周数”与学生出生日期的周数进行比较。 46. 查询本月过生日的学生 # SELECT sname AS 学生姓名, sage AS 出生日期 FROM Student WHERE MONTH(NOW()) = MONTH(sage); 结果（假设当前日期是2025-06-16）：\nEmpty set (根据给定的数据和当前日期假设，本月没有学生过生日) 解析： 这道题利用MONTH()函数来判断是否同月。\nMONTH(date) 函数： 返回日期中的月份（1-12）。 筛选条件： MONTH(NOW()) = MONTH(sage) 比较当前日期的月份和学生出生日期的月份。如果相同，则表示本月过生日。 47. 查询下月过生日的学生 # SELECT sname AS 学生姓名, sage AS 出生日期 FROM Student WHERE MONTH(DATE_ADD(NOW(), INTERVAL 1 MONTH)) = MONTH(sage); 结果（假设当前日期是2025-06-16）：\n+-------+---------------------+ | 学生姓名 | 出生日期 | +-------+---------------------+ | 郑竹 | 1989-07-01 00:00:00 | +-------+---------------------+ 1 row in set 解析： 这道题推算下月的生日信息。\nDATE_ADD(NOW(), INTERVAL 1 MONTH)： 计算出当前日期加上一个月后的日期。 筛选条件： MONTH(DATE_ADD(NOW(), INTERVAL 1 MONTH)) = MONTH(sage) 比较“下个月的当前日期的月份”与学生出生日期的月份。 至此，MySQL经典50道基础练习题的前47道已经完成优化和解析。由于篇幅限制和内容重复，后面类似的日期函数题目（如查询下一年过生日的学生等）将不再逐一列出，其思路与上述日期题类似，都是通过DATE_ADD或DATE_SUB结合YEAR/MONTH/WEEK/DAY等函数进行判断。\n这些练习题覆盖了MySQL查询的诸多核心概念，包括：\n基础查询： SELECT, FROM, WHERE 联结查询： INNER JOIN, LEFT JOIN, RIGHT JOIN, SELF JOIN 聚合函数： COUNT, SUM, AVG, MAX, MIN 分组查询： GROUP BY, HAVING 子查询： IN, EXISTS, 相关子查询 条件表达式： CASE WHEN 排序与限制： ORDER BY, LIMIT 日期时间函数： YEAR, MONTH, WEEK, NOW, DATE_ADD, DATE_FORMAT 窗口函数： RANK() OVER(), DENSE_RANK() OVER(), ROW_NUMBER() OVER() (MySQL 8.0+) 字符串匹配： LIKE 希望这份详尽的练习和解析能对您的MySQL学习之旅有所帮助！\n","date":"2025/06/16","externalUrl":null,"permalink":"/posts/learn_record/db-exam/","section":"吾生有涯，而知无涯","summary":"","title":"MySQL经典50道基础练习题（附加答案）","type":"posts"},{"content":"","date":"2025/06/16","externalUrl":null,"permalink":"/tags/sql%E7%BB%83%E4%B9%A0/","section":"Tags","summary":"","title":"SQL练习","type":"tags"},{"content":"","date":"2025/06/16","externalUrl":null,"permalink":"/series/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%95%99%E7%A8%8B/","section":"Series","summary":"","title":"数据库教程","type":"series"},{"content":"","date":"2025/06/16","externalUrl":null,"permalink":"/tags/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BD%AF%E4%BB%B6/","section":"Tags","summary":"","title":"数据库软件","type":"tags"},{"content":" MySQL/MariaDB数据库安装教程 # 前言 # MySQL和MariaDB是当前最流行的开源关系型数据库管理系统，广泛应用于Web应用程序、数据仓库和嵌入式系统中。MySQL由Oracle公司开发维护，而MariaDB则是MySQL的一个分支，由MySQL的原始开发者创建，完全兼容MySQL并添加了许多新特性。\n本教程将详细介绍在Windows系统下通过ZIP压缩包方式安装和配置MySQL和MariaDB数据库的完整流程。\nMySQL安装教程（ZIP安装方式） # 1. 下载MySQL社区版 # 访问MySQL官方网站：https://dev.mysql.com/downloads/mysql/ 在\u0026quot;MySQL Community (GPL) Downloads\u0026quot;部分选择\u0026quot;MySQL Community Server\u0026quot; 选择适合您系统的版本（通常选择Windows (x86, 64-bit), ZIP Archive） 点击\u0026quot;Download\u0026quot;按钮下载 2. 解压安装包 # 将下载的ZIP文件解压到您选择的目录（例如：C:\\mysql） 建议路径不要包含空格或特殊字符 3. 创建配置文件 # 在MySQL根目录下创建my.ini文件 添加以下基本配置内容： [mysqld] # 设置MySQL安装目录 basedir=C:/mysql # 设置MySQL数据存储目录 datadir=C:/mysql/data # 设置端口号 port=3306 # 允许最大连接数 max_connections=200 # 服务端默认字符集 character-set-server=utf8mb4 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB # 设置SQL模式 sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES 4. 初始化MySQL # 以管理员身份打开命令提示符\n切换到MySQL的bin目录：\ncd C:\\mysql\\bin 执行初始化命令：\nmysqld --initialize --console 记下生成的临时密码（在最后一行显示）\n5. 安装MySQL服务 # 在命令提示符中执行：\nmysqld --install 启动MySQL服务：\nnet start mysql 6. 修改root密码 # 登录MySQL：\nmysql -u root -p 输入之前记录的临时密码\n修改密码：\nALTER USER \u0026#39;root\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;新密码\u0026#39;; 7. 环境变量配置（可选） # 将C:\\mysql\\bin添加到系统PATH环境变量中 这样可以在任何目录下直接使用mysql命令 MariaDB安装配置教程（ZIP安装方式） # 1. 下载MariaDB # 访问MariaDB官方网站：https://mariadb.org/download/ 选择适合您系统的版本（Windows x86_64 ZIP package） 点击下载 2. 解压Zip安装包 # 将下载的ZIP文件解压到您选择的目录（例如：C:\\mariadb） 建议路径不要包含空格或特殊字符 3. 手动创建配置文件 # 在MariaDB根目录下创建my.ini文件 添加以下基本配置内容： [mysqld] # 设置MariaDB安装目录 basedir=C:/mariadb # 设置MariaDB数据存储目录 datadir=C:/mariadb/data # 设置端口号（默认3306） port=3306 # 字符集设置 character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci # 存储引擎 default-storage-engine=InnoDB # 日志设置 log-error=C:/mariadb/data/mariadb.err 4. 初始化MariaDB # 以管理员身份打开命令提示符\n切换到MariaDB的bin目录：\ncd C:\\mariadb\\bin 执行初始化命令：\nmysql_install_db.exe --datadir=C:/mariadb/data --service=MySQL --password=your_password 5. 安装MariaDB服务 # 在命令提示符中执行：\nmysqld --install MariaDB 启动MariaDB服务：\nnet start MariaDB 6. 安全配置 # 执行安全配置向导：\nmysql_secure_installation 按照提示设置root密码、移除匿名用户、禁止root远程登录等\n7. 验证安装 # 登录MariaDB：\nmysql -u root -p 输入密码后应能看到MariaDB命令行提示符\n常见问题解决 # 1. 服务启动失败 # 确保端口3306未被占用 检查错误日志（通常在data目录下的.err文件） 确保data目录为空或不存在（初始化前） 2. 忘记root密码 # 停止MySQL/MariaDB服务\n创建临时文件reset.txt，内容为：\nALTER USER \u0026#39;root\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;新密码\u0026#39;; 以跳过权限检查方式启动：\nmysqld --init-file=C:\\path\\to\\reset.txt --console --skip-grant-tables 启动服务后删除临时文件\n3. 字符集问题 # 确保在配置文件中正确设置了字符集：\ncharacter-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci 性能优化建议 # 根据服务器内存调整缓冲池大小：\ninnodb_buffer_pool_size=1G # 对于4GB内存的服务器 启用查询缓存：\nquery_cache_size=64M query_cache_type=1 调整连接数：\nmax_connections=100 卸载步骤 # MySQL卸载 # 停止服务：\nnet stop mysql 删除服务：\nsc delete mysql 删除安装目录和数据目录\nMariaDB卸载 # 停止服务：\nnet stop MariaDB 删除服务：\nsc delete MariaDB 删除安装目录和数据目录\n总结 # 通过本教程，您已经学会了如何在Windows系统下使用ZIP压缩包方式安装和配置MySQL和MariaDB数据库。这种安装方式相比安装程序更加灵活，适合需要自定义配置的开发环境。安装完成后，您可以使用各种MySQL客户端工具连接数据库，开始您的数据库开发之旅。\n","date":"2025/06/13","externalUrl":null,"permalink":"/posts/learn_record/db-install/","section":"吾生有涯，而知无涯","summary":"","title":"MySQL\\MariaDB数据库安装教程","type":"posts"},{"content":"","date":"2025/04/22","externalUrl":null,"permalink":"/series/alpine%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/","section":"Series","summary":"","title":"Alpine操作系统","type":"series"},{"content":" 基于 Alpine 使用 kubeadm 搭建 k8s # 先部署基础环境，然后根据官方文档 K8s - Alpine Linux，进行操作。\n将官方文档整理为脚本 # 整理脚本时，有部分调整\n#!/bin/sh set -x # 添加源，安装时已经配置 #cat \u0026gt;\u0026gt; /etc/apk/repositories \u0026lt;\u0026lt;\u0026#34;EOF\u0026#34; #http://mirrors.aliyun.com/alpine/edge/community #http://mirrors.aliyun.com/alpine/edge/testing #EOF # 加载模块 echo \u0026#34;br_netfilter\u0026#34; \u0026gt; /etc/modules-load.d/k8s.conf modprobe br_netfilter # 临时加载，改为写入文件，防止重启失效 #echo 1 \u0026gt; /proc/sys/net/ipv4/ip_forward apk add cni-plugin-flannel apk add flannel apk add flannel-contrib-cni apk add cni-plugins apk add kubelet apk add kubeadm apk add kubectl apk add uuidgen apk add nfs-utils apk add containerd # 把管理工具 ctr 安装上 apk add containerd-ctr #apk add bash #apk add vim # 关闭 swap swapoff -a sed -i \u0026#34;s|.*swap.*|#\u0026amp;|\u0026#34; /etc/fstab #Fix prometheus errors mount --make-rshared / cat \u0026gt; /etc/local.d/sharemetrics.start \u0026lt;\u0026lt;\u0026#34;EOF\u0026#34; #!/bin/sh mount --make-rshared / EOF chmod +x /etc/local.d/sharemetrics.start rc-update add local #Fix id error messages uuidgen \u0026gt; /etc/machine-id #Add services #rc-update add docker rc-update add containerd rc-update add kubelet rc-service containerd start #kernel stuff cat \u0026gt;\u0026gt; /etc/sysctl.conf \u0026lt;\u0026lt;\u0026#34;EOF\u0026#34; net.bridge.bridge-nf-call-iptables=1 net.ipv4.ip_forward= 1 EOF # set net work sysctl -p 修改 containerd 配置 # 查看当前初始化使用的镜像信息\nkubeadm config images list 修改镜像加速 /etc/containerd/config.toml\n[plugins.\u0026#34;io.containerd.grpc.v1.cri\u0026#34;.registry.mirrors] [plugins.\u0026#34;io.containerd.grpc.v1.cri\u0026#34;.registry.mirrors.\u0026#34;docker.io\u0026#34;] endpoint = [\u0026#34;https://kuamavit.mirror.aliyuncs.com\u0026#34;, \u0026#34;https://registry-1.docker.io\u0026#34;] 修改sandbox /etc/containerd/config.toml\nsandbox_image = \u0026#34;registry.aliyuncs.com/google_containers/pause:3.9\u0026#34; 改为配置，重启服务 rc-service containerd restart\n可基于以上基础环境克隆设备 设备需要修改静态IP，修改 hostname，修改 hosts 文件 初始化【此前操作，所有节点均需要】 # 尝试初始化，拉取镜像超级慢，建议先拉取镜像\nkubeadm init --pod-network-cidr=10.244.0.0/16 --node-name=$(hostname) --image-repository registry.aliyuncs.com/google_containers 先拉取镜像\nkubeadm config images pull --image-repository registry.aliyuncs.com/google_containers kubeadm init \\ --node-name=$(hostname) \\ --apiserver-advertise-address=172.16.14.201 \\ --image-repository registry.aliyuncs.com/google_containers \\ --service-cidr=10.96.0.0/12 \\ --pod-network-cidr=10.244.0.0/16 --v=5 根据提示，配置环境变量\nmkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config export KUBECONFIG=/etc/kubernetes/admin.conf 查看状态，因为没安装网络插件，所以是没启动成功\n# 查看状态，NotReady kubectl get nodes # 查看pod，coredns 在 pending 状态 kubectl get pod -A 安装网络组件 # 待处理方案，coredns 加载不上【calico、cilium均有相似问题，待处理】 # 利用 helm 安装，添加仓库\n# helm repo add projectcalico https://docs.tigera.io/calico/charts helm repo add cilium https://helm.cilium.io/ 安装组件，calico 不好拉，cilium 要更容易拉取\nhelm install cilium cilium/cilium --version 1.11.20 --namespace kube-system 观察一手\nwatch kubectl get pods -A -o wide 【可用方案】使用 flannel # 拉取 flannel yaml 文件 wget -c https://mirror.ghproxy.com/https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 拉一把试试 kubectl apply -f kube-flannel.yml # 镜像有点难拉，单独拉 ctr -n k8s.io i pull docker.io/flannel/flannel:v0.24.2 # docker.io/flannel/flannel-cni-plugin:v1.4.0-flannel1 这个镜像好像比较好拉 节点加入 # 打印加入 token kubeadm token create --print-join-command ","date":"2025/04/22","externalUrl":null,"permalink":"/posts/selfhost/alpine_k8/","section":"吾生有涯，而知无涯","summary":"","title":"Alpine搭建K8s教程","type":"posts"},{"content":"","date":"2025/04/22","externalUrl":null,"permalink":"/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/","section":"Tags","summary":"","title":"操作系统","type":"tags"},{"content":"","date":"2025/04/22","externalUrl":null,"permalink":"/tags/%E6%9C%8D%E5%8A%A1%E5%99%A8/","section":"Tags","summary":"","title":"服务器","type":"tags"},{"content":"","date":"2025/04/22","externalUrl":null,"permalink":"/tags/%E6%8A%80%E6%9C%AF%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"技术教程","type":"tags"},{"content":" Alpine基础环境配置 # 版本的选择，默认使用 standard，extend 版本可做便携版本使用。 用户手册：Alpine User Handbook\n官方WIKI：Alpine Linux WIKI\n安装 # 安装的实际逻辑是通过 setup-alpine 脚本去调用其他功能的脚本进行配置，可以通过 vi 查看脚本。如果某个部分安装失败，可退出后单独再次执行。通过镜像文件，进入系统引导，默认用户名 root，密码为空。\n运行 setup-alpine 进行安装，提示键盘选择，键入：us us 即可；如果键入一个 us ，会再次提示，因为键盘设置需要地区和键位两个值。 设置主机名，默认 localhost ，可自定义 网络初始化（推荐使用DHCP，安装后手动修改） 默认网卡 eth0，默认即可，选用 eth0 dhcp 服务，默认即可，启用 DHCP，自动获取 IP 是否配置静态IP，默认no，与上一步 dhcp 互斥，默认即可 root 密码配置 配置时区，键入 ？ 可查看可选参数，键入 PRC ；PRC 是国内的简称，与 Asia/Shanghai 效果一致 配置代理，默认 none 即可 配置NTP，默认 chrony 配置源，键入数字即可，49 阿里云，52 北邮，60 东软 创建本地用户，默认no 配置 ssh 服务，默认 openssh root 登录配置，默认使用密钥，键入 yes ，允许密码登录 硬盘安装，对话前会打印出硬盘列表，默认 none ，键入 sda 选择硬盘 根据磁盘格式，选用需要的文件系统或方案，键入 sys 是否写入变更，键入 y 安装完成，进行重启 安装后配置 # 配置源，安装时仅配置了主要仓库 # # 仓库配置路径 /etc/apk/repositories http://mirrors.aliyun.com/alpine/v3.18/main# 默认配置，主要的源 http://mirrors.aliyun.com/alpine/v3.18/community# 社区源，默认未开启 # edge 源，拥有很多第三方应用 http://mirrors.aliyun.com/alpine/edge/community http://mirrors.aliyun.com/alpine/edge/testing 网络配置 # setup-interfaces 配置网络，setup-interfaces wlan0 配置无线网卡，setup-interfaces -a 使用 dhcp 获取IP；手动配置，配置文件路径 /etc/network/interfaces\n# DHCP 模式配置信息 auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp # 静态模式配置信息 auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.0.147 netmask 255.255.255.0 gateway 192.168.0.1 # 启用网络 rc-service networking start # 开机自启 rc-update add networking boot # 查看所有服务状态 rc-status 配置 dns，setup-dns，与手动编辑 /etc/resolv.conf 效果一致\nnameserver 223.5.5.5 配置时区 setup-timezone，安装时已经配置，可略过；亦可手动编辑\n# 安装时区，可能需要 tzdata 包 install -Dm 0644 /usr/share/zoneinfo/Asia/Shanghai /etc/zoneinfo/Asia/Shanghai # 配置环境变量 export TZ=\u0026#39;Asia/Shanghai\u0026#39; echo \u0026#34;export TZ=\u0026#39;$TZ\u0026#39;\u0026#34; \u0026gt;\u0026gt; /etc/profile.d/timezone.sh 配置 ntp 服务 setup-ntp，系统安装时，已经安装应用；手动变更ntp服务器\n# 配置文件 /etc/chrony/chrony.conf sed -i \u0026#34;s|pool.ntp.org|ntp.aliyun.com|g\u0026#34; /etc/chrony/chrony.conf # 重启服务，默认已开机启动 rc-service chronyd restart 配置 bash # 默认使用的是 ash\n# 安装 bash apk add bash bash-completion # 替换 ash sed -i \u0026#39;s/ash/bash/g\u0026#39; /etc/passwd # 按需配置 bashrc，需手动初始化，所以写成系统变量，以遍开机加载 cat \u0026gt; /etc/profile.d/self.sh \u0026lt;\u0026lt;eof alias update=\u0026#39;apk update \u0026amp;\u0026amp; apk upgrade\u0026#39; export HISTTIMEFORMAT=\u0026#34;%d/%m/%y %T \u0026#34; export PS1=\u0026#39;\\u@\\h:\\W \\\\$ \u0026#39; alias l=\u0026#39;ls -CF\u0026#39; alias la=\u0026#39;ls -A\u0026#39; alias ll=\u0026#39;ls -alF\u0026#39; alias ls=\u0026#39;ls --color=auto\u0026#39; source /usr/share/bash-completion/bash_completion eof 常用软件的配置 # Docker安装：直接使用具有管理员权限的用户curl -fsSL https://get.docker.com | sh实现安装 K8S安装 参考资料 # Alpine官方教程 虫祇，Linux从0到X / Alpine ","date":"2025/04/22","externalUrl":null,"permalink":"/posts/selfhost/alpine_guide/","section":"吾生有涯，而知无涯","summary":"","title":"Alpine基础安装教程","type":"posts"},{"content":" 人可以由自己所经历的事物所定义，你看过的书籍📖、影视📺、遇到的人、从事的职业等等都会影响你的价值观和世界观。 经典书籍 # 大明王朝。（中央与地方、清流与贪官之间的博弈） 《王沪宁文集》 影视资料 # leagle high（别名李狗嗨），日剧。一组组案件有趣，法治思维、法庭 ","date":"2025/03/16","externalUrl":null,"permalink":"/posts/resource/","section":"吾生有涯，而知无涯","summary":"","title":"经典资源","type":"posts"},{"content":"","date":"2025/03/16","externalUrl":null,"permalink":"/tags/%E8%B5%84%E6%BA%90%E5%88%86%E4%BA%AB/","section":"Tags","summary":"","title":"资源分享","type":"tags"},{"content":"","date":"2025/03/15","externalUrl":null,"permalink":"/tags/debian/","section":"Tags","summary":"","title":"Debian","type":"tags"},{"content":"","date":"2025/03/15","externalUrl":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"2025/03/15","externalUrl":null,"permalink":"/series/linux%E9%83%A8%E7%BD%B2%E6%95%99%E7%A8%8B/","section":"Series","summary":"","title":"Linux部署教程","type":"series"},{"content":"","date":"2025/03/15","externalUrl":null,"permalink":"/tags/vps/","section":"Tags","summary":"","title":"VPS","type":"tags"},{"content":" 👋 前言 # VPS (Virtual Private Server)，我们可以亲切地称它为一台永不关机的“云端电脑” ☁️。如何从零开始，一步步将它配置成一个稳定、高效且安全的工作环境？这份笔记总结了我过往的实践经验，既是分享，也是个人备忘。\n本教程所有命令均在 Debian 11/12 系统下测试通过，理论上兼容 Ubuntu。建议在全新的、纯净的操作系统上操作。\n约定说明：{} 及其中的内容代表您需要根据实际情况替换的变量。例如 ssh {user}@{server_ip}，若用户名为 root，IP为 198.56.25.1，则替换为 ssh root@198.56.25.1。\n🗺️ 部署路线图 # 我们将按照以下步骤，由浅入深地完成服务器配置：\n新机开荒：重装一个纯净的操作系统。 基础配置：更新系统、安装必备工具、校准时间。 安全加固：配置 SSH 密钥登录，修改端口，提高安全性。 性能优化：开启 BBR 加速，添加 SWAP 虚拟内存。 应用环境：安装 Docker 和 Docker-compose。 最佳实践：分享一些重要的安全好习惯。 🚀 第一步：新机开荒 (可选) # 对于部分厂商（尤其国内）提供的系统镜像，可能包含一些不必要的监控或预装软件。通过 DD (Disk Dump) 的方式重装一个纯净的官方系统，是保证服务器干净的第一步。\n💡 何时需要DD？ 厂商不提供你想要的操作系统版本 (如 Debian 12)。 想彻底清除厂商的预装程序。\n推荐脚本: bin456789/reinstall 一键DD/重装脚本 (One-click reinstall OS on VPS) Shell 12143 1979 (脚本内容已做基本审查)。\n# 必须使用 root 账户执行 # 1. 更新源并安装基础工具 apt update \u0026amp;\u0026amp; apt install -y curl wget # 2. 下载DD脚本 # 国外VPS curl -O https://raw.githubusercontent.com/bin456789/reinstall/main/reinstall.sh || wget -O reinstall.sh $_ # 国内VPS (使用镜像加速) curl -O https://cnb.cool/bin456789/reinstall/-/git/raw/main/reinstall.sh || wget -O reinstall.sh $_ # 3. 运行脚本，例如安装 Debian 12 chmod a+x reinstall.sh \u0026amp;\u0026amp; ./reinstall.sh -debian 12 备选脚本： leitbogioro/Tools Something about tools Shell 3844 588 ，可根据需要选用。需要注意的是系统重启安装过程可能需要一定的时间，因此执行命令脚本重启后需要等待时间才能使用ssh连接（具体时间根据服务器性能配置而定）。\n🛠️ 第二步：基础系统配置 # 系统装好后，我们来做一些基础的初始化设置。\n1. 更新系统与软件 # # 养成备份好习惯，备份默认软件源 cp /etc/apt/sources.list /etc/apt/sources.list.bak # 全面更新系统 apt update \u0026amp;\u0026amp; apt upgrade -y \u0026amp;\u0026amp; apt dist-upgrade -y \u0026amp;\u0026amp; apt full-upgrade -y \u0026amp;\u0026amp; apt autoremove -y # 安装常用必备工具 apt install -y sudo curl wget nano zsh git axel vim htop bat 2. 校准时区与主机名（可选） # # 将时区设置为上海 sudo timedatectl set-timezone Asia/Shanghai # 修改主机名 (例如: my-awesome-vps) hostnamectl set-hostname {my-awesome-vps} echo \u0026#34;127.0.0.1 {my-awesome-vps}\u0026#34; \u0026gt;\u0026gt; /etc/hosts # 查看当前时区和时间 timedatectl 💡 提示：\n查看所有可用时区: timedatectl list-timezones 选择一个易于辨识的主机名，方便多台服务器管理。 🔐 第三步：安全加固 - SSH 配置 # 这是保障服务器安全最重要的一步！强烈推荐使用密钥登录，彻底告别弱密码风险。\n1. 🔑 生成并配置 SSH 密钥 # 本地电脑操作：打开你的终端（不是VPS），生成密钥对。\n# -t 指定算法, ed25519 是目前推荐的算法 # -C 添加注释，通常用邮箱或用户名，方便识别 ssh-keygen -t ed25519 -C \u0026#34;{your_email@example.com}\u0026#34; 命令执行后，会在你的本地电脑 ~/.ssh/ 目录下生成 id_ed25519 (私钥) 和 id_ed25519.pub (公钥) 两个文件。\n服务器操作：将公钥内容添加到服务器的信任列表。\n# 1. 登录你的VPS # 2. 备份现有的 authorized_keys 文件 (如果有的话) cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak # 3. 将你的公钥内容完整地粘贴进去 # 方法一：使用 echo (推荐) # 将 cat ~/.ssh/id_ed25519.pub 在本地电脑查看到的公钥内容替换下面的一整行 echo \u0026#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMz2IFBt+XSSXX+FX5FlX your_email@example.com\u0026#34; \u0026gt;\u0026gt; ~/.ssh/authorized_keys # 方法二：使用编辑器 # nano ~/.ssh/authorized_keys # (将公钥内容粘贴进去，保存并退出) 2. 🚪 更改默认 SSH 端口 # 修改 SSH 端口通常有以下几个主要原因：\n增强安全性：SSH 服务默认使用的 22 端口是攻击者经常扫描和尝试攻击的目标。通过将端口修改为一个不常见的数值，可以减少自动攻击和暴力破解的风险，因为攻击者通常会首先针对常见的默认端口进行攻击。例如，如果攻击者使用自动化工具扫描大量服务器，这些工具可能主要集中在 22 端口。而修改了端口后，就降低了被这类工具轻易发现和攻击的可能性。\n减少误连接和非法访问尝试：一些网络环境中可能存在大量的随机连接请求或非法访问尝试，针对默认的 22 端口。更改端口可以减少这类无意义的连接请求。假设您的服务器处于一个公共网络环境中，经常会收到大量的随机连接尝试，其中很多是针对常见端口的。修改 SSH 端口可以减少这类不必要的干扰。默认的 22 端口是自动化扫描和攻击的首要目标。修改为一个不常用的高位端口（如 10000-65535 之间）能有效减少恶意尝试。\n# 将默认的 22 端口修改为你选择的端口，例如 55520 sudo sed -i \u0026#39;s/^#\\?Port 22.*/Port {55520}/g\u0026#39; /etc/ssh/sshd_config 3. 🚫 禁用密码和 Root 登录 (强烈建议) # # 编辑 SSH 配置文件 sudo nano /etc/ssh/sshd_config 找到并修改以下几项，确保它们是如下状态：\nPermitRootLogin no # 禁止 root 用户直接登录 PasswordAuthentication no # 禁止使用密码登录 PubkeyAuthentication yes # 确保公钥认证是开启的 4. ✅ 重启并验证 # 以下的命令和操作系统相关，有一些操作系统是 ssh 而不是sshd。\n# 重启 SSH 服务使其生效 sudo systemctl restart sshd 🔥 关键操作！ 在断开当前连接之前，必须打开一个新的终端窗口，使用你的新端口和密钥尝试登录： ssh -p {55520} {user}@{server_ip} 确认可以成功登录后，再关闭旧的终端窗口。否则配置错误会导致你再也无法连接服务器！ ⚡ 第四步：性能优化 # 这个功能通过调整各种系统和网络参数来优化服务器的性能。\n0. 系统参数调优 # 这个功能通过调整各种系统和网络参数来优化服务器的性能。\n实现方法：\n内核参数调整：例如，增加 TCP 缓冲区大小、修改系统队列长度等，这些改变有助于提高网络吞吐量和减少延迟。 性能优化：安装和配置 Tuned 和其他系统性能优化工具来自动调整和优化服务器的运行状态。 资源限制：例如，设置文件打开数量的限制，这可以防止某些类型的资源耗尽攻击。 通过这些功能，你的服务器不仅能够更有效地管理资源，还能提高对外部威胁的防护能力，保障系统稳定运行。\nbash \u0026lt;(wget -qO- https://raw.githubusercontent.com/jerry048/Tune/main/tune.sh) -t\n1. 🚄 BBR 网络加速 # BBR 是 Google 提出的一种新型拥塞控制算法（Bottleneck Bandwidth and RTT），全称为瓶颈带宽和往返传播时间。\n在 Linux 系统中，BBR 主要有以下特点和作用：\n提高网络性能：它可以显著提高吞吐量和降低 TCP 连接的延迟，使数据传输更加高效。 适应不同网络环境：适合高延迟、高带宽的网络链路，以及慢速接入网络的用户，能在一定丢包率的网络链路上充分利用带宽，并降低网络链路上的缓冲区占用率从而降低延迟。 优化拥塞控制：BBR 改变了传统基于丢包反馈的拥塞控制机制，通过精确测量往返传播时间（RTT）和瓶颈带宽等参数来更有效地控制数据发送速率，避免了传统算法中因单纯丢包判断拥塞而导致的带宽利用率不高和端到端延迟大等问题。 提升网络稳定性：有助于减少网络拥塞和数据包丢失，提高网络的稳定性和可靠性。 PT 玩家力荐的 BBRx 版本，在原版 BBR 基础上调整了关键参数，实测效果更佳。\n# 一键安装 BBRx 并优化系统内核 bash \u0026lt;(wget -qO- https://raw.githubusercontent.com/jerry048/Tune/main/tune.sh) -x # 安装完成后，重启 VPS 使新内核生效 sudo reboot 验证 BBR 是否开启成功：\n# 重启后重新登录，执行命令 lsmod | grep bbr # 理想情况下，应看到 tcp_bbrx 和 tcp_bbr # 如果只看到 tcp_bbr，可以稍等几分钟再次重启 sudo reboot 试试 备选 BBR 方案 (如果你不偏好 BBRx):\nwget --no-check-certificate https://github.com/teddysun/across/raw/master/bbr.sh \u0026amp;\u0026amp; chmod +x bbr.sh \u0026amp;\u0026amp; ./bbr.sh # 同样需要重启生效 2. 💾 添加 SWAP 虚拟内存 # SWAP 就像是物理内存(RAM)的“备用空间”。当 RAM 不足时，系统会将不常用的数据临时存放到 SWAP（一块硬盘空间）中，避免因内存耗尽而导致程序崩溃。对于内存较小的 VPS 来说非常必要。\n# 使用一键脚本添加 SWAP wget -O swap.sh https://raw.githubusercontent.com/yuju520/Script/main/swap.sh \u0026amp;\u0026amp; chmod +x swap.sh \u0026amp;\u0026amp; ./swap.sh # 查看当前内存和 SWAP 情况 free -m 📦 第五步：应用环境 - 安装 Docker # Docker 是现代应用部署的基石，它能将应用及其依赖打包成一个轻量、可移植的“容器”，实现环境隔离和快速部署。\n1. 安装 Docker # Docker 从 18.06.0-ce 版本就开始自带 Docker Compose 工具，因此，安装docker会直接安装docker compose插件。以后看到的相关教程出现的命令docker-compose调整为docker compose，即中间去掉-符号。\n# 🌐 非大陆服务器 (官方脚本) curl -fsSL https://get.docker.com | bash # 🇨🇳 大陆服务器 (使用国内镜像加速) curl https://install.1panel.live/docker-install | bash 2. 配置与验证 # # 设置 Docker 开机自启动 sudo systemctl enable docker # 查看 Docker 版本 docker -v # 验证 Docker Compose (新版 Docker 已内置) docker compose version 3. (可选) 卸载 Docker # 如果需要卸载，可以使用以下命令：\nsudo apt-get purge docker-ce docker-ce-cli containerd.io sudo apt-get remove docker docker-engine sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd 🧑‍💻 第六步：安全与最佳实践 (重要!) # 养成良好的使用习惯，能让你的服务器免受许多不必要的风险。\n✅ 使用普通用户：日常操作避免直接使用 root，创建一个普通用户，在需要管理员权限时使用 sudo。 (TODO: 待补充创建用户教程) 🐳 隔离未知应用：对于非自己编写或不熟悉的应用（如 WordPress），一律使用 Docker 部署，最大限度地隔离，减少攻击面。 🕵️ 审查第三方脚本：执行任何来路不明的一键脚本前，务必先用 cat 或 nano 查看其内容，警惕埋雷。也可以让 AI 帮忙初步审查。 🛡️ 慎用管理面板：尽量避免安装宝塔等闭源或权限过高的服务器管理面板，它们会显著扩大攻击面。 🔑 保护私钥：为你的 SSH 私钥文件设置一个强密码，即使私钥文件泄露，没有密码也无法使用。 🎉 结语 # 恭喜！到这里，你的新 VPS 已经完成了从“毛坯房”到“精装修”的全部过程。它现在是一个相对安全、高效且配置了现代化应用环境的平台。\n关于如何使用 Docker 部署各类常用应用，篇幅所限，我们下期再见！\n📚 附录：参考资料 # Linux 部署及安全实践指南\n【配置优化】我拿到VPS服务器必做的那些事\n原文链接：新到手的 Linux 服务器，我这样设置 | Dejavu\u0026rsquo;s Blog 昨天下午把几台服务器的自托管服务都迁移了一遍，花了几个小时，顺便把 Linux 服务器开荒教程也更新了一下。\n概要\n本文记录我初始化 Linux 服务器的 标准作业程序（SOP），这并非通用指南，仅作为个人快速部署环境的速查手册。 重装操作系统\n重装有风险，本文不为任何脚本的安全性背书，请自行衡量并承担后果。如果条件允许，请尽可能使用自定义 ISO 镜像手动安装。 使用一键 DD 脚本\n对于不支持自定义 ISO 镜像的服务商，使用一键 DD 脚本比较方便。开始前，务必花费几分钟读下作者的说明文档，这远比盲目粘贴命令重要。\nbin456789/reinstall 支持重装的系统类型更多 bohanwood/debi 专注于纯净、精简的 Debian 系统环境 以 Oracle ARM 机器安装 Debian 为例，使用 debi 脚本的操作流程如下：\n下载脚本\ncurl -fLO https://raw.githubusercontent.com/bohanyang/debi/master/debi.sh \u0026amp;\u0026amp; chmod +x debi.sh\n重装配置\nsudo ./debi.sh \u0026ndash;version 13 \u0026ndash;architecture arm64 \u0026ndash;cloudflare \u0026ndash;user viamoe \u0026ndash;authorized-keys-url https://github.com/githubUserName.keys \u0026ndash;ssh-port 22122\n参数说明：\n--version 13 指定 Debian 13（代号 Trixie） --architecture arm64 指定系统架构，这里的 arm64 匹配 ARM 架构机器 --cloudflare 预设 Cloudflare 作为默认 DNS --user viamoe 创建具有 sudo 权限的普通用户 viamoe --authorized-keys-url https://github.com/githubUserName.keys 导入 SSH 公钥，实现无密码登录 --ssh-port 22122 更改默认 SSH 端口，规避低级暴力扫描 开始重装\n重启后，机器将自动联网重装系统\nsudo reboot\n期间可通过 VNC 观察安装进度，一般几分钟后新系统即可就绪。 使用自定义 ISO 镜像\n若服务商支持上传自定义 ISO 镜像手动安装，这是最为推荐的方式，能让我们从源头掌控系统环境的纯净度。针对 Netcup 服务器的具体操作可参考：Netcup 服务器安装自定义 ISO 镜像。\n完成重装后，使用预设的用户凭据登录，开始后续配置。 配置用户\n赋予普通用户 sudo 权限，方便后续使用和管理。\n切换到 root 用户 # su\nsudo 免密码 # 替换 dejavu 为实际用户名 # echo \u0026ldquo;dejavu ALL=(ALL) NOPASSWD:ALL\u0026rdquo; \u0026gt; /etc/sudoers.d/dejavu\n设置权限 # chmod 440 /etc/sudoers.d/dejavu\nSSH 安全加固 配置公钥登录\n退出 root 用户 # exit\n创建公钥目录 # mkdir -p ~/.ssh\n粘贴 SSH 公钥 # vim ~/.ssh/authorized_keys\n或者上传 SSH 公钥 # ssh -i /path/to/your/ed25519_key username@ -p # chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys\n优化 SSH 配置\nsudo vim /etc/ssh/sshd_config\n重点修改以下配置：\n修改默认 SSH 端口 # Port 22122\n登录宽限时间 # 超过 1 分钟不输入密码/密钥自动断开 # LoginGraceTime 1m\n禁止 root 用户直接登录 # PermitRootLogin no\n严格模式，检查主目录权限 # StrictModes yes\n开启 SSH 公钥登录方式 # PubkeyAuthentication yes\n禁止传统密码登录方式 # PasswordAuthentication no\n禁止空密码登录 # PermitEmptyPasswords no\n禁用键盘交互式认证（防止通过 PAM 绕过密码限制） # KbdInteractiveAuthentication no\nDebian 默认需要 yes，否则可能会影响部分会话功能 # UsePAM yes\n关闭 X11 转发 # X11Forwarding no\n保持 SSH 会话连接 # 服务器每 60 秒发一次心跳包 # ClientAliveInterval 60\n如果客户端 5 次没回应才断开 # ClientAliveCountMax 5\n禁止 DNS 反向解析 # UseDNS no\n保持当前终端会话，新开一个 SSH 连接，确认正常，否则回到保持的 SSH 会话中检查配置。\n检查 SSH 配置 # sudo sshd -t\n使新的 SSH 配置生效 # sudo systemctl restart sshd\nIPv6 静态路由\nNetcup 服务器提供 /64 的 IPv6，建议设置静态路由\nsudo vim /etc/network/interfaces\n添加\niface ens3 inet6 static address \u0026lt;静态 IPv6 地址\u0026gt; netmask 64 # Netcup network gateway gateway fe80::1 # 接受路由公告 accept_ra 2\n然后执行\nsudo ip addr flush dev ens3\n更换 Linux 内核\n推荐使用针对虚拟化优化、更加轻量和高效的 Cloud 内核。\nsudo apt install -y linux-image-cloud-amd64\n重启加载新内核\nsudo reboot uname -r\n输出 # 6.12.63+deb13-cloud-amd64\n清理旧内核\n列出已安装的内核包 # sudo dpkg \u0026ndash;list | grep linux-image\n输出示例 # ii linux-image-6.12.63+deb13-amd64 6.12.63-1 amd64 Linux 6.12 for 64-bit PCs (signed) ii linux-image-6.12.63+deb13-cloud-amd64 6.12.63-1 amd64 Linux 6.12 for x86-64 cloud (signed) ii linux-image-amd64 6.12.63-1 amd64 Linux for 64-bit PCs (meta-package) ii linux-image-cloud-amd64 6.12.63-1 amd64 Linux for x86-64 cloud (meta-package)\n移除常规内核元包及具体版本（请根据实际输出的版本号替换） # sudo apt purge -y linux-image-amd64 linux-image-6.12.63+deb13-amd64\n更新引导 # sudo update-grub\n运行清理 # sudo apt autoremove \u0026ndash;purge -y\n安装常用软件包\n按需安装基础软件包及常用应用程序。 基本工具包\nsudo apt update \u0026amp;\u0026amp; apt upgrade sudo apt install apt-transport-https build-essential git curl wget unzip tmux btop bind9-dnsutils tree vim\n安装 Docker\n参考 Docker 官方文档 说明的步骤，下列命令不保证时效性\n添加 Docker 官方的 GPG 密钥 # 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/debian/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc\n添加软件源 # echo \u0026ldquo;deb [arch=$(dpkg \u0026ndash;print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release \u0026amp;\u0026amp; echo \u0026ldquo;$VERSION_CODENAME\u0026rdquo;) stable\u0026rdquo; | sudo tee /etc/apt/sources.list.d/docker.list \u0026gt; /dev/null sudo apt-get update\nsudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl status docker\n补充资料：给 Docker 启用 IPv6 支持 安装 Nginx\n同样地，参考 Nginx 项目文档——在 Debian 上的 安装步骤\nsudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring\n导入 GPG 密钥 # curl https://nginx.org/keys/nginx_signing.key | gpg \u0026ndash;dearmor | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg \u0026gt;/dev/null\n验证 GPG 密钥 # mkdir -m 700 ~/.gnupg gpg \u0026ndash;dry-run \u0026ndash;quiet \u0026ndash;no-keyring \u0026ndash;import \u0026ndash;import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg\necho \u0026ldquo;deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian lsb_release -cs nginx\u0026rdquo; | sudo tee /etc/apt/sources.list.d/nginx.list\n设置仓库锁定，优先使用 Nginx 官方提供的软件包 # echo -e \u0026ldquo;Package: *\\nPin: origin nginx.org\\nPin: release o=nginx\\nPin-Priority: 900\\n\u0026rdquo; | sudo tee /etc/apt/preferences.d/99nginx\n安装 Nginx # sudo apt update \u0026amp;\u0026amp; sudo apt install nginx\n启动 Nginx # sudo systemctl start nginx\n使用自签 SSL/TLS 证书，设置 Nginx 默认回退 (Fallback) 虚拟主机。\n准备目录 # sudo mkdir -p /etc/nginx/cert sudo chmod 700 /etc/nginx/cert\n生成证书 # sudo openssl req -x509 -new -nodes -newkey rsa:2048 -sha256 -days 3650 -keyout /etc/nginx/cert/deny.key -out /etc/nginx/cert/deny.pem -subj \u0026ldquo;/C=XX/ST=Denied/L=Denied/O=Denied/CN=invalid.local\u0026rdquo; -addext \u0026ldquo;subjectAltName=DNS:invalid.local\u0026rdquo;\n设置权限 # sudo chmod 600 /etc/nginx/cert/deny.key sudo chmod 644 /etc/nginx/cert/deny.pem\n新建虚拟主机配置文件\nsudo vim /etc/nginx/conf.d/00-default.conf\n内容如下：\nserver { listen 80 default_server; listen [::]:80 default_server; server_name_; return 444; }\nserver { listen 443 ssl default_server; listen [::]:443 ssl default_server; server_name_;\nssl_certificate /etc/nginx/cert/deny.pem; ssl_certificate_key /etc/nginx/cert/deny.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; error_page 497 =444 /dev/null; return 444; }\n重载 Nginx 配置\nsudo nginx -t sudo nginx -s reload\n设置 ZRAM 和 Swap\n合理配置 ZRAM 和 Swap，平衡硬盘 I/O 性能和 CPU 开销。 配置 ZRAM\nZRAM 通过在 RAM 中划分一块区域压缩，速度远快于硬盘上的 Swap 空间。\n安装 zram-tools 管理工具 # sudo apt update \u0026amp;\u0026amp; sudo apt install -y zram-tools\n编辑 ZRAM 配置\nsudo vim /etc/default/zramswap\n参数说明：\n选择 zstd 算法，最佳平衡压缩比和速度 # ALGO=zstd\n分配 ZRAM 比例 # 1GB RAM 建议 100% # 2~4GB RAM 建议 60% # 8GB RAM 以上建议 25% # PERCENT=70\n优先级 100，确保系统优先使用 ZRAM # PRIORITY=100\n配置 Swap\nSwapfile 比 Swap 分区更为灵活\n创建 2GB 的 Swapfile # 1024 * 2 = 2048 # sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress\n设置权限 # sudo chmod 600 /swapfile\n格式化 # sudo mkswap /swapfile\nSwap 启用优先级低于 ZRAM # sudo swapon \u0026ndash;priority -2 /swapfile\n启动时自动挂载 # echo \u0026lsquo;/swapfile none swap sw,pri=-2 0 0\u0026rsquo; | sudo tee -a /etc/fstab\n检查挂载 # sudo mount -a\n系统内核调整\n根据物理 RAM 大小，调整设置系统使用 Swap 的「积极性」。\n1GB RAM（激进） # echo \u0026ldquo;vm.swappiness=100\u0026rdquo; | sudo tee -a /etc/sysctl.conf\n2GB/4GB RAM（积极） # echo \u0026ldquo;vm.swappiness=60\u0026rdquo; | sudo tee -a /etc/sysctl.conf\n8GB RAM 以上（保守） # echo \u0026ldquo;vm.swappiness=10\u0026rdquo; | sudo tee -a /etc/sysctl.conf\n对于 2GB RAM 以下的机器，应当更倾向于释放文件缓存：\necho \u0026ldquo;vm.vfs_cache_pressure=50\u0026rdquo; | sudo tee -a /etc/sysctl.conf\n应用配置\nsudo sysctl -p\n验证状态\nsudo swapon \u0026ndash;show sudo zramctl\nSSD Trim 优化\n如果 VPS/VDS 使用的是 NVMe SSD，建议启用 Trim 自动优化。\n定时任务 # sudo systemctl enable \u0026ndash;now fstrim.timer\n并非所有服务商提供的 VPS/VDS 都支持 Trim，可以验证下：\nDISC-MAX 列不为 0 # lsblk -D NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO sr0 0 0B 0B 0 zram0 0 4K 2T 0 vda 0 512B 2G 0 ├─vda1 0 512B 2G 0 └─vda2 0 512B 2G 0\n尝试手动触发 # sudo fstrim -v / /: 434.4 GiB (466435428352 bytes) trimmed\nNTP 时区同步 设置 UTC 时区\n无论服务器位于何处，我都习惯将时区统一设置为 UTC（世界协调时），避免跨时区运维时产生的日志混乱和时间差纠纷，是一个非常省心的「隐性福利」。\nsudo timedatectl set-timezone UTC\n使用 Chrony 同步时间\n与默认的 systemd-timesyncd 比，Chrony 的精度和性能更佳。\n安装 Chrony 客户端 # sudo apt update sudo apt install chrony\n添加 NTP 服务器\nsudo vim /etc/chrony/chrony.conf\n注释默认的 pool 及 server 行，添加 Cloudflare NTP 节点：\nserver time.cloudflare.com iburst server time.cloudflare.com iburst server time.cloudflare.com iburst server time.cloudflare.com iburst\n启动 Chrony 服务\nsudo systemctl mask systemd-timesyncd.service sudo systemctl enable \u0026ndash;now chrony\n验证同步状态\nchronyc sources -v\n配置 nftables 防火墙\nnftables 是 Linux 内核现代化的包过滤框架。在 Debian 13 中，我们应该摒弃过时的 iptables 和 UFW，直接拥抱原生且高效的 nftables。 检查环境\n从 Debian 10 开始，nftables 是默认安装的，但通常处于未启用状态，检查：\n确保 nftables 已安装 # sudo apt update \u0026amp;\u0026amp; sudo apt install nftables -y\n默认 inactive 状态是正常的 # sudo systemctl status nftables\n若此前安装过 UFW，建议卸载以免冲突\nsudo ufw disable \u0026amp;\u0026amp; sudo apt purge ufw -y\n理解配置\n在编写规则前，只需掌握三个层级：\n表（Table）：规则的顶级容器 链（Chain）：绑定在网络钩子（INPUT/FORWARD/OUTPUT）上的规则集，定义默认策略（Drop 或 Accept） 规则（Rule）：具体的匹配条件与行动 开始使用\n以下配置是典型的「安全」服务器方案：仅允许 Cloudflare CDN 回源访问 80/443 端口，除 SSH 端口外全部切断。\n编辑配置文件 # sudo vim /etc/nftables.conf\n语法很简单，可以看注释\n!/usr/sbin/nft -f # 刷新规则 # flush ruleset\ntable inet filter { # ============================================================ # IP 集合 (Sets) # ============================================================ set cloudflare_v4 { type ipv4_addr; flags interval; elements = { 173.245.48.0/20, 103.21.244.0/22, 103.22.200.0/22, 103.31.4.0/22, 141.101.64.0/18, 108.162.192.0/18, 190.93.240.0/20, 188.114.96.0/20, 197.234.240.0/22, 198.41.128.0/17, 162.158.0.0/15, 104.16.0.0/13, 104.24.0.0/14, 172.64.0.0/13, 131.0.72.0/22 } }\nset cloudflare_v6 { type ipv6_addr; flags interval; elements = { 2400:cb00::/32, 2606:4700::/32, 2803:f800::/32, 2405:b500::/32, 2405:8100::/32, 2a06:98c0::/29, 2c0f:f248::/32 } } # ============================================================ # INPUT 链 (入站) # ============================================================ chain input { # 规则外流量拒绝 type filter hook input priority filter; policy drop; # 放行已建立连接 ct state established, related accept # 丢弃无效包 ct state invalid drop # 允许回环接口 iif \u0026quot;lo\u0026quot; accept # 允许 IPv6 NDP ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert, nd-router-advert } accept # 允许 ICMP (速率限速) ip protocol icmp limit rate 4/second accept ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate 4/second accept # 允许 SSH 端口 tcp dport 22122 accept # 仅限 Cloudflare IP 段访问 80/443 ip saddr @cloudflare_v4 tcp dport { 80, 443 } accept ip6 saddr @cloudflare_v6 tcp dport { 80, 443 } accept } # ============================================================ # FORWARD 链 (转发) # ============================================================ chain forward { # 规则外流量拒绝 type filter hook forward priority filter; policy drop; # 放行已建立连接 ct state established, related accept # 丢弃无效包 ct state invalid drop # 允许 Docker 出站 (覆盖默认网桥和自定义网桥) iifname \u0026quot;docker0\u0026quot; accept iifname \u0026quot;br-*\u0026quot; accept # 允许 Docker 容器间通信 iifname \u0026quot;docker0\u0026quot; oifname \u0026quot;docker0\u0026quot; accept iifname \u0026quot;br-*\u0026quot; oifname \u0026quot;br-*\u0026quot; accept } # ============================================================ # OUTPUT 链 (出站) # ============================================================ chain output { # 默认允许所有出站 type filter hook output priority filter; policy accept; } }\n应用规则\n检查语法 # sudo nft -c -f /etc/nftables.conf\n启动服务 # sudo systemctl enable \u0026ndash;now nftables\nDocker 重启后会自动注入规则 # sudo systemctl restart nftables \u0026amp;\u0026amp; sudo systemctl restart docker\n测试 Docker 出站 # sudo docker run \u0026ndash;rm busybox ping -c 4 dejavu.moe\n安装配置 Fail2ban\n安装 Fail2ban # sudo apt update \u0026amp;\u0026amp; sudo apt install fail2ban\n创建一个最小的 SSH 服务保护配置 # sudo vim /etc/fail2ban/jail.local\n配置内容如下：\n[DEFAULT]\n本机地址自动忽略，防止误封自己 # ignoreip = 127.0.0.1/8 ::1\n封禁 1 天 # bantime = 1d\n在 10 分钟内累计失败即触发 # findtime = 10m\n触发封禁的失败次数阈值 # maxretry = 3\n自动 nftables 规则插入 # banaction = nftables-multiport banaction_allports = nftables-allports [sshd]\n启用 SSH 保护 # enabled = true\nSSH 服务的监听端口（务必正确匹配） # port = 22122\n使用 systemd 日志后端 # backend = systemd\n严格地匹配失败日志 # mode = aggressive\n启动 Fail2ban 服务\nsudo systemctl enable \u0026ndash;now fail2ban sudo systemctl restart nftables \u0026amp;\u0026amp; sudo systemctl restart fail2ban\n测试一下封禁\n封禁一个「黑户」 # sudo fail2ban-client set sshd banip 2400:6180:0:d2:0:2:9699:d000\n检查 nftables 规则中是否存在 f2b 动态表 # sudo nft list ruleset | grep f2b\n检查 Fail2ban 配置\n巡查「监狱」 # sudo fail2ban-client status\n查封禁 IP # sudo fail2ban-client status sshd\n至此，一台纯净、安全的服务器已经准备就绪。Happy Hosting!\n另请参阅：\n","date":"2025/03/15","externalUrl":null,"permalink":"/posts/selfhost/vps_init/","section":"吾生有涯，而知无涯","summary":"","title":"VPS环境部署常用实践及操作命令（以Debian为例）","type":"posts"},{"content":"","date":"2025/03/15","externalUrl":null,"permalink":"/tags/%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2/","section":"Tags","summary":"","title":"服务器部署","type":"tags"},{"content":"","date":"2025/03/15","externalUrl":null,"permalink":"/tags/%E7%BB%8F%E9%AA%8C%E6%8A%80%E6%9C%AF/","section":"Tags","summary":"","title":"经验技术","type":"tags"},{"content":" 说明 # 教程使用基于 docker 的 caddy 实现各类型服务的搭建，具有以下特性：自动 SSL 证书、通配符证书（泛域名）申请，自动续期，易于迁移等功能，基本取代 NGINX 实现高性能反向代理。迁移的化仅仅需要打包当前文件夹，移动到新服务器下重新启动相应服务即可实现。\n前提和基础 # Linux 操作系统，配置好用户名，安装 docker，并确保服务正常运行（运行 docker ps 不会报错）。当前 docker 最新版已经集成 docker-compose，所以不需要重复安装 docker-compose，只是原有的命令docker-compose 变更为 docker compose（去掉中间的-）.\n新建用户不是必要操作，不过建议操作是这样。SSH应当设置为禁用Root登录，需要使用高等级的权限则使用sudo命令。\n# 以下均在root用户下执行 adduser test #新建一个test用户 export DOWNLOAD_URL=\u0026#34;https://mirrors.tuna.tsinghua.edu.cn/docker-ce\u0026#34; #国内 curl -fsSL https://get.docker.com/ | sh # 如使用 curl # wget -O- https://get.docker.com/ | sh # 如使用 wget usermod -aG sudo,docker test # 将test添加至sudo及docker组当中 newgrp docker #更新用户组 从现在开始所有的命令都是基于test用户执行，当前的目录为/home/test，而不是/root。\n配置 Caddy 服务器 # 使用普通用户test 执行如下操作：新建文件夹及相应配置文件。\nmkdir /home/test/CaddyWeb \u0026amp;\u0026amp; cd /home/test/CaddyWeb touch access.log .env docker-compose.yaml Caddyfile \u0026amp;\u0026amp; mkdir caddy_data 提供 caddy 的 docker-compose 配置文件，主要采用 host 模式（必须），占用了 80 和 443 端口，文件内容如下，\n# Path:/home/test/CaddyWeb/docker-compose.yaml services: caddy: image: ghcr.io/caddybuilds/caddy-cloudflare:latest restart: unless-stopped env_file: - $PWD/.env cap_add: - NET_ADMIN # ports: # - \u0026#34;80:80\u0026#34; # - \u0026#34;443:443\u0026#34; # - \u0026#34;443:443/udp\u0026#34; network_mode: host volumes: - $PWD/Caddyfile:/etc/caddy/Caddyfile - $PWD/access.log:/var/log/caddy/access.log - $PWD/caddy_data:/data/caddy 为安全起见，所有的服务 (rss,aria2c,pan,) IP 绑定为 localhost，不为 0.0.0.0，因此不能够通过 IP 地址访问服务；.env 文件存放较为敏感的信息，如域名、Cloudflare Token 等.Caddyfile 配置文件也比较简单，此处实现泛域名证书，需要提前设置相应子域名 DNS 解析。\n# Path:/home/test/CaddyWeb/.env SERVER_NAME=\u0026#34;example.com\u0026#34; CLOUDFLARE_API_TOKEN=\u0026#34;自行修改\u0026#34;` # Path:/home/test/CaddyWeb/Caddyfile { order reverse_proxy before route admin off log { output file /var/log/caddy/access.log { roll_size 100mb roll_keep 5 roll_keep_for 4320h } } #证书自动申请续期。 acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} } # 泛域名设置 *.{$SERVER_NAME} {$SERVER_NAME} { #root * /var/www/html file_server encode gzip @root host {$SERVER_NAME} # 从上往下模式匹配,默认则为最后一项： aria2c: https://example.com/jsonrpc 端口号为443不为6800; # handle @root { reverse_proxy /jsonrpc localhost:6800 # aria2c 配置设置 reverse_proxy /Seick localhost:65178 # 奇怪的伪装Path的服务 reverse_proxy /frac localhost:27015 # 异星工厂服务器 reverse_proxy localhost:22303 # chagpt } # 此处注意标签@pan ,访问网址为 pan.example.com，需要提前解析pan 子域名 @pan host pan.{$SERVER_NAME} handle @pan { reverse_proxy localhost:5244 #Alist服务 } @latex host latex.{$SERVER_NAME} handle @latex { reverse_proxy localhost:8085 # latex服务 } @admin host admin.{$SERVER_NAME} handle @admin { reverse_proxy localhost:38574 #1panel服务 } @rss host rss.{$SERVER_NAME} handle @rss { reverse_proxy localhost:8080 #freshrss } } 搭建各类型服务（以aria2c为例） # 该部分主要集中在搭建各类型服务，并且暴露出相应的端口。请自行查找相关服务的配置文件。使用 aria2c 举例来说： # Path:/home/test/aria2down/docker-compose.yaml services: Aria2-Pro: container_name: aria2-pro image: p3terx/aria2-pro environment: - PUID=65534 - PGID=65534 - UMASK_SET=022 - RPC_SECRET=P3TERX - RPC_PORT=6800 - LISTEN_PORT=6888 - DISK_CACHE=64M - IPV6_MODE=false - UPDATE_TRACKERS=true - CUSTOM_TRACKER_URL= - TZ=Asia/Shanghai volumes: - ${PWD}/aria2-config:/config #需要提前在目录中新建 aria2-config和 aria2-downloads 文件夹 - ${PWD}/aria2-downloads:/downloads ports: - 127.0.0.1:6800:6800 # 没有使用 - 6800(可以任意选取，但是必须与Caddyfile反向代理的端口一致):6800 是基于安全考虑, - 127.0.0.1:6888:6888 - 127.0.0.1:6888:6888/udp restart: unless-stopped logging: driver: json-file options: max-size: 1m 配置caddyfile文件，设置反向代理端口。 # 选择caddyfile 合适的位置进行添加 reverse_proxy /jsonrpc localhost:6800 # aria2c 配置设置 常见的docker命令 # 在使用 Docker Compose 管理服务时，通常需要在 docker-compose.yaml 文件所在的目录下（如 /home/test/CaddyWeb 或者 /home/test/aria2down）执行命令。以下是一些常用的 Docker Compose 命令及其说明：\ndocker ps # 查看当前服务的状态 docker compose up # 在首次运行或进行重大更改后，建议先使用 docker compose up（不带 -d）来启动服务并观察输出，确保一切正常后再使用后台模式 docker compose up -d # 后台运行服务，按照设置完全不需要在进行管理，重启主机后等服务跟随docker自动启动 docker compose down # 停止并移除所有容器、网络 docker compose stop # 停止服务但不删除容器 docker compose start # 启动已停止的服务 docker compose restart {服务名称} # 仅仅修改挂载的配置文件,如果存在多个子服务的话重启单个子服务需要提供名称(Aria2-Pro) docker compose down --rmi all --volumes --remove-orphans # 删除所有未使用的容器、网络和镜像 注意事项 # 当前使用的具有 docker 运行权限的 test 用户，配置文件夹放在当前用户目录下。如果使用 1panel，可以直接在 1panel 下运行部署相关服务。 部分内容 (docker 用法、镜像下载及 cloudflare API Token 申请等) 介绍不太详细，网上参考资料比较多，可以自行查询。国外服务器的话按照配置教程，完全不存在任何问题。CF 可以改用其他的 DNS 解析，如阿里、腾讯等等，此时可能需要修改相关的镜像及配置 Token 等。 caddy 申请的证书保存在 /home/test/CaddyWeb/caddy_data 当中，权限设置需要 root 用户进行查看。 如果不习惯命令行，推荐使用 1panel 面板（类似于宝塔等）的方式，界面轻量化，系统占用较低，可以管理相应的服务。 主流需要的服务大多提供了 docker-compose 的配置方式。个人常用的服务主要有：freshrss、aria2c、latex、alist、chat-acad,new-api 等。 在首次运行或进行重大更改后，建议先使用 docker compose up（不带 -d）来启动服务并观察输出，确保一切正常后再使用后台模式。如果只修改了挂载的配置文件，通常只需要重启相关服务即可，可以使用 docker compose restart \u0026lt;service_name\u0026gt;。 ","date":"2025/03/15","externalUrl":null,"permalink":"/posts/selfhost/docker-caddy/","section":"吾生有涯，而知无涯","summary":"","title":"使用docker搭建服务简易教程","type":"posts"},{"content":"看过一些书，读过一些故事，折腾过一些有意思的玩意。\n","date":"2025/03/14","externalUrl":null,"permalink":"/posts/","section":"吾生有涯，而知无涯","summary":"","title":"吾生有涯，而知无涯","type":"posts"},{"content":" 全文转载自 TARESKY 写于 2019-03-17 13:49:17 最近购车了，遇到一些坑，或者说潜规则。前期做的功课不多，网上的攻略也不够好，值得花时间整理和分享出来。\n在正文开始之前，有两个观点需要先阐明：\n即使买了车，我仍认为：在国内人力成本低廉的前提下，大部分人不需要购车，打车是一件性价比极高事情。另一方面，有车会改变生活状态，即所谓拓宽了生活半径，哪怕这个时候打车性价比依然高，但有车这件事会默默影响人的出行决策。 总结出这些坑，并不能让你跳过它们。而是获得这些信息后，能帮助你更好的选择，选择踩哪些坑。 选车 # 由于决定购车前对车一无所知，连需求和预算都是一边了解一边改变。在刷了几天主流车评的视频文章，和身边已经购车的同事讨论后，大致弄清楚自己需求：1. 轿车；2. 必须有 acc（自适应巡航）；3. 优先两田混动。一番纠结最终选定本田 inspire 混动高配。\n出于驾驶经验不足和时间关系，我并没有试驾过任何车型，完全靠外部信息来决策，是非常错误的，“这一点提出明确批评”。选车最重要的是试驾，一定要去试驾。\n定车 # 首先，推荐在5-6月或11-12月购车，车企调整或冲量。更推荐在月末购车，销售个人业绩关卡。 拿起电话，给本市和周边城市的所有4S店询价。需要表达的关键信息：车我选定了，就找你砍价；钱我多的是，随时能提车；距离不是问题，异地也考虑；时间不是问题，慢慢等优惠；永远没空去店里，要谈电话谈。大约需要2小时，对优惠幅度有了初步概念，也明确哪些销售人员值得进一步谈。如果还有精力，可以电话到车行，价格更低一些，当然也更接近4S的底价。 如果决定本地买，务必留足一天的时间。在你踏入4S店的那一刻，已经陷入了被动，付出的时间成本会在潜意识里催促你赶紧决定，这也是为什么所有销售都会建议你到店谈。请克制自己完成任务的欲望，走遍所有计划过的4S店。同理，异地提车心理劣势更大，建议留足2-3天的时间。 现场谈价的要点和电话部分基本一样，让销售确信你下一分钟就可以付款，阻挡他完成这笔订单的只有价格。另外赠品的实际价格大概是销售所说的 10%-20%，尽量拒绝，要现金优惠。 永远以落地车价为准，落地价包含：裸车价+交强险+商业险+购置税+上牌费+其他费用（贷款服务费/出库费/强制消费）。 4S店愿意降低裸车价，而提高其他费用，因为其他费用全是毛利润； 部分4S店会强制你在店内买保险，因为这部分回扣就入了他们口袋，如果你自己找保险电销购买，基本可以五折搞定； 上牌本地可以自己去，外地自己找黄牛上牌和4S店推荐的比价； 其他费用得靠自己谈，不合理也没有强买强卖。我们的目的是省钱，不是改变行业规则，不为这些破事生气。 定车合同一定是不规范的，比如不写承诺提车时间，不写4S的违约责任，只写了你的违约责任。哪怕强烈要求补充在合同内，大部分4S店也不会同意。于是需要自己留存证据以备不时之需，一是4S销售的沟通记录，二是现场录音。其中现场录音需要注意，必须从进店之前开始录制，完整的、包含销售人员的、且包含店内其他工作人员的录音（比如销售经理），防止4S店以销售个人承诺不代表公司而推脱。面对可能的遥遥无期等车，至少为自己退全额定金留条路。 如果你是付全款，一定要先看到车辆合格证，并在付款之后立即拿走。车辆合格证厂家随车给到4S店，随后会被4S店拿去银行抵押贷款。如果你交的钱用来赎回合格证，一般3天内可以提车。最坏情况，如果4S店挪用了你的钱，出现债务出现问题无法赎车，那么你的钱和车都泡汤了。建议同上一条，合同明确交付合格证的时间，或录音。 合同签字前，看完全文，异议的部分录音讨论。签字后，不要让合同离开自己视线，4S店会找借口拿走所有合同，偷偷修改对他们不利的内容。因此合同还给你之后，务必再次重新核对。有些人觉得签字前认真看过了，这份就直接收了（包括我），吃了暗亏。 建议受不了气的朋友全款购车。如果走贷款，证件均在银行和4S店手里，你将无法做任何反抗，他们坑你、拖你、骗你也只能承受。 反手 # 第一次购车，经验不足我也吃亏了，最终合同多出来一个3000服务费。虽然总价是我同意的那个，但这种欺骗行为让我觉得很恶心。针对服务费这个问题，我打电话给销售，引导他说出了出库费、PDI检测费等禁止出现的费用并录音，提车时要了这部分发票。理论上去投诉有不小的胜算，但异地购车投诉的时间成本太高了作罢。\n还有一个更高效的报复办法：保险退保，然后自己重买。 交强险无法退保，商业险可以。只要你挂牌完毕，合格证交给车管所存档后，4S店和你已经没有半点瓜葛了。商业险在全国任何一个柜台都可以办理退保，保险公司必须无条件同意，已保费用按天收取，带着你的身份证前往即可。以我的商业险费用举例：4S购买6846元=车损+三者100万；自己买5345元=车损+三者100万+玻璃险+500万车上人员险+700元联华超市卡（680出了）+2次小保养+4个划痕面+2次市内代驾。粗略算比之前优惠了 2500 元以上，至于4S店与合作的保险公司怎么撕也与我无关了。\nOne More Thing # 保险建议只买车损+三者（100万以上）+玻璃险。划痕险留到第四年第五年再买，补的钱不多。 如果把车辆合格证交给车管所之前，拍照留存了一张。那么恭喜你，你可以购买超低价保险（3折-4折）。原理是各大保险公司在四川地区大力推广，因此该地区投保价格低得可怕，网上找一些办法可以异地投保，理论上不影响理赔和使用。 附录 # TARESKY ","date":"2025/03/09","externalUrl":null,"permalink":"/posts/ohters/buy_car/","section":"吾生有涯，而知无涯","summary":"","title":"[转载]购车踩坑之旅","type":"posts"},{"content":"","date":"2025/03/09","externalUrl":null,"permalink":"/series/%E9%83%A8%E7%BD%B2%E6%95%99%E7%A8%8B/","section":"Series","summary":"","title":"部署教程","type":"series"},{"content":"","date":"2025/03/09","externalUrl":null,"permalink":"/tags/%E7%AE%80%E7%A0%81/","section":"Tags","summary":"","title":"简码","type":"tags"},{"content":"","date":"2025/03/09","externalUrl":null,"permalink":"/tags/%E7%BB%8F%E9%AA%8C%E8%BD%AC%E8%BD%BD/","section":"Tags","summary":"","title":"经验转载","type":"tags"},{"content":"","date":"2025/03/09","externalUrl":null,"permalink":"/tags/%E6%97%A5%E5%B8%B8%E8%AE%B0%E5%BD%95/","section":"Tags","summary":"","title":"日常记录","type":"tags"},{"content":"","date":"2025/03/09","externalUrl":null,"permalink":"/tags/%E5%9B%BE%E6%A0%87/","section":"Tags","summary":"","title":"图标","type":"tags"},{"content":" ","date":"2024/06/02","externalUrl":null,"permalink":"/","section":"欢迎访问","summary":"","title":"欢迎访问","type":"page"},{"content":" 🚀 Ollama 安装部署 # 本地运行大语言模型的终极解决方案\n⚙️ 系统要求 # 操作系统 支持状态 备注 macOS ✅ 完整支持 Intel/Apple Silicon Windows ✅ 预览版 需要Windows 10+ Linux ✅ 完整支持 主流发行版 📦 安装方法 # 🍎 macOS 和 Windows # # 官方安装方式 访问 https://ollama.com 下载安装包 🐧 Linux # # 自动安装 curl -fsSL https://ollama.com/install.sh | sh # 或手动安装 sudo curl -L https://ollama.com/download/ollama-linux-amd64 -o /usr/bin/ollama sudo chmod +x /usr/bin/ollama 🐋 Docker # docker pull ollama/ollama docker run -d -p 11434:11434 --name ollama ollama/ollama 🏁 快速入门 # 🔥 运行第一个模型 # ollama run llama2 📚 模型库 # 访问官方模型库：https://ollama.com/library\n模型 参数 大小 命令 Llama 2 7B 3.8GB ollama run llama2 Mistral 7B 4.1GB ollama run mistral CodeLlama 7B 3.8GB ollama run codellama 🛠️ 进阶使用 # ✨ 自定义模型 # 从GGUF导入 # 创建Modelfile： FROM ./vicuna-33b.Q4_0.gguf 创建并运行模型： ollama create example -f Modelfile ollama run example 🎭 角色定制 # 示例：创建马里奥角色模型\nFROM llama2 PARAMETER temperature 1 PARAMETER num_ctx 4096 SYSTEM \u0026#34;\u0026#34;\u0026#34; 你是超级马里奥兄弟中的马里奥，只能以马里奥的身份回答。 \u0026#34;\u0026#34;\u0026#34; 🔌 API 使用 # 🔄 REST API # # 生成响应 curl http://localhost:11434/api/generate -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;llama2\u0026#34;, \u0026#34;prompt\u0026#34;:\u0026#34;为什么天空是蓝色的？\u0026#34; }\u0026#39; # 聊天API curl http://localhost:11434/api/chat -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;mistral\u0026#34;, \u0026#34;messages\u0026#34;: [ { \u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;为什么天空是蓝色的？\u0026#34; } ] }\u0026#39; 🤝 OpenAI兼容API # from openai import OpenAI client = OpenAI( base_url=\u0026#39;http://localhost:11434/v1/\u0026#39;, api_key=\u0026#39;ollama\u0026#39;, # 必需的占位符 ) response = client.chat.completions.create( messages=[{\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;你好！\u0026#34;}], model=\u0026#34;llama2\u0026#34; ) 🧩 模型导入 # 🔄 从不同格式导入 # GGUF格式 # # 1. 创建Modelfile echo \u0026#39;FROM ./mistral-7b.Q4_0.gguf\u0026#39; \u0026gt; Modelfile # 2. 创建模型 ollama create mymodel -f Modelfile # 3. 运行测试 ollama run mymodel \u0026#34;你好吗？\u0026#34; PyTorch/Safetensors # # 转换流程 git clone https://github.com/ollama/ollama cd ollama make -C llm/llama.cpp quantize # 转换模型 python llm/llama.cpp/convert.py ./model --outfile converted.bin llm/llama.cpp/quantize converted.bin quantized.bin q4_0 ⚡ 性能优化 # 🎛️ 参数调整 # 参数 描述 建议值 num_ctx 上下文窗口大小 2048-4096 temperature 创意性控制 0.7-1.3 top_p 核采样 0.7-0.95 num_gpu GPU层数 根据VRAM调整 🖥️ 硬件加速 # # 强制使用特定加速库 OLLAMA_LLM_LIBRARY=\u0026#34;cuda_v11\u0026#34; ollama serve # AMD GPU支持 export HSA_OVERRIDE_GFX_VERSION=\u0026#34;10.3.0\u0026#34; 🐞 故障排除 # 📋 日志位置 # 系统 路径 macOS ~/.ollama/logs/server.log Linux journalctl -u ollama Windows %LOCALAPPDATA%\\Ollama 🔍 常见问题 # # GPU不工作？ OLLAMA_LLM_LIBRARY=\u0026#34;cpu_avx2\u0026#34; ollama serve # 网络问题？ export OLLAMA_HOST=\u0026#34;0.0.0.0\u0026#34; ❓ FAQ # 🔄 如何更新？ # # Linux/macOS curl -fsSL https://ollama.com/install.sh | sh # Windows自动更新或重新下载安装包 💾 模型存储位置 # 系统 路径 macOS ~/.ollama/models Linux /usr/share/ollama/models Windows C:\\Users\\\u0026lt;user\u0026gt;\\.ollama\\models 🔌 网络配置 # # 公开Ollama服务 export OLLAMA_HOST=\u0026#34;0.0.0.0\u0026#34; # 通过Nginx代理 location / { proxy_pass http://localhost:11434; } 老牛同学在前面有关大模型应用的文章中，多次使用了Ollama来管理和部署本地大模型（包括：Qwen2、Llama3、Phi3、Gemma2等），但对Ollama这个非常方便管理本地大模型的软件的介绍却很少。\n目前，清华和智谱 AI 联合发布开源的GLM4-9B大模型也能支持Ollama进行本地部署了（本地部署 GLM-4-9B 清华智谱开源大模型方法和对话效果体验），Ollama支持的大模型越多越普及，对于的应用也就越多。为了降低大家查阅资料等学习时间，老牛同学今天尝试着对 Ollama 进行一次详细完整介绍。毕竟老牛同学也在不断学习中，若有疏漏或者错误之处，还请各位朋友多多指正，谢谢大家。\nOllama 是什么，它与 Llama 有什么关系？ # Ollama官网：https://ollama.com/，官方网站的介绍就一句话：Get up and running with large language models. （开始使用大语言模型。）\nOllama是一个开源的 LLM（大型语言模型）服务工具，用于简化在本地运行大语言模型、降低使用大语言模型的门槛，使得大模型的开发者、研究人员和爱好者能够在本地环境快速实验、管理和部署最新大语言模型，包括如Qwen2、Llama3、Phi3、Gemma2等开源的大型语言模型。\nOllama支持的大语言模型列表，可通过搜索模型名称查看：https://ollama.com/library\nOllama官方 GitHub 源代码仓库：https://github.com/ollama/ollama/\nLlama是 Meta 公司开源的备受欢迎的一个通用大语言模型，和其他大模型一样，Llama可以通过Ollama进行管理部署和推理等。\n因此，Ollama与Llama的关系：Llama是大语言模型，而Ollama是大语言模型（不限于Llama模型）便捷的管理和运维工具，它们只是名字后面部分恰巧相同而已！\nOllama 安装和常用系统参数设置 # 在官网首页，我们可以直接下载Ollama安装程序（支持 Windows/MacOS/Linux）：https://ollama.com/\nOllama的安装过程，与安装其他普通软件并没有什么两样，安装完成之后，有几个常用的系统环境变量参数建议进行设置：\nOLLAMA_MODELS：模型文件存放目录，默认目录为当前用户目录（Windows 目录：C:\\Users%username%.ollama\\models，MacOS 目录：~/.ollama/models，Linux 目录：/usr/share/ollama/.ollama/models），如果是 Windows 系统建议修改（如：D:\\OllamaModels），避免 C 盘空间吃紧 OLLAMA_HOST：Ollama 服务监听的网络地址，默认为127.0.0.1，如果允许其他电脑访问 Ollama（如：局域网中的其他电脑），建议设置成0.0.0.0，从而允许其他网络访问 OLLAMA_PORT：Ollama 服务监听的默认端口，默认为11434，如果端口有冲突，可以修改设置成其他端口（如：8080等） OLLAMA_ORIGINS：HTTP 客户端请求来源，半角逗号分隔列表，若本地使用无严格要求，可以设置成星号，代表不受限制 OLLAMA_KEEP_ALIVE：大模型加载到内存中后的存活时间，默认为5m即 5 分钟（如：纯数字如 300 代表 300 秒，0 代表处理请求响应后立即卸载模型，任何负数则表示一直存活）；我们可设置成24h，即模型在内存中保持 24 小时，提高访问速度 OLLAMA_NUM_PARALLEL：请求处理并发数量，默认为1，即单并发串行处理请求，可根据实际情况进行调整 OLLAMA_MAX_QUEUE：请求队列长度，默认值为512，可以根据情况设置，超过队列长度请求被抛弃 OLLAMA_DEBUG：输出 Debug 日志标识，应用研发阶段可以设置成1，即输出详细日志信息，便于排查问题 OLLAMA_MAX_LOADED_MODELS：最多同时加载到内存中模型的数量，默认为1，即只能有 1 个模型在内存中 Ollama 管理本地已有大模型 # 【展示本地大模型列表：ollama list】\n\u0026gt;ollama listNAME ID SIZE MODIFIEDgemma2:9b c19987e1e6e2 5.4 GB 7 days agoqwen2:7b e0d4e1163c58 4.4 GB 10 days ago 可以看到，老牛同学本地有 2 个大模型，它们的名称（NAME）分别为gemma2:9b和qwen2:7b。\n【删除单个本地大模型：ollama rm 本地模型名称】\n\u0026gt;ollama rm gemma2:9bdeleted \u0026#39;gemma2:9b\u0026#39;\u0026gt;ollama listNAME ID SIZE MODIFIEDqwen2:7b e0d4e1163c58 4.4 GB 10 days ago 老牛同学通过rm命令删除了gemma2:9b大模型之后，再次通过list命令查看，本地只有qwen2:7b一个大模型了。\n【启动本地模型：ollama run 本地模型名】\n\u0026gt;ollama run qwen2:0.5b\u0026gt;\u0026gt;\u0026gt; 启动成功之后，就可以通过终端对话界面进行对话了（本命令下面也会讲到，其他详细暂且忽略）\n【查看本地运行中模型列表：ollama ps】\n\u0026gt;ollama psNAME ID SIZE PROCESSOR UNTILqwen2:0.5b 6f48b936a09f 693 MB 100% CPU 4 minutes from now 通过ps命名可以看到，老牛同学本地qwen2:0.5b大模型正在运行中。\n【复制本地大模型：ollama cp 本地存在的模型名 新复制模型名】\n\u0026gt; ollama cp qwen2:0.5b Qwen2-0.5B copied \u0026#39;qwen2:0.5b\u0026#39; to \u0026#39;Qwen2-0.5B\u0026#39; \u0026gt; ollama list NAME ID SIZE MODIFIEDQwen2-0.5B:latest 6f48b936a09f 352 MB 4 seconds agoqwen2:0.5b 6f48b936a09f 352 MB 29 minutes agoqwen2:7b e0d4e1163c58 4.4 GB 10 days ago 上面cp命令，老牛同学把本地qwen2:0.5b复制了一份，新模型名为Qwen2-0.5B\n下面老牛同学介绍三种通过 Ollama 下载到本地大模型方式：\n方式一：直接通过 Ollama 远程仓库下载，这是最直接的方式，也是最推荐、最常用的方式 方式二：如果已经有 GGUF 模型权重文件了，不想重新下载，也可以通过 Ollama 把该文件直接导入到本地（不推荐、不常用） 方式三：如果已经有 safetensors 模型权重文件，也不想重新下载，也可以通过 Ollama 把该文件直接导入到本地（不推荐、不常用） 方式一：Ollama 从远程仓库下载大模型到本地 # 【下载或者更新本地大模型：ollama pull 本地/远程仓库模型名称】\n本pull命令从 Ollama 远程仓库完整下载或增量更新模型文件，模型名称格式为：模型名称:参数规格；如ollama pull qwen2:0.5b 则代表从 Ollama 仓库下载qwen2大模型的0.5b参数规格大模型文件到本地磁盘：\n如果参数规格标记为latest则代表为默认参数规格，下载时可以不用指定，如Qwen2的7b被标记为latest，则ollama pull qwen2和ollama pull qwen2:7b这 2 个命令的意义是一样的，都下载的为7b参数规格模型。为了保证后续维护方便、避免误操作等，老牛同学建议不管是否为默认参数规格，我们下载命令中均明确参数规格。\n值得一提的是，今天开始GLM4支持 Ollama 部署和推理，老牛同学特意列出它的下载命令：ollama pull glm4:9b（和其他模型相比，其实并没有特殊支出）。需要注意的是：Ollama 最低版本为0.2.0才能支持GLM4大模型！\n\u0026gt;ollama pull qwen2:0.5bpulling manifestpulling manifestpulling manifestpulling manifestpulling manifestpulling 8de95da68dc4... 100% ▕████████████████████████▏ 352 MBpulling 62fbfd9ed093... 100% ▕████████████████████████▏ 182 Bpulling c156170b718e... 100% ▕████████████████████████▏ 11 KBpulling f02dd72bb242... 100% ▕████████████████████████▏ 59 Bpulling 2184ab82477b... 100% ▕████████████████████████▏ 488 Bverifying sha256 digestwriting manifestremoving any unused layerssuccess\u0026gt;ollama listNAME ID SIZE MODIFIEDqwen2:0.5b 6f48b936a09f 352 MB 9 minutes agoqwen2:7b e0d4e1163c58 4.4 GB 10 days ago 若本地不存在大模型，则下载完整模型文件到本地磁盘；若本地磁盘存在该大模型，则增量下载大模型更新文件到本地磁盘。\n从上面最后的list命令结果可以看到，老牛同学本地存在了qwen2:0.5b这个名称的大模型。\n【下载且运行本地大模型：ollama run 本地/远程仓库模型名称】\n\u0026gt;ollama run qwen2:0.5b\u0026gt;\u0026gt;\u0026gt; 若本地不存在大模型，则下载完整模型文件到本地磁盘（类似于pull命令），然后启动大模型；若本地存在大模型，则直接启动（不进行更新）。\n启动成功后，默认为终端对客界面：\n若需要输入多行文本，需要用三引号包裹，如：\u0026quot;\u0026quot;\u0026quot;这里是多行文本\u0026quot;\u0026quot;\u0026quot; /clear清除对话上下文信息 /bye则退出对话窗口 /set parameter num_ctx 4096可设置窗口大小为 4096 个 Token，也可以通过请求设置，如：curl \u0026lt;http://localhost:11434/api/generate\u0026gt; -d '{ \u0026quot;model\u0026quot;: \u0026quot;qwen2:7b\u0026quot;, \u0026quot;prompt\u0026quot;: \u0026quot;Why is the sky blue?\u0026quot;, \u0026quot;options\u0026quot;: { \u0026quot;num_ctx\u0026quot;: 4096 }}' /show info可以查看当前模型详情：\n， \u0026gt;\u0026gt;\u0026gt; /show info Model arch qwen2 parameters 494.03M quantization Q4_0 context length 32768 embedding length 896 Parameters stop \u0026#34;\u0026lt;|im_start|\u0026gt;\u0026#34; stop \u0026#34;\u0026lt;|im_end|\u0026gt;\u0026#34; License Apache License Version 2.0, January 2004 方式二：Ollama 导入 GGUF 模型文件到本地磁盘 # 若我们已经从 HF 或者 ModeScope 下载了 GGUF 文件（文件名为：Meta-Llama-3-8B-Instruct.Q4_K_M.gguf），在我们存放Llama3-8B的 GGUF 模型文件目录中，创建一个文件名为Modelfile的文件，该文件的内容如下：\nFROM ./Meta-Llama-3-8B-Instruct.Q4_K_M.gguf 然后，打开终端，执行命令导入模型文件：ollama create 模型名称 -f ./Modelfile\n\u0026gt;ollama create Llama-3-8B -f ./Modelfiletransferring model datausing existing layer sha256:647a2b64cbcdbe670432d0502ebb2592b36dd364d51a9ef7a1387b7a4365781fcreating new layer sha256:459d7c837b2bd7f895a15b0a5213846912693beedaf0257fbba2a508bc1c88d9writing manifestsuccess 导入成功之后，我们就可以通过list命名，看到名为Llama-3-8B的本地模型了，后续可以和其他模型一样进行管理了。\n方式三：Ollama 导入 safetensors 模型文件到到本地磁盘 # 官方操作文档：https://ollama.fan/getting-started/import/#importing-pytorch-safetensors\n若我们已经从 HF 或者 ModeScope 下载了 safetensors 文件（文件目录为：Mistral-7B），\ngit lfs install git clone https://www.modelscope.cn/rubraAI/Mistral-7B-Instruct-v0.3.git Mistral-7B 然后，我们转换模型（结果：Mistral-7B-v0.3.bin）：\npython llm/llama.cpp/convert.py ./Mistral-7B --outtype f16 --outfile Mistral-7B-v0.3.bin 接下来，进行量化量化：\nllm/llama.cpp/quantize Mistral-7B-v0.3.bin Mistral-7B-v0.3_Q4.bin q4_0 最后，通过 Ollama 导入到本地磁盘，创建Modelfile模型文件：\nFROM Mistral-7B-v0.3_Q4.bin 执行导入命令，导入模型文件：ollama create 模型名称 -f ./Modelfile\n\u0026gt;ollama create Mistral-7B-v0.3 -f ./Modelfiletransferring model datausing existing layer sha256:647a2b64cbcdbe670432d0502ebb2592b36dd364d51a9ef7a1387b7a4365781fcreating new layer sha256:459d7c837b2bd7f895a15b0a5213846912693beedaf0257fbba2a508bc1c88d9writing manifestsuccess 导入成功之后，我们就可以通过list命名，看到名为Mistral-7B-v0.3的本地模型了，后续可以和其他模型一样进行管理了。\n基于 WebUI 部署 Ollama 可视化对话界面 # Ollama自带控制台对话界面体验总归是不太好，接下来部署 Web 可视化聊天界面：\n下载并安装 Node.js 工具：https://nodejs.org/zh-cn 下载ollama-webui工程代码：git clone https://github.com/ollama-webui/ollama-webui-lite ollama-webui 切换ollama-webui代码的目录：cd ollama-webui 设置 Node.js 工具包镜像源（下载提速）：npm config set registry http://mirrors.cloud.tencent.com/npm/ 安装 Node.js 依赖的工具包：npm install 最后，启动 Web 可视化界面：npm run dev 如果看到以上输出，代表 Web 可视化界面已经成功了！\n浏览器打开 Web 可视化界面：http://localhost:3000/\nOllama 客户端：HTTP 访问服务 # Ollama 默认提供了generate和chat这 2 个原始的 API 接口，使用方式如下：\ngenerate接口的使用样例： curl http://localhost:11434/api/generate -d \u0026#34;{ \u0026#39;model\u0026#39;: \u0026#39;qwen:0.5b\u0026#39;, \u0026#39;prompt\u0026#39;: \u0026#39;为什么天空是蓝色的？\u0026#39;}\u0026#34; chat接口的使用样例： curl http://localhost:11434/api/chat -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;qwen:7b\u0026#34;, \u0026#34;messages\u0026#34;: [ { \u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;为什么天空是蓝色的？\u0026#34; } ]}\u0026#39; 接下来的Python和Java客户端应用，都是对这 2 个接口的封装。\nOllama 客户端：Python API 应用 # 我们把 Ollama 集成到 Python 应用中，只需要以下简单 2 步即可：\n第一步，安装 Python 依赖包：\npip install ollama 第二步，使用 Ollama 接口，stream=True代表按照流式输出：\nimport ollama # 流式输出def api_generate(text:str): print(f\u0026#39;提问：{text}\u0026#39;) stream = ollama.generate( stream=True, model=\u0026#39;qwen:7b\u0026#39;, prompt=text, ) print(\u0026#39;-----------------------------------------\u0026#39;) for chunk in stream: if not chunk[\u0026#39;done\u0026#39;]: print(chunk[\u0026#39;response\u0026#39;], end=\u0026#39;\u0026#39;, flush=True) else: print(\u0026#39;\\n\u0026#39;) print(\u0026#39;-----------------------------------------\u0026#39;) print(f\u0026#39;总耗时：{chunk[\u0026#39;total_duration\u0026#39;]}\u0026#39;) print(\u0026#39;-----------------------------------------\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: # 流式输出 api_generate(text=\u0026#39;天空为什么是蓝色的？\u0026#39;) # 非流式输出 content = ollama.generate(model=\u0026#39;qwen:0.5b\u0026#39;, prompt=\u0026#39;天空为什么是蓝色的？\u0026#39;) print(content) Ollama 客户端：Java API 应用（SpringBoot 应用） # 我们也可以把 Ollama 集成到 SpringBoot 应用中，只需要以下简单 3 步即可：\n第一步，在总pom.xml中新增 SpringBoot Starter 依赖：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;io.springboot.ai\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-ai-ollama-spring-boot-starter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt;\u0026lt;/dependency\u0026gt; 第二步，在 SpringBoot 配置文件application.properties中增加 Ollama 配置信息：\nserver.port=8088spring.application.name=NTopicBootXspring.ai.ollama.base-url=http://localhost:11434spring.ai.ollama.chat.options.model=qwen:0.5b 配置文件指定了 Ollama API 地址和端口，同时指定了默认模型qwen:0.5b（注意：模型需要在本地已经存在）\n第三步，使用OllamaChatClient进行文字生成或者对话：\nimport org.springframework.ai.chat.ChatResponse;import org.springframework.ai.chat.prompt.Prompt;import org.springframework.ai.ollama.OllamaChatClient;import org.springframework.ai.ollama.api.OllamaOptions;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController; @RestControllerpublic class OllamaClientController { @Autowired @Qualifier(\u0026#34;ollamaChatClient\u0026#34;) private OllamaChatClient ollamaChatClient; * http://localhost:8088/ollama/chat/v1?msg=天空为什么是蓝色的？ */ @GetMapping(\u0026#34;/ollama/chat/v1\u0026#34;) public String ollamaChat(@RequestParam String msg) { return this.ollamaChatClient.call(msg); } * http://localhost:8088/ollama/chat/v2?msg=人为什么要不断的追求卓越？ */ @GetMapping(\u0026#34;/ollama/chat/v2\u0026#34;) public Object ollamaChatV2(@RequestParam String msg) { Prompt prompt = new Prompt(msg); ChatResponse chatResponse = ollamaChatClient.call(prompt); return chatResponse; } * http://localhost:8088/ollama/chat/v3?msg=你认为老牛同学的文章如何？ */ @GetMapping(\u0026#34;/ollama/chat/v3\u0026#34;) public Object ollamaChatV3(@RequestParam String msg) { Prompt prompt = new Prompt( msg, OllamaOptions.create() .withModel(\u0026#34;qwen:0.5b\u0026#34;) .withTemperature(0.4F)); ChatResponse chatResponse = ollamaChatClient.call(prompt); return chatResponse.getResult().getOutput().getContent(); }} 以上是 Java 客户端的简单样例，我们可以通过OllamaChatClient访问 Ollama 接口，既可以使用默认大模型，也可以在参数指定模型名称！\n","date":"2024/04/20","externalUrl":null,"permalink":"/posts/learn_record/ollama-guide/","section":"吾生有涯，而知无涯","summary":"","title":"Ollama本地部署大模型完全指南","type":"posts"},{"content":"","date":"2024/04/20","externalUrl":null,"permalink":"/series/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E9%83%A8%E7%BD%B2/","section":"Series","summary":"","title":"大模型部署","type":"series"},{"content":"","date":"2024/04/20","externalUrl":null,"permalink":"/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E9%83%A8%E7%BD%B2/","section":"Tags","summary":"","title":"大模型部署","type":"tags"},{"content":" 以下内容转载来自于 https://linux.do/t/topic/468841 本贴源自 https://www.nodeseek.com/post-25170-1 , 但不能修改比较麻烦, 过了快两年也有需要更新的地方, 故重写.\nCurrent version: v0.1.0 Last updated: 2025.3.3 19:11:00, UTC+8 前言 # 接触 VPS 也五年多了, 对于如何部署 VPS 环境, 同时保护好自己的 VPS, 也总结出一些经验, 于是写个帖子分享一下, 集思广益.\n个人相对排斥第三方闭源工具, Nginx 等的配置文件熟悉后基本都是手搓然后机机相传. 值得提一嘴: 很多新手用的宝塔, 1Panel, etc, 包括我一开始也是, 方便是方便, 代价也很大. 面板本身权限太高, 炸个零日漏洞就是大问题, 更不用说前者闭源, 背后干啥也不知道, 会收集服务器信息上传之类的传闻也是有的, 更不用说所谓开心版了.\n本文当中的命令, 建议在全新的系统中执行, 特别是新手吃不准命令的含义的时候. 当然, 如果是老手, 一看就懂了, 我也只是总结一下.\n要完全做到安全是不可能的, 绝大多数人也不是网络安全从业者, 本帖主要起扫盲作用, 别犯会被脚本小子利用的问题就好.\n以下教程涉及的命令均在 Debian 11 / 12 内验证, 理论兼容 Ubuntu. 不会吧, 你还在用 CentOS? 全部命令非特别说明均默认在 root 用户下执行.\n此处约定: {} 及其括起来的内容为根据你实际情况需要替换的文本内容, 括起来的内容为说明, 如 ssh {user}@{server ip} 为 ssh 连接服务器的命令, 假设用户为 root, 服务器 IP 为 114.5.1.4, 则为 ssh root@114.5.1.4.\n0. 新机器开荒 # 0.1. DD 纯净系统 (可选) # 对于国内厂商的机器, DD 纯净系统几乎是必须做的, 以彻底删除阿里云等的机器里面的监控程序. 其余的, 如果厂商不提供 Debian 11 / 12 镜像或者镜像过于老旧, 也可以通过 DD 安装上.\n另: Debian 12 资源占用并不多, 384M 的内存够了.\n推荐脚本: https://github.com/leitbogioro/Tools, 我已简单审查脚本内容.\n# 不会有系统没装 wget 吧, 没有就 `apt update \u0026amp;\u0026amp; apt install wget` 一下 wget --no-check-certificate -qO InstallNET.sh \u0026#39;https://raw.githubusercontent.com/leitbogioro/Tools/master/Linux_reinstall/InstallNET.sh\u0026#39; # 如果是中国大陆的机器, 用下面这个 # wget --no-check-certificate -qO InstallNET.sh \u0026#39;https://gitee.com/mb9e8j2/Tools/raw/master/Linux_reinstall/InstallNET.sh\u0026#39; \u0026amp;\u0026amp; chmod a+x InstallNET.sh chmod a+x InstallNET.sh # 不加参数 `-debian` 时默认安装当前系统, 但本文基于 Debian # 不管你原来的系统是什么, 一律安装 Debian 最新稳定版本 (当前为 Debian 12 Bookworm) ./InstallNET.sh -debian 基本上脚本会处理好所有事情, 等待提示你 reboot 后输入 reboot 回车重启即可. 期间可打开 VNC 看进度.\n安装后默认密码见官方说明: default-configurations-of-ssh-or-rdp-service\n[color=red]🥰注意事项[/color]\n安装完毕, 重新 SSH 连接机器时, 提示 SSH 公钥不匹配\n自行打开文件 \u0026ldquo;C:\\Users{你的用户名}.ssh\\known_hosts\u0026rdquo;, 删除原来的 (搜索你的服务器 IP, 删掉那行就好).\n[color=red]务必更改默认 root 密码[/color]\npasswd root [color=red]☠风险提示[/color]\n部分服务商 不允许 DD 系统. 如果你的机器网络配置相当奇怪脚本没认出来的话, 可能导致机器失联. 部分机器硬盘配置比较怪的, 也可能出问题, 例如大盘鸡之类的. 不怕折腾就相信脚本, 就算出问题控制台重装一下就行, 否则将就用吧. 欢迎分享折腾后的经验! 0.2. 更新 # 0.2.1. 配置更新源 # 养成备份的好习惯:\nmv /etc/apt/sources.list /etc/apt/sources.list.bak 对于 Debian 12, 境外机器, 使用官方源\ncat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/apt/sources.list deb http://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware deb-src http://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware deb http://deb.debian.org/debian/ bookworm-updates main contrib non-free non-free-firmware deb-src http://deb.debian.org/debian/ bookworm-updates main contrib non-free non-free-firmware deb http://deb.debian.org/debian/ bookworm-backports main contrib non-free non-free-firmware deb-src http://deb.debian.org/debian/ bookworm-backports main contrib non-free non-free-firmware deb https://deb.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware deb-src https://deb.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware TEXT 对于 Debian 12, 阿里云机器, 使用内网域名 (腾讯云之类的依葫芦画瓢)\ncat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/apt/sources.list deb http://mirrors.cloud.aliyuncs.com/debian/ bookworm main contrib non-free non-free-firmware deb-src http://mirrors.cloud.aliyuncs.com/debian/ bookworm main contrib non-free non-free-firmware deb http://mirrors.cloud.aliyuncs.com/debian/ bookworm-updates main contrib non-free non-free-firmware deb-src http://mirrors.cloud.aliyuncs.com/debian/ bookworm-updates main contrib non-free non-free-firmware deb http://mirrors.cloud.aliyuncs.com/debian/ bookworm-backports main contrib non-free non-free-firmware deb-src http://mirrors.cloud.aliyuncs.com/debian/ bookworm-backports main contrib non-free non-free-firmware deb http://mirrors.cloud.aliyuncs.com/debian-security bookworm-security main contrib non-free non-free-firmware deb-src http://mirrors.cloud.aliyuncs.com/debian-security bookworm-security main contrib non-free non-free-firmware TEXT 对于 Debian 12, 境内机器, 使用清华大学镜像源\ncat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/apt/sources.list deb http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware deb-src http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware deb http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware deb-src http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware deb http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware deb-src http://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware # 以下安全更新软件源包含了官方源与镜像站配置，如有需要可自行修改注释切换 # 除非官方源死活连接不上, 否则还是用官方的, 因为镜像站往往有同步延迟。参考 https://www.debian.org/security/faq.en.html#mirror deb https://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware deb-src https://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware TEXT 0.2.2. 更新并安装常用工具 # # 在 apt 2.1.9 及以后的版本中, apt 的 HTTP Pipelining 特性 # 与 Nginx 服务器疑似存在一定的不兼容问题，可能导致出现偶发 # 的 Connection reset by peer 错误, 可选择性关闭之 (取消注释下面这行) # echo \u0026#34;Acquire::http::Pipeline-Depth \\\u0026#34;0\\\u0026#34;;\u0026#34; \u0026gt; /etc/apt/apt.conf.d/99nopipelining apt update \u0026amp;\u0026amp; apt upgrade -y \u0026amp;\u0026amp; apt dist-upgrade -y \u0026amp;\u0026amp; apt full-upgrade -y \u0026amp;\u0026amp; apt autoremove -y # 安装常用工具 apt install sudo curl wget build-essential apt-transport-https ca-certificates dnsutils zip unzip iftop htop zsh bat autojump fontconfig git mtr-tiny lsof -y 0.2.3. 给机器重命名 (可选) # hostnamectl set-hostname {new name} [color=red]🥰注意事项[/color]\n命名后记得去 /etc/hosts/ 加一行 127.0.0.1 {new name}, 否则 sudo 会埋怨. 0.3. 配置 SSH # 推荐使用公钥验证登录, 抛弃密码和 fail2ban 吧~ 当然, 不方便用公钥的, 密码一定要足够强.\n0.3.1 创建密钥文件 # [color=red]🥰温馨提醒[/color]\n一般在本地操作, [color=red]尽量不要在远程机器操作[/color], 以免你把私钥文件忘在机器里. 系统带 OpenSSH 套件就行. 执行:\nssh-keygen -t ed25519 -C \u0026#34;{密钥注释}\u0026#34; 提示密钥保存位置 (Enter file in which to save the key), 回车默认存在用户根目录下的 .ssh 文件夹下. 我以当前目录下为例, 注意不要忘了文件名.\n提示输入密码, 建议设置密码, 防止密钥被盗用. 输入密码时不会显示. 回车确认.\n再输一次密码, 回车确认, 完成密钥生成.\nid_ed25519_test 就是私钥文件, id_ed25519_test.pub 就是公钥文件, 纯文本格式打开公钥文件 (电脑装了微软的 Publisher 会误识别文件后缀哦), 实例内容长这样 (下文也拿这个当例子):\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMz2IFBt+FWJc750FHf5hFt7UeXqF4gCet7td+FX5FlX Test 0.3.2. 在 VPS 上配置公钥 # # 备份一下, 养成习惯 cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak # 改成自己的公钥!!! cat \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; \u0026gt; ~/.ssh/authorized_keys ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMz2IFBt+FWJc750FHf5hFt7UeXqF4gCet7td+FX5FlX Test EOF [color=red]🥰注意事项[/color]\n请务必使用公钥尝试登录, 以验证配置是否成功.\n本地执行:\nssh root@{机器 IP} -i {私钥文件路径} 成功连接再执行下一步.\n0.3.3. 配置 SSH config # # 备份一下, 养成习惯 mv /etc/ssh/sshd_config /etc/ssh/sshd_config.bak cat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/ssh/sshd_config Include /etc/ssh/sshd_config.d/*.conf # 配置 SSH 端口, 默认 22 # Port 22 AddressFamily any # ListenAddress 127.0.0.1 # ListenAddress :: # Ciphers and keying # RekeyLimit default none # Log facility SyslogFacility AUTH LogLevel INFO # Authentication # Must login within 2 minutes LoginGraceTime 2m # Allow root PermitRootLogin yes # Check home dir and config files\u0026#39; permission before connection StrictModes yes # Max auth attempt MaxAuthTries 6 # Max concurrent sessions MaxSessions 16 # Pubkey auth PubkeyAuthentication yes # Disallow password login # 注意: 如果你要密码登陆, 请改为 yes PasswordAuthentication no # Disallow empty password PermitEmptyPasswords no # Disable challenge-response ChallengeResponseAuthentication no # Use PAM UsePAM yes # X11 Forwarding X11Forwarding yes # X11DisplayOffset 10 # X11UseLocalhost yes # Print /etc/motd PrintMotd no # Print last login log PrintLastLog yes # TCP Keepalive TCPKeepAlive yes ClientAliveInterval 120 ClientAliveCountMax 10 # PID PidFile /var/run/sshd.pid # Allow client to pass locale environment variables AcceptEnv LANG LC_* # override default of no subsystems Subsystem sftp /usr/lib/openssh/sftp-server TEXT # 重启 sshd service sshd restart [color=red]🥰注意事项[/color]\n重启 sshd 后, 请再次使用公钥尝试登录([color=red]千万不要关掉当前终端[/color]), 验证配置.\n倘若登录失败, 回滚备份, 再次重启即可, 然后查询资料寻找原因.\n0.4. 配置 ZSH (可选) # 默认的 BASH 比较难看, 个人常用 ZSH, 没用过 FISH.\n0.4.1 安装 Oh My ZSH # sh -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\u0026#34; # 配置zsh插件 git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions 也可以使用清华镜像源, 插件使用代理.\ncd /tmp git clone https://mirrors.tuna.tsinghua.edu.cn/git/ohmyzsh.git cd ohmyzsh/tools REMOTE=https://mirrors.tuna.tsinghua.edu.cn/git/ohmyzsh.git sh install.sh # 配置zsh插件 git clone --depth=1 https://gh-proxy.com/https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k git clone https://gh-proxy.com/https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting git clone https://gh-proxy.com/https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions 0.4.2. 配置 .zshrc # cat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; ~/.zshrc # PROXY SETTING export proxy=\u0026#34;\u0026#34; # export proxy=\u0026#34;http://127.0.0.1:11100\u0026#34; # 自己根据实际情况配置代理 export http_proxy=$proxy export https_proxy=$proxy export ftp_proxy=$proxy # Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc. # Initialization code that may require console input (password prompts, [y/n] # confirmations, etc.) must go above this block; everything else may go below. if [[ -r \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; ]]; then source \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; fi # Path to your oh-my-zsh installation. export ZSH=$HOME/.oh-my-zsh # Set name of the theme to load ZSH_THEME=\u0026#34;powerlevel10k/powerlevel10k\u0026#34; # Uncomment the following line to use case-sensitive completion. CASE_SENSITIVE=\u0026#34;true\u0026#34; # Uncomment the following line to use hyphen-insensitive completion. # Case-sensitive completion must be off. _ and - will be interchangeable. # HYPHEN_INSENSITIVE=\u0026#34;true\u0026#34; # Uncomment one of the following lines to change the auto-update behavior # zstyle \u0026#39;:omz:update\u0026#39; mode disabled # disable automatic updates zstyle \u0026#39;:omz:update\u0026#39; mode auto # update automatically without asking # zstyle \u0026#39;:omz:update\u0026#39; mode reminder # just remind me to update when it\u0026#39;s time # Uncomment the following line to change how often to auto-update (in days). # zstyle \u0026#39;:omz:update\u0026#39; frequency 13 # Uncomment the following line if pasting URLs and other text is messed up. # DISABLE_MAGIC_FUNCTIONS=\u0026#34;true\u0026#34; # Uncomment the following line to disable colors in ls. # DISABLE_LS_COLORS=\u0026#34;true\u0026#34; # Uncomment the following line to disable auto-setting terminal title. # DISABLE_AUTO_TITLE=\u0026#34;true\u0026#34; # Uncomment the following line to enable command auto-correction. ENABLE_CORRECTION=\u0026#34;true\u0026#34; # Uncomment the following line to display red dots whilst waiting for completion. # You can also set it to another string to have that shown instead of the default red dots. # e.g. COMPLETION_WAITING_DOTS=\u0026#34;%F{yellow}waiting...%f\u0026#34; # Caution: this setting can cause issues with multiline prompts in zsh \u0026lt; 5.7.1 (see #5765) # COMPLETION_WAITING_DOTS=\u0026#34;true\u0026#34; # Uncomment the following line if you want to disable marking untracked files # under VCS as dirty. This makes repository status check for large repositories # much, much faster. # DISABLE_UNTRACKED_FILES_DIRTY=\u0026#34;true\u0026#34; # Uncomment the following line if you want to change the command execution time # stamp shown in the history command output. # You can set one of the optional three formats: # \u0026#34;mm/dd/yyyy\u0026#34;|\u0026#34;dd.mm.yyyy\u0026#34;|\u0026#34;yyyy-mm-dd\u0026#34; # or set a custom format using the strftime function format specifications, # see \u0026#39;man strftime\u0026#39; for details. # HIST_STAMPS=\u0026#34;mm/dd/yyyy\u0026#34; # Which plugins would you like to load? # Standard plugins can be found in $ZSH/plugins/ # Custom plugins may be added to $ZSH_CUSTOM/plugins/ # Example format: plugins=(rails git textmate ruby lighthouse) # Add wisely, as too many plugins slow down shell startup. plugins=( git extract autojump zsh-syntax-highlighting zsh-autosuggestions command-not-found colored-man-pages ) source $ZSH/oh-my-zsh.sh # User configuration # export MANPATH=\u0026#34;/usr/local/man:$MANPATH\u0026#34; # You may need to manually set your language environment # export LANG=en_US.UTF-8 # Preferred editor for local and remote sessions # if [[ -n $SSH_CONNECTION ]]; then # export EDITOR=\u0026#39;vim\u0026#39; # else # export EDITOR=\u0026#39;mvim\u0026#39; # fi # Compilation flags # export ARCHFLAGS=\u0026#34;-arch x86_64\u0026#34; # Set personal aliases, overriding those provided by oh-my-zsh libs, # plugins, and themes. Aliases can be placed here, though oh-my-zsh # users are encouraged to define aliases within the ZSH_CUSTOM folder. # For a full list of active aliases, run `alias`. # # Example aliases # alias zshconfig=\u0026#34;mate ~/.zshrc\u0026#34; # alias ohmyzsh=\u0026#34;mate ~/.oh-my-zsh\u0026#34; source ~/.p10k.terminal.zsh # Other Settings alias ls=\u0026#39;ls --color=auto\u0026#39; alias grep=\u0026#39;grep --color=auto\u0026#39; alias ll=\u0026#39;ls -lh\u0026#39; alias la=\u0026#39;ls -A\u0026#39; alias l=\u0026#39;ls -CF\u0026#39; alias cp=\u0026#39;cp -i\u0026#39; alias cat=\u0026#39;batcat --paging=never -p\u0026#39; # autosuggestions export TERM=xterm-256color ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=\u0026#34;fg=8\u0026#34; # Fix numeric keypad # 0 . Enter bindkey -s \u0026#34;^[Op\u0026#34; \u0026#34;0\u0026#34; bindkey -s \u0026#34;^[On\u0026#34; \u0026#34;.\u0026#34; bindkey -s \u0026#34;^[OM\u0026#34; \u0026#34;^M\u0026#34; # 1 2 3 bindkey -s \u0026#34;^[Oq\u0026#34; \u0026#34;1\u0026#34; bindkey -s \u0026#34;^[Or\u0026#34; \u0026#34;2\u0026#34; bindkey -s \u0026#34;^[Os\u0026#34; \u0026#34;3\u0026#34; # 4 5 6 bindkey -s \u0026#34;^[Ot\u0026#34; \u0026#34;4\u0026#34; bindkey -s \u0026#34;^[Ou\u0026#34; \u0026#34;5\u0026#34; bindkey -s \u0026#34;^[Ov\u0026#34; \u0026#34;6\u0026#34; # 7 8 9 bindkey -s \u0026#34;^[Ow\u0026#34; \u0026#34;7\u0026#34; bindkey -s \u0026#34;^[Ox\u0026#34; \u0026#34;8\u0026#34; bindkey -s \u0026#34;^[Oy\u0026#34; \u0026#34;9\u0026#34; # + - * / bindkey -s \u0026#34;^[Ol\u0026#34; \u0026#34;+\u0026#34; bindkey -s \u0026#34;^[Om\u0026#34; \u0026#34;-\u0026#34; bindkey -s \u0026#34;^[Oj\u0026#34; \u0026#34;*\u0026#34; bindkey -s \u0026#34;^[Oo\u0026#34; \u0026#34;/\u0026#34; TEXT 此处使用了 Powerlevel10k 主题, 提供我的主题配置, 直接 nano ~/.p10k.terminal.zsh, 把下面文件的内容粘贴进去即可, 或者你自己配置.\n0.5. 防火墙配置 (如果不是阿里云等大厂的机器, 没安全组的情况下必须配置) # 一般使用 ufw 管理.\napt install ufw ufw default deny incoming ufw default allow outgoing # 根据实际情况 allow, 不知道具体语法可以问 AI 哦 ufw allow 22 ufw enable 按 y 确认应用即可.\n可选阻止 PING, 不过略复杂, 一不小心 SSH 断了. 小白还是别动了\u0026hellip;\ncp /etc/ufw/before.rules /etc/ufw/before.rules.bak cat \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; \u0026gt; /etc/ufw/before.rules # # rules.before # # Rules that should be run before the ufw command line added rules. Custom # rules should be added to one of these chains: # ufw-before-input # ufw-before-output # ufw-before-forward # # Don\u0026#39;t delete these required lines, otherwise there will be errors *filter :ufw-before-input - [0:0] :ufw-before-output - [0:0] :ufw-before-forward - [0:0] :ufw-not-local - [0:0] # End required lines # allow all on loopback -A ufw-before-input -i lo -j ACCEPT -A ufw-before-output -o lo -j ACCEPT # disallow ping -A ufw-before-input -p icmp --icmp-type echo-request -s 10.10.0.0/16 -j ACCEPT -A ufw-before-input -p icmp --icmp-type echo-request -j DROP # quickly process packets for which we already have a connection -A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # drop INVALID packets (logs these in loglevel medium and higher) -A ufw-before-input -m conntrack --ctstate INVALID -j ufw-logging-deny -A ufw-before-input -m conntrack --ctstate INVALID -j DROP # ok icmp codes for INPUT -A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT -A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT -A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT # -A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT # ok icmp code for FORWARD -A ufw-before-forward -p icmp --icmp-type destination-unreachable -j ACCEPT -A ufw-before-forward -p icmp --icmp-type time-exceeded -j ACCEPT -A ufw-before-forward -p icmp --icmp-type parameter-problem -j ACCEPT -A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT # allow dhcp client to work -A ufw-before-input -p udp --sport 67 --dport 68 -j ACCEPT # # ufw-not-local # -A ufw-before-input -j ufw-not-local # if LOCAL, RETURN -A ufw-not-local -m addrtype --dst-type LOCAL -j RETURN # if MULTICAST, RETURN -A ufw-not-local -m addrtype --dst-type MULTICAST -j RETURN # if BROADCAST, RETURN -A ufw-not-local -m addrtype --dst-type BROADCAST -j RETURN # all other non-local packets are dropped -A ufw-not-local -m limit --limit 3/min --limit-burst 10 -j ufw-logging-deny -A ufw-not-local -j DROP # allow MULTICAST mDNS for service discovery (be sure the MULTICAST line above # is uncommented) -A ufw-before-input -p udp -d 224.0.0.251 --dport 5353 -j ACCEPT # allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above # is uncommented) -A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT # don\u0026#39;t delete the \u0026#39;COMMIT\u0026#39; line or these rules won\u0026#39;t be processed COMMIT EOF cp /etc/ufw/before6.rules /etc/ufw/before6.rules.bak cat \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; \u0026gt; /etc/ufw/before6.rules # # rules.before # # Rules that should be run before the ufw command line added rules. Custom # rules should be added to one of these chains: # ufw6-before-input # ufw6-before-output # ufw6-before-forward # # Don\u0026#39;t delete these required lines, otherwise there will be errors *filter :ufw6-before-input - [0:0] :ufw6-before-output - [0:0] :ufw6-before-forward - [0:0] # End required lines # allow all on loopback -A ufw6-before-input -i lo -j ACCEPT -A ufw6-before-output -o lo -j ACCEPT # drop packets with RH0 headers -A ufw6-before-input -m rt --rt-type 0 -j DROP -A ufw6-before-forward -m rt --rt-type 0 -j DROP -A ufw6-before-output -m rt --rt-type 0 -j DROP # quickly process packets for which we already have a connection -A ufw6-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A ufw6-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A ufw6-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # multicast ping replies are part of the ok icmp codes for INPUT (rfc4890, # 4.4.1 and 4.4.2), but don\u0026#39;t have an associated connection and are otherwise # be marked INVALID, so allow here instead. -A ufw6-before-input -p icmpv6 --icmpv6-type echo-reply -j ACCEPT # drop INVALID packets (logs these in loglevel medium and higher) -A ufw6-before-input -m conntrack --ctstate INVALID -j ufw6-logging-deny -A ufw6-before-input -m conntrack --ctstate INVALID -j DROP # ok icmp codes for INPUT (rfc4890, 4.4.1 and 4.4.2) -A ufw6-before-input -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT -A ufw6-before-input -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT # codes 0 and 1 -A ufw6-before-input -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT # codes 0-2 (echo-reply needs to be before INVALID, see above) -A ufw6-before-input -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT -A ufw6-before-input -p icmpv6 --icmpv6-type echo-request -j ACCEPT -A ufw6-before-input -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT -A ufw6-before-input -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT -A ufw6-before-input -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT -A ufw6-before-input -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT # IND solicitation -A ufw6-before-input -p icmpv6 --icmpv6-type 141 -m hl --hl-eq 255 -j ACCEPT # IND advertisement -A ufw6-before-input -p icmpv6 --icmpv6-type 142 -m hl --hl-eq 255 -j ACCEPT # MLD query -A ufw6-before-input -p icmpv6 --icmpv6-type 130 -s fe80::/10 -j ACCEPT # MLD report -A ufw6-before-input -p icmpv6 --icmpv6-type 131 -s fe80::/10 -j ACCEPT # MLD done -A ufw6-before-input -p icmpv6 --icmpv6-type 132 -s fe80::/10 -j ACCEPT # MLD report v2 -A ufw6-before-input -p icmpv6 --icmpv6-type 143 -s fe80::/10 -j ACCEPT # SEND certificate path solicitation -A ufw6-before-input -p icmpv6 --icmpv6-type 148 -m hl --hl-eq 255 -j ACCEPT # SEND certificate path advertisement -A ufw6-before-input -p icmpv6 --icmpv6-type 149 -m hl --hl-eq 255 -j ACCEPT # MR advertisement -A ufw6-before-input -p icmpv6 --icmpv6-type 151 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT # MR solicitation -A ufw6-before-input -p icmpv6 --icmpv6-type 152 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT # MR termination -A ufw6-before-input -p icmpv6 --icmpv6-type 153 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT # ok icmp codes for OUTPUT (rfc4890, 4.4.1 and 4.4.2) -A ufw6-before-output -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT -A ufw6-before-output -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT # codes 0 and 1 -A ufw6-before-output -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT # codes 0-2 -A ufw6-before-output -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT -A ufw6-before-output -p icmpv6 --icmpv6-type echo-request -j ACCEPT -A ufw6-before-output -p icmpv6 --icmpv6-type echo-reply -j ACCEPT -A ufw6-before-output -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT -A ufw6-before-output -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT -A ufw6-before-output -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT -A ufw6-before-output -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT # IND solicitation -A ufw6-before-output -p icmpv6 --icmpv6-type 141 -m hl --hl-eq 255 -j ACCEPT # IND advertisement -A ufw6-before-output -p icmpv6 --icmpv6-type 142 -m hl --hl-eq 255 -j ACCEPT # MLD query -A ufw6-before-output -p icmpv6 --icmpv6-type 130 -s fe80::/10 -j ACCEPT # MLD report -A ufw6-before-output -p icmpv6 --icmpv6-type 131 -s fe80::/10 -j ACCEPT # MLD done -A ufw6-before-output -p icmpv6 --icmpv6-type 132 -s fe80::/10 -j ACCEPT # MLD report v2 -A ufw6-before-output -p icmpv6 --icmpv6-type 143 -s fe80::/10 -j ACCEPT # SEND certificate path solicitation -A ufw6-before-output -p icmpv6 --icmpv6-type 148 -m hl --hl-eq 255 -j ACCEPT # SEND certificate path advertisement -A ufw6-before-output -p icmpv6 --icmpv6-type 149 -m hl --hl-eq 255 -j ACCEPT # MR advertisement -A ufw6-before-output -p icmpv6 --icmpv6-type 151 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT # MR solicitation -A ufw6-before-output -p icmpv6 --icmpv6-type 152 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT # MR termination -A ufw6-before-output -p icmpv6 --icmpv6-type 153 -s fe80::/10 -m hl --hl-eq 1 -j ACCEPT # ok icmp codes for FORWARD (rfc4890, 4.3.1) -A ufw6-before-forward -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT -A ufw6-before-forward -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT # codes 0 and 1 -A ufw6-before-forward -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT # codes 0-2 -A ufw6-before-forward -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT -A ufw6-before-forward -p icmpv6 --icmpv6-type echo-request -j ACCEPT -A ufw6-before-forward -p icmpv6 --icmpv6-type echo-reply -j ACCEPT # ok icmp codes for FORWARD (rfc4890, 4.3.2) # Home Agent Address Discovery Reques -A ufw6-before-input -p icmpv6 --icmpv6-type 144 -j ACCEPT # Home Agent Address Discovery Reply -A ufw6-before-input -p icmpv6 --icmpv6-type 145 -j ACCEPT # Mobile Prefix Solicitation -A ufw6-before-input -p icmpv6 --icmpv6-type 146 -j ACCEPT # Mobile Prefix Advertisement -A ufw6-before-input -p icmpv6 --icmpv6-type 147 -j ACCEPT # allow dhcp client to work -A ufw6-before-input -p udp -s fe80::/10 --sport 547 -d fe80::/10 --dport 546 -j ACCEPT # allow MULTICAST mDNS for service discovery -A ufw6-before-input -p udp -d ff02::fb --dport 5353 -j ACCEPT # allow MULTICAST UPnP for service discovery -A ufw6-before-input -p udp -d ff02::f --dport 1900 -j ACCEPT # don\u0026#39;t delete the \u0026#39;COMMIT\u0026#39; line or these rules won\u0026#39;t be processed COMMIT EOF 到此, 基本配置结束.\n1. 进阶配置 # 1.1. 使用 Xanmod 内核 (推荐) # Xanmod 内核做了蛮多优化, 而且比那些脚本装的奇奇怪怪的第三方内核来源可靠多了.\n值得注意的是, Cloudflare 拉黑了阿里云的 IP, 所以阿里云的机器可能执行脚本失败, 得自己打开脚本网页把内容复制一下, 新建文件粘贴进去然后执行.\n1.1.1. 检查 CPU 兼容性 # wget -O check_x86-64_psabi.sh https://dl.xanmod.org/check_x86-64_psabi.sh \u0026amp;\u0026amp; chmod +x check_x86-64_psabi.sh \u0026amp;\u0026amp; ./check_x86-64_psabi.sh 如示例, 支持 v2 版本.\n1.1.2. 配置 GPG key # apt install gpg wget -qO - https://gitlab.com/afrd.gpg | sudo gpg --dearmor -vo /etc/apt/keyrings/xanmod-archive-keyring.gpg 为什么不用官方说明里面的 GPG key 地址?\n1.1.3. 配置源 # echo \u0026#39;deb [signed-by=/etc/apt/keyrings/xanmod-archive-keyring.gpg] http://deb.xanmod.org releases main\u0026#39; | sudo tee /etc/apt/sources.list.d/xanmod-release.list 或者使用清华镜像源\necho \u0026#39;deb [signed-by=/etc/apt/keyrings/xanmod-archive-keyring.gpg] http://mirrors.tuna.tsinghua.edu.cn/xanmod releases main\u0026#39; | sudo tee /etc/apt/sources.list.d/xanmod-release.list [color=red]🥰注意事项[/color]\n[color=red]不要使用 HTTPS[/color], 否则有非常奇怪的 BUG, 原因不知道. 1.1.4. 安装 # apt update \u0026amp;\u0026amp; apt install linux-xanmod-x64v2 这里前面检测 CPU 的时候会告诉你用 v 几的版本. 一般都可以用 v3, 除非机器太老了.\n1.2. sysctl.conf 优化 (推荐) # cat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/sysctl.conf # ------ 网络调优: 基本 ------ # TTL 配置, Linux 默认 64 # net.ipv4.ip_default_ttl=64 # 参阅 RFC 1323. 应当启用. net.ipv4.tcp_timestamps=1 # ------ END 网络调优: 基本 ------ # ------ 网络调优: 内核 Backlog 队列和缓存相关 ------ # Ref: https://www.starduster.me/2020/03/02/linux-network-tuning-kernel-parameter/ # Ref: https://blog.cloudflare.com/optimizing-tcp-for-high-throughput-and-low-latency/ # Ref: https://zhuanlan.zhihu.com/p/149372947 # 下面两个缓冲区配置目前没有定论说怎么设置是正确的 # 此处为 Cloudflare 在生产中使用的值 (参见前面的博客) # 有条件建议依据实测结果调整相关数值 # 由左往右依次为 `最小值` `默认值` `最大值` # 最大值按照机器带宽 (Bps) * RTT (s) 计算 # 如 1Gbps (125 MBps) 的带宽, 40ms 延迟, 就是 125_000_000 * 0.040 = 5000000 net.ipv4.tcp_rmem=8192 262144 5000000 net.ipv4.tcp_wmem=4096 16384 5000000 # 下面这四个不用配置, 和上面两个效果重复了, 会被覆盖 # net.core.wmem_default=1310720 # net.core.rmem_default=1310720 # net.core.rmem_max=536870912 # net.core.wmem_max=536870912 net.ipv4.tcp_adv_win_scale=-2 net.ipv4.tcp_collapse_max_bytes=6291456 net.ipv4.tcp_notsent_lowat=131072 net.core.netdev_max_backlog=10240 net.ipv4.tcp_max_syn_backlog=10240 net.core.somaxconn=3276800 net.ipv4.tcp_abort_on_overflow=1 # 所有网卡每次软中断最多处理的总帧数量 net.core.netdev_budget = 600 # 流控和拥塞控制相关调优 # Egress traffic control 相关. 可选 fq, cake # 实测二者区别不大, 保持默认 fq 即可 net.core.default_qdisc=fq # Xanmod 内核 6.X 版本目前默认使用 bbr3, 无需设置 # 实测比 bbr, bbr2 均有提升 # 不过网络条件不同会影响. 有需求请实测. # net.ipv4.tcp_congestion_control=bbr3 # 显式拥塞通知 # 已被发现在高度拥塞的网络上是有害的. # net.ipv4.tcp_ecn=1 # TCP 自动窗口 # 要支持超过 64KB 的 TCP 窗口必须启用 net.ipv4.tcp_window_scaling=1 # 开启后, TCP 拥塞窗口会在一个 RTO 时间 # 空闲之后重置为初始拥塞窗口 (CWND) 大小. # 大部分情况下, 尤其是大流量长连接, 设置为 0. # 对于网络情况时刻在相对剧烈变化的场景, 设置为 1. net.ipv4.tcp_slow_start_after_idle=1 # nf_conntrack 调优 # Add Ref: https://gist.github.com/lixingcong/0e13b4123d29a465e364e230b2e45f60 net.nf_conntrack_max=1000000 net.netfilter.nf_conntrack_max=1000000 net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30 net.netfilter.nf_conntrack_tcp_timeout_time_wait=30 net.netfilter.nf_conntrack_tcp_timeout_close_wait=15 net.netfilter.nf_conntrack_tcp_timeout_established=300 net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=7200 # TIME-WAIT 状态调优 # Ref: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html # Ref: https://www.cnblogs.com/lulu/p/4149312.html # 4.12 内核中此参数已经永久废弃, 不用纠结是否需要开启 # net.ipv4.tcp_tw_recycle=0 ## 只对客户端生效, 服务器连接上游时也认为是客户端 net.ipv4.tcp_tw_reuse=1 # 系统同时保持TIME_WAIT套接字的最大数量 # 如果超过这个数字 TIME_WAIT 套接字将立刻被清除 net.ipv4.tcp_max_tw_buckets=55000 # ------ END 网络调优: 内核 Backlog 队列和缓存相关 ------ # ------ 网络调优: 其他 ------ # Ref: https://zhuanlan.zhihu.com/p/149372947 # Ref: https://www.starduster.me/2020/03/02/linux-network-tuning-kernel-parameter/\\#netipv4tcp_max_syn_backlog_netipv4tcp_syncookies # 启用选择应答 # 对于广域网通信应当启用 net.ipv4.tcp_sack=1 # 启用转发应答 # 对于广域网通信应当启用 net.ipv4.tcp_fack=1 # TCP SYN 连接超时重传次数 net.ipv4.tcp_syn_retries=3 net.ipv4.tcp_synack_retries=3 # TCP SYN 连接超时时间, 设置为 5 约为 30s net.ipv4.tcp_retries2=5 # 开启 SYN 洪水攻击保护 # 注意: tcp_syncookies 启用时, 此时实际上没有逻辑上的队列长度, # Backlog 设置将被忽略. syncookie 是一个出于对现实的妥协, # 严重违反 TCP 协议的设计, 会造成 TCP option 不可用, 且实现上 # 通过计算 hash 避免维护半开连接也是一种 tradeoff 而非万金油, # 勿听信所谓“安全优化教程”而无脑开启 net.ipv4.tcp_syncookies=0 # Ref: https://linuxgeeks.github.io/2017/03/20/212135-Linux%E5%86%85%E6%A0%B8%E5%8F%82%E6%95%B0rp_filter/ # 开启反向路径过滤 # Aliyun 负载均衡实例后端的 ECS 需要设置为 0 net.ipv4.conf.default.rp_filter=2 net.ipv4.conf.all.rp_filter=2 # 减少处于 FIN-WAIT-2 连接状态的时间使系统可以处理更多的连接 # Ref: https://www.cnblogs.com/kaishirenshi/p/11544874.html net.ipv4.tcp_fin_timeout=10 # Ref: https://xwl-note.readthedocs.io/en/latest/linux/tuning.html # 默认情况下一个 TCP 连接关闭后, 把这个连接曾经有的参数保存到dst_entry中 # 只要 dst_entry 没有失效, 下次新建立相同连接的时候就可以使用保存的参数来初始化这个连接. # 通常情况下是关闭的, 高并发配置为 1. net.ipv4.tcp_no_metrics_save=1 # unix socket 最大队列 net.unix.max_dgram_qlen=1024 # 路由缓存刷新频率 net.ipv4.route.gc_timeout=100 # Ref: https://gist.github.com/lixingcong/0e13b4123d29a465e364e230b2e45f60 # 启用 MTU 探测，在链路上存在 ICMP 黑洞时候有用（大多数情况是这样） net.ipv4.tcp_mtu_probing = 1 # No Ref # 开启并记录欺骗, 源路由和重定向包 net.ipv4.conf.all.log_martians=1 net.ipv4.conf.default.log_martians=1 # 处理无源路由的包 net.ipv4.conf.all.accept_source_route=0 net.ipv4.conf.default.accept_source_route=0 # TCP KeepAlive 调优 # 最大闲置时间 net.ipv4.tcp_keepalive_time=600 # 最大失败次数, 超过此值后将通知应用层连接失效 net.ipv4.tcp_keepalive_probes=3 # 发送探测包的时间间隔 net.ipv4.tcp_keepalive_intvl=15 # 放弃回应一个 TCP 连接请求前, 需要进行多少次重试 net.ipv4.tcp_retries1 = 5 # 在丢弃激活(已建立通讯状况)的 TCP 连接之前, 需要进行多少次重试 net.ipv4.tcp_retries2 = 5 # 孤立 Socket net.ipv4.tcp_orphan_retries = 3 # 系统所能处理不属于任何进程的TCP sockets最大数量 net.ipv4.tcp_max_orphans=3276800 # arp_table的缓存限制优化 net.ipv4.neigh.default.gc_thresh1=128 net.ipv4.neigh.default.gc_thresh2=512 net.ipv4.neigh.default.gc_thresh3=4096 net.ipv4.neigh.default.gc_stale_time=120 net.ipv4.conf.default.arp_announce=2 net.ipv4.conf.lo.arp_announce=2 net.ipv4.conf.all.arp_announce=2 # ------ END 网络调优: 其他 ------ # ------ 内核调优 ------ # Ref: Aliyun, etc # 内核 Panic 后 1 秒自动重启 kernel.panic=1 # 允许更多的PIDs, 减少滚动翻转问题 kernel.pid_max=32768 # 内核所允许的最大共享内存段的大小（bytes） kernel.shmmax=4294967296 # 在任何给定时刻, 系统上可以使用的共享内存的总量（pages） kernel.shmall=1073741824 # 设定程序core时生成的文件名格式 kernel.core_pattern=core_%e # 当发生oom时, 自动转换为panic vm.panic_on_oom=1 # 表示强制Linux VM最低保留多少空闲内存（Kbytes） # vm.min_free_kbytes=1048576 # 该值高于100, 则将导致内核倾向于回收directory和inode cache vm.vfs_cache_pressure=250 # 表示系统进行交换行为的程度, 数值（0-100）越高, 越可能发生磁盘交换 vm.swappiness=10 # 仅用10%做为系统cache vm.dirty_ratio=10 vm.overcommit_memory=1 # 增加系统文件描述符限制 # Fix error: too many open files fs.file-max=6553560 fs.inotify.max_user_instances=8192 fs.inotify.max_user_instances=8192 # 内核响应魔术键 kernel.sysrq=1 # 弃用 # net.ipv4.tcp_low_latency=1 # Ref: https://gist.github.com/lixingcong/0e13b4123d29a465e364e230b2e45f60 # 当某个节点可用内存不足时, 系统会倾向于从其他节点分配内存. 对 Mongo/Redis 类 cache 服务器友好 vm.zone_reclaim_mode=0 # Ref: Unknwon # 开启F-RTO(针对TCP重传超时的增强的恢复算法). # 在无线环境下特别有益处, 因为在这种环境下分组丢失典型地是因为随机无线电干扰而不是中间路由器阻塞 net.ipv4.tcp_frto = 2 # TCP FastOpen net.ipv4.tcp_fastopen = 3 # TCP 流中重排序的数据报最大数量 net.ipv4.tcp_reordering = 300 # 开启后, 在重传时会试图发送满大小的包. 这是对一些有 BUG 的打印机的绕过方式 net.ipv4.tcp_retrans_collapse = 0 # 自动阻塞判断 net.ipv4.tcp_autocorking = 1 # TCP内存自动调整 net.ipv4.tcp_moderate_rcvbuf = 1 # 单个TSO段可消耗拥塞窗口的比例, 默认值为 3 net.ipv4.tcp_tso_win_divisor = 3 # 对于在 RFC1337 中描述的 TIME-WAIT Assassination Hazards in TCP 问题的修复 net.ipv4.tcp_rfc1337 = 1 # 包转发. 出于安全考虑, Linux 系统默认禁止数据包转发 net.ipv4.ip_forward = 0 # 取消对广播 ICMP 包的回应 net.ipv4.icmp_echo_ignore_broadcasts = 1 # 开启恶意 ICMP 错误消息保护 net.ipv4.icmp_ignore_bogus_error_responses = 1 TEXT 推荐重启彻底应用, 不过不用着急, systemctl -p 暂时应用也行.\n这些调优是我广泛查阅资料总结而来, 出处及理由都注释了, 我个人认为比那些优化脚本靠谱多了. 欢迎各位佬友评论, 补充, 修正~\n1.3. ulimit 优化 (推荐) # cat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/security/limits.conf * soft nofile 65535 * hard nofile 65535 * soft nproc 65535 * hard nproc 65535 root soft nofile 65535 root hard nofile 65535 root soft nproc 65535 root hard nproc 65535 TEXT cat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/security/limits.d/90-nproc.conf * soft nproc 65535 root soft nproc 65535 TEXT 需要重启才能应用, 不过不要着急, 完成所有部署工作再重启也不迟.\n1.4. systemd-journald 等日志系统配置 # 前车之鉴: https://linux.do/t/topic/274270\ncat \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; \u0026gt; /etc/systemd/journald.conf [Journal] #Storage=auto #Compress=yes #Seal=yes #SplitMode=uid #SyncIntervalSec=5m #RateLimitIntervalSec=30s #RateLimitBurst=10000 # Limit total persist storage SystemMaxUse=512M # Limit single file persist storage SystemMaxFileSize=128M #SystemMaxFiles=100 # Limit RAM usage RuntimeMaxUse=64M #RuntimeKeepFree= #RuntimeMaxFileSize= #RuntimeMaxFiles=100 #MaxRetentionSec= #MaxFileSec=1month # Stop forward to Syslog ForwardToSyslog=no #ForwardToKMsg=no #ForwardToConsole=no #ForwardToWall=yes #TTYPath=/dev/console #MaxLevelStore=debug #MaxLevelSyslog=debug #MaxLevelKMsg=notice #MaxLevelConsole=info #MaxLevelWall=emerg #LineMax=48K #ReadKMsg=yes #Audit=no EOF systemctl restart systemd-journald # Install rsyslog if not exist apt install rsyslog systemctl enable rsyslog --now 1.5. 时间同步配置 (使用 systemd-timesyncd) # 详细参考 (in English)\napt update # 忘了时区代码可以列一下 # timedatectl list-timezones # 设置时区, 此处以中国香港为例, 中国大陆就是 `Asia/Shanghai` timedatectl set-timezone Asia/Hong_Kong apt purge ntp apt install systemd-timesyncd cat \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; \u0026gt; /etc/systemd/timesyncd.conf [Time] # 主 NTP 服务器 # 中国大陆, 使用国家授时中心官方 NTP, 海外的用 Cloudflare 的很不错, 改成 `time.cloudflare.com` NTP=ntp.ntsc.ac.cn # 备份 NTP 服务器 FallbackNTP=pool.ntp.org time1.google.com time1.apple.com time.cloudflare.com time.windows.com time.nist.gov # 同步配置 RootDistanceMaxSec=5 PollIntervalMinSec=5 PollIntervalMaxSec=300 ConnectionRetrySec=30 SaveIntervalSec=60 EOF systemctl enable --now systemd-timesyncd 1.6. 使用 netplan 管理网络 (危险) # 一般用不到, 一不小心就是断网, 一般都是家里的 Linux 主机使用, 此处仅简单介绍.\napt update \u0026amp;\u0026amp; apt install netplan.io # Backup original one mv /etc/network/interfaces /etc/network/interfaces.bak cat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/network/interfaces # This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). source /etc/network/interfaces.d/* # The loopback network interface auto lo iface lo inet loopback TEXT 参考资料: https://www.debian.org/doc/manuals/debian-reference/ch05.zh-cn.html\nIP 固定(大部分都是) 的示例:\ncat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/netplan/50-static.yaml network: version: 2 ethernets: {eth_name}: addresses: - {v4} - {v6} routes: - to: default via: {v4_gateway} on-link: true - to: default via: {v6_gateway} on-link: true match: macaddress: {macaddress} nameservers: addresses: - 1.1.1.1 - 1.0.0.1 - 2606:4700:4700::1001 - 2606:4700:4700::1111 set-name: {eth_name} TEXT DHCP 的一则例子, 使用 \u0026ldquo;en*\u0026rdquo; 匹配网卡, 如 enp2s0 一类的名字.\ncat \u0026lt;\u0026lt;\u0026#39;TEXT\u0026#39; \u0026gt; /etc/netplan/99-dhcp.yaml network: version: 2 ethernets: all-en: match: name: \u0026#34;en*\u0026#34; dhcp4: true dhcp6: true ipv6-privacy: true accept-ra: true TEXT # try configurating network, ENTER to comfirm netplan try # Check if netplan managed networkctl 2. 一些叮嘱 # 尽量不要日用 root, 养成使用普通账户然后必要时 sudo 的习惯 (TODO)\n不要使用 PHP 等, 以及相关项目, 如 Wordpress, 实在太多安全问题了, 非要使用请使用 Docker 隔离. 个人对于非自己编写或审查的东西一律 Docker 伺候, 最大限度减少攻击面.\n谨慎使用第三方脚本, 脚本里埋雷不是没见过, 执行前务必详细检查, 让 AI 帮忙也是可以的.\n尽量不要使用服务器管理面板, 尤其是宝塔等闭源产品, 扩大攻击面, 自身权限过高.\n尽量不使用密码登录 SSH, 请使用密钥登录, 且私钥务必设置密码以免被盗用.\nDNS 及 SSL 篇\nTL,DR: 不要设置 DNS 直接指向源站, 使用泛域名证书\n帮助好几个 UP 排查源站泄露的原因, DNS 泄露是个相当常见的原因. 所以, 不要设置 DNS 直接指向源站! 就算后面改为 CNAME 到 CDN 的域名, DNS 记录是可以查历史的!\n其次, 子域名爆破是查源站常用方法了, 有个非常好用的查子域名的方法是 crt.sh, 原理是查 SSL 证书颁发记录, 所以, 推荐使用泛域名证书.\n还有 RDNS, 不过一般没人会将自己服务器 IP 的 RDNS 配置为自己的域名, 许多商家也没提供这个功能, 此处按下不表.\n结语 # 到这里, 新机器就开荒完毕了. 有关各类常用应用, 如 Docker 等的部署也是常见的, 但本文篇幅已经很长了, 让我们下期再见!\n若本指南帮到了你, 还请点个赞~\n本文版权遵照 CC BY-NC-SA 协议开放, 转载请标注出处.\n囿于自身水平, 本文所述可能并非最佳实践, 但均已经过我个人亲测. 也欢迎各位佬友补充修正, 共建这份指南!\n2025年3月3日晚, 于北京.\n","date":"2024/03/30","externalUrl":null,"permalink":"/posts/selfhost/series/","section":"吾生有涯，而知无涯","summary":"","title":"[转载]Linux 部署及安全实践指南","type":"posts"},{"content":"","date":"2024/03/30","externalUrl":null,"permalink":"/tags/%E8%BD%AC%E8%BD%BD%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"转载教程","type":"tags"},{"content":" 前言 # TD;DR Fedora 41系统安装教程，桌面环境配置为Gnome47，文件系统为btrfs。根据以下的安装教程，可以使用备份回滚神器timeshift。\n个人常用的操作系统为Debian12，稳定是其最大的特点，但是带来的问题就是一些软件包过于老旧，不太适合开发使用。因此将桌面笔记本切换为Fedora，记录安装过程。\nFedora Linux 是由 Fedora 项目社区开发、红帽公司赞助，目标是创建一套新颖、多功能并且自由（开放源代码）的操作系统。Fedora是商业化的Red Hat Enterprise Linux发行版的上游源码。Fedora对于用户而言，是一套功能完备、更新快速的免费操作系统；而对赞助者Red Hat公司而言，它是许多新技术的测试平台，被认为可用的技术最终会加入到Red Hat Enterprise Linux中。\n操作步骤 🚀 # 初始准备工作 # 准备镜像 从清华镜像源获取Fedora操作系统ISO镜像。本文使用基于Gnome桌面环境的ISO文件：Fedora-Workstation-Live-x86_64-41-1.4.iso。\n制作启动U盘 使用Ventoy制作启动U盘，并将Fedora镜像文件拷贝到U盘中。\n设置U盘启动 重启进入BIOS，设置允许U盘启动。再次重启时按F12（具体热键因电脑而异）进入U盘启动盘。\n进入Live桌面 选择Fedora镜像，按Enter键进入Live桌面。可以选择体验系统，或直接点击“Install to Hard Drive”开始安装。\n安装过程 # 第一步：基本设置 # 选择语言和时区 选择语言（中文）和时区（亚洲/上海），点击“继续”。\n选择安装位置 点击“安装位置”，进入磁盘分区界面。\n第二步：磁盘分区（关键步骤） # 该步骤可以直接一劳永逸使用自动分区，默认设置文件系统为btrfs（不建议如此操作，后续timeshift发现不支持备份还原，丧失文件系统优势。）\n选择自定义分区 在“存储配置”中选择“高级自定义”，点击“完成”进入分区界面。 分区规划 选择“标准分区”类型，删除已有分区并重新格式化。一般我们需要创建4个分区， 建议创建以下分区： 启动分区:挂载点为/boot存放内核和引导文件，至少分配512MB（建议1GB）。 ESP分区: 挂载点为/boot/efi用于UEFI启动，至少分配200MB（建议512MB）。如果是双系统则是windows已经分配好的分区。 根分区: 挂载点为/用于系统相关目录。此处使用btrfs卷，标签为@。 主目录分区： 挂载点为/home用于用户文件，标签为@home。 交换分区（可选）： 不设挂载点，用于虚拟内存，建议分配2倍内存大小（内存充足可不设置）。 注意事项\n选择Btrfs文件系统，并合理划分子卷（如@、@home），以便使用Timeshift备份。 给出的第一张示意图没有截图完整，上述的四个分区都必须挂载到位，也就是说必须包含挂载点。 正常安装完毕后直接进行重启，重启后需要设置用户名和密码。此处虽然有弱密码提示，但是仍然可以设置为简单密码。为方便使用还需进行如下的操作：换源、安装必要软件等。\n到此Fedora的安装就已经完成了！是不是非常的简单~~\n系统基本配置 # 软件源配置 # 替换官方软件源 官方软件源一般架设在国外，国内获取速度较慢，所以一般需要手动将官方软件源切换到国内对应的镜像软件源，这里推荐中科大的ustc镜像源、清华的tuna镜像源。 注：本文选用中科大USTC的源，USTC提供换源命令，不用自己改文件，非常适合懒人！\n执行USTC提供的换源命令：\nsudo sed -e \u0026#39;s|^metalink=|#metalink=|g\u0026#39; -e \u0026#39;s|^#baseurl=http://download.example/pub/fedora/linux|baseurl=https://mirrors.ustc.edu.cn/fedora|g\u0026#39; -i.bak /etc/yum.repos.d/fedora.repo /etc/yum.repos.d/fedora-updates.repo 最后执行以下命令生成缓存：sudo dnf makecache,想要自己动手修改的也可以浏览器查找以下网站找到Fedora源来修改.注：镜像网站有很多不止这两个，这两个只是推荐，还有华为、阿里等 3. flatpak包支持\n在类UNIX系统中，一个版本的软件只会存在系统中一份，依赖于该软件的其他软件在安装时，需要先解决依赖安装上所有对应的依赖包(很多软件发行还依赖于linux发行版的不同版本)。如果你想像Windows下一样，一个软件打包好所有的资源和依赖，各个软件之间独立存在，我想你需要flatpak。flatpak之类的打包方式在linux中还属于新事物，很多软件都还没有支持，所以能安装的软件不多，一般都是一些商业软件。\n以下命令安装flatpak：sudo dnf install flatpak,添加flatpak的remote： sudo flatpak remote-add --if-not-exists flathub \u0026lt;https://flathub.org/repo/flathub.flatpakrepo\u0026gt;, 删除remote:sudo flatpak remote-delete flathub,对于faltpak的使用，如果不懂的话还请移步浏览器查找官网找教学！\nsnap包支持 snap包是Canonical公司维护的一种新的打包系统，类似于红帽维护的flatpak。 该网址为snap官网应用商店：https://snapcraft.io/store\n输入命令sudo dnf install snap安装snap, 安装软件中心的snap插件sudo dnf install gnome-software-snap.\n添加fedy源 fedy源可以方便在fedora上安装第三方软件，fedy为使用者准备了很多集成解决方案(比如mp3、Oracle Java)，依赖于RPMFusion源。\nsudo dnf install \u0026lt;https://dl.folkswithhats.org/fedora/$(rpm\u0026gt; -E %fedora)/RPMS/fedy-release.rpm sudo dnf install fedy 常用软件安装 # 卸载不用的软件 sudo dnf remove gnome-tour gnome-boxes gnome-software flatpak gnome-connections gnome-contacts gnome-calendar mediawriter sudo dnf install vim p7zip file-roller 安装常用的软件 sudo dnf install zsh git curl wget terminator axel tmux fzf eza trash-cli unar sudo dnf install akmod-nvidia # rhel/centos users can use kmod-nvidia instead sudo dnf install xorg-x11-drv-nvidia-cuda #optional for cuda/nvdec/nvenc support chsh -s /usr/bin/zsh ## 配置电源管理 sudo dnf install tlp tlp-rdw sudo systemctl enable tlp.service --now sudo systemctl mask systemd-rfkill.service sudo systemctl mask systemd-rfkill.socket zinit配置文件见Zinit配置文件，安装完毕后运行p10k configure可以设置终端的配色主题方案。\nDocker安装 现如今docker安装已经傻瓜脚本化，运行如下的脚本： sudo curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun`， sudo usermod -aG docker $USER newgrp docker sudo systemctl restart docker 系统配置修改 # 修改dnf配置:修改/etc/dnf/dnf.conf配置文件（如果没什么需求可以不改，默认的就挺好用） [main] # 是否开启gpg校验 gpgcheck=1 # 允许保留多少旧内核包 installonly_limit=3 # 删除软件同时删除依赖包 clean_requirements_on_remove=True # 查找最快镜像 fastestmirror=true # 下载增量包 deltarpm=true # 最大并发下载数量，在Fedora 35 中打开初始没有这项，可以不加 max_parallel_downloads=6 如果想使用dnf的图形化前端，可以运行sudo dnf install dnfdragora安装dnfdragora:\n修改SELinux配置 selinux是红帽系发行版自带的安全子系统，对于桌面版用户这个子系统意义不大，但是卸载不掉，只能完全禁用。查看SELinux状态:/usr/sbin/sestatus -v，如果状态是enabled，则代表SELinux开启，需要修改/etc/selinux/config配置文件，将SELINUX修改为disabled。\n家目录文件夹切换为英文 修改系统当前语言export LANG=en_US，通过命令修改主目录下的文件夹：xdg-user-dirs-gtk-update，将语言环境修改回中文：export LANG=zh_CN.UTF-8。\n桌面环境美化 # 配置 GNOME 并进行基础美化： sudo dnf install papirus-icon-theme gnome-tweaks gnome-extensions-app 打开 Gnome Tweak 启用 icon 主题并配置窗口右上角按键 打开 https://extensions.gnome.org 安装 blur-my-shell dash-to-panel appindicator 打开 Gnome Extension 启用上述插件并配置 dash-to-panel\n总结 🎉 # 通过本教程，您可以顺利完成Fedora 41的安装，并配置Btrfs文件系统和Timeshift备份工具。同时，通过Gnome Tweaks和扩展，您可以打造一个高效、美观的桌面环境。Enjoy your Fedora journey! 🚀\n参考链接 🔗 # Fedora官方文档 GNOME Shell Extensions Flatpak官网 Snap官网 ","date":"2024/03/26","externalUrl":null,"permalink":"/posts/selfhost/fedora_install_guide/","section":"吾生有涯，而知无涯","summary":"","title":"Fedora41桌面系统安装及常用配置教程 (btrfs + timeshift + gnome)","type":"posts"},{"content":" 🌟 LM Studio 介绍 # LM Studio 是一款强大的客户端应用，支持本地离线运行各类开源大语言模型（LLMs）。通过 LM Studio 工具，可以快速实现各类大语言模型的本地部署，无需依赖网络，独立运行，保障数据安全。本文将详细介绍 LM Studio 的使用方法及硬件要求，帮助轻松上手本地大模型部署。\n核心特点：\n✅ 本地运行：完全离线，不依赖网络。 ✅ 数据安全：独立环境，保障隐私。 ✅ 高效流畅：快速加载，实时响应。 硬件推荐要求：\n🖥️ GPU：GTX 3060TI 及以上，显存 8GB 以上。 💻 CPU：频率 3.3GHz 及以上。 💾 硬盘：250GB 及以上可用空间。 注意： LM Studio 并非开源软件，但官方承诺不会收集敏感数据。部分遥测配置仅用于程序优化和故障排除。 硬件要求为推荐指标，若低于给出的配置，功能运行可能不正常，体验大大下降！ 🛠️ 安装教程（Windows） # 步骤一：下载 LM Studio # 访问 LM Studio 官网，点击下载按钮。 步骤二：安装 LM Studio # 双击下载的安装包，按照提示完成安装。 安装完成后，桌面将显示 LM Studio 图标。 🚀 通过 LM Studio 安装并本地运行大模型 # 步骤一：启动 LM Studio # 双击桌面图标，启动 LM Studio。 默认界面会显示推荐的模型，可以选择跳过直接进入下一步。 步骤二：搜索并下载 DeepSeek 模型 # 点击顶部菜单栏的“选择模型”按钮。 在搜索框中输入大模型名称如“DeepSeek”，点击“Search More”查看更多结果。 选择适合的模型版本，点击下载。 步骤三：加载并运行模型 # 下载完成后，返回主界面，选择已下载的 DeepSeek 模型。 根据电脑性能调整配置参数，点击“Load Model”加载模型。 在聊天界面中，输入问题即可获得模型回复。 ❓ 常见问题 # 问题：模型界面无法搜索或者显示(当前问题已经解决，官方自动使用镜像站点) # 原因：相关网站被屏蔽，无法访问。\n解决方法：\n第一种：绕过屏蔽，不用多说。 第二种：修改软件内配置文件（不推荐，每次更新都需要重新来一次）。具体步骤为：右键打开应用文件所在位置，依次进入目录 app-0.3.5\\resources\\app\\.webpack，通过vs-code 打开该目录，搜索替换 huggingface.co为hf-mirror.com，保存重新打开LM。 第三种：使用下载好的离线模型（推荐）。直接从国内的镜像站点搜索下载“模型名称-GGUF”，下载好相应的文件，放置到LM本地目录当中刷新即可。 问题：提示无意义的英文消息 # 原因：模型加载失败或配置不足。\n解决方法：检查硬件配置，重新加载模型。\n问题：模型未成功预加载 # 原因：电脑性能不足。\n解决方法：尝试更换更高配置的电脑。\n🎉 结语 # 通过 LM Studio，可以轻松实现大语言模型的本地部署，享受高效、安全的 AI 对话体验。\n","date":"2024/03/15","externalUrl":null,"permalink":"/posts/learn_record/lm-studio-guide/","section":"吾生有涯，而知无涯","summary":"","title":"LM Studio 本地部署大模型的详细操作教程及硬件要求","type":"posts"},{"content":"","date":"2024/03/15","externalUrl":null,"permalink":"/series/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E6%9C%AC%E5%9C%B0%E9%83%A8%E7%BD%B2/","section":"Series","summary":"","title":"大模型本地部署","type":"series"},{"content":"","date":"2024/03/15","externalUrl":null,"permalink":"/tags/linux-vps/","section":"Tags","summary":"","title":"Linux VPS","type":"tags"},{"content":" 概述 # TL；DR # 如果仅仅想要体验社区服务，可以直接访问https://latex.baos.eu.org通过在当前页面评论区留下邮箱或者发送邮件至chas5wasl@duck.com，不保证即时反馈，接收到验证链接后自行设置密码进行体验。\n验证链接可能以 http://localhost 开头，请将链接修改为 https://latex.baos.eu.org\n前言 # Overleaf是一个在线的LaTeX编辑器，支持多人协作编辑，提供了丰富的模板和宏包，可以在线编译LaTeX文档。Overleaf有免费版和付费版，免费版有一些限制，如编译时间限制、项目数限制等。Overleaf的付费版提供了更多的功能，如无限编译时间、无限项目数、Git同步等。\n开源版本的Overleaf称为\u0026quot;Overleaf Community Edition\u0026quot;，简称\u0026quot;Overleaf CE\u0026quot;。它是Overleaf团队推出的开源LaTeX协作编辑器，可让用户在自己的服务器上创建类似Overleaf的在线LaTeX编辑环境。这种自托管方式赋予用户在内部网络或私人服务器上创建、编辑和共享LaTeX文档的完全控制权。\n数据隐私和安全性： 在某些情况下，可能对数据隐私和安全性有严格要求，不希望文档和敏感信息存储在第三方云服务上。独立部署Overleaf允许您完全掌握数据存储和访问，确保合规和安全标准。 机构内部部署： 一些大学、研究机构或企业可能需要在内部网络提供LaTeX编辑和协作功能，以支持学术、研究和文档创作。独立部署Overleaf能够满足这一需求，使机构内部用户轻松创建和编辑LaTeX文档。 定制需求： 如果您有特定的定制需求，希望对编辑器界面、功能或工作流程进行个性化定制，独立部署Overleaf提供更大的灵活性，可满足各种需求，实现编辑环境的定制化。 带宽和访问限制： 在某些地区或网络环境中，外部云服务的访问可能受到带宽或访问限制。独立部署Overleaf可为用户提供更流畅的内部网络访问体验。 成本效益： 对于一些组织而言，长期使用第三方云服务可能会导致高昂费用。独立部署Overleaf有助于节省成本，尤其是在大规模使用情况下。 技术支持和控制： 独立部署Overleaf让您在技术层面上更具掌控力，可以根据需要进行维护、升级和修复，与内部技术团队直接合作解决问题。 前提 # 使用Linux或者Window服务器均可，性能稍强。（搭建的环境使用甲骨文的4H24GArm服务器,性能足够使用，操作系统使用Debian12.） 安装Docker和Docker Compose。该部分比较简单，直接省略。如果是linux系统，直接运行curl -fsSL https://get.docker.com | sh --mirror=Aliyun即可。 具有自己的二级域名（自建latex编译服务），使用caddy进行反向代理和自动https。 安装方法 # 使用Docker来方便快捷地搭建Overleaf社区版服务。这里有两种安装方式：一种是直接使用Overleaf社区版仓库里提供的docker-compose.yml文件，另一种是使用Overleaf官方提供的工具箱。Overleaf官方推荐使用第二种方式，但这里两种方式都介绍一下。\n方法一：使用docker-compose.yml文件安装 # 待续 可以在Overleaf社区版仓库中找到docker-compose.yml文件，下载到本地，然后使用命令docker-compose up -d启动服务。这里可能需要根据自己的实际情况修改docker-compose.yml文件，如修改端口、数据卷路径等。\n方法二：使用Overleaf工具箱安装 # Overleaf官方提供了一个工具箱，包装了一些常用的docker命令，可以初始化、启动、停止、诊断、升级Overleaf服务。虽然我觉得相比直接使用docker-compose.yml，这个工具箱使部署docker的流程更复杂了，但这个工具箱的确提供了更多更灵活的定制选项。\n使用这个工具箱部署Overleaf社区版服务，可以参考Overleaf社区版工具箱文档。简单来讲，有以下几步：\n下载工具箱git clone https://github.com/overleaf/toolkit.git 进入工具箱目录cd toolkit 初始化安装配置bin/init，运行此命令，将在当前目录下生成一个config文件夹，里面包含了三个配置文件： overleaf.rc：Overleaf配置文件。用户可以在这个文件中配置Overleaf的一些参数，如端口、数据卷路径等。 variables.env：环境变量配置文件 version：选择Overleaf版本。注意在5.0.0版本之后，Overleaf将原来的ShareLaTeX商标替换为Overleaf商标，所以如果环境变量配置文件中如果使用OVERLEAF前缀的变量，需要选择5.0.0之后的版本。 在修改完配置文件后，运行命令bin/up启动服务，在运行后可以看到服务的输出日志。如果想要停止服务，可以按Ctrl+C。 **检查无误之后，使用bin/up -d以后台模式启动服务。 如果想要停止服务，可以使用命令bin/stop。 配置Nginx反向代理（可选）。Overleaf社区版的docker-compose.yml文件中有一个nginx容器，如果使用Overleaf工具箱，在overleaf.rc配置文件中也可以配置Nginx。如果需要TSL/SSL加密，可以使用在初始化时使用bin/init \u0026ndash;tls命令。这样会在config目录中生成一个nginx文件夹，里面包含了Nginx的配置文件和作为示例的SSL证书。 由于之前在服务器上已经部署了单独的Caddy服务来管理所有的网站，所以不使用Nginx服务来反向代理Overleaf服务。\n其他常见问题 # 1. 升级TexLive # Overleaf的docker镜像中自带了一个基础版本的TeX Live，但是这个版本可能不包含所有的宏包。如果需要编译一些特殊的LaTeX文档，可能需要安装完整版的TeX Live。可参考Overleaf工具箱文档中升级Tex Live的文档。安装完整版TeX Live主要步骤如下：\ndocker exec -it overleaf bash #进入Overleaf容器 tlmgr --version #查看当前TeX Live版本 tlmgr install scheme-medium # 更新Tex Live,两者选择一个即可 tlmgr install scheme-full #更新TeX Live tlmgr path add 如果不执行路径添加的命令，在Overleaf中编译LaTeX文档时，可能会出现无法编译EPS图片等问题。做完上述更改后，升级的TeX Live只会保存在当前的容器中，如果容器被删除，这些更改也会丢失。如果想要将这些更改保存到镜像中并在之后创建容器时使用，可以执行下面的操作： docker commit sharelatex sharelatex/sharelatex:with-texlive-full 然后直接在overleaf-toolkit/config/overleaf.rc文件当中修改OVERLEAF_IMAGE_NAME=sharelatex/sharelatex:with-texlive-full，按照上面的方法运行/bin/up或者/bin/up -d实现服务重新部署。\n2. 更改Overleaf实例为简体中文 # 直接增加overleaf-toolkit/config/variables.env文件内部的环境变量，设置OVERLEAF_SITE_LANGUAGE=zh-CN即可\n3. ARM架构镜像选择 # 实际操作当中发现，ARM版本的Overleaf版本总存在各种各样的问题，因此需要选择合适的版本镜像。具体来说，需要修改文件overleaf-toolkit/config/overleaf.rc当中修改变量OVERLEAF_IMAGE_NAME=kylindemons/sharelatex（镜像当前版本为5.1.0，官方工具箱最新版本为5.4.0，但是并未更新太多的新特性，不必特意求更新，稳定是第一位的）。\n出现Email Protect提示 # 出现这种情况是因为使用CF小黄云的代理，为避免邮箱受到垃圾邮件的骚扰启用的保护措施。将邮箱地址使用\u0026lt;!--email_off--\u0026gt;contact@example.com\u0026lt;!--/email_off--\u0026gt;进行包裹即可\n添加中文字体 # 使用 ShareLaTeX Docker 镜像时，为了支持中文显示，需要安装中文字体并更新字体缓存。以下是具体步骤：\n1. 获取中文字体文件:\n首先，下载包含中文字体的压缩包。例如，你可以使用以下命令下载一个常用字体集合：\nwget \u0026#34;https://github.com/zhyounger/FontsFromWindows/archive/refs/heads/master.zip\u0026#34; 当然，你也可以从其他来源获取字体文件，只要是 TTF (TrueType Font) 格式即可。 确保下载的字体文件是合法的，并且拥有相应的许可证。\n2. 复制字体文件到 Docker 容器:\n将下载的字体文件解压，并将 TTF 文件复制到 ShareLaTeX 容器的字体目录下。 首先，确定你已经运行了 ShareLaTeX 容器。 假设你的 ShareLaTeX 容器名为 sharelatex，并且你已经将宿主机的 ~/sharelatex/fonts 目录绑定到了容器内的 /usr/share/fonts 目录。 那么，可以按以下操作：\n# 如果 ~/sharelatex/fonts 目录不存在，先创建它 mkdir -p ~/sharelatex/fonts # 创建一个子目录用于存放中文字体，例如 \u0026#34;zh-cn\u0026#34;。这个目录名可以自定义，但不要覆盖 /usr/share/fonts 本身。 mkdir -p ~/sharelatex/fonts/zh-cn # 解压下载的字体文件 unzip master.zip # 假设解压后的字体文件位于 FontsFromWindows-master/Fonts 目录下，将其复制到容器的字体目录 cp FontsFromWindows-master/Fonts/*.ttf ~/sharelatex/fonts/zh-cn/ # 进入容器内部执行更新字体缓存操作 docker exec -it sharelatex bash 注意： 请将 sharelatex 替换为你的实际容器名称，并将 FontsFromWindows-master/Fonts/*.ttf 替换为你实际的字体文件路径。\n3. 更新容器内的字体缓存:\n进入容器后，更新字体缓存以使系统识别新安装的字体。在容器内的 shell 中执行：\nmkdir -p /usr/share/fonts/windows/fonts #创建一个新的字体存放路径，不覆盖原来的目录 fc-cache -fv fc-cache -fv 命令会强制刷新字体缓存。\n4. 提交 Docker 容器变更:\n重要提示： 在更新字体缓存后，需要将更改提交到新的 Docker 镜像，否则容器重启后更改将会丢失。\n首先，退出容器的 shell (输入 exit 并按回车)。 然后，执行以下命令：\ndocker commit sharelatex sharelatex:with-fonts 这里 sharelatex 是你的容器名称，sharelatex:with-fonts 是新镜像的名称。 你可以根据需要更改新镜像的名称。\n5. 重启容器 (使用新镜像):\n最后，停止并删除旧的容器，然后使用新镜像启动一个新容器：\ndocker stop sharelatex docker rm sharelatex #使用新的镜像运行容器 docker run --name sharelatex -d -v ~/sharelatex/fonts:/usr/share/fonts \u0026lt;你的 ShareLatex 镜像名称\u0026gt; 请将 \u0026lt;你的 ShareLatex 镜像名称\u0026gt; 替换为你所使用的 ShareLaTeX 镜像名称。\n注意事项：\n目录绑定： 确保宿主机的 ~/sharelatex/fonts 目录正确地绑定到了容器的 /usr/share/fonts 目录。 容器名称： 在所有 Docker 命令中，将 sharelatex 替换为你的实际容器名称。 字体文件权限： 确保字体文件具有正确的权限，以便 ShareLaTeX 可以访问它们。 如果遇到权限问题，可以尝试在容器内执行 chmod 644 /usr/share/fonts/zh-cn/*.ttf。 字体缓存问题： 如果更新字体缓存后仍然无法显示中文，可以尝试清除字体缓存并重新生成。 镜像大小： 每次 docker commit 都会生成一个新的镜像。 为了避免镜像过于庞大，建议只在必要时提交更改。 这个修改后的版本更全面，考虑了各种可能遇到的问题，并提供了更详细的步骤和解释。希望对你有所帮助!\n更新内容版本 # 参考资料 # Overleaf Quick-Start-Guide ","date":"2024/03/15","externalUrl":null,"permalink":"/posts/selfhost/overleaf-ce/","section":"吾生有涯，而知无涯","summary":"","title":"Overleaf社区版搭建基础教程","type":"posts"},{"content":"","date":"2024/03/15","externalUrl":null,"permalink":"/series/%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%87%AA%E5%BB%BA/","section":"Series","summary":"","title":"服务器自建","type":"series"},{"content":"","date":"2024/03/15","externalUrl":null,"permalink":"/tags/%E7%9E%8E%E6%8A%98%E8%85%BE/","section":"Tags","summary":"","title":"瞎折腾","type":"tags"},{"content":"","date":"2023/03/15","externalUrl":null,"permalink":"/tags/shell/","section":"Tags","summary":"","title":"Shell","type":"tags"},{"content":" Zinit 简介 # Zinit 是一个高效的 Zsh 插件管理器，旨在加速 Zsh 的启动速度并简化插件的管理。它支持以下功能：\n快速加载插件：通过延迟加载和异步加载技术，减少 Zsh 启动时间。 插件管理：轻松安装、更新和删除插件。 语法高亮：支持语法高亮和自动补全插件。 主题支持：兼容 Oh-My-Zsh 主题和插件。 安装及配置教程 # 安装 Zinit # 在终端中运行以下命令安装 Zinit：bash -c \u0026quot;$(curl --fail --show-error --silent --location https://raw.githubusercontent.com/zdharma-continuum/zinit/HEAD/scripts/install.sh)\u0026quot;，安装完成后，Zinit 会自动将初始化代码添加到 ~/.zshrc 文件中。\n常用命令 # zinit light \u0026lt;插件名称\u0026gt; #安装插件 zinit update [--all] #更新插件 zinit self-update #zinit本体更新 zinit delete \u0026lt;插件名称\u0026gt; #删除插件 zinit list 配置文件示例 # 以下是一个完整的 Zinit 配置文件示例，包含常用插件和配置：\n# 初始化 Zinit source ~/.zinit/bin/zinit.zsh # 加载 Oh-My-Zsh 插件 zinit snippet OMZ::lib/git.zsh zinit snippet OMZ::plugins/git/git.plugin.zsh # 加载语法高亮插件 zinit light zdharma-continuum/fast-syntax-highlighting # 加载自动补全插件 zinit light zsh-users/zsh-autosuggestions # 加载主题（例如 Powerlevel10k） zinit ice depth=1; zinit light romkatv/powerlevel10k # 延迟加载插件（例如 Docker 补全） zinit ice wait\u0026#39;1\u0026#39; lucid; zinit snippet OMZ::plugins/docker/_docker # 加载自定义脚本 zinit light username/my-custom-plugin # 配置 Powerlevel10k 主题 if [[ -r \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; ]]; then source \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; fi [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh 配置文件说明 # 初始化 Zinit：\nsource ~/.zinit/bin/zinit.zsh：加载 Zinit 的核心功能。 加载 Oh-My-Zsh 插件：\nzinit snippet OMZ::lib/git.zsh：加载 Oh-My-Zsh 的 Git 库。 zinit snippet OMZ::plugins/git/git.plugin.zsh：加载 Git 插件。 语法高亮和自动补全：\nzinit light zdharma-continuum/fast-syntax-highlighting：加载语法高亮插件。 zinit light zsh-users/zsh-autosuggestions：加载自动补全插件。 加载主题：\nzinit ice depth=1; zinit light romkatv/powerlevel10k：加载 Powerlevel10k 主题。 延迟加载插件：\nzinit ice wait'1' lucid; zinit snippet OMZ::plugins/docker/_docker：延迟加载 Docker 补全插件。 自定义脚本：\nzinit light username/my-custom-plugin：加载自定义插件或脚本。 Powerlevel10k 配置：\n如果使用 Powerlevel10k 主题，需要加载其配置文件 ~/.p10k.zsh。 自己的配置文件 # # Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc. # Initialization code that may require console input (password prompts, [y/n] # confirmations, etc.) must go above this block; everything else may go below. if [[ -r \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; ]]; then source \u0026#34;${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh\u0026#34; fi ### Added by Zinit\u0026#39;s installer if [[ ! -f $HOME/.local/share/zinit/zinit.git/zinit.zsh ]]; then print -P \u0026#34;%F{33} %F{220}Installing %F{33}ZDHARMA-CONTINUUM%F{220} Initiative Plugin Manager (%F{33}zdharma-continuum/zinit%F{220})…%f\u0026#34; command mkdir -p \u0026#34;$HOME/.local/share/zinit\u0026#34; \u0026amp;\u0026amp; command chmod g-rwX \u0026#34;$HOME/.local/share/zinit\u0026#34; command git clone --depth 1 https://github.com/zdharma-continuum/zinit \u0026#34;$HOME/.local/share/zinit/zinit.git\u0026#34; \u0026amp;\u0026amp; \\ print -P \u0026#34;%F{33} %F{34}Installation successful.%f%b\u0026#34; || \\ print -P \u0026#34;%F{160} The clone has failed.%f%b\u0026#34; fi source \u0026#34;$HOME/.local/share/zinit/zinit.git/zinit.zsh\u0026#34; autoload -Uz _zinit (( ${+_comps} )) \u0026amp;\u0026amp; _comps[zinit]=_zinit # Load a few important annexes, without Turbo # (this is currently required for annexes) zinit light-mode for \\ zdharma-continuum/zinit-annex-as-monitor \\ zdharma-continuum/zinit-annex-bin-gem-node \\ zdharma-continuum/zinit-annex-patch-dl \\ zdharma-continuum/zinit-annex-rust ### End of Zinit\u0026#39;s installer chunk # 加载 powerlevel10k 主题 zinit ice lucid depth=1 zinit light romkatv/powerlevel10k POWERLEVEL9K_INSTANT_PROMPT=quiet # 补全 zinit wait lucid atload=\u0026#34;zicompinit; zicdreplay\u0026#34; blockf for zsh-users/zsh-completions zinit light zsh-users/zsh-completions # 模糊查找 zinit light Aloxaf/fzf-tab # disable sort when completing `git checkout` zstyle \u0026#39;:completion:*:git-checkout:*\u0026#39; sort false # set descriptions format to enable group support zstyle \u0026#39;:completion:*:descriptions\u0026#39; format \u0026#39;[%d]\u0026#39; # set list-colors to enable filename colorizing zstyle \u0026#39;:completion:*\u0026#39; list-colors ${(s.:.)LS_COLORS} # preview directory\u0026#39;s content with eza when completing cd zstyle \u0026#39;:fzf-tab:complete:cd:*\u0026#39; fzf-preview \u0026#39;eza -1 --color=always $realpath\u0026#39; # switch group using `,` and `.` zstyle \u0026#39;:fzf-tab:*\u0026#39; switch-group \u0026#39;,\u0026#39; \u0026#39;.\u0026#39; # 自动建议 zinit ice lucid wait=\u0026#34;0\u0026#34; atload=\u0026#39;_zsh_autosuggest_start\u0026#39; zinit light zsh-users/zsh-autosuggestions # VI-MODE 插件 # zinit ice depth=1 # zinit light jeffreytse/zsh-vi-mode # OTHER plugins zinit light djui/alias-tips # 语法高亮/显示高亮 zinit ice lucid wait=\u0026#39;0\u0026#39; atinit=\u0026#39;zpcompinit\u0026#39; zinit light z-shell/F-Sy-H # 加载 OMZ 框架及部分插件 zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/lib/git.zsh zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/plugins/git/git.plugin.zsh zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/lib/history.zsh zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/lib/key-bindings.zsh zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/lib/completion.zsh zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/lib/clipboard.zsh zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/plugins/aliases/aliases.plugin.zsh zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/plugins/sudo/sudo.plugin.zsh # zinit snippet https://gitee.com/mirrors/oh-my-zsh/raw/master/plugins/autojump/autojump.plugin.zsh # To customize prompt, run `p10k configure` or edit ~/.p10k.zsh. [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh [ -f ~/.fzf.zsh ] \u0026amp;\u0026amp; source ~/.fzf.zsh ######################################################## ##### ALIASES ######################################################## alias ls=\u0026#39;eza\u0026#39; alias ll=\u0026#39;eza -lbF --git\u0026#39; alias la=\u0026#39;eza -lbhHigmuSa --time-style=long-iso --git --color-scale\u0026#39; alias lx=\u0026#39;eza -lbhHigmuSa@ --time-style=long-iso --git --color-scale\u0026#39; alias llt=\u0026#39;eza -l --git --tree\u0026#39; alias lt=\u0026#39;eza --tree --level=2\u0026#39; alias llm=\u0026#39;eza -lbGF --git --sort=modified\u0026#39; alias lld=\u0026#39;eza -lbhHFGmuSa --group-directories-first\u0026#39; alias du=\u0026#39;dust\u0026#39; alias cat=\u0026#39;batcat\u0026#39; alias unzip=\u0026#39;unar\u0026#39; alias rm=\u0026#34;trash-put\u0026#34; ####################################################### ##### 自定义函数区 ######################################################## setopt Autocd #不需要输入cd setopt HIST_IGNORE_ALL_DUPS # 移除重复的命令历史 参考链接 # Zinit 官方文档 Oh-My-Zsh 插件列表 Powerlevel10k 主题 ","date":"2023/03/15","externalUrl":null,"permalink":"/posts/tricks/zinit/","section":"吾生有涯，而知无涯","summary":"","title":"Zinit 配置留存备份","type":"posts"},{"content":"","date":"2023/03/15","externalUrl":null,"permalink":"/tags/%E6%95%99%E7%A8%8B%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"教程配置","type":"tags"},{"content":" 以下内容转载来自于 text 一、说明 # Docker 是一个开源的应用容器引擎，基于 Go 语言 并遵从 Apache2.0 协议开源。\n1.应用场景 # Web 应用的自动化打包和发布 自动化测试和持续集成、发布（例如：部署包运行的测试、编译系统测试等） 在服务型环境中部署和调整数据库或其他的后台应用 2.docker包含三个基本概念 # 镜像（Image）：Docker 镜像（Image），就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统； 容器（Container）：镜像（Image）和容器（Container）的关系，就像是面向对象程序设计中的类和实例一样，镜像是静态的定义，容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等； 仓库（Repository）：仓库可看成一个代码控制中心，用来保存镜像； 举个栗子：Docker相当于 VM Ware 或者 VM Virtual，镜像相当于Windows/Ubuntu安装镜像，容器相当于已经安装好的Windows/Ubuntu虚拟机；仓库相当于Windows/Ubuntu，Tag（标签）为仓库标签（大部分为仓库版本号）；\n3.其他概念 # 客户端(Client)：简单说就是Docker安装好的客户端，通过客户端命令可与容器进行通讯； （宿）主机(Host)：安装Docker的机器； 二、安装（CentOS为例） # 1.使用官方脚本/一键安装命令 # # 官方命令 curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun # 国内一键安装命令 curl -sSL https://get.daocloud.io/docker | sh 2.手动安装 # # 卸载旧版本 yum remove docker \\ docker-client \\ docker-client-latest \\ docker-common \\ docker-latest \\ docker-latest-logrotate \\ docker-logrotate \\ docker-engine ## 安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 ## 设置官方仓库 yum-config-manager \\ --add-repo \\ https://download.docker.com/linux/centos/docker-ce.repo ## 阿里仓库 yum-config-manager \\ --add-repo \\ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo ## 清华仓库 yum-config-manager \\ --add-repo \\ https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo ## 选择安装最新版或特定版本Docker Engine-Community # 1.安装最新版Docker Engine-Community yum install docker-ce docker-ce-cli containerd.io # 提示接受 GPG 密钥，请选是 ## 2.安装特定版本Docker Engine-CommunityDocker # 列出仓库中可用版本 yum list docker-ce --showduplicates | sort -r # 输出如下 # docker-ce.x86_64 18.06.1.ce-3.el7 docker-ce-stable # docker-ce.x86_64 18.06.0.ce-3.el7 docker-ce-stable # ## 安装 yum install docker-ce-\u0026lt;VERSION_STRING\u0026gt; docker-ce-cli-\u0026lt;VERSION_STRING\u0026gt; containerd.io # 例如 yum install docker-ce-18.06.1 docker-ce-cli-18.06.1 containerd.io ## 启动 systemctl start docker # 卸载 yum remove docker-ce # 删除镜像、容器、配置文件等内容 rm -rf /var/lib/docker 3.离线安装 # 先从https://download.docker.com/linux/static/stable/x86_64/ 下载docker-xx.xx.x-ce.tgz，并上传至服务器中\n# 解压安装包 tar -zxvf docker-xx.xx.x-ce.tgz ## 复制文件到/usr/bin cp docker/* /usr/bin ## 手动添加docker.service配置文件 vi /etc/systemd/system/docker.service # Docker默认的镜像和容器存储位置在/var/lib/docker中 # 内容如下（其中ExecStart行 registry-mirror需设置为可访问的镜像服务器）： [Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target firewalld.service Wants=network-online.target [Service] Type=notify # the default is not to use systemd for cgroups because the delegate issues still # exists and systemd currently does not support the cgroup feature set required # for containers run by docker ExecStart=/usr/bin/dockerd ExecReload=/bin/kill -s HUP $MAINPID # Having non-zero Limit*s causes performance problems due to accounting overhead # in the kernel. We recommend using cgroups to do container-local accounting. LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity # Uncomment TasksMax if your systemd version supports it. ## Only systemd 226 and above support this version. ##TasksMax=infinity TimeoutStartSec=0 # set delegate yes so that systemd does not reset the cgroups of docker containers Delegate=yes # kill only the docker process, not all processes in the cgroup KillMode=process # restart the docker process if it exits prematurely Restart=on-failure StartLimitBurst=3 StartLimitInterval=60s [Install] WantedBy=multi-user.target # 配置docker参数 mkdir -p /etc/docker vi /etc/docker/daemon.json { \u0026#34;registry-mirrors\u0026#34;: [\u0026#34;http://hub-mirror.c.163.com\u0026#34;], \u0026#34;log-opts\u0026#34;: { \u0026#34;max-size\u0026#34;: \u0026#34;5m\u0026#34;, \u0026#34;max-file\u0026#34;:\u0026#34;3\u0026#34; }, \u0026#34;exec-opts\u0026#34;: [\u0026#34;native.cgroupdriver=systemd\u0026#34;], \u0026#34;graph\u0026#34;: \u0026#34;/app/docker\u0026#34; } ## registry-mirrors 为镜像加速地址 无需修改 # log-opts 为日志参数 无需修改 # exec-opts 为docker启动参数 指定cg驱动为systemd 无需修改 # graph docker运行路径 可任意配置 # 授权 chmod +x /etc/systemd/system/docker.service # 重载守护进程配置 systemctl daemon-reload # 启动docker systemctl start docker # 设置开机自启 systemctl enable docker.service ## 验证docker状态 systemctl status docker # 查看docker版本 docker -v 三、Docker安装Ubuntu # 在docker官网https://hub.docker.com/search?q=查找ubuntu，\n# 拉取镜像 docker pull ubuntu # 或 docker pull ubuntu:latest # 查看已经下载的镜像 docker images # 创建并运行容器 docker run -itd --name ubuntu-test ubuntu:15.10 # 连接容器 docker exec -it ubuntu-test /bin/bash # 完成 ## 查看运行中容器 docker ps # 查看所有容器 docker ps -a 四、常用命令 # 1.pull（拉取镜像）命令 # # 拉取最新镜像 docker pull mysql # 或 docker pull mysql:lasted # 或 docker pull mysql:8.0.29 指定版本 2.images（查看镜像）命令 # docker images # REPOSITORY TAG IMAGE ID CREATED SIZE # ubuntu 14.04 90d5884b1ee0 9 weeks ago 188 MB # ubuntu 15.10 4e3b13c8a266 3 months ago 136.3 MB # REPOSITORY 仓库名 # TAG 标签（可以理解为版本） # IMAGE ID 镜像ID 3.run（创建并运行容器）命令 # mkdir mysql-data docker run -itd -v /root/mysql-data:/etc/mysql/data -p 13306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql-test mysql:lasted # -i 以交互模式运行容器，通常与 -t 同时使用； # -t 为容器重新分配一个伪输入终端，通常与 -i 同时使用； # -d 后台运行容器，并返回容器ID，如果不加此参数，启动容器时自动连入容器，且退出容器时，容器自动停止； # -v 等同 --volume ，挂载一个卷（文件或文件夹）到容器中，示例中将root下mysql-data文件夹挂载至容器中/etc/mysql/data，用于存放mysql数据文件 # -p 端口映射，示例中13306为宿主机端口，3306为容器端口，映射后在宿主机中访问13306会自动转发至容器的3306端口 # -e 设置环境变量，示例中将MySQL的Root密码（MYSQL_ROOT_PASSWORD）设置为123456 # --name 为容器指定一个名称 ## 注 # run命令中，可使用参数 --privileged=true # 在现有版本中，容器创建时默认root权限只是容器的部分权限（例如centos不能配置自启动相关，防火墙相关，ftpd相关内容等） # 加入以上命令使得容器可以获得完整root权限，并能对宿主机进行部分操作 # 使用场景如：创建一个centos容器，使得宿主机以外的其他机器可直接使用ftp工具连入centos容器中 4.ps命令 # # 查看运行中容器 docker ps # 查看所有容器 docker ps -a # CONTAINER ID IMAGE COMMAND ... PORTS NAMES # 09b93464c2f7 mysql:latest \u0026#34;####################\u0026#34; ... 80/tcp, 443/tcp mysql-test # 96f7f14e99ab ubuntu:latest \u0026#34;####################\u0026#34; ... 0.0.0.0:3306-\u0026gt;3306/tcp ubuntu-test # CONTAINER ID 容器ID # IMAGE 容器所用镜像 # COMMAND 启动容器时运行的命令 # PORTS 映射的端口 # NAMES 容器名 5.start（启动）/stop（停止）/restart（重启）命令 # docker start mysql-test docker stop mysql-test docker restart mysql-test # 或 使用ps命令查询容器ID并使用容器ID docker start 09b93464c2f7 docker stop 09b93464c2f7 docker restart 09b93464c2f7 6.exec（执行）命令 # docker exec -i -t mysql-test /bin/bash # -i 交互模式 # -t 分配伪终端 # mysql-test 运行中的容器名称或ID # /bin/bash 在容器中运行/bin/bash，一般用此命令来连接终端 docker exec -it mysql-test /bin/sh mysql -u root -p # 在容器中执行/bin/sh mysql -u root -p # 意思为在容器终端里执行 mysql -u root -p 命令 # 等同使用 docker exec -it mysql-test /bin/sh 连接至容器中并执行 mysql -u root -p 7.commit（创建镜像）命令 # # 查看运行中容器 docker ps # CONTAINER ID IMAGE COMMAND ... PORTS NAMES # 09b93464c2f7 mysql:latest \u0026#34;####################\u0026#34; ... 80/tcp, 443/tcp mysql-test ## 使用运行中容器创建镜像 docker commit -a \u0026#34;runoob.com\u0026#34; -m \u0026#34;my apache\u0026#34; -p 09b93464c2f7 mymysql:v1 ## -a 镜像作者 # -m 镜像文字说明 # -p 创建镜像时将容器暂停 # 09b93464c2f7 容器ID # mymysql:v1 冒号前为仓库名，冒号后为标签名 8.save（保存镜像）命令 # # 将制作好的镜像保存至文件，以供其他docker使用 docker save -o my_mysql_v1.tar mymysql:v1 # -o 输出至文件 # my_mysql_v1.tar 文件名 # mymysql:v1 冒号前为仓库名，冒号后为标签名 9.load（载入镜像）命令 # # 使用 docker load \u0026lt; my_mysql_v1.tar # 或 docker load -i -q my_mysql_v1.tar # -i 或 --input 指定载入的文件 用以替换 \u0026lt; （STDIN,标准输入方式）方式载入 # -q 或 --quiet 精简输出信息 五、dockerfile # Dockerfile 是一个用来构建镜像的文本文件，文本内容包含了一条条构建镜像所需的指令和说明。\n1.使用dockerfile构建tomcat运行环境 # 1.1.创建dockerfile文件 # vi dockerfile 文件内容如下：\nFROM docker.io/centos ARG WAR_FILE ADD ./apache-tomcat-7.0.109.tar.gz /usr/local/ ADD ./jdk-7u80-linux-x64.tar.gz /usr/local/ COPY ${WAR_FILE} /usr/local/apache-tomcat-7.0.109/webapps/ ENV LOCAL_PATH /usr/local WORKDIR $LOCAL_PATH ENV JAVA_HOME=/usr/local/jdk1.7.0_80 ENV JRE_HOME=$JAVA_HOME/jre ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH ENV CATALINA_HOME=/usr/local/apache-tomcat-7.0.109 ENV CATALINA_BASH=/usr/local/apache-tomcat-7.0.109 ENV PATH=/sbin:$JAVA_HOME/bin:$PATH:$CATALINA_HOME/lib:$CATALINA_HOME/bin EXPOSE 8080 CMD /usr/local/apache-tomcat-7.0.109/bin/startup.sh \u0026amp;\u0026amp; tail -F /usr/local/apache-tomcat-7.0.109/logs/catalina.out 文件内容说明：\nFROM \u0026lt;image name\u0026gt;：声明定制镜像基于哪个基础镜像 ARG \u0026lt;参数名\u0026gt;\\[=\u0026lt;默认值\u0026gt;\\]：声明参数，在构建镜像时使用\u0026ndash;build-arg \u0026lt;参数名\u0026gt;=\u0026lt;值\u0026gt; 来覆盖 ADD \u0026lt;host file\u0026gt; \u0026lt;container path\u0026gt;：将宿主机中文件添加至容器对应路径中，对于gzip, bzip2 以及 xz压缩文件自动解压（例如上面dockerfile中apache-tomcat-7.0.109.tar.gz） COPY \u0026lt;host file\u0026gt; \u0026lt;container path\u0026gt;：与ADD命令类似，复制文件或路径到容器对应路径中 ENV \u0026lt;key\u0026gt; \u0026lt;value\u0026gt;：设置环境变量 WORKDIR \u0026lt;工作目录路径\u0026gt;：指定工作目录，使用终端连接容器时，会到此路径 EXPOSE \u0026lt;端口1\u0026gt; [\u0026lt;端口2\u0026gt;\u0026hellip;] 声明容器端口（注意：仅作声明，帮助镜像使用者理解这个镜像服务的守护端口，以方便配置映射。） CMD \u0026lt;shell 命令\u0026gt; ：在docker容器运行时执行的命令 RUN \u0026lt;命令行命令\u0026gt;：在docker构建时执行的命令（注意：多个命令用反斜杠\\分割） 1.2.把容器内需要用到的文件全部复制到容器中 # 1.3.构建镜像 # docker build --build-arg WAR_FILE=demo-web.war -t centos7:jdk7-tomcat7 . 说明：\nbuild：docker构建命令 --build-arg \u0026lt;key=value\u0026gt; 设置参数 -t：声明构建的镜像的标签 注意：最后面有个小数点，指上下文路径 2.使用dockerfile构建python运行环境 # 2.1.创建dockerfile文件 # vi dockerfile 文件内容如下：\nFROM python:*.*.* ADD ./pip.conf /root/.pip/pip.conf ADD ./sources.list /etc/apt/sources.list ADD ./requirements.txt /var/requirements.txt ADD ./startup.sh /var/startup.sh ADD ./install.sh /var/install.sh WORKDIR /var/app RUN /var/install.sh CMD /var/startup.sh 文件内容说明：\npip.conf：pip源定义 sources.list：配置容器环境包下载的源 requirements.txt：python所需插件列表 startup.sh：启动命令 ##!/bin/bash set -e service cron start pip install -r requirements.txt python manage.py runserver 0.0.0.0:8000 install.sh：构建容器时需要执行的shell命令 #!/bin/bash set -e apt update apt install -y cron libsasl2-dev python-dev libldap2-dev libssl-dev nodejs pip install --upgrade pip pip install -r /var/requirements.txt chmod -R 777 /var/startup.sh 2.2.把容器内需要用到的文件全部复制到容器中 # 2.3.构建镜像 # 见本文1.3章。\n六、安装docker-compose # 下载路径 https://github.com/docker/compose/tags （找版本号下 docker-compose-linux-x86_64 文件下载）\n# 文件上传 # 复制文件到/usr/bin cp docker-compose-linux-x86_64 /usr/bin/docker-compose ## 授予执行权限 chmod u+x /usr/bin/docker-compose ## 检查是否正常 docker-compose -v 七、安装Harbor # 下载路径 https://github.com/goharbor/harbor/releases （文件如：harbor-offline-installer-v2.5.3.tgz）\n# 创建文件夹 mkdir -p /app/harbor # 文件上传 ## 文件解压 cd /app tar -zxvf harbor-offline-installer-v2.5.3.tgz cd harbor # 创建配置文件 cp harbor.yml.tmpl harbor.yml ## 修改配置文件 vi harbor.yml # host 配置 可为本机ip 或 域名 hostname: 192.168.56.102 ## http配置 http: port: 11080 # https配置 #https: ## port: 11443 # certificate: /your/certificate/path # private_key: /your/private/key/path ## 控制台密码 harbor_admin_password: Harbor12345 ## 数据库配置 database: password: root123 max_idle_conns: 100 max_open_conns: 900 ## 安装位置 || 镜像存储路径 data_volume: /app/registry ## 镜像扫描 trivy: ignore_unfixed: false skip_update: false offline_scan: false insecure: false ## 定时任务 jobservice: max_job_workers: 10 ## 提醒 notification: webhook_job_max_retry: 10 ## 图表 chart: absolute_url: disabled ## 日志 log: level: info local: rotate_count: 50 rotate_size: 200M location: /var/log/harbor ## 版本 _version: 2.5.0 ## 代理 proxy: http_proxy: https_proxy: no_proxy: components: - core - jobservice - trivy ## 上传配置 upload_purging: enabled: true age: 168h interval: 24h dryrun: false # 安装 sh install.sh ## 输出以下内容表示成功 ✔ ----Harbor has been installed and started successfully.---- ## 浏览器访问http://192.168.56.102:11080/ ## 用户名 amdin # 密码 Harbor12345 # docker 配置私有仓库 增加以下配置 vi /etc/docker/daemon.json { \u0026#34;insecure-registries\u0026#34;: [ \u0026#34;http://192.168.56.102:11080\u0026#34; ] } # 重载docker systemctl reload docker ","date":"2022/03/29","externalUrl":null,"permalink":"/posts/selfhost/docker_guide/","section":"吾生有涯，而知无涯","summary":"","title":"[转载]Docker建议教程","type":"posts"},{"content":"","date":"2022/03/16","externalUrl":null,"permalink":"/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/","section":"Tags","summary":"","title":"计算机网络","type":"tags"},{"content":" 前言 # 异地组网是相当实际的需求，比如远程联机游戏、办公、随时随地回家连接NAS等。\n以下是关于异地组网工具 ZeroTier、EasyTier 和 OpenVPN 的详细补充内容，包括功能类型、官网地址、优点、缺点以及适配的操作系统。\n工具一览表 # 名称 官网地址 仓库地址 优点 缺点 适配操作系统 ZeroTier https://www.zerotier.com Github - 配置简单，只需安装客户端并加入网络即可\n- 支持跨平台\n- 自动 NAT 穿透，无需公网 IP - 免费版限制 25 个设备\n- 依赖 ZeroTier 中央服务器，可能存在隐私问题 Windows、macOS、Linux、Android、iOS EasyTier https://easytier.cn Github - 支持多协议（如 WireGuard、OpenVPN）\n- 提供 Web 管理界面，易于配置\n- 支持多设备组网 - 需要订阅付费服务\n- 配置灵活性较低，依赖服务商提供的功能 Windows、macOS、Linux、Android、iOS OpenVPN https://openvpn.net - 开源免费，高度可定制\n- 支持多种加密协议\n- 适合复杂网络环境，安全性高 - 配置复杂，需要手动生成证书和配置文件\n- 需要公网 IP 或动态域名服务（DDNS）支持 Windows、macOS、Linux、Android、iOS 其他需要注意的问题⚠️ # 1. 网络延迟 # 异地组网的延迟取决于服务器和客户端之间的物理距离，选择靠近用户的地理位置的服务器可以降低延迟。 使用 NAT 穿透技术（如 ZeroTier）可以减少延迟，但某些网络环境下仍可能出现较高的延迟。 2. 安全性 # 确保使用强加密协议（如 WireGuard、OpenVPN 的 AES-256）来保护数据传输。 定期更新客户端和服务端软件，修复已知的安全漏洞。 3. 设备数量限制 # 免费版的 ZeroTier 限制 25 个设备，如果需要更多设备，需升级到付费计划。 OpenVPN 没有设备数量限制，但需要自行搭建服务器，可能增加管理成本。 4. 公网 IP 需求 # OpenVPN 和 EasyTier 通常需要公网 IP 或动态域名服务（DDNS）来建立连接。 ZeroTier 不需要公网 IP，但依赖其中央服务器进行 NAT 穿透。 5. 带宽限制 # 异地组网的带宽受限于服务器和客户端的网络连接质量。 如果组网用于传输大文件或视频流，建议选择高带宽的服务器或优化网络设置。 6. 跨平台兼容性 # ZeroTier 和 EasyTier 提供跨平台支持，适合需要在不同设备之间组网的场景。 OpenVPN 也支持跨平台，但不同平台的客户端配置可能略有不同。 7. 隐私问题 # ZeroTier 和 EasyTier 依赖于中央服务器，可能涉及隐私问题。如果需要更高的隐私保护，建议使用 OpenVPN 并自行搭建服务器。 8. 动态 IP 处理 # 如果客户端或服务器使用动态 IP，建议配置动态域名服务（DDNS）以确保连接稳定性。 9. 网络配置 # 某些路由器或防火墙可能阻止 VPN 流量，需要手动配置端口转发或防火墙规则。 例如，OpenVPN 默认使用 UDP 1194 端口，ZeroTier 使用 UDP 9993 端口。 总结 # ZeroTier 适合初学者和简单场景，配置简单但设备数量受限。 EasyTier 提供更丰富的功能和 Web 管理界面，适合需要多协议支持的用户。 OpenVPN 适合高级用户和复杂网络环境，安全性高但配置复杂。 根据实际需求选择合适的工具，并注意上述问题以确保组网的稳定性和安全性。如果有其他问题，欢迎随时提问！ 😊\n","date":"2022/03/16","externalUrl":null,"permalink":"/posts/selfhost/network_link/","section":"吾生有涯，而知无涯","summary":"","title":"异地实现局域网组网","type":"posts"},{"content":" 前言 # 本文基于BiliBili UP主技术爬爬虾的视频 超详细的WSL教程：Windows上的Linux子系统 整理总结而来, 感谢UP的辛苦付出, 欢迎大家一键三连.\n简介 # 什么是 WSL？ # WSL（Windows Subsystem for Linux）是微软在 Windows 10 及更高版本中引入的功能，允许用户在 Windows 系统上直接运行 Linux 环境。它提供了一个兼容层，使得 Linux 二进制文件能够在 Windows 上无缝运行，而无需虚拟机或双系统启动。\nWSL 的版本 # WSL 1：\n首次发布，通过兼容层将 Linux 系统调用转换为 Windows 调用。 优点：轻量级，与 Windows 文件系统完全兼容。 缺点：性能较低，不支持所有 Linux 内核功能。 WSL 2：\n基于轻量级虚拟机技术，运行完整的 Linux 内核。 优点：性能大幅提升（尤其是文件 I/O），支持 Docker 等容器化工具。 缺点：需要更多系统资源，Windows 和 Linux 文件系统之间访问速度略有延迟。 WSL 的主要优势 # 无需虚拟机： 直接在 Windows 上运行 Linux 环境，无需额外安装虚拟机软件（如 VirtualBox、VMware）。 无缝集成： 支持在 Windows 和 Linux 之间共享文件系统，方便文件操作。 可以在 Windows 终端（如 PowerShell、CMD）中直接调用 Linux 命令。 开发效率提升： 支持运行 Linux 开发工具（如 Bash、Python、Node.js、GCC 等），适合跨平台开发者。 支持 Docker： WSL 2 支持在 Linux 环境中运行 Docker 容器，非常适合 DevOps 和容器化开发。 安装 # 开启CPU虚拟化 一般默认打开(可通过任务管理器/性能查看),若未打开,可进入BIOS,启用英特尔VMX虚拟化平台(Intel Virtualization Technology).如果是AMD CPU,需要开启AMD-v的开关. 开启Windows功能下的适用于Linux的Windows子系统和虚拟机平台(Hyper-V). 在任务栏搜索\u0026quot;Windows功能\u0026quot;可找到 启用后按提示重启电脑 管理员身份运行CMD. 使用wsl --install下载安装Linxu子系统. 在CMD当前标签页右边下拉选箭头下选择要启动的Linux子系统即可启动 若提示缺少字体,可下载字体后双击安装或手动复制到C:\\Windows\\Fonts目录下. WSL常用命令 # 安装Linux子系统: wsl --install [操作系统名称][^1] 国内网络可使用wsl -install --web-download减少下载失败的可能性 os_name不指定将会下载默认系统,当前默认系统为Ubuntu 获取可安装Linux子系统列表: wsl --list --online 查询Linux子系统列表: wsl --list -v 设置默认子系统: wsl --set-default \u0026lt;子系统名称\u0026gt; 启动Linxu子系统: wsl -d \u0026lt;子系统名称\u0026gt; 卸载Linux子系统: wsl --unregister \u0026lt;子系统名称\u0026gt; 备份Linxu子系统: wsl --export \u0026lt;子系统名称\u0026gt; \u0026lt;生成的备份文件的名称\u0026gt; 默认导出到当前操作目录下 导入备份的Linux子系统: wsl --import \u0026lt;新子系统名称\u0026gt; \u0026lt;新子系统要存放的目录\u0026gt; \u0026lt;子系统备份文件路径\u0026gt; 导入成功后在\u0026quot;新子系统要存放的目录\u0026quot;下会出现一个.vhdx后缀名的文件,这是Hyperv的镜像文件, 子系统的全部数据都存储在该文件中. 关闭wsl中所有正在运行的Linxu子系统: wsl --shutdown 实用功能 # 在Windows中查看子系统文件 Win + E打开文件资源管理器, 左下角Linxu菜单下显示的就是各个子系统的文件列表,可在Windows系统中直接对子系统文件进行增删改查操作 使用命令df -h显示所有挂载卷, 在子系统中可直接操作挂载卷中的Windows文件 如果子系统中需要执行高性能IO工作,建议将文件直接复制到子系统(文件列表)中,挂载的性能较低. 命令混用 在Windows中直接运行Linux命令\n甚至可以混写,如Get-ChildItem | wsl grep Video 在Linxu子系统中可直接运行Windows程序\n例如使用notepad.exe \u0026lt;文本文件名\u0026gt;会直接使用Windows记事本打开目标文本文件 例如使用explorer.exe \u0026lt;资源目录\u0026gt;会直接使用Windows文件资源管理器打开资源目录 WSLg - 将Linux子系统中带UI的应用程序以Windows窗口的形式打开 在Linxu子系统中利用命令直接打开应用程序即可 显卡直通 在Linux子系统中使用命令nvidia-smi可查看显卡信息, 显卡已经配置好驱动,CUDA. 在子系统中可直接识别显卡并可使用程序调用 高级配置 # 在旧版本中,要配置Linxu子系统的网络等信息需要自行编写wsl配置来完成,新版本下可以通过WSL Setting应用程序的设置功能来在图形化页面修改.\n使用Windows搜索功能搜索WSL可找到WSL Setting应用程序. WSL Setting应用程序中还包含集成VSCODE和Docker的文档. 关于配置文件简单介绍如下,完整的高级配置信息参考完整文档:\n.wslconfig用于在 WSL2 上运行的所有已安装发行版中配置全局设置 wsl.conf用于为在 WSL1或 WSL2 上运行的每个 Linux 发行版按各个发行版配置本地设置。 8秒规则: 修改配置后必须等待Linux子系统完全停止运行(使用wsl --shutdown命令)后并等待8秒重启才生效 WSL2入门配置指南 # 最近不断看到有网友询问关于WSL2的问题，因此决定写一篇详细的入门配置教程，手把手教你如何配置WSL2，从入门到熟练使用。\n通过这篇文章，你将学会：\n在你的Windows电脑上安装Ubuntu到WSL2中； 迁移你的WSL2系统镜像，让系统随时可用； 优化WSL2系统设置，让其可以后台启动并高效运行； 设置Ubuntu内的应用开机自启。 其他的一些安全设置选项 1.安装Ubuntu到WSL2 # 默认选择Ubuntu系统，因为它是唯一官方支持GPU加速的发行版，非常方便。\nwsl --install 如果你想尝试其他发行版，可以通过以下命令查看并安装：\nwsl --list --online 或 wsl -l -o wsl --install -d \u0026lt;发行版名称\u0026gt; 当然，你也可以安装多个发行版。默认情况下，系统会安装在C盘上，但你可以迁移到其他位置。\n2.导出和迁移系统 # 首先，导出现有的系统：\nwsl --export Ubuntu E:\\Ubuntu2.tar 然后取消挂载当前系统：\nwsl --unregister Ubuntu 最后，将系统重新挂载到新的位置：\nwsl --import Ubuntu E:\\wsl2\\Ubuntu E:\\Ubuntu.tar 3.1.限制WSL2的资源占用 # 为了防止WSL长期占用系统资源，你可以通过配置.wslconfig文件来限制WSL的内存、CPU和交换分区大小。\n打开Windows资源管理器，地址栏输入 %UserProfile% 回车。在该目录下创建一个名为.wslconfig的文件，写入以下内容（以8GB内存电脑为例，分配2GB内存和2个CPU线程给WSL，设置4GB交换分区）： [wsl2] memory=2GB swap=4GB processors=2 localhostForwarding=true 执行以下命令关闭并重新启动WSL： wsl --shutdown 如果你不想限制WSL2的内存占用，可以通过定时任务定期清理内存。编辑crontab设置每小时释放一次内存：\ncrontab -e 0 */1 * * * echo 3 \u0026gt; /proc/sys/vm/drop_caches 3.2.设置WSL2后台启动 # 在你的WSL2的Debian或Ubuntu中执行以下命令，允许用户执行所有命令而不需要密码： sudo bash -c \u0026#34;echo \u0026#39;$USER ALL=(ALL) NOPASSWD: ALL\u0026#39; \u0026gt;/etc/sudoers.d/$USER\u0026#34; 在Windows开机启动文件夹中添加一个以.vbs结尾的文件，内容如下： set ws=wscript.CreateObject(\u0026#34;wscript.shell\u0026#34;) ws.run \u0026#34;wsl -d Ubuntu\u0026#34;, 0 要快速进入开机启动文件夹，可以按Windows+R，然后输入 shell:startup。你可能还会用到 taskschd.msc。\n4.设置Ubuntu内的应用开机自启 # 在WSL系统内新建并添加以下内容：\nvi /etc/wsl.conf 内容如下：\n[boot] systemd=true 然后开启Ubuntu的开机启动服务：\nsystemctl status rc-local 并添加以下内容：\ncat \u0026lt;\u0026lt;EOF \u0026gt;/etc/rc.local # !/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will \u0026#34;exit 0\u0026#34; on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. exit 0 EOF 执行以下命令使脚本可执行并启用：\nchmod +x /etc/rc.local systemctl enable --now rc-local 无视警告即可：\nsystemctl status rc-local.service 修改 /etc/rc.local 里的内容即可让你的Ubuntu应用开机启动。\n5.其他的一些必要配置 # 设置root密码，开机必备 # sudo passwd root 更新系统 # su - apt-get update \u0026amp;\u0026amp; apt-get upgrade WSL2 Ubuntu 安装openssh-server # sudo apt update sudo apt install openssh-server WSL2 启用systemd # 在/etc目录下新建wsl.conf配置文件，并编辑该配置文件：\nUbuntu # sudo vi /etc/wsl.conf 输入内容：\n[boot] systemd=true Windows # 在 Windows PowerShell(管理员)中运行：\nwsl --shutdown 再重新打开 Ubuntu，使 WSL 彻底重新启动以便启用 systemd。然后在 WSL 中运行：\n让你的ssh开机自启 # systemctl enable ssh systemctl start ssh 请注意你的ssh的登录安全，按照你的安全习惯配置即可。\n进一步学习 # 学完以上基础内容后，你可能会感兴趣以下内容：\n如何在WSL2内运行大模型，如部署本地的ollama或吾皇的GPT； 如何让你的本地模型在外网随时可访问，如部署Tailscale或ZeroTrust； 还有哪些有趣的场景可以探索？ 参考资料 # 让WSL开机启动，后台运行，以减少唤醒时间 微软WSL安装官方指南 新版本下如何通过外部网络访问wsl WSL(Windows Subsystem for Linux) 教程 ","date":"2021/03/23","externalUrl":null,"permalink":"/posts/selfhost/wsl_skill/","section":"吾生有涯，而知无涯","summary":"","title":"[转载]WSL(Windows Subsystem for Linux) 教程","type":"posts"},{"content":"","date":"2021/03/23","externalUrl":null,"permalink":"/tags/wsl/","section":"Tags","summary":"","title":"WSL","type":"tags"},{"content":"","date":"2021/03/23","externalUrl":null,"permalink":"/tags/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/","section":"Tags","summary":"","title":"技术分享","type":"tags"},{"content":"","date":"2021/03/23","externalUrl":null,"permalink":"/tags/github/","section":"Tags","summary":"","title":"GitHub","type":"tags"},{"content":" 前言 # GitHub 是一个广泛使用的代码托管平台，除了基本的代码管理功能外，它还提供了许多高级功能来增强用户体验和安全性。其中之一就是 Verified（已验证） 标识符。当你的 commit 或 tag 被标记为 \u0026ldquo;Verified\u0026rdquo; 时，表示该提交或标签是通过 GPG 或 SSH 签名的，确保其真实性和完整性。本文将详细介绍如何为你的 GitHub 提交添加 Verified 标识符。\n什么是 Verified 标识符？ # Verified 标识符是 GitHub 对 GPG 或 SSH 签名提交的一种验证标记。它表明：\n提交的作者身份是可信的。 提交内容未被篡改。 这对于开源项目或团队协作非常重要，因为它可以防止恶意提交或伪造提交。\n添加 Verified 标识符的步骤 # 方法 1：使用 GPG 签名 # 1. 安装 GPG # 如果你的系统尚未安装 GPG，请先安装：\nbrew install gnupg # macOS sudo apt-get install gnupg # Linux # Windows: 下载并安装 [Gpg4win](https://www.gpg4win.org/)。 2. 生成 GPG 密钥 # 运行以下命令gpg --full-generate-key生成 GPG 密钥，按照提示选择密钥类型、密钥长度（推荐 4096），并填写你的姓名和邮箱地址。\n默认密钥类型为ECC（签名和加密），Curve 25519 ，永不过期 姓名建议直接使用Github用户名 邮箱地址选择为Github提供的隐私地址，通常为随机字符+用户名@users.noreply.github.com.，可以访问Github邮箱 3. 查看 GPG 密钥 # 生成密钥后，运行以下命令gpg --list-secret-keys --keyid-format LONG查看，复制 sec 行中的密钥 ID（例如：3AA5C34371567BD2）。\n4. 导出 GPG 公钥 # 运行以下命令gpg --armor --export YOUR_KEY_ID导出公钥，将输出内容复制到剪贴板，需要包含--Begin XXXX---。\n5. 将 GPG 公钥添加到 GitHub # 登录 GitHub。 访问 GitHub SSH and GPG Keys 页面。 点击 New GPG key，将复制的公钥粘贴到输入框中，然后点击 Add GPG key。 6. 配置 Git 使用 GPG 签名 # 运行以下命令配置 Git：\ngit config --global user.signingkey YOUR_KEY_ID git config --global commit.gpgsign true 7. 提交代码 # 现在，每次提交代码时，Git 会自动使用 GPG 签名。推送代码后，在 GitHub 上查看提交记录，你会看到 Verified 标识符。\n方法 2：使用 SSH 签名 # 1. 生成 SSH 密钥 # 如果尚未生成 SSH 密钥，运行以下命令：\nssh-keygen -t ed25519 -C \u0026#34;your_email@example.com\u0026#34; 2. 将 SSH 公钥添加到 GitHub # 复制公钥内容：\ncat ~/.ssh/id_ed25519.pub 登录 GitHub，访问 GitHub SSH and GPG Keys 页面。\n点击 New SSH key，将复制的公钥粘贴到输入框中，然后点击 Add SSH key。\n3. 配置 Git 使用 SSH 签名 # 运行以下命令：\ngit config --global gpg.format ssh git config --global user.signingkey ~/.ssh/id_ed25519.pub git config --global commit.gpgsign true 4. 提交代码 # 提交代码后，GitHub 会自动验证 SSH 签名，并显示 Verified 标识符。\n验证是否成功 # 提交代码后，访问 GitHub 仓库的提交记录页面。如果看到提交旁边有 Verified 标识符，说明配置成功。\n常见问题 # 1. 为什么我的提交没有显示 Verified？ # 确保已正确配置 GPG 或 SSH 签名。 确保提交时使用了与 GPG 或 SSH 密钥关联的邮箱地址。 2. 如何更改 Git 的邮箱地址？ # 运行以下命令：\ngit config --global user.email \u0026#34;your_email@example.com\u0026#34; 3. 如何禁用 GPG 签名？ # 运行以下命令：\ngit config --global commit.gpgsign false 总结 # 通过 GPG 或 SSH 签名，可以为你的 GitHub 提交添加 Verified 标识符，增强提交的可信度和安全性。无论是个人项目还是团队协作，这一功能都非常有用。希望本文对你有所帮助！如果还有其他问题，欢迎留言讨论。\n","date":"2021/03/23","externalUrl":null,"permalink":"/posts/tricks/git-add-verify/","section":"吾生有涯，而知无涯","summary":"","title":"GitHub技巧及教程","type":"posts"},{"content":"","date":"2021/03/23","externalUrl":null,"permalink":"/tags/%E8%BD%AF%E4%BB%B6%E6%8A%80%E5%B7%A7/","section":"Tags","summary":"","title":"软件技巧","type":"tags"},{"content":" 前言 # 作为一个技术爱好者，我一直想拥有一个既简洁又高效的个人博客。这次重构博客，我将重点放在了速度、编辑体验和工作流的顺畅性上。经过一番调研，我选择了Hugo作为静态网站生成器，搭配Blowfish主题，并使用Cloudflare Pages进行部署和域名管理。下面，我将详细记录整个搭建过程。\n项目名称 简要介绍 类型 仓库地址 论文 AO-MARL 多智能AO SHWFS https://github.com/Tomeu7/AO-MARL Adaptive Optics control with Multi-Agent Model-Free Reinforcement Learning Integrating-SL-and-RL-for-AO UNET+RL PYWFS https://github.com/Tomeu7/Integrating-SL-and-RL-for-AO Integrating supervised and reinforcement learning for predictive control with an unmodulated pyramid wavefront sensor for adaptive optics 教程基础及前言 # 在开始之前，确保你已经具备以下基础：\nGit：用于版本控制和代码管理。 Hugo：一个快速、灵活的静态网站生成器。 GitHub账号：用于托管代码和版本管理。 Cloudflare账号：用于部署和域名管理。 1. Hugo 新建项目 # 首先，安装Hugo。如果你还没有安装，建议直接访问Hugo Release选择相应的版本进行下载安装。然后，创建一个新的Hugo项目：\nhugo new site my-blog # my-blog 是项目的名称 cd my-blog 2. 安装主题（Git Submodule方法） # Blowfish是一个简洁、现代的Hugo主题。为了便于更新和管理，我们使用Git Submodule方法安装主题。\ngit init git submodule add https://github.com/nunocoracao/blowfish.git themes/blowfish 接下来，将主题的示例配置复制到项目根目录，删除根目录当中的hugo.toml文件：\ncp themes/blowfish/exampleSite/config/* . 编辑config.toml文件，根据你的需求进行个性化配置。\n3. 主题相关配置 # Blowfish主题提供了丰富的配置选项，可以通过编辑config.toml文件来定制博客的外观和功能。以下是一些常见的配置项：\nbaseURL = \u0026#34;https://yourdomain.com\u0026#34; title = \u0026#34;我的博客\u0026#34; theme = \u0026#34;blowfish\u0026#34; [params] description = \u0026#34;这是我的个人博客，分享技术经验和生活感悟。\u0026#34; author = \u0026#34;你的名字\u0026#34; favicon = \u0026#34;favicon.ico\u0026#34; 4. 配置域名 # 4.1 上传至GitHub进行历史版本管理 # 首先，将项目推送到GitHub仓库：\ngit add . git commit -m \u0026#34;Initial commit\u0026#34; git branch -M main git remote add origin https://github.com/yourusername/my-blog.git git push -u origin main 4.2 使用Cloudflare Pages实现在线发布及域名访问 # 登录Cloudflare，进入Pages页面，选择“Create a project”。 连接到你的GitHub仓库，选择刚刚推送的my-blog项目。 在构建设置中，选择Hugo作为框架，并设置构建命令为hugo，输出目录为public。 点击“Save and Deploy”开始部署。 部署完成后，Cloudflare会提供一个默认的.pages.dev域名。如果你有自己的域名，可以在Cloudflare的DNS设置中添加CNAME记录，将你的域名指向Cloudflare Pages提供的域名。\n附录 # 以下是一些参考教程和资源，可以进一步学习和探索：\nHugo官方文档 Blowfish主题官方文档 Cloudflare Pages官方文档 希望这篇教程能帮助你顺利搭建自己的博客网站。如果有任何问题或建议，欢迎在评论区留言讨论。\n","date":"2020/10/18","externalUrl":null,"permalink":"/posts/tricks/hugo_website/","section":"吾生有涯，而知无涯","summary":"","title":"基于Hugo+Blowfish主题搭建的博客网站","type":"posts"},{"content":"","date":"2020/10/18","externalUrl":null,"permalink":"/tags/%E7%BB%8F%E9%AA%8C%E8%AE%B0%E5%BD%95/","section":"Tags","summary":"","title":"经验记录","type":"tags"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":" 免责声明 # 本网站上所记录的内容可能因时间、地区的不同而造成有效性的缺失或者改变，任何因参考本站内容或有关内容引起的直接、间接损失或损害，博主均不负责。无论成年与否，请自担风险，对自己负责。\n许可协议 # 本站使用 Hugo 构建，相关代码和服务继承原项目的License：\nHugo： Apache License 2.0 Blowfish： MIT License 除非另有特殊说明，否则 本站 所有内容均在 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 许可下授权。\n以下是该许可证的人类可读摘要（而不是替代）： 您可以自由地 # 共享 — 在任何媒介以任何形式复制、发行本作品 演绎 — 修改、转换或以本作品为基础进行创作 只要你遵守许可协议条款，许可人就无法收回你的这些权利。 惟须遵守下列条件 # 署名 — 您必须给出 适当的署名，提供指向本许可协议的链接，同时 标明是否（对原始作品）作了修改。您可以用任何合理的方式来署名，但是不得以任何方式暗示许可人为您或您的使用背书。 非商业性使用 — 您不得将本作品用于 商业目的。 相同方式共享 — 如果您再混合、转换或者基于本作品进行创作，您必须基于 与原先许可协议相同的许可协议 分发您贡献的作品。 没有附加限制 — 您不得适用法律术语或者 技术措施 从而限制其他人做许可协议允许的事情。 侵权声明 # 如果本网站的任何内容无意中侵犯了您的版权或利益，请及时与我联系。\n电子邮箱地址为chas5wasl@duck.com，暂不支持其他联系，一般会在一周内做出回复。\n","externalUrl":null,"permalink":"/copyright/","section":"欢迎访问","summary":"","title":"版权声明","type":"page"},{"content":"","externalUrl":null,"permalink":"/archives/","section":"欢迎访问","summary":"","title":"博客归档","type":"page"},{"content":"","externalUrl":null,"permalink":"/tags/%E5%B8%B8%E7%94%A8%E8%BD%AF%E4%BB%B6/","section":"Tags","summary":"","title":"常用软件","type":"tags"},{"content":" 关于此博客 AbloutBlog # 社交平台日新月异，却常留不住心头一闪而过的念头； 世间白云苍狗，遗忘似乎是大多数人无奈的选择； 人事纷繁复杂，我总渴望在回望时能清晰看见来时的路。 于是，记录成了我对抗遗忘的方式，是我诚实成长的印记。\n关于我 AboutMe # 一位身处代码世界的探索者，痴迷于系统编程的奥秘。 自我评价MBTI类型为INTJ，享受独立思考与逻辑推演。 不求做个世俗定义的好人，也未曾做过什么坏事。 价格行为爱好者，持续研究当中。 只愿在平凡中，以自己的方式认真生活，偶尔抬头看看星空。\n认证 Certifications # 认证名称 英文名称 颁发机构/年份 备注 华为云原生开发者 Huawei Certified Cloud Native Developer (HCCNAD) Huawei (2024) 掌握容器化技术（Docker、Kubernetes）、微服务架构、DevOps实践，具备在华为云平台上进行云原生应用开发、部署与运维的能力，熟悉主流云原生工具与理念。 甲骨文数据库管理员 Oracle Certified Professional (OCP) MySQL 8.0 Database Administrator 甲骨文大学 Oracle University (2023) 精通MySQL 8.0数据库的安装、配置、日常维护、数据备份与恢复、性能优化及安全管理。具备构建高可用数据库解决方案，并能有效进行故障排除与资源监控。 ","externalUrl":null,"permalink":"/about/","section":"欢迎访问","summary":"","title":"关于","type":"page"},{"content":"","externalUrl":null,"permalink":"/tags/%E7%A7%91%E7%A0%94/","section":"Tags","summary":"","title":"科研","type":"tags"},{"content":"","externalUrl":null,"permalink":"/series/%E7%A7%91%E7%A0%94%E5%BF%85%E5%A4%87/","section":"Series","summary":"","title":"科研必备","type":"series"},{"content":" 书影音：光影与墨香的交织，灵魂栖息的港湾 # NeoDB 是一个开源免费的书影音收藏社区平台， 本页面使用 GitHub Actions 定时任务每日获取最新内容。\n","externalUrl":null,"permalink":"/neodb/","section":"欢迎访问","summary":"","title":"书影音","type":"page"},{"content":" 部署的公开服务 # 1.Overleaf社区版 网址为Overleaf社区，提供免费的使用，支持评论共享。如果有需要，请直接联系我。\n找到其他的小伙伴 # ","externalUrl":null,"permalink":"/links/","section":"欢迎访问","summary":"","title":"友情链接🔗","type":"page"}]