Linux์˜ Powershell์—์„œ MS SQL ์ž‘์—…

์ด ๊ธฐ์‚ฌ๋Š” ์ˆœ์ „ํžˆ ์‹ค์šฉ์ ์ด๋ฉฐ ๋‚ด ์Šฌํ”ˆ ์ด์•ผ๊ธฐ์— ์ „๋…ํ•ฉ๋‹ˆ๋‹ค

์ค€๋น„ํ•˜๊ธฐ ์ œ๋กœํ„ฐ์น˜ํ”„๋กœ๋“œ ์šฐ๋ฆฌ ๋ชจ๋‘๊ฐ€ ์›…์„ฑ๊ฑฐ๋ฆฌ๊ณ  ์žˆ๋Š” RDS(MS SQL)์— ๋Œ€ํ•ด ์ž๋™ํ™”์— ๋Œ€ํ•œ ํ”„๋ ˆ์  ํ…Œ์ด์…˜(POC - Proof Of Concept), ์ฆ‰ ์ผ๋ จ์˜ Powershell ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ ˆ์  ํ…Œ์ด์…˜์ด ๋๋‚œ ํ›„ ํญํ’์šฐ๊ฐ€ ์น˜๋Š” ๊ธด ๋ฐ•์ˆ˜๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ๋Š์ž„์—†๋Š” ๋ฐ•์ˆ˜๋กœ ๋ณ€ํ–ˆ์„ ๋•Œ ๊ทธ๋“ค์€ ๋‚˜์—๊ฒŒ ๋งํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋“  ๊ฒƒ์ด ์ข‹์ง€๋งŒ ์ด๋…์  ์ด์œ ๋กœ ๋งŒ ๋ชจ๋“  Jenkins ๋…ธ์˜ˆ๊ฐ€ Linux์—์„œ ์ž‘์—…ํ•ฉ๋‹ˆ๋‹ค!

์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•œ๊ฐ€? Windows์—์„œ ๋”ฐ๋œปํ•œ ๋žจํ”„ DBA๋ฅผ ๊ฐ€์ ธ์™€ Linux์—์„œ Powershell์˜ ์—ด๊ธฐ์— ๋ถ™์ด์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์ด๊ฑด ์ž”์ธํ•˜์ง€ ์•Š๋‚˜์š”?

Linux์˜ Powershell์—์„œ MS SQL ์ž‘์—…
๋‚˜๋Š” ์ด ์ด์ƒํ•œ ๊ธฐ์ˆ ์˜ ์กฐํ•ฉ์— ๋ชฐ์ž…ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  30๊ฐœ ์ด์ƒ์˜ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋ชจ๋‘ ์ž‘๋™์„ ๋ฉˆ์ท„์Šต๋‹ˆ๋‹ค. ๋†€๋ž๊ฒŒ๋„ ๋‚˜๋Š” ๊ทผ๋ฌด์ผ ๊ธฐ์ค€์œผ๋กœ ํ•˜๋ฃจ ๋งŒ์— ๋ชจ๋“  ๊ฒƒ์„ ๊ณ ์น  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋œจ๊ฑฐ์šด ์ถ”๊ตฌ์— ๊ธ€์„ ์“ฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด Powershell ์Šคํฌ๋ฆฝํŠธ๋ฅผ Windows์—์„œ Linux๋กœ ์ „์†กํ•  ๋•Œ ์–ด๋–ค ํ•จ์ •์— ์ง๋ฉดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

sqlcmd ๋Œ€ Invoke-SqlCmd

๊ทธ๋“ค ์‚ฌ์ด์˜ ์ฃผ์š” ์ฐจ์ด์ ์„ ์ƒ๊ธฐ์‹œ์ผœ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ข‹์€ ์˜ค๋ž˜๋œ ์œ ํ‹ธ๋ฆฌํ‹ฐ SQLcmd ๋˜ํ•œ ๊ฑฐ์˜ ๋™์ผํ•œ ๊ธฐ๋Šฅ์œผ๋กœ Linux์—์„œ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. -Q๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์ฟผ๋ฆฌ๋ฅผ ์ „๋‹ฌํ•˜๊ณ , ์ž…๋ ฅ ํŒŒ์ผ์„ -i๋กœ, ์ถœ๋ ฅ์„ -o๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํŒŒ์ผ ์ด๋ฆ„์€ ๋ฌผ๋ก  ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. -i๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ํŒŒ์ผ ๋์— ๋‹ค์Œ์„ ์”๋‹ˆ๋‹ค.

GO
EXIT

๋์— EXIT๊ฐ€ ์—†์œผ๋ฉด sqlcmd๋Š” ๊ณ„์†ํ•ด์„œ ์ž…๋ ฅ์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. EXIT ~ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค GO, ๋งˆ์ง€๋ง‰ ๋ช…๋ น์€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ถœ๋ ฅ ํŒŒ์ผ์—๋Š” ๋ชจ๋“  ์ถœ๋ ฅ, ์„ ํƒ, ๋ฉ”์‹œ์ง€, ์ธ์‡„ ๋“ฑ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

Invoke-SqlCmd๋Š” ๊ฒฐ๊ณผ๋ฅผ DataSet, DataTables ๋˜๋Š” DataRows๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‹จ์ˆœ ์„ ํƒ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. SQLcmd, ์ถœ๋ ฅ์„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•œ ํ›„์—๋Š” ๋ณต์žกํ•œ ๊ฒƒ์„ ํŒŒ์ƒํ•˜๋Š” ๊ฒƒ์ด ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ˜ธ์ถœ-SqlCmd. ํ•˜์ง€๋งŒ ์ด ํŒ€์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋†๋‹ด๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋‹ค์Œ์„ ํ†ตํ•ด ๊ทธ๋…€์—๊ฒŒ ํŒŒ์ผ์„ ์ „์†กํ•˜๋Š” ๊ฒฝ์šฐ -์ž…๋ ฅ ํŒŒ์ผ, ๊ทธ๋Ÿฐ ๋‹ค์Œ EXIT ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋˜ํ•œ ๊ตฌ๋ฌธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • -๊ฒฐ๊ณผ๋ฌผ ํŒŒ์ผ ์•„๋‹ˆ์š”, ๋ช…๋ น์€ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐ๋Š” ๋‘ ๊ฐ€์ง€ ๊ตฌ๋ฌธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. -ServerInstance -์‚ฌ์šฉ์ž ์ด๋ฆ„ -์•”ํ˜ธ -๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ทธ๋ฆฌ๊ณ  ํ†ตํ•ด -์—ฐ๊ฒฐ ๋ฌธ์ž์—ด. ์ด์ƒํ•˜๊ฒŒ๋„ ์ฒซ ๋ฒˆ์งธ ๊ฒฝ์šฐ์—๋Š” 1433 ์ด์™ธ์˜ ํฌํŠธ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ํ…์ŠคํŠธ ์ถœ๋ ฅ์— PRINT๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๊ฐ„๋‹จํžˆ "์žกํžˆ๊ฒŒ" ๋ฉ๋‹ˆ๋‹ค. SQLcmd~์„ ์œ„ํ•ด ํ˜ธ์ถœ-SqlCmd ๋ฌธ์ œ์ด๋‹ค
  • ๊ทธ๋ฆฌ๊ณ  ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ : Linux์— ์ด cmdlet์ด ์—†์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค!

๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์ด ์ฃผ์š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. XNUMX์›”์—๋งŒ ์ด cmdlet Windows ์ด์™ธ์˜ ํ”Œ๋žซํผ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค., ๊ทธ๋ฆฌ๊ณ  ๋งˆ์นจ๋‚ด ์šฐ๋ฆฌ๋Š” ์•ž์œผ๋กœ ๋‚˜์•„๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

๋ณ€์ˆ˜ ๋Œ€์ฒด

sqlcmd์—๋Š” -v๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€์ˆ˜ ๋Œ€์ฒด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

# $conn ัะพะดะตั€ะถะธั‚ ะฝะฐั‡ะฐะปะพ ะบะพะผะฐะฝะดั‹ sqlcmd
$cmd = $conn + " -i D:appsSlaveJobsKillSpid.sql -o killspid.res 
  -v spid =`"" + $spid + "`" -v age =`"" + $age + "`""
Invoke-Expression $cmd

SQL ์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ๋Œ€์ฒด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

set @spid=$(spid)
set @age=$(age)

์—ฌ๊ธฐ ์žˆ์Šต๋‹ˆ๋‹ค. *nix์—์„œ๋Š” ๋ณ€์ˆ˜ ๋Œ€์ฒด๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ์ˆ˜ -v ๋ฌด์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์œ  ํ˜ธ์ถœ-SqlCmd ๋ฌด์‹œ๋จ -๋ณ€์ˆ˜. ๋ณ€์ˆ˜ ์ž์ฒด๋ฅผ ์ง€์ •ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ๋ฌด์‹œ๋˜์ง€๋งŒ ๋Œ€์ฒด ์ž์ฒด๋Š” ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. Shell์˜ ๋ชจ๋“  ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ๋ณ€์ˆ˜์— ๊ธฐ๋ถ„์ด ์ƒํ–ˆ๊ณ  ๋ณ€์ˆ˜์— ์ „ํ˜€ ์˜์กดํ•˜์ง€ ์•Š๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์œผ๋ฉฐ SQL ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์งง๊ธฐ ๋•Œ๋ฌธ์— ๋ฌด๋ก€ํ•˜๊ณ  ์›์‹œ์ ์œผ๋กœ ํ–‰๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

# 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"

์•„์‹œ๋‹ค์‹œํ”ผ ์ด๊ฒƒ์€ ์ด๋ฏธ Unix ๋ฒ„์ „์˜ ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.

ํŒŒ์ผ ์—…๋กœ๋“œ

Windows ๋ฒ„์ „์—์„œ๋Š” ๋ชจ๋“  ์ž‘์—…์— ๊ฐ์‚ฌ๊ฐ€ ์ˆ˜๋ฐ˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค. sqlcmd๋ฅผ ์‹คํ–‰ํ–ˆ๊ณ  ์ถœ๋ ฅ ํŒŒ์ผ์—์„œ ์ผ์ข…์˜ ๋‚จ์šฉ์„ ๋ฐ›์•˜๊ณ  ์ด ํŒŒ์ผ์„ ๊ฐ์‚ฌ ํ”Œ๋ ˆ์ดํŠธ์— ์ฒจ๋ถ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„ SQL ์„œ๋ฒ„๋Š” Jenkins์™€ ๋™์ผํ•œ ์„œ๋ฒ„์—์„œ ์ž‘๋™ํ–ˆ์œผ๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜ํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

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
  return

๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” BCP ํŒŒ์ผ ์ „์ฒด๋ฅผ ์‚ผ์ผœ ๊ฐ์‚ฌ ํ…Œ์ด๋ธ”์˜ nvarchar(max) ํ•„๋“œ์— ๋ฐ€์–ด ๋„ฃ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก , ์ด ์ „์ฒด ์‹œ์Šคํ…œ์€ ๋ฌด๋„ˆ์กŒ์Šต๋‹ˆ๋‹ค. SQL ์„œ๋ฒ„ ๋Œ€์‹  RDS๋ฅผ ์–ป์—ˆ๊ณ  ํŒŒ์ผ์— ๋Œ€ํ•œ ๋ฐฐํƒ€์  ์ž ๊ธˆ์„ ์‹œ๋„ํ•˜๊ธฐ ๋•Œ๋ฌธ์— UNC๋ฅผ ํ†ตํ•ด BULK INSERT๊ฐ€ ์ „ํ˜€ ์ž‘๋™ํ•˜์ง€ ์•Š๊ณ  RDS๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ผ๋ฐ˜์ ์œผ๋กœ ํŒŒ๋ฉธ๋ฉ๋‹ˆ๋‹ค. ๋งจ ์ฒ˜์Œ. ๊ทธ๋ž˜์„œ ์ €๋Š” ์‹œ์Šคํ…œ ์„ค๊ณ„๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ๊ฐ์‚ฌ๋ฅผ ํ•œ ์ค„์”ฉ ์ €์žฅํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

CREATE TABLE AuditOut (
  ID int NULL,
  TextLine nvarchar(max) NULL,
  n int IDENTITY(1,1) PRIMARY KEY
  )

๊ทธ๋ฆฌ๊ณ  ์ด ํ‘œ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜์„ธ์š”.

function WriteAudit([string]$Filename, [string]$ConnStr, 
     [string]$Tabname, [string]$Jobname)
{
  # get $lastid of the last execution  -- ะฟั€ะพัะบะธะฟะฐะฝะพ ะดะปั ัั‚ะฐั‚ัŒะธ
	
  #create grid and populate it with data from file
  $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)   
    } 

  # write it to table
  $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() 
  }  

์ฝ˜ํ…์ธ ๋ฅผ ์„ ํƒํ•˜๋ ค๋ฉด ID๋ณ„๋กœ ์„ ํƒํ•ด์•ผ ํ•˜๋ฉฐ, n(ID) ์ˆœ์œผ๋กœ ์„ ํƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๊ธฐ์‚ฌ์—์„œ๋Š” ์ด ๋ชจ๋“  ๊ฒƒ์ด Jenkins์™€ ์–ด๋–ป๊ฒŒ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋Š”์ง€ ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€