0%

Azure DevOps Engineer Expert

DevOps 是人员、流程和产品的集合体现,它可让我们向最终用户持续交付价值。 ———— Donovan Brown

Azure DevOps

Azure DevOps 是 Microsoft 提供的一种软件即服务 (SaaS) 平台,它能提供用于开发和部署软件的端到端 DevOps 工具链。
组成

  • Azure Repos 源代码管理
  • Azure Pipelines CI/CD服务
  • Azure Boards 类似TP的kanban工具以及Agile tools等
  • Azure Test Plans 测试工具,包括manual/exploratory testing 和 continuous testing
  • Azure Artifacts 大致上就是构建自己的库(allows teams to share packages such as Maven, npm, NuGet and more from public and private sources and integrate package sharing into your CI/CD pipelines)

生产DevOps的内容

  • 操作系统
  • 脚本
  • 容器
  • 其他

对于branch

对于敏捷开发的团队,选择避免使用长期分支(long-lived branch),以致力于短期功能和 Bug 修复分支,任何工作付出的目标都是以生成pull request将工作合并回master

A long-lived branch is a Git branch that is never deleted. Some teams prefer to avoid them altogether in favor of short-lived feature and bug fix branches. For those teams, the goal of any effort is to produce a pull request that merges their work back into master.
对于web应用,往往不会支持或回退到起初的版本,适用于上述工作方式,但也有其他场景需要长期保留分支,如用于同时支持市场上的多个版本,release V1, release V2将持续维护

来源微软Docs:GitHub基于Release的工作流

master是稳定版分支,与线上版本保持绝对一致,release是预发布分支,从develop创建出来进行测试。

关于release

语义化版本标签
release notes(存目)

配置和管理虚拟网络占比30-35%
目标导向:

  • 学习创建虚拟网络
  • 创建虚拟VPN网关
  • 认识使用ExpressRouter

    虚拟网络

    功能:
  • 隔离和分割(Isolation & segmentation)
  • 网络通信、资源间通信、与本地网络(on-premises)通信
  • 连接虚拟网络
  • 路由|筛选|连接网络流量(route|filter network traffic)

VPN(virtual private networks):

  • Point-to-site
  • Site-to-site
  • Azure ExpressRoute

    Network Monitor

  • 监视vm与endpoint(可以是其他vm)之间的通信
  • 查看vnet中的资源及其关系
  • 诊断(Diagnose)出入vm的网络流量筛选问题
  • 诊断vm网络路由问题
  • 诊断vm出站连接(outbound connections)
  • 捕获出入vm的数据包
  • 诊断vnet网关与连接的问题
  • 检查区域与internet相对延迟
  • 查看安全规则

    概念

    IP地址空间:举个栗子地址空间192.168.1.0/24,子网掩码255.255.255.248。子网掩码用来指明某个IP地址哪些位是网络位,哪些是主机位,同网络位IP之间的通信不需要通过网关,主机位数值就是有多少主机。IP总共32位,‘/24’是指前24位都是网络位,主机坐在的网络,248即11111000,这个网络有可以有 25 即32个子网,每个子网可分配地址为 23 - 2(减去广播地址和网络地址),为6个

    虚拟机通过虚拟网络通信的实践

    创建虚拟网络,名为default
    1
    2
    $Subnet=New-AzVirtualNetworkSubnetConfig -Name default -AddressPrefix 10.0.0.0/24
    New-AzVirtualNetwork -Name myVnet -ResourceGroupName vm-networks -Location $Location -AddressPrefix 10.0.0.0/16 -Subnet $Subnet
    使用powershell创建两个Azure VM
    1
    2
    3
    4
    5
    6
    7
    New-AzVm `
    -ResourceGroupName "vm-networks" `
    -Name "testvm1" `
    -VirtualNetworkName "myVnet" `
    -SubnetName "default" `
    -image "Win2016Datacenter" `
    -Size "Standard_DS2_v2"
    *取消其中一台的公共IP
    1
    2
    3
    $nic = Get-AzNetworkInterface -Name testvm2 -ResourceGroup vm-networks
    $nic.IpConfigurations.publicipaddress.id = $null
    Set-AzNetworkInterface -NetworkInterface $nic
    使用PublicIP远程VM1,在VM1使用计算机名访问同一虚拟网络的VM2

VPN网关

Azure虚拟网关为‘从本地到Azure’的传入连接提供一个endpoint,VPN网关是一种虚拟网关类型,可以作为加密的endpoint,在Azure的实践中,VPN网关用以在不同区域之间安全地链接虚拟机和服务

将图标转为字体格式

icomoon

将图标封装到css伪类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.icon.icon-add{
position: relative;
padding-left: 22px;
margin-left: 4px;
&::before{
content: '';
position: absolute;
left: 0px;
bottom: calc(50% - 10px);
background: url(./assets/image/icon_add.svg) no-repeat top left;
width: 18px;
height: 18px;
}
}

通常调用一个功能,将其所属类型new出一个实例,随后调用这个对象的方法,new的过程使用具体的构造方法,后来我们将接口和实现分离,调用功能的地方用接口编码,而具体实现的方法在某处独立配置,一旦需要修改实现,不至于到处替换代码。

ASP.NET Core提供内置服务容器IServiceProvider,将服务注入到使用它的类的构造函数中。 框架负责创建依赖关系的实例,并在不再需要时将其释放。

注册服务

1
2
3
4
5
6
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDateTime, SystemDateTime>();

services.AddControllersWithViews();
}

其中IDateTime是接口类,SystemDateTime是前者的实现类,AddSingleton(IServiceCollection, Type)方法将 serviceType 中指定类型的单一实例服务添加到指定的 IServiceCollection 中。
以生命周期区分的三种方式还有services.AddScoped()的注册方式以及services.AddTransient()方式, 区别如下

  • singleton: IoC容器将在应用程序的整个生命周期内创建和共享服务的单个实例。
  • transient:每次您请求时,IoC容器都会创建一个指定服务类型的新实例。
  • scope: IoC容器将为每个请求创建一次指定服务类型的实例,并将在单个请求中共享。

    构造函数注入

    调用接口时,将接口类作为构造函数参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Index2Model : PageModel
    {
    private readonly IMyDependency _myDependency;
    public Index2Model(IMyDependency myDependency)
    {
    _myDependency = myDependency;
    }
    public void OnGet()
    {
    _myDependency.WriteMessage("Index2Model.OnGet");
    }
    }

    FromServices attribute注入

    1
    2
    3
    4
    public IActionResult About([FromServices] IDateTime dateTime)
    {
    return Content( $"Current server time: {dateTime.Now}");
    }

    Controller settings注入

    setttings类
    1
    2
    3
    4
    5
    public class SampleWebSettings
    {
    public string Title { get; set; }
    public int Updates { get; set; }
    }
    将Configuration类加入到services collection
    1
    2
    3
    4
    5
    6
    7
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddSingleton<IDateTime, SystemDateTime>();
    services.Configure<SampleWebSettings>(Configuration);

    services.AddControllersWithViews();
    }

Caution! 这个东西非常昂贵 不要在自己的Azure上学习实践!

Azure AD DS

域名(domain name),是命名空间,域名用于在网络传输中标识资源的电子方位。

Azure Active Directory 域服务 (AD DS) 提供托管域服务,如域加入(domain join),group policy,轻量目录访问协议(lightweight directory access protocol,LDAP)等。无须在云端手动部署和管理domain controllers即可使用上述服务

创建托管域

Azure Portal上搜索并创建Azure AD Domain Services

注意事项

  • 内置域名(Built-in domain name) 以 .onmicrosoft.com 为后缀的内置域名
  • 自定义域名,指定自定义域名,通常是你已拥有且可路由的域名
  • 前缀限制 15字符
  • 网络名称冲突: 托管域的 DNS 域名不能已存在于虚拟网络中。
    确认创建后便开始漫长的等待部署的过程

    后续步骤

  • 更新虚拟网络DNS
  • “forest(林)”

SSDT Project

SQL Server Data Tools (SSDT) 通过引入跨 Visual Studio 内所有数据库开发阶段的无所不在的声明性模型,为数据库开发带来变革。创建一个数据库项目进行脱机的数据库开发(不直接对数据库服务进行在线修改),像编辑声明定义一样创建、编辑、重命名和删除表、存储过程、类型和函数。

为Visual Studio安装SSDT的features,加载sqlproj或dtproj项目(VS2010数据库和服务器项目、数据层应用程序项目),用所提供的方法build、publish所作的修改。

参考Microsoft Docs:面向项目的脱机数据库开发

创建visual studio sql project

参考SQL Server 数据库项目

publish

右键项目 选择Publish
注意 Scripts目录下的脚本也会执行,因此在这里编写初始数据是可行的
如果是初始化数据,为了防止脚本重复执行,可以按如下方式插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
DECLARE @CSDid uniqueidentifier
SELECT @CSDid=NEWID()
INSERT INTO [ent].[ScannerGroup]
([ID]
,[Name]
,[ParentID]
,[Type]
,[BrandID]
,[GroupLevel])
SELECT
@CSDid
,'CSD'
,null
,10
,null
,HierarchyID::GetRoot()
WHERE NOT EXISTS (SELECT 1 FROM [ent].[ScannerGroup])

INSERT INTO [ent].[PartnerAdmin]
([ID]
,[Email]
,[GroupID]
,[Name])
SELECT
NEWID()
,'qqqqq@qqqq.qqq'
,@CSDid
,'QQs'
WHERE NOT EXISTS (SELECT 1 FROM [ent].[PartnerAdmin])
GO

Predeployment Scripts & Postdeployment Scripts

Predeployment Scripts和Postdeployment Scripts分别在数据库项目生成的主要部署脚本之前和之后执行,在 Visual Studio 中,从架构比较结果更新目标时(Compare之后的Update),将不执行Predeployment Scripts。
一个项目只能有一个Predeployment Scripts和一个Postdeployment Scripts。

Scripts文件夹右键Add —> Script… 选择Pre Deployment Scripts或Post Deployment Scripts

issues

Only one statement is allowed per batch. A batch separator, such as ‘GO’, might be required between statements.

Scripts目录下的sql文件属性中,默认Build Action = Build导致编译失败,应改为Build Action = None

.jfm文件,可以认为是对项目操作的备份,若未自动添加到gitignore,则可手动添加

Cannot import the following key file: RightCheckDB.pfx. The key file may be password protected. To correct this, try to import the certificate again or manually install the certificate to the Strong Name CSP with the following key container name: VS_KEY_E7D8A7C85598CE59

pfx是保存SSL Certificate的一种文件格式,上述错误表示SQL项目需要重新认证凭据。
重新写入密码的方式

1
"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\sn.exe" -i companyname.pfx VS_KEY_3E185446540E7F7A

用MSBuild和Jenkins实现Continuse Integration

1
2
3
## set msbuild.exe=C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/MSBuild/15.0/Bin/MSBuild.exe
msbuild.exe /t:Build "MyDB.sqlproj"
msbuild.exe /t:Publish /p:SqlPublishProfilePath="MyDB.publish.staging.xml" "MyDB.sqlproj"

DevOps issues

  • 实现一键部署
  • 实现集成过程可配置
  • 自动化脚本提交到版本控制库
  • 利用版本控制回退数据库
  • 应用的更新的集成使用最新的数据库
  • pipeline执行测试

包如其名,是对openID connection协议的实现

包主要提供基于两个class的功能,UserManager和OidcClient。UserManager提供登录/登出,管理OIDC provider返回的用户信息(user claims),管理access token等,是oidc-client package的主要功能。OidcClient是更底层的协议实现,并被UserManager调用。

UserManager初始化

UserManager使用OIDC provider的相关配置进行初始化

1
2
3
4
5
6
7
8
9
10
11
readonly setting = {
client_id: "c5a333eb-fbbd-4643-b8fb-846e0c82ca03", // Required!
authority: "https://qqstudio.b2clogin.cn/qqstudio.onmicrosoft.com/B2C_1_pp_userflow/v2.0/", // 即{tenant}/{directory}{userflow} Required!
response_type: "id_token token", // string, default: 'id_token' Required!
scope: "openid" // Required!OIDC provider授权的scope
redirect_uri: "https://localhost:44362/signin-callback.html", // 返回的token将被链在这个url后面 Required!

silent_redirect_uri: "https://localhost:44362/silent-callback.html",
automaticSilentRenew: true
}
var _userManager = new UserManager(settings);

其他参数见oidc-client wiki

跳转到登录

当access_token缺失或过期,抑或由api发现失效而返回401时,可以调用UserManager的下述方法跳转到登录

1
2
3
4
5
this._userManager.signinRedirect({ state: location.href, prompt: "login" }).then(function () {
console.log('signinRedirect done');
}).catch(function (err) {
console.log(err);
});

方法支持传键值对作为参数,这些参数将拼接在登录地址的url parameter中形如&prompt=login,登录成功重定向回到app时可以取回这些参数,从而继续被登录中断的操作。

重定向回到App

在signin-callback.html页面的初始化方法中调用userManager的signinRedirectCallback,该方法从url的parameter取出access token以及前文所述的其他登录跳转参数(这些参数可以用回调函数取出处理)

1
2
3
4
5
6
7
this._userManager.signinRedirectCallback((user: User) => {
if (user && user.state) {
this.router.navigateByUrl(user.state).then(b => {
console.log("navigate to url", user.state, " return", b);
})
}
});

实际上callback url后带的是形如&state=b2983fd692b94528852297782ef93bbf的参数,推测此为指向原value的标记,原value存于localstorage

User

调用getUser方法取用户信息, 部分信息是从access token中解析得出,部分可能需要配置其他workflow比如profile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class User {
constructor(settings: UserSettings);

/** The id_token returned from the OIDC provider */
id_token: string;
/** The session state value returned from the OIDC provider (opaque) */
session_state?: string;
/** The access token returned from the OIDC provider. */
access_token: string;
/** Refresh token returned from the OIDC provider (if requested) */
refresh_token?: string;
/** The token_type returned from the OIDC provider */
token_type: string;
/** The scope returned from the OIDC provider */
scope: string;
/** The claims represented by a combination of the id_token and the user info endpoint */
profile: Profile;
/** The expires at returned from the OIDC provider */
expires_at: number;
/** The custom state transferred in the last signin */
state: any;

toStorageString(): string;
static fromStorageString(storageString: string): User;

/** Calculated number of seconds the access token has remaining */
readonly expires_in: number;
/** Calculated value indicating if the access token is expired */
readonly expired: boolean;
/** Array representing the parsed values from the scope */
readonly scopes: string[];
}

调用removeUser 清除localstorage中的登录信息 非常的方便~

知乎:什么是微服务

参考 微服务的演进
单体服务
单体服务
垂直分层架构
垂直分层架构

微服务是一种小型的SOA架构(Service Oriented Architecture 面向服务的架构),其理念是将业务系统彻底地组件化和服务化,形成多个可以独立开发、部署和维护的服务或者应用的集合,以应对更快的需求变更和更短的开发迭代周期。

拓展:传统SOA使用ESB(Enterprise Service Bus 企业服务总线)进行各业务系统间的通信

目的或优点:

  • 服务模块解耦
  • 团队分工更容易,更明确, 技术栈异构
  • 独立部署,可针对独立模块进行发布 更快的迭代
  • 扩展能力强 不至于牵一发动全身
    相应的缺点是服务划分的困扰,系统复杂化,实施部署、纠错的难度增大等

“无非是业务拆分和基架体系搭建”

SOA

Service Oriented Ambiguity 即面向服务架构

SOAP(web service) http+xml
REST http+json
RPC socket

oidc-client

oidc-client

Passport.js

Passport.js是Express.js的中间件,
Azure Samples中的node.js API active-directory-b2c-javascript-nodejs-webapi使用了这个包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const passport = require("passport");
const BearerStrategy = require('passport-azure-ad').BearerStrategy;
const bearerStrategy = new BearerStrategy(config,
function (token, done) {
// Send user info using the second argument
done(null, {}, token);
}
);
const app = express();
app.use(passport.initialize());
passport.use(bearerStrategy);

// API endpoint
app.get("/hello",
passport.authenticate('oauth-bearer', {session: false}),
(req, res) => {
console.log('User info: ', req.user);
console.log('Validated claims: ', req.authInfo);

if ('scp' in req.authInfo && req.authInfo['scp'].split(" ").indexOf("demo.read") >= 0) {
// Service relies on the name claim.
res.status(200).json({'name': req.authInfo['name']});
} else {
console.log("Invalid Scope, 403");
res.status(403).json({'error': 'insufficient_scope'});
}
}
);

栗子中该中间件将认证失败的请求拦截住并返回401, 省略了web api授权失败时的重定向配置,私以为可以配置signin重定向,使得可以从浏览器访问api

MSAL.net

Microsoft Authentication Library(微软身份认证库MSAL),在ASP和SPA一文中有引用。

The Microsoft Authentication Library for JavaScript enables client-side JavaScript web applications, running in a web browser, to authenticate users using Azure AD. MSAL.js用以浏览器中运行的js web 使用Azure AD认证

上述是基于msal.js的JavaScript Packages, 对于客户端应用,微软提供.net framework运行时环境的一套SDK:

MSAL for .NET, UWP, NetCore, MAUI, Xamarin Android and iOS

The MSAL library for .NET is part of the Microsoft identity platform for developers (formerly named Azure AD) v2.0. It enables you to acquire security tokens to call protected APIs. It uses industry standard OAuth2 and OpenID Connect. The library also supports Azure AD B2C.

官方文档

我们关注使用WPF客户端打开登录ADB2C认证页面的应用场景
MSAL.NET使用Web浏览器

MSAL.NET 是一个多框架库,它具有特定于框架的代码,可在 UI 控件中托管浏览器(例如,在 .NET Classic 中,它使用 WinForms;在 .NET 5.0+ 中,它使用 WebView2;在 Xamarin 中,它使用本机移动控件,等等)。 此控件称为 embedded Web UI。 另外,MSAL.NET 还能够启动系统 OS 浏览器。

1
Install-Package Microsoft.Identity.Client -Pre
1
2
3
4
5
6
7
using Microsoft.Identity.Client;
...

IPublicClientApplication publicClientApp = PublicClientApplicationBuilder.Create(ClientId)
.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
.WithAuthority(AzureCloudInstance.AzurePublic, Tenant)
.Build();

使用自定义交互界面, 调用 WithCustomWebUi() 方法 传入自定义页面的实例,自定义页面类需要实现ICustomWebUi接口,接口定义了异步方法 AcquireAuthorizationCodeAsync,该方法参数

  • authorizationUri Uri
    URI computed by MSAL.NET that will let the UI extension navigate to the STS authorization endpoint in order to sign-in the user and have them consent

  • redirectUri Uri The redirect URI that was configured. The auth code will be appended to this redirect URI and the browser will redirect to it.