%OUTPUT_GAME% : 젠킨스를 이용하여 빌드된 결과 파일과 배포에 필요한 파일이 있는 경로
아래 내용은 젠킨스에서 Execute Windows batch command 항목의 내용입니다.
:: GAME 압축 및 S3 업로드 작업
:: %DEPLOY_GAME%
:: %OUTPUT_GAME%
:: 압축할 파일 이름에 넣을 날짜, 시간를 설정합니다.
for /f "tokens=1,2 delims=:" %%a in ("%time%") do set hh=%%a&set mm=%%b
SET hh=%hh: =0%
:: 압축 파일 이름입니다.
SET FILENAME=Super_Game_%date:~0,4%%date:~5,2%%date:~8,2%_%hh%%mm%
:: 압축 파일 풀 경로
SET ZIPFILE=%DEPLOY_GAME%\%FILENAME%.zip
if not exist %DEPLOY_GAME%\NUL (
md %DEPLOY_GAME%
)
:: 배포용 파일 압축
D:\Jenkins\script\7za.exe a %ZIPFILE% %OUTPUT_GAME%\*.* -r -x!*.pdb
:: 버전 파일 생성
SET BUILD_INFO=%DEPLOY_GAME%\build_game.xml
@ECHO Create build_game.xml.
@ECHO ^<?xml version="1.0" encoding="utf-8"?^> > %BUILD_INFO%
@ECHO ^<File^>%FILENAME%^</File^> >> %BUILD_INFO%
cd /D %DEPLOY_GAME%
:: 생성된 파일 S3 업로드
PowerShell.exe -executionpolicy remotesigned -Command D:\Jenkins\script\upload_game_s3.ps1 -file %ZIPFILE% -path %DEPLOY_GAME% -mode "qa"
배포를 위한 S3 폴더 구성
S3 버킷의 폴더 구성은 아래의 이미지와 같습니다.
/build 이하에 각 서버별 배포 파일을 보관 할 수 있는 폴더와, 각 서버별 버전 xml 파일로 구성 되어 집니다.
Rundeck Node(Windows) 폴더 구성
배포를 위해서는 각 노드들의 경로를 통일 시켜야 합니다. Rundeck은 각노드들의 일괄 명령 처리로 동작을 하기 때문에 서버 배포 위치를 아래의 그림과 같이 드라이버 폴더 구성을 통일 시켜야 합니다.
/script : 배포에 필요한 파일들이 위치합니다. 배포 스크립트, 압축 파일등
/work : 배포 스크립트에서 사용되며, 배포 파일을 임시로 다운로드 및 압축, 백업 등에 사용됩니다.
/Gnss_Game : 게임 서버 배포가 되는 위치 입니다. 각 종 설정 파일과 게임에 필요한 파일들이 위치하며, 배포 스크립트에서 경로를 설정하면 자동 생성 됩니다.
/Gnss_Auth : 인증 서버 배포 되는 위치입니다. 게임서버와 동일하게 필요 파일들이 위치하며, 역시 스크립트에서 자동 생성됩니다.
배포를 위한 스크립트 구성
deploy_build_game.ps1 파일의 내용입니다. 아래의 스크립트를. [script] 폴더에 생성한 후에 필요 항목들을 수정 하시면 됩니다.
param([Parameter(Mandatory=$false)][string]$mode="qa")$ErrorActionPreference="Stop"$DEBUG_MODE=1# 1. Argument set #$GAME_BUILDZONE=$mode$GAME_BUILDTYPE="Super_Game"# S3 bucket set #$AWS_S3_ADDR="s3://super-build"$AWS_S3_REGION="us-east-1"# 2. define variable #write-output"."write-output"[INFO][1] Define Variable..."$TIME_CUR=Get-Date-UFormat'%Y-%m-%d %H:%M:%S'write-output"[INFO] Start TIME : $TIME_CUR"write-output"."# 2-1. get hostname #$HOSTNAME=hostname# 2-2. get date / custom type #$DATE_CUR=Get-Date-UFormat'%Y%m%d-%H%M'$TIME_CUR=Get-Date-UFormat'%Y-%m-%d %H:%M:%S'# 2-3. default PATH #$DEF_FILE_INFO="build_game.xml"$DEF_FILE_INFO_SAMPLE="build_game_sample.xml"$DEF_PATH_BASE="super"$DEF_PATH_SCRIPT="script"#$DEF_PATH_SERVER = $GAME_BUILDTYPE$DEF_PATH_BACKUP=Join-Path"work""backup"$DEF_PATH_WORK=Join-Path"work""download"$DEF_PATH_TMP=join-path"work""tmp"$DriveLetter=$PSCommandPath[0]$PATH_BASE=$DriveLetter+":\"+$DEF_PATH_BASE$PATH_SCRIPT=join-path$PATH_BASE$DEF_PATH_SCRIPT$PATH_SERVER=join-path$PATH_BASE$GAME_BUILDTYPE$PATH_SERVER_CONF=join-path$PATH_SERVER"conf"$PATH_CONF=join-path$PATH_BASE"conf_game"$PATH_BACKUP=join-path$PATH_BASE$DEF_PATH_BACKUP$PATH_WORK=join-path$PATH_BASE$DEF_PATH_WORK$PATH_WORK_SERVER=join-path$PATH_WORK$GAME_BUILDTYPE$PATH_WORK_SERVER_CONF=$PATH_WORK_SERVER$PATH_TMP=join-path$PATH_BASE$DEF_PATH_TMP$FILE_BUILD_INFO=join-path$PATH_SERVER$DEF_FILE_INFOif($DEBUG_MODE=1){write-output"[DEBUG] FILE_BUILD_INFO : $FILE_BUILD_INFO"}$FILE_BUILD_INFO_SAMPLE=join-path$PATH_SCRIPT$DEF_FILE_INFO_SAMPLEif($DEBUG_MODE=1){write-output"[DEBUG] FILE_BUILD_INFO_SAMPLE : $FILE_BUILD_INFO_SAMPLE"}$FILE_BUILD_INFO_TMP=join-path$PATH_WORK_SERVER$DEF_FILE_INFOif($DEBUG_MODE=1){write-output"[DEBUG] FILE_BUILD_INFO_TMP : $FILE_BUILD_INFO_TMP"}$PATH_S3_BUILD="$AWS_S3_ADDR/"+"$GAME_BUILDZONE/"+"build/"$PATH_S3_BUILD_CONF="$AWS_S3_ADDR/"+"$GAME_BUILDZONE/"+"conf/"$PATH_S3_SERVER="$AWS_S3_ADDR/"+"$GAME_BUILDZONE/"+"build/"+"$GAME_BUILDTYPE/"$PATH_S3_SERVER_CONF="$AWS_S3_ADDR/"+"$GAME_BUILDZONE/"+"conf/"+"$GAME_BUILDTYPE/"$FILE_S3_BUILD_INFO="$AWS_S3_ADDR/"+"$GAME_BUILDZONE/"+"build/"+"$DEF_FILE_INFO"if(!(Test-Path-Path$PATH_SERVER)){mkdir$PATH_SERVER}if(!(Test-Path-Path$PATH_BACKUP)){mkdir$PATH_BACKUP}if(!(Test-Path-Path$PATH_WORK)){mkdir$PATH_WORK}if(!(Test-Path-Path$PATH_WORK_SERVER)){mkdir$PATH_WORK_SERVER}if(!(Test-Path-Path$PATH_TMP)){mkdir$PATH_TMP}if(Test-Path$FILE_BUILD_INFO_TMP){remove-Item-force$FILE_BUILD_INFO_TMPwrite-output"[INFO] Completed for del to buildinfo.xml Temporary File"}if($DEBUG_MODE=1){write-output"[DEBUG] DEBUG MODE : On"if(Test-Path$FILE_BUILD_INFO){#remove-Item -force $FILE_BUILD_INFO}write-output"[DEBUG] Completed for del to buildinfo.xml File"}# 2. Download & Check for buildinfo.xml #write-output"."write-output"[INFO] BUILD Version Downloading & Checking Version..."write-output"."if(!(Test-Path$FILE_BUILD_INFO)){write-output"[INFO] File not found buildinfo.xml for Current BUILD"write-output"[INFO] Copy from Sample buildinfo.xml file"if(!(Test-Path-Path$PATH_SERVER)){mkdir$PATH_SERVER}copy-item$FILE_BUILD_INFO_SAMPLE$FILE_BUILD_INFO}if(!(Test-Path$FILE_BUILD_INFO_TMP)){write-output"[INFO] Download from buildinfo.xml for NEW BUILD"awss3cp--region$AWS_S3_REGION$FILE_S3_BUILD_INFO$FILE_BUILD_INFO_TMP>$nullif($DEBUG_MODE=1){write-output"[DEBUG] aws s3 cp --region $AWS_S3_REGION$FILE_S3_BUILD_INFO$FILE_BUILD_INFO_TMP"}}[xml]$xml_cur_buildinfo=get-content$FILE_BUILD_INFO$cur_ver=$xml_cur_buildinfo.File[xml]$xml_new_buildinfo=get-content$FILE_BUILD_INFO_TMP$new_ver=$xml_new_buildinfo.Filewrite-output"[INFO] +BUILD (Current Ver) = $cur_ver"write-output"[INFO] +BUILD (New Ver) = $new_ver"# -le means <=if($cur_ver-ne$new_ver){Write-output"[INFO] Deploy start"$PATH_WORK_UNZIP=join-path$PATH_WORK_SERVER$new_verif(!(Test-Path-Path$PATH_WORK_UNZIP)){mkdir$PATH_WORK_UNZIP}#Download (BUILD)write-output"."write-output"[INFO] Downloading Build..."write-output"."# "GameServer-2016110300".zip/.tar.gz/$FILENAME_BUILD="$new_ver"+".zip"if($DEBUG_MODE=1){write-output"[DEBUG] FILENAME_BUILD : $FILENAME_BUILD"}$FILE_BUILD_WORK=join-path$PATH_WORK$FILENAME_BUILDif($DEBUG_MODE=1){write-output"[DEBUG] FILE_BUILD_WORK : $FILE_BUILD_WORK"}$FILE_S3_BUILD="$PATH_S3_SERVER"+"$FILENAME_BUILD"if($DEBUG_MODE=1){write-output"[DEBUG] FILE_S3_BUILD : $FILE_S3_BUILD"}#downloadwrite-output"[INFO] Download to $FILE_S3_BUILD"awss3cp--region$AWS_S3_REGION$FILE_S3_BUILD$PATH_WORK>$nullif($DEBUG_MODE=1){write-output"[DEBUG] aws s3 cp --region $AWS_S3_REGION$FILE_S3_BUILD$PATH_WORK"}$exe_zip="$PATH_SCRIPT\unzip.exe"#uncompress#$exe_zip -x $FILE_BUILD_WORK $PATH_WORK_SERVERInvoke-Expression"& `"$exe_zip`" -o $FILE_BUILD_WORK -d $PATH_WORK_UNZIP"#Download (CONF)write-output"."write-output"[INFO] Downloading Build Configuration..."write-output"."$TARGET_PATH=join-path$PATH_WORK_UNZIP"."xcopy/E/Y$PATH_CONF\.$TARGET_PATH#aws s3 cp --region $AWS_S3_REGION --recursive $PATH_S3_SERVER_CONF $TARGET_PATH > $nullif($DEBUG_MODE=1){write-output"[DEBUG] aws s3 cp --region $AWS_S3_REGION --recursive $PATH_S3_SERVER_CONF$TARGET_PATH"}#Deploywrite-output"."write-output"[INFO] Deploying build..."write-output"."copy-item-recurse$PATH_WORK_UNZIP$PATH_SERVERwrite-output"[INFO] deploy source complted"copy-item-force$FILE_BUILD_INFO_TMP$PATH_SERVERwrite-output"[INFO] deploy buildinfo.xml complted"#cleanwrite-output"."write-output"[INFO] Cleaning Work Build..."write-output"."if(Test-Path$FILE_BUILD_INFO_TMP){remove-item-force$FILE_BUILD_INFO_TMP}if(Test-Path-Path$PATH_WORK_SERVER){remove-item-recurse-force-path$PATH_WORK_SERVER}$TIME_CUR=Get-Date-UFormat'%Y-%m-%d %H:%M:%S'write-output"[INFO] END TIME : $TIME_CUR"Write-output"[INFO] Deploy done."}else{Write-output"[INFO] Current BUILD is same or high version. Deploy cancel!!"$TIME_CUR=Get-Date-UFormat'%Y-%m-%d %H:%M:%S'write-output"[INFO] END TIME : $TIME_CUR"}
Rundeck Job 설정하기
기본적으로 배포를 위한 rundeck 프로젝트는 설정되어 있는 상태를 가정합니다.
프로젝트의 화면에서 [Create Job] 을 클릭하여, 신규 Job 을 생성합니다.
Job Name : 작업의 간략한 이름을 설정 합니다.
Description : 작업에 대한 처리 내용을 간략히 작성하여, 다른 작업자 분들이 해당 작업이 어떤 내용들을 수행하는지 작성해주시면 됩니다.
Group : 작업을을 한 그룹으로 묶어서 화면에 표시 되게 됩니다. 게임 관련 작업이면, GAME 등으로 식별이 가능한 이름을 작성하시면 됩니다.
해당 노드들에게 실행할 커맨드를 입력을 하기 위해서 [Command] 버튼을 클릭합니다.
Command : 실행할 Windows Command 명령을 입력 하시면 됩니다. 우리는 배포를 위해서 작성해 놓은 파워쉘 스크립트 파일명을 입력 하시면됩니다.
Node Filter : 위의 명령을 수행할 노드들을 선택합니다. Rundeck 노드 설정에서 입력 한 값을 이용하여, 배포할 노드들을 선택 할수 있습니다.
Matched Nodes : 입력된 필터에 맞는 결과 노드들이 표시 되며, 작업을 수행시 이 노드들에 대해서 수행을 하게 됩니다.
기본적인 입력항목은 작성이 되었습니다. 추가 적으로 작업의 성공 실패 여부를 Dooray 등으로 출력을 위해서는.
Send Notification [Yes] 를 클릭 후, Http Notification 을 선택하신 후에 항목들을 입력 하시면 됩니다.
마지막
이제 생성된 Jobs 을 이용해서 배포를 진행 하도록 하겠습니다. 위 방법으로 작업이 생성되면 아래의 그림과 같은 메뉴들을 볼수 있습니다. [GAME_DEPLOY] 를 클릭하여 배포를 진행 하도록 합니다.
배포할 Nodes 를 선택한 후 [Run Job Now] 를 클릭합니다.
정상적으로 실행된다면, 아래의 그림과 같이 수행되는 콘솔로그를 [Log Output] 탭에서 확인 할 수 있습니다.
DataInfoinfo=newDataInfo();info.Val1=1;info.Val2=1;info.Val3=1;info.Val4=1;info.Val5=1;info.Grade=1;info.Level=25;// 이 값이 대입되면, Scale 값이 이상한 값으로 셋팅됨info.AWake=1;info.Scale=1f;// 이 값이 대입되면, Level = 0, AWake = 128 으로 값이 셋팅됨
이 현상은 mono 빌드에서 발생하며, 에디터와, IL2CPP 에서는 오류 없이 정상 동작한다.
mono의 낮은 버전으로 발생되는 현상으로 보이는데, 닷넷 버전을 4.6으로 설정후 테스트 해볼 예정이다.
방화벽 오픈
윈도우는 기본적으로 방화벽 기능이 활성화 되어 있으므로. 서비스를 위해서. 방화벽 오픈이 필요합니다. 윈도우 명령창에서 아래의 명령을 입력하세요.
방화벽 오픈
netsh advfirewall firewall add rule name="rundeck Http Port : 4440" dir=in action=allow protocol=TCP localport=4440
Rundeck node 추가
노드는 프로젝트에서 사용될 배포하게될 해당 머신을 뜻하며, 노드 추가를 위해선 먼저 프로젝트가 생성되어야 합니다. 프로젝트가 생성되어 있다면, 아래의 경로에 파일을 수정하여 노드를 추가 합니다.
[rundeck 설치 경로]\projects\[프로젝트명]\[etc]\resources.xml
<nodename="GAME-01"connectionType="WINRM_NATIVE"node-executor="overthere-winrm"winrm-password-option="winrmPassword"winrm-protocol="http"winrm-auth-type="basic"username="접속 아이디"winrmPassword="암호"description="ZK Game sever"tags="game"hostname="10.0.0.228:5985"osArch="x86_64"osFamily="windows"osName="Microsoft Windows Server 2016"osVersion="Microsoft Windows Server 2016"/>
name : 노드를 표현하는 이름입니다. 배포 구성시 필터에 사용됩니다.
tags : 배포 구성시 필터에 사용되며, 구성을 표현할 수 있는 키워드라고 생각하시면 됩니다.
winrmPassword
현재 버전에서는 node 정보에 wirm-password-option 을 이용하여 암호 설정을 하여도 적용 되지 않는 문제가 있습니다.
오른쪽 상단의 설정에서 [Key Storage] 메뉴를 이용하여 암호를 설정합니다.
프로젝트 설정에서 [Default Node Executor] -> [WinRM] 옵션을 활성화 후 아까 생성한 키를 선택합니다.
윈도우 노드의 winrm 설정
Winrm 이란 Windows 원격 관리 명령줄 도구이며, Microsoft가 구현한 WS-Management 프토콜로서, 웹 서비스를 이용하여 로컬 및 원격 컴퓨터와 안전하게 통신을 할 수 있는 방법입니다.