Ky kjo artikull është krejtësisht praktik dhe i dedikohet historisë sime të trishtë
Duke u përgatitur për Zero Touch PROD për RDS (MS SQL), për të cilin na kanë thënë vazhdimisht, bëra një prezantim (POC — Provë e Konceptit) të automatizimit: një set skenarësh powershell. Pas prezantimit, kur u qetësuan duarthithjet e zhurmshme, duke kaluar në brohoritje të pandërprera, më thanë — gjithçka është në rregull, por për arsye ideologjike të gjithë Jenkins slaves punojnë nën Linux!
A është e mundur? Të marrësh një DBA të tillë të ngrohtë nga Windows dhe ta fusësh në ferrin e powershell nën Linux? A nuk është kjo e egër?

Duhet të zhytsha në këtë kombinim të çuditshëm teknologjish. Natyrisht, të gjitha 30+ skenarët e mi u ndalën së funksionuari. Për habinë time, për një ditë të vetme pune, arrita të gjithçka ta rregulloja. Po shkruaj në gjurmët e nxehura. Pra, cilat pengesa mund të gjeni gjatë transferimit të skenarëve powershell nga Windows në Linux?
sqlcmd vs Invoke-SqlCmd
Të kujtoj ndryshimin kryesor mes tyre. Utilitari i vjetër sqlcmd punon gjithashtu nën Linux, me funksionalitet praktikisht identik. Pyetja për ekzekutim jepet me -Q, skedari i hyrjes si -i, dhe kyçi -o. Megjithatë, emrat e skedarëve, natyrisht, janë të ndjeshëm ndaj rastit. Nëse po përdorni -i, atëherë në fund të skedarit shkruani:
GO
EXITNëse në fund nuk ka EXIT, sqlcmd do të kalojë në pritje të hyrjes, dhe nëse para EXIT nuk ka GO, atëherë komanda e fundit nuk do të funksionojë. Në skedarin e daljes shkruhen të gjitha daljet, selects, mesazhe, print etj.
Invoke-SqlCmd jep rezultatet në formën e DataSet, DataTables ose DataRows. Prandaj, nëse mund të përpunoni rezultatin e një select të thjeshtë nëpërmjet sqlcmd, duke shqyrtuar daljen e tij, është praktikisht e pamundur të nxirrni diçka të komplikuar: për këtë ka Invoke-SqlCmd. Por kjo komandë ka edhe hile të sajat:
- Nëse i jepni një skedar përmes -InputFile, nuk është e nevojshme, madje, ajo jep një gabim sintaksor EXIT -OutputFile
- jo, komanda ju kthen rezultatin në formën e një objekti Për të specifikuar serverin ka dy sintaksa:
- -ServerInstance -Username -Password -Database -ConnectionString dhe përmes . Siç duket, në rastin e parë nuk mund të specifikoni një port që është ndryshe nga 1433.dalja tekstuale, si PRINT, e cila kapet lehtësisht
- , për sqlcmdparaqet një problem Invoke-SqlCmd
- probabilisht në Linux tuaj ky cmdlet nuk ekziston!
u bë i disponueshëm për platformat që nuk janë Windows Zëvendësimi i variablave
Подстановка переменных
Në sqlcmd ka zëvendësime variable me anë të -v, për shembull, kështu:
# $conn содержит начало команды sqlcmd
$cmd = $conn + " -i D:appsSlaveJobsKillSpid.sql -o killspid.res
-v spid =`"" + $spid + "`" -v age =`"" + $age + "`""
Invoke-Expression $cmdNë skriptin SQL ne përdorim zëvendësime:
set @spid=$(spid)
set @age=$(age)Kështu që. Në *nix zëvendësimet e variablave nuk funksionojnë. Parametri -v inxhorsohet. Në Invoke-SqlCmd inxhorsohet -Variablat. Edhe pse parametri që përcakton vetë variablat inxhorsohet, zëvendësimet funksionojnë — mund të përdorni çdo variabël nga Shell. Megjithatë, unë u mërzita me variablat dhe vendosa që të mos varem nga to fare, dhe e bëra në mënyrë primitive, e mira që skriptet SQL janë të shkurtra:
# prepend the parameters
"declare @age int, @spid int" | Add-Content "q.sql"
"set @spid=" + $spid | Add-Content "q.sql"
"set @age=" + $age | Add-Content "q.sql"
foreach ($line in Get-Content "Sqlserver/Automation/KillSpid.sql") {
$line | Add-Content "q.sql"
}
$cmd = "/opt/mssql-tools/bin/" + $conn + " -i q.sql -o res.log"Kjo, siç e kuptuat, është një test nga versioni Unix.
Ngarkimi i skedave
Në versionin Windows çdo operacion ishte i shoqëruar me auditimin: ekzekutuam sqlcmd, morëm ndonjë sharje në file-in e output-it, e bashkëngjitëm atë file në tavolinën e auditit. Më vjen mirë që SQL server punon në të njëjtin server me Jenkins, kjo bëhej më pak siç vijon:
CREATE procedure AuditUpload
@id int, @filename varchar(256)
as
set nocount on
declare @sql varchar(max)
CREATE TABLE #multi (filer NVARCHAR(MAX))
set @sql='BULK INSERT #multi FROM '''+@filename
+''' WITH (ROWTERMINATOR = '' '',CODEPAGE = ''ACP'')'
exec (@sql)
select @sql=filer from #multi
update JenkinsAudit set multiliner=@sql where ID=@id
returnKështu që ne e thithim file-in BCP plotësisht, dhe e futim në fushën nvarchar(max) të tavolinës së auditi. Natyrisht, e gjithë kjo sistem ra, pasi në vend të SQL server, morëm RDS, dhe BULK INSERT nuk punon fare për UNC për shkak të përpjekjes për të marrë një lok ekskluziv mbi file-in, dhe ndihmës me RDS është në fillim i destinuar për të dështuar. Prandaj, vendosa të ndryshoj dizajnin e sistemit, duke mbajtur auditin rresht pas rreshti:
CREATE TABLE AuditOut (
ID int NULL,
TextLine nvarchar(max) NULL,
n int IDENTITY(1,1) PRIMARY KEY
)Dhe të shkruaj në këtë tavolinë kështu:
function WriteAudit([string]$Filename, [string]$ConnStr,
[string]$Tabname, [string]$Jobname)
{
# merr $lastid e ekzekutimit të fundit -- e hequr për artikull
#krijo grid dhe e mbush atë me të dhëna nga skedari
$audit = Get-Content $Filename
$DT = new-object Data.DataTable
$COL1 = new-object Data.DataColumn;
$COL1.ColumnName = "ID";
$COL1.DataType = [System.Type]::GetType("System.Int32")
$COL2 = new-object Data.DataColumn;
$COL2.ColumnName = "TextLine";
$COL2.DataType = [System.Type]::GetType("System.String")
$DT.Columns.Add($COL1)
$DT.Columns.Add($COL2)
foreach ($line in $audit)
{
$DR = $dt.NewRow()
$DR.Item("ID") = $lastid
$DR.Item("TextLine") = $line
$DT.Rows.Add($DR)
}
# shkruaj në tabelë
$conn=new-object System.Data.SqlClient.SQLConnection
$conn.ConnectionString = $ConnStr
$conn.Open()
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $ConnStr
$bulkCopy.DestinationTableName = $Tabname
$bulkCopy.BatchSize = 50000
$bulkCopy.BulkCopyTimeout = 0
$bulkCopy.WriteToServer($DT)
$conn.Close()
}
Për të përzgjedhur përmbajtjen duhet bërë select sipas ID-së, duke përzgjedhur në rendin n (identiteti).
Në artikullin e ardhshëm, do të ndalem më thellësisht se si ndërveprojnë të gjitha këto me Jenkins.
Burimi: habr.com
