2025-08-10 10:55:01
摘要:一、安装Harbor
1. Harbor介绍
Harbor是由VMWare在Docker Registry的基础之上进行了二次封装,加进去了很多额外程序,而且提供了一个非常漂亮的web界面。
Project Harbor是一个开源的受信任的云本地注册表项目,用于存储、标记和扫描上下文。
Harbor扩展了开源Docker发行版,增加了用户通常需要的功能,如安全、身份和管理。
Harbor支持高级特性,如用户管理、访问控制、活动监视和实例之间的复制。
2. 功能
多租户内容签名和验证
安全性与漏洞分析
审计日志记录
身份集成和基于角色的访问控制
实例间的镜像复制
可扩展的API和图形UI
国际化(目前为英文和中文)
3. docker compose
Harbor在物理机上部署是非常难的,而为了简化Harbor的应用,Harbor官方直接把Harbor做成了在容器中运行的应用,而且这个容器在Harbor中依赖类似redis、mysql、pgsql等很多存储系统,所以它需要编排很多容器协同起来工作,因此VMWare Harbor在部署和使用时,需要借助于Docker的单机编排工具(Docker compose)来实现。
Compose是一个用于定义和运行多容器Docker应用程序的工具。使用Compose,我们可以使用YAML文件来配置应用程序的服务。然后,只需要一个命令,就可以从配置中创建并启动所有服务。
4. 部署
需要提前安装好docker
提前下载好安装包:Release v2.13.2 · goharbor/harbor · GitHub
安装docker-compose
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.39.1/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
docker comp……
阅读全文
2025-08-09 10:45:35
摘要:最近公司有个.net项目需要集群部署,由于不打算使用K8S,计划用docker swarm来搭建整个集群环境。
所以计划用2篇文章,记录一下docker swarm部署的整个过程。
文章使用的环境是本地虚拟机环境,生产环境大同小异。
文中用的到资料下载:docker-swarm.zip
一、安装 Docker
对于 Ubuntu/Debian 系统:
# 查看 ip
ip a
使用工具测试链接,如putty
# 1. 更新软件包索引
sudo apt-get update
# 2. 安装依赖包
sudo apt-get install ca-certificates curl gnupg lsb-release
# 3. 添加 Docker 官方 GPG 密钥
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 4. 设置稳定版仓库
echo deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null
# 5. 安装 Docker 引擎
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 检查 Docker 版本
docker --version
对于 CentOS/RHEL 系统:
# 安装依赖包
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加 Docker 仓库
sudo yum-config-manager --add-r……
阅读全文
2024-09-01 23:15:28
摘要:Dapr 完全离线安装方案
以下是完整的 Dapr 离线安装指南,无需任何网络连接即可完成全部安装过程:
准备工作(在可联网的机器上)
1. 下载所有必需文件
# 创建离线安装目录
mkdir dapr-offline cd dapr-offline
# 下载 Dapr CLI
wget https://github.com/dapr/cli/releases/download/v1.15.0/dapr_linux_amd64.tar.gz
# 下载 Dapr 运行时
wget https://github.com/dapr/dapr/releases/download/v1.15.5/daprd_linux_amd64.tar.gz
# 下载 Dashboard
wget https://github.com/dapr/dashboard/releases/download/v0.15.0/dashboard_linux_amd64.tar.gz
# 下载默认配置文件
wget https://raw.githubusercontent.com/dapr/dapr/master/daprd-system/config.yaml
2. 下载 Docker 镜像并保存
# 拉取所需镜像
docker pull daprio/dapr:1.15.5
docker pull daprio/placement:1.15.5
docker pull daprio/sentry:1.15.5
docker pull daprio/dashboard:0.15.0
docker pull redis:6-alpine
docker pull openzipkin/zipkin:latest
# 保存镜像为 tar 文件
docker save -o dapr-1.15.5.tar daprio/dapr:1.15.5
docker save -o placement-1.15.5.tar daprio/placement:1.15.5
docker save -o sentry-1.15.5.tar daprio/sentry:1.15.5
docker save -o dashboard-0.15.0.tar daprio/dashboard:0.15.0
d……
阅读全文
2021-11-14 11:12:25
摘要:一、基础概念
1. 聚集索引(Clustered Index)
结构特点
数据存储:
聚集索引决定了表中数据的物理存储顺序。
表中的每一行数据都会按照聚集索引的键值进行排序存储。
叶节点:
聚集索引的叶节点包含实际的数据行。
叶节点的数据行是按照聚集索引的键值连续存储的。
唯一性:
每个表只能有一个聚集索引。
聚集索引的键值必须是唯一的,除非在创建时允许重复键值(通过 ALLOW_ROW_LOCKS 和 ALLOW_PAGE_LOCKS 选项)。
存储效率:
由于数据按照键值连续存储,聚集索引在范围查询和排序操作中非常高效。
如果表中有大量数据,聚集索引的维护成本相对较高,因为插入、删除和更新操作需要重新排列数据。
使用场景
范围查询:
适用于需要频繁进行范围查询(如 BETWEEN、、)的表。
排序和分组:
适用于需要频繁进行排序和分组操作的列。
主键:
通常主键会创建为聚集索引,因为主键需要唯一标识每一行数据,并且主键列通常用于范围查询和排序操作。
数据访问模式:
适用于访问模式以顺序访问数据为主的场景。
2. 非聚集索引(Non-Clustered Index)
结构特点
数据存储:
非聚集索引的键值存储的是指向实际数据行的指针。
表中的数据行可以按照插入顺序或其他顺序存储,但非聚集索引提供了一种快速查找数据的方式。
叶节点:
非聚集索引的叶节点包含指向实际数据行的指针。
叶节点的数据行指针是按照非聚集索引的键值排序的。
唯一性:
每个表可以有多个非聚集索引。
非聚集索引的键值可以是唯一的,也可以不唯一。
存储效率:
非聚集索引的维护成本相对较低,因为插入、删除和更新操作不会重新排列实际数据行。
非聚集索引可以提高特定列的查询性能,但对整个表的数据存储没有影响。
使用场景
等值查询:
适用于需要频繁进行等值查询(如 =、IN)的列。
范围查询:
虽然非聚集索引也可以用于范围查询,但效率可能不如聚集索引,尤其是在范围较大时。
排序和分组:
适用于需要频繁进行排序和分组操作的列。
外键:
外键列通常会创建非聚集索引,以提高外键约束的性能。
数据访问模式:
适用于访问模式以随机访问数据为主的场景。
3. 索引优化规则
避免过多索引:
每个表的……
阅读全文
2019-11-16 15:35:24
摘要:影响性能因素
数据库结构设计
T-SQL语句
数据量大
事务和隔离级别
硬件资源
IO阻塞
批量删除表数据:大量删除时会记录到日志中,也会造成IO阻塞
优化和注意事项
了解业务
优先考虑第三范式设计,参考设计范式
表关联尽可能少
坚持最小原则
在适当的地方使用约束
用户数据和日志文件隔离存放
T-SQL语句优化
使用字段名,尽量不适用*
条件
从左边开始,先写最小条件锁定最少数据
索引
尽量使用索引字段
索引字段放到左边
不能计算也不要使用函数,否则索引失效
以小表关联大表
SQL语句尽量简单
执行计划
sql官方执行计划文档
执行计划图标和运算符
MSSQLSERVER执行计划详解 - 张龙豪 - 博客园
SQL Server执行计划的理解 - 馨馨妙 - 博客园
点击开启【包括实际的执行计划】
执行计划关键字和图标理解
表扫描:
Parameter Table Scan 运算符扫描在当前查询中用作参数的表。 该运算符一般用于存储过程内的 INSERT 查询。 Parameter Table Scan 既是一个逻辑运算符,也是一个物理运算符。
就是扫描查询列整个表全部数据,最耗时性能最低的。
嵌套循环:
Nested Loops 运算符执行内部联接、左外部联接、左半部联接和左反半部联接逻辑运算。 嵌套循环联接通常使用索引,针对外部表的每一行在内部表中执行搜索。 查询处理器根据预计的开销来决定是否对外部输入进行排序,以改进内部输入索引上的搜索定位。 将基于所执行的逻辑操作返回所有满足 Argument 列中的(可选)谓词的行。 如果 OPTIMIZED 特性设置为“True”,则表示使用了优化的嵌套循环(或批处理排序) 。 Nested Loops 是一个物理运算符。 有关详细信息,请参阅了解嵌套循环联接。
RID查询 :
RID Lookup 是使用提供的行标识符 (RID) 在堆上进行的书签查找。 Argument 列包含用于查找表中的行的书签标签和从中查找行的表的名称。 RID Lookup 通常带有 NESTED LOOP JOIN。 RID Lookup 是一个物理运算符。 有关书签查找的详细信息,请参阅 MSDN SQL Server 博客中的Bookmark Lookup(书签查找)。
哈希匹配:
Hash Ma……
阅读全文
2018-11-16 16:46:20
摘要:需求背景
假设,你正在参与开发一个微服务。微服务通过 HTTP 协议暴露接口给其他系统调用,说直白点就是,其他系统通过 URL 来调用微服务的接口。有一天,你的 leader 找到你说,“为了保证接口调用的安全性,我们希望设计实现一个接口调用鉴权功能,只有经过认证之后的系统才能调用我们的接口,没有认证过的系统调用我们的接口会被拒绝。我希望由你来负责这个任务的开发,争取尽快上线。”
需求分析
1. 第一轮基础分析
对于如何做鉴权这样一个问题,最简单的解决方案就是,通过用户名加密码来做认证。我们给每个允许访问我们服务的调用方,派发一个应用名(或者叫应用 ID、AppID)和一个对应的密码(或者叫秘钥)。调用方每次进行接口请求的时候,都携带自己的 AppID 和密码。微服务在接收到接口调用请求之后,会解析出 AppID 和密码,跟存储在微服务端的 AppID 和密码进行比对。如果一致,说明认证成功,则允许接口调用请求;否则,就拒绝接口调用请求。
2. 第二轮分析优化
不过,这样的验证方式,每次都要明文传输密码。密码很容易被截获,是不安全的。那如果我们借助加密算法(比如 SHA),对密码进行加密之后,再传递到微服务端验证,是不是就可以了呢?实际上,这样也是不安全的,因为加密之后的密码及 AppID,照样可以被未认证系统(或者说黑客)截获,未认证系统可以携带这个加密之后的密码以及对应的 AppID,伪装成已认证系统来访问我们的接口。 这就是典型的“重放攻击” 。
提出问题,然后再解决问题,是一个非常好的迭代优化方法。对于这个问题,我们可以借助 OAuth 的验证思路来解决。调用方将请求接口的 URL 跟 AppID、密码拼接在一起,然后进行加密,生成一个 token。调用方在进行接口请求的的时候,将这个 token 及 AppID,随 URL 一块传递给微服务端。微服务端接收到这些数据之后,根据 AppID 从数据库中取出对应的密码,并通过同样的 token 生成算法,生成另外一个 token。用这个新生成的 token 跟调用方传递过来的 token 对比。如果一致,则允许接口调用请求;否则,就拒绝接口调用请求。
3. 第三轮分析优化
不过,这样的设计仍然存在重放攻击的风险,还是不够安全。每个 URL 拼接上 AppID、密码生成的 token 都是固定的。未认证系统截……
阅读全文
2018-08-21 22:37:30
摘要:1. 目标
如下代码:我们要实现缓存,但希望让使用者不用关心缓存的具体实现,只需要使用者在要操作缓存的方法上加上特性标注即可。
[Caching(CachingMethod.Remove, GetLinksQuery)]
public class CreateLinkCommand
{
}
[Caching(CachingMethod.Get)]
public class GetLinksQuery : IRequestListLinkViewModel
{
}
要实现我们的目标,我们把任务分成2部分,首先实现缓存逻辑,然后将缓存基于特性做AOP实现。
2. 缓存实现
首先我们定义一个缓存接口
public interface ICacheProvider
{
/// summary
/// 向缓存中添加一个对象。
/// /summary
/// param name=key缓存的键值,该值通常是使用缓存机制的方法的名称。/param
/// param name=valKey缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。/param
/// param name=value需要缓存的对象。/param
void Add(string key, string valKey, object value);
void Put(string key, string valKey, object value);
object Get(string key, string valKey);
void Remove(string key);
bool Exists(string key);
bool Exists(string key, string valKey);
}
如上代码,为什么接口中key和valKey2个参数呢?这是因为我们可能会缓存同一个方法不同参数的结果,如在一个获取分页结果的方法中,我们可能会返回不同页的结果。如我们目标中GetLinksQuery方法缓存的值会是一个分页显示结果的字典。key是我们缓存的方法名,valKey这是缓存的字典结果中的字典key,value则是字典的结果。要进一步理解可以查看下面基于内存的默……
阅读全文
2008-05-08 12:28:02
摘要:源码
/* ******************************************************************* */
/* CSS FUNCTIONS */
/* ******************************************************************* */
var CSS = (function() {
var css = {};
// 转换的RGB字符串的形式“的RGB ( 255 , 255 , 255 ) ”到“ #ffffff ”
css.rgb2hex = function(rgbString) {
if (typeof (rgbString) != string || !defined(rgbString.match)) { return null; }
var result = rgbString.match(/^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*/);
if (result == null) { return rgbString; }
var rgb = +result[1] 16 | +result[2] 8 | +result[3];
var hex = ;
var digits = 0123456789abcdef;
while (rgb != 0) {
hex = digits.charAt(rgb 0xf) + hex;
rgb = 4;
}
while (hex.length 6) { hex = '0' + hex; }
return # + hex;
};
// 转换字符样式
css.hyphen2camel = function(property) {
if (!defined(property) || ……
阅读全文
2008-05-07 21:28:34
摘要:源码
/* ******************************************************************* */
/* EVENT FUNCTIONS */
/* ******************************************************************* */
var Event = (function() {
var ev = {};
//阻止事件冒泡
ev.stopBubble = function(e) {
// 如果传入了事件对象,那么就是非IE浏览器
if (e)
// 因此它支持W3C的stopPropagation
e.stopPropagation();
else
// 否则,我们得使用IE的方式取消事件冒泡
window.event.cancelBubble = true;
};
//防止发生默认浏览器行为
ev.stopDefault = function(e) {
// 防止默认浏览器行为(W3C)
if (e) e.preventDefault();
// IE中防止浏览器行为的捷径
return false;
};
//
// 由 Dean Edwards 所编写的addEvent/removeEvent 2005
// 由Tino Zijdel整理
// http://dean.edwards.name/weblog/2005/10/add-event/
ev.addEvent = function(element, type, handler) {
//为每个事件处理函数赋予一个独立的ID
if (!handler.$$guid) handler.$$guid = ev.addEvent.guid++;
//……
阅读全文
2008-05-06 23:33:23
摘要:源码
/* ******************************************************************* */
/* DOM FUNCTIONS */
/* ******************************************************************* */
var DOM = (function() {
var dom = {};
//查找相关元素的前兄弟元素
dom.prev = function(elem) {
do {
elem = elem.previousSibling;
} while (elem elem.nodeType != 1);
return elem;
};
//查找相关元素的后兄弟元素
dom.next = function(elem) {
do {
elem = elem.nextSibling;
} while (elem elem.nodeType != 1);
return elem;
};
//查找第一个子元素
dom.first = function(elem) {
elem = elem.firstChild;
return elem elem.nodeType != 1 ? nextSibling(elem) : elem;
};
//查找最后一个子元素
dom.last = function(elem) {
elem = elem.lastChild;
return elem elem.nodeType != 1 ? prevSibling(elem) : elem;
};
//查找父元素
dom.parent = function(elem, num) {
num = nu……
阅读全文