58. Azure Function을 위한 CI/CD 파이프라인 설정하기
<참조> https://www.azuredevopslabs.com/labs/vstsextend/azurefunctions/
개요
Azure Function은 on-prem 뿐 아니라 Azure 또는 타사 서비스에서 발생하는 이벤트에 의해 트리거 되는 코드를 구현할 수 있는 기능을 갖춘 기존 Azure 애플리케이션 플랫폼을 확장하는 이벤트 중심 컴퓨팅 온디맨드 경험이다. Azure Functions를 사용하면 개발자가 데이터 원본 또는 메시징 솔루션에 연결하여 작업을 수행 할 수 있으므로 이벤트를 쉽게 처리하고 대응할 수 있다. 개발자는 Azure Function을 활용하여 다양한 애플리케이션, 모바일, IoT 디바이스가 접근할 수 있는 HTTP 기반 API Endpoint를 구축할 수 있다.
랩 시나리오: 이 랩에서는 가상의 eCommerce 웹 사이트인 PartsUnlimited를 사용한다. PartsUnlimited 팀은 직원과 고객을 위해 새로운 할인 상품을 출시하고, 로그인한 사용자가 직원인지 고객인지에 따라 적절한 할인을 검색하는 Azure Function을 구축하고자 한다.
추가 학습을 하려면 Microsoft Learn에서 Azure Pipelines 모듈을 사용하여 Azure Function 배포 자동화를 확인한다.
이 랩에서 다루는 것
이 랩에서는 접근 방식을 모두 다루며 다음 태스크가 수행된다.
- Azure DevOps 데모 생성기 도구를 사용하여 Azure DevOps Services 조직에서 PartsUnlimited 프로젝트 생성
- Azure Portal에서 Azure Function을 설정하고 Visual Studio를 통해 코드 추가
- Azure DevOps 조직에서 빌드 파이프라인을 구성하여 코드를 빌드하고 테스트
- 웹 사이트, API 및 Azure Function용 Azure DevOps Organization에서 릴리즈 파이프라인 구성
랩을 위한 전제조건
1. 이 실습의 전제조건을 알아 보려면 시작하기 페이지를 참조한다.
2. 랩에는 .Net Core SDK 및 Visual Studio 용 Azure 개발 도구가 설치된 Visual Studio 2017 버전 15.4 이상이 추가로 필요하다.
2. Azure DevOps 데모 생성기 링크를 클릭하고 시작하기 페이지의 지침에 따라 프로젝트를 Azure DevOps 조직에 프로비전 한다.
필요한 Azure 리소스 생성
이 랩을 위해 두 개의 Azure 앱 서비스를 만들어야 한다.
1. Azure Portal에서 Azure Cloud Shell을 시작하고 Bash를 선택한다.
2. 리소스 그룹을 만든다. <region>을 선택한 지역 (예 : eastus)으로 바꾼다.
az group create -n MyResourceGroup -l <region> |
kim@Azure:~$ az group create -n MyResourceGroup -l koreacentral
{
"id": "/subscriptions/f8764f39-01ee-4631-9941-77b286ed971b/resourceGroups/MyResourceGroup",
"location": "koreacentral",
"managedBy": null,
"name": "MyResourceGroup",
"properties": {
"provisioningState": "Succeeded"
},
"tags": null,
"type": "Microsoft.Resources/resourceGroups"
}
3. 앱 서비스 계획을 만들려면
az group create -n MyResourceGroup -l <region>az appservice plan create -g MyResourceGroup -n MyPlan --sku S1 |
kim@Azure:~$ az appservice plan create -g MyResourceGroup -n MyPlan --sku S1
{- Finished ..
"freeOfferExpirationTime": null,
"geoRegion": "Korea Central",
"hostingEnvironmentProfile": null,
"hyperV": false,
"id": "/subscriptions/f8764f39-xxxx-xxxx-xxxx-77b286ed971b/resourceGroups/MyResourceGroup/providers/Microsoft.Web/serverfarms/MyPlan",
"isSpot": false,
"isXenon": false,
"kind": "app",
"location": "Korea Central",
"maximumElasticWorkerCount": 1,
"maximumNumberOfWorkers": 10,
"name": "MyPlan",
"numberOfSites": 0,
"perSiteScaling": false,
"provisioningState": "Succeeded",
"reserved": false,
"resourceGroup": "MyResourceGroup",
"sku": {
"capabilities": null,
"capacity": 1,
"family": "S",
"locations": null,
"name": "S1",
"size": "S1",
"skuCapacity": null,
"tier": "Standard"
},
"spotExpirationTime": null,
"status": "Ready",
"subscription": "f8764f39-xxxx-xxxx-xxxx-77b286ed971b",
"tags": null,
"targetWorkerCount": 0,
"targetWorkerSizeId": 0,
"type": "Microsoft.Web/serverfarms",
"workerTierName": null
}
4. 고유한 앱 이름으로 두 개의 웹앱을 만든다.
az webapp create -g MyResourceGroup -p MyPlan -n PartsUnlimited-Web az webapp create -g MyResourceGroup -p MyPlan -n PartsUnlimited-API |
kim@Azure:~$ az webapp create -g MyResourceGroup -p MyPlan -n PartsUnlimited-ZeroWeb
{- Finished ..
"availabilityState": "Normal",
"clientAffinityEnabled": true,
"clientCertEnabled": false,
"clientCertExclusionPaths": null,
"cloningInfo": null,
"containerSize": 0,
"dailyMemoryTimeQuota": 0,
"defaultHostName": "partsunlimited-zeroweb.azurewebsites.net",
"enabled": true,
"enabledHostNames": [
"partsunlimited-zeroweb.azurewebsites.net",
"partsunlimited-zeroweb.scm.azurewebsites.net"
],
"ftpPublishingUrl": "ftp://waws-prod-se1-005.ftp.azurewebsites.windows.net/site/wwwroot",
"hostNameSslStates": [
{
"hostType": "Standard",
"ipBasedSslResult": null,
"ipBasedSslState": "NotConfigured",
"name": "partsunlimited-zeroweb.azurewebsites.net",
"sslState": "Disabled",
"thumbprint": null,
"toUpdate": null,
"toUpdateIpBasedSsl": null,
"virtualIp": null
},
{
"hostType": "Repository",
"ipBasedSslResult": null,
"ipBasedSslState": "NotConfigured",
"name": "partsunlimited-zeroweb.scm.azurewebsites.net",
"sslState": "Disabled",
"thumbprint": null,
"toUpdate": null,
"toUpdateIpBasedSsl": null,
"virtualIp": null
}
],
"hostNames": [
"partsunlimited-zeroweb.azurewebsites.net"
],
"hostNamesDisabled": false,
"hostingEnvironmentProfile": null,
"httpsOnly": false,
"hyperV": false,
"id": "/subscriptions/f8764f39-xxxx-xxxx-xxxx-77b286ed971b/resourceGroups/MyResourceGroup/providers/Microsoft.Web/sites/PartsUnlimited-ZeroWeb",
"identity": null,
"inProgressOperationId": null,
"isDefaultContainer": null,
"isXenon": false,
"kind": "app",
"lastModifiedTimeUtc": "2020-11-13T08:45:58.570000",
"location": "Korea Central",
"maxNumberOfWorkers": null,
"name": "PartsUnlimited-ZeroWeb",
"outboundIpAddresses": "52.231.77.58,52.231.73.183,52.231.71.204,52.231.66.104,52.231.77.171",
"possibleOutboundIpAddresses": "52.231.77.58,52.231.73.183,52.231.71.204,52.231.66.104,52.231.77.171,52.231.69.238,52.231.78.172,52.231.69.251",
"redundancyMode": "None",
"repositorySiteName": "PartsUnlimited-ZeroWeb",
"reserved": false,
"resourceGroup": "MyResourceGroup",
"scmSiteAlsoStopped": false,
"serverFarmId": "/subscriptions/f8764f39-xxxx-xxxx-xxxx-77b286ed971b/resourceGroups/MyResourceGroup/providers/Microsoft.Web/serverfarms/MyPlan",
"siteConfig": {
"acrUseManagedIdentityCreds": false,
"acrUserManagedIdentityId": null,
"alwaysOn": null,
"apiDefinition": null,
"apiManagementConfig": null,
"appCommandLine": null,
"appSettings": null,
"autoHealEnabled": null,
"autoHealRules": null,
"autoSwapSlotName": null,
"azureMonitorLogCategories": null,
"azureStorageAccounts": null,
"connectionStrings": null,
"cors": null,
"customAppPoolIdentityAdminState": null,
"customAppPoolIdentityTenantState": null,
"defaultDocuments": null,
"detailedErrorLoggingEnabled": null,
"documentRoot": null,
"experiments": null,
"fileChangeAuditEnabled": null,
"ftpsState": null,
"functionAppScaleLimit": null,
"functionsRuntimeScaleMonitoringEnabled": null,
"handlerMappings": null,
"healthCheckPath": null,
"http20Enabled": null,
"httpLoggingEnabled": null,
"ipSecurityRestrictions": [
{
"action": "Allow",
"description": "Allow all access",
"ipAddress": "Any",
"name": "Allow all",
"priority": 1,
"subnetMask": null,
"subnetTrafficTag": null,
"tag": null,
"vnetSubnetResourceId": null,
"vnetTrafficTag": null
}
],
"javaContainer": null,
"javaContainerVersion": null,
"javaVersion": null,
"limits": null,
"linuxFxVersion": null,
"loadBalancing": null,
"localMySqlEnabled": null,
"logsDirectorySizeLimit": null,
"machineKey": null,
"managedPipelineMode": null,
"managedServiceIdentityId": null,
"metadata": null,
"minTlsVersion": null,
"minimumElasticInstanceCount": 0,
"netFrameworkVersion": null,
"nodeVersion": null,
"numberOfWorkers": null,
"phpVersion": null,
"powerShellVersion": null,
"preWarmedInstanceCount": null,
"publishingPassword": null,
"publishingUsername": null,
"push": null,
"pythonVersion": null,
"remoteDebuggingEnabled": null,
"remoteDebuggingVersion": null,
"requestTracingEnabled": null,
"requestTracingExpirationTime": null,
"routingRules": null,
"runtimeADUser": null,
"runtimeADUserPassword": null,
"scmIpSecurityRestrictions": [
{
"action": "Allow",
"description": "Allow all access",
"ipAddress": "Any",
"name": "Allow all",
"priority": 1,
"subnetMask": null,
"subnetTrafficTag": null,
"tag": null,
"vnetSubnetResourceId": null,
"vnetTrafficTag": null
}
],
"scmIpSecurityRestrictionsUseMain": null,
"scmMinTlsVersion": null,
"scmType": null,
"tracingOptions": null,
"use32BitWorkerProcess": null,
"virtualApplications": null,
"vnetName": null,
"vnetRouteAllEnabled": null,
"webSocketsEnabled": null,
"websiteTimeZone": null,
"winAuthAdminState": null,
"winAuthTenantState": null,
"windowsFxVersion": null,
"xManagedServiceIdentityId": null
},
"slotSwapStatus": null,
"state": "Running",
"suspendedTill": null,
"tags": null,
"targetSwapSlot": null,
"trafficManagerHostNames": null,
"type": "Microsoft.Web/sites",
"usageState": "Normal"
}
kim@Azure:~$ az webapp create -g MyResourceGroup -p MyPlan -n PartsUnlimited-zeroAPI
{- Finished ..
"availabilityState": "Normal",
"clientAffinityEnabled": true,
"clientCertEnabled": false,
"clientCertExclusionPaths": null,
"cloningInfo": null,
"containerSize": 0,
"dailyMemoryTimeQuota": 0,
"defaultHostName": "partsunlimited-zeroapi.azurewebsites.net",
"enabled": true,
"enabledHostNames": [
"partsunlimited-zeroapi.azurewebsites.net",
"partsunlimited-zeroapi.scm.azurewebsites.net"
],
"ftpPublishingUrl": "ftp://waws-prod-se1-005.ftp.azurewebsites.windows.net/site/wwwroot",
"hostNameSslStates": [
{
"hostType": "Standard",
"ipBasedSslResult": null,
"ipBasedSslState": "NotConfigured",
"name": "partsunlimited-zeroapi.azurewebsites.net",
"sslState": "Disabled",
"thumbprint": null,
"toUpdate": null,
"toUpdateIpBasedSsl": null,
"virtualIp": null
},
{
"hostType": "Repository",
"ipBasedSslResult": null,
"ipBasedSslState": "NotConfigured",
"name": "partsunlimited-zeroapi.scm.azurewebsites.net",
"sslState": "Disabled",
"thumbprint": null,
"toUpdate": null,
"toUpdateIpBasedSsl": null,
"virtualIp": null
}
],
"hostNames": [
"partsunlimited-zeroapi.azurewebsites.net"
],
"hostNamesDisabled": false,
"hostingEnvironmentProfile": null,
"httpsOnly": false,
"hyperV": false,
"id": "/subscriptions/f8764f39-xxxx-xxxx-xxxx-77b286ed971b/resourceGroups/MyResourceGroup/providers/Microsoft.Web/sites/PartsUnlimited-zeroAPI",
"identity": null,
"inProgressOperationId": null,
"isDefaultContainer": null,
"isXenon": false,
"kind": "app",
"lastModifiedTimeUtc": "2020-11-13T08:46:40.743333",
"location": "Korea Central",
"maxNumberOfWorkers": null,
"name": "PartsUnlimited-zeroAPI",
"outboundIpAddresses": "52.231.77.58,52.231.73.183,52.231.71.204,52.231.66.104,52.231.77.171",
"possibleOutboundIpAddresses": "52.231.77.58,52.231.73.183,52.231.71.204,52.231.66.104,52.231.77.171,52.231.69.238,52.231.78.172,52.231.69.251",
"redundancyMode": "None",
"repositorySiteName": "PartsUnlimited-zeroAPI",
"reserved": false,
"resourceGroup": "MyResourceGroup",
"scmSiteAlsoStopped": false,
"serverFarmId": "/subscriptions/f8764f39-xxxx-xxxx-xxxx-77b286ed971b/resourceGroups/MyResourceGroup/providers/Microsoft.Web/serverfarms/MyPlan",
"siteConfig": {
"acrUseManagedIdentityCreds": false,
"acrUserManagedIdentityId": null,
"alwaysOn": null,
"apiDefinition": null,
"apiManagementConfig": null,
"appCommandLine": null,
"appSettings": null,
"autoHealEnabled": null,
"autoHealRules": null,
"autoSwapSlotName": null,
"azureMonitorLogCategories": null,
"azureStorageAccounts": null,
"connectionStrings": null,
"cors": null,
"customAppPoolIdentityAdminState": null,
"customAppPoolIdentityTenantState": null,
"defaultDocuments": null,
"detailedErrorLoggingEnabled": null,
"documentRoot": null,
"experiments": null,
"fileChangeAuditEnabled": null,
"ftpsState": null,
"functionAppScaleLimit": null,
"functionsRuntimeScaleMonitoringEnabled": null,
"handlerMappings": null,
"healthCheckPath": null,
"http20Enabled": null,
"httpLoggingEnabled": null,
"ipSecurityRestrictions": [
{
"action": "Allow",
"description": "Allow all access",
"ipAddress": "Any",
"name": "Allow all",
"priority": 1,
"subnetMask": null,
"subnetTrafficTag": null,
"tag": null,
"vnetSubnetResourceId": null,
"vnetTrafficTag": null
}
],
"javaContainer": null,
"javaContainerVersion": null,
"javaVersion": null,
"limits": null,
"linuxFxVersion": null,
"loadBalancing": null,
"localMySqlEnabled": null,
"logsDirectorySizeLimit": null,
"machineKey": null,
"managedPipelineMode": null,
"managedServiceIdentityId": null,
"metadata": null,
"minTlsVersion": null,
"minimumElasticInstanceCount": 0,
"netFrameworkVersion": null,
"nodeVersion": null,
"numberOfWorkers": null,
"phpVersion": null,
"powerShellVersion": null,
"preWarmedInstanceCount": null,
"publishingPassword": null,
"publishingUsername": null,
"push": null,
"pythonVersion": null,
"remoteDebuggingEnabled": null,
"remoteDebuggingVersion": null,
"requestTracingEnabled": null,
"requestTracingExpirationTime": null,
"routingRules": null,
"runtimeADUser": null,
"runtimeADUserPassword": null,
"scmIpSecurityRestrictions": [
{
"action": "Allow",
"description": "Allow all access",
"ipAddress": "Any",
"name": "Allow all",
"priority": 1,
"subnetMask": null,
"subnetTrafficTag": null,
"tag": null,
"vnetSubnetResourceId": null,
"vnetTrafficTag": null
}
],
"scmIpSecurityRestrictionsUseMain": null,
"scmMinTlsVersion": null,
"scmType": null,
"tracingOptions": null,
"use32BitWorkerProcess": null,
"virtualApplications": null,
"vnetName": null,
"vnetRouteAllEnabled": null,
"webSocketsEnabled": null,
"websiteTimeZone": null,
"winAuthAdminState": null,
"winAuthTenantState": null,
"windowsFxVersion": null,
"xManagedServiceIdentityId": null
},
"slotSwapStatus": null,
"state": "Running",
"suspendedTill": null,
"tags": null,
"targetSwapSlot": null,
"trafficManagerHostNames": null,
"type": "Microsoft.Web/sites",
"usageState": "Normal"
}
5. 생성한 리소스 그룹으로 이동하면 그림과 같이 리소스가 표시된다.
두 개의 App Services와 하나의 App Service 계획을 만들었다.
- 웹 앱 - Parts Unlimited 웹 사이트를 배포하는 데 사용된다.
- API 앱 - 사용자 로그인에 따라 사용자를 다른 할인 페이지로 리디렉션하는 데 사용된다.
실습1: 기존 리포지토리 복제하기
1. Repos로 이동한다. 오른쪽 상단 모서리에서 Clone을 선택한다. Clone Repository 탭의 IDE 목록에서 Clone in Visual Studio를 선택한다.
2. Visual Studio의 인스턴스가 열린다.
메시지가 나타나면 Visual Studio에 로그인 한다.
3. 로컬 리포지토리를 배치할 로컬 경로를 설정하고 Clone을 선택한다.
4. Solutions 아래의 팀 탐색기에서 PartsUnlimited.sln을 볼 수 있다. 솔루션을 두 번 클릭하여 프로젝트를 연다.
실습2: Azure Portal 및 Visual Studio에서 Azure 함수 만들기
이 실습에서는 Azure Portal에서 Azure Functions 앱을 만든 다음 Visual Studio에서 Azure Function 프로젝트를 만들어 코드를 추가한다.
이 실습에서 생성된 Azure Function은 애플리케이션에 로그인한 사용자에 기초하여 다른 (할인) 정보로 돌아가는 메커니즘 또는 전환 프록시 역할을 할 것이다. 비록 여러분이 여기서 간단한 조건을 사용했지만, 이것은 또한 다른 웹 API 호출 뒤에 잠재적으로 숨겨질 수 있는 더 복잡한 규칙을 사용할 수도 있다.
1. Azure Portal에 로그인한다. Azure Portal의 왼쪽 위 모서리에 있는 + Create a resource 단추를 선택한 다음
Compute > Function App을 선택한다.
2. 아래 이미지에 지정된 것과 유사한 설정으로 Function 앱을 만든다. Review + create를 클릭한 다음 Create를 클릭하여 함수 앱을 프로비저닝하고 배포한다.
3. 포털의 오른쪽 상단 모서리에 있는 알림 아이콘을 선택하고 배포 성공 메시지를 확인한다. Go to resource를 선택하여 새 함수 앱을 본다.
4. 함수 앱 창에서 Functions을 선택한다. +Add를 클릭하고 템플릿에서 Http trigger를 선택한다.
이 랩에서는 CSharp를 함수의 개발 언어로 사용하지만 지원되는 모든 언어로 함수를 만들 수 있다.
5. New Function 창에서 기본값을 그대로 두고 Create function을 클릭한다.
6. HttpTrigger1 함수 페이지에서 Get Function Url을 클릭한다. Azure Function URL을 복사하고 메모장에 저장한다. 이 URL은 나중에 실습에서 필요하다.
7. Visual Studio로 돌아가 PartsUnlimited.sln을 두 번 클릭하여 연다. 함수 앱에 코드를 추가해야 할 것이다. 여러 가지 방법이 있지만, 이 랩에서는 Visual Studio를 이용하게 될 것이다. 사용자 로그인을 기반으로 올바른 API로 리디렉션하고 다른 (할인) 정보를 반환하는 코드를 작성한다.
8. 솔루션을 마우스 오른쪽 단추로 클릭하고 Add 를 선택하고 New Project를 선택한다.
9. C# 범주에서 Azure, Cloud를 선택하고이 프로젝트의 유형으로 Azure Functions을 선택하고 Next를 누른다.
이름으로 PartsUnlimited.AzureFunction을 입력하고 위치 끝에 \src를 추가한 다음 Create를 클릭한다.
10. 프레임워크 드롭 다운에서 HttpTrigger 템플릿, Azure Functions v1 (.NET Framework)을 선택하고 Create를 클릭한다.
11. PartsUnlimited.AzureFunction 프로젝트를 확장한다. Function1.cs을 열고 기존 코드를 다음 코드로 교체한다.
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
namespace PartsUnlimited.AzureFunction
{
public static class Function1
{
[FunctionName("HttpTrigger1")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
var userIdKey = req.GetQueryNameValuePairs().FirstOrDefault(q => string.Equals(q.Key, "UserId", StringComparison.OrdinalIgnoreCase));
var userId = string.IsNullOrEmpty(userIdKey.Value) ? int.MaxValue : Convert.ToInt64(userIdKey.Value);
var url = $"https://<<YourAPIAppServiceUrl>>/api/{(userId > 10 ? "v1" : "v2")}/specials/GetSpecialsByUserId?id={userId}";
using (HttpClient httpClient = new HttpClient())
{
return await httpClient.GetAsync(url);
}
}
}
}
12. Create required Azure resources 실습에서 구성된 리소스 그룹으로 이동한다. PartsUnlimited-API-XXXXXXX.azurewebsites.net을 클릭하고 URL 섹션 아래의 복사 아이콘을 클릭하여 전체 URL을 복사한다. URl 변수의 https://<<**YourAPIAppServiceUrl**>> 값을 API 앱 서비스 이름으로 복사하고 대체한다.
13. Visual Studio에서 PartsUnlimitedWebsite > Controllers > StoreController.cs 경로에서 StoreController.cs를 연다.
14. StoreController.cs 파일에서 46 행의 URL 변수를 스텝 7에서 복사한 Function url로 바꾼다.
15. Team Explorer에서 Changes를 클릭하고 커멘트를 입력한 다음 모두 Commit all and Push를 선택하여 변경 사항을 원격 저장소에 푸시한다.
실습3: Azure Build Pipelines과의 지속적인 통합 설정
이 실습에서는 코드가 CI 파이프 라인의 일부로 빌드되는 방법에 대한 통찰력을 얻기 위해 빌드 정의를 살펴본다.
1. Azure DevOps 포털의 Pipelines | Pipelines으로 이동한다. AzureFunctions_CI를 선택하고 Edit을 클릭한다.
2. 빌드가 실행되기 전에 이 빌드를 CI 빌드로 만든다. 빌드 정의에서 Triggers 탭을 누른다. Continuous Integration 트리거를 활성화 한다. 변경 내용을 저장하고 빌드를 트리거하려면 Save & queue를 클릭한다. 이렇게 하면 리포지토리의 변경 사항을 커밋할 때마다 빌드 프로세스가 자동으로 트리거되도록 할 수 있다.
3. 새 빌드가 대기열에 있다. 진행되는 빌드의 라이브 로그를 보려면 잡을 클릭한다. 다음 섹션으로 진행하기 전에 빌드가 완료되고 성공할 때까지 기다린다.
실습4: Azure 릴리스 파이프라인을 사용하여 지속적인 배포 구성하기
이 실습에서는 코드가 CI 파이프 라인의 일부로 빌드되는 방법에 대한 통찰력을 얻기 위해 빌드 정의를 살펴본다.
1. 빌드가 성공하면 Pipelines | Releases로 이동한다.
2. AzureFunctions_CD 파이프라인을 선택하고 Edit을 클릭한다.
3. 아티팩트 트리거를 선택하고 Continuous deployment 트리거를 활성화 한다.
4. PartsUnlimited Website를 배포하려면 태스크를 클릭하고 Deploy PartsUnlimited Website 태스크를 선택하고 아래와 같이 입력을 구성한다.
- Azure 구독이 나열되지 않거나 기존 서비스 주체를 사용하려는 경우 Manage 링크를 클릭한다.
- +New Service Connection 버튼을 클릭하고 Azure Resource Manager 옵션을 선택한다. 연결 이름을 제공하고 목록에서 Azure Subscription을 선택한 후 OK 버튼을 클릭한다. 연결을 승인하려면 Azure 자격 증명이 필요하다.
- Azure 구독이 이미 나열되어 있는 경우 드롭 다운 목록에서 Azure 구독을 선택하고 Authorize를 클릭한다.
5. 드롭 다운에서 App Service Name에 대해 미리 생성된 PartsUnlimited-Web-XXXX 이름을 선택한다.
6. PartsUnlimited APIs,의 경우 두 번째 태스크를 선택하고 아래와 같이 입력을 구성한다. 드롭 다운에서 App Service Name 필드로 PartsUnlimited-API-XXX를 선택한다.
7. PartsUnlimited Azure Function을 배포할 세 번째 태스크를 선택하고 아래와 같이 입력을 구성한다. 드롭 다운에서 App Name 필드에 대해 미리 생성된 Azure functions 이름을 선택한다.
Azure Function Apps는 Azure App Service 인프라를 사용하므로 여기에서 참조하는 함수 앱은 App Service의 모든 기능을 사용할 수 있다.
8. Save을 클릭한다. 저장 대화 상자에서 확인을 클릭합니다. 릴리스 정의를 테스트하려면 Create Release를 클릭한다.
9. 새 릴리즈 만들기 대화 상자에서 Create를 클릭한다.
10. 새 릴리즈가 생성되고 있음을 알 수 있다. 릴리즈로 이동하려면 링크를 선택한다.
11. 전개를 위한 라이브 로그를 그대로 볼 수 있다. 릴리즈가 Azure 웹 앱에 배포될 때까지 기다린다. 다음 섹션으로 진행하기 전에 릴리즈가 완료되고 발생하기를 기다린다.
실습5: 배포 검증
이 실습에서는 코드가 CI 파이프 라인의 일부로 빌드되는 방법에 대한 통찰력을 얻기 위해 빌드 정의를 살펴본다.
1. 배포가 완료되면 Azure Portal로 이동한다. 리소스 그룹에서 PartsUnlimited-Web-xxxxx 웹 앱을 선택하고 찾아보기를 클릭한다.
2. 오일 카테고리로 이동하면 제품이 10%로 할인 된 것으로 표시된다.
3. 이제 암호 YouShouldChangeThisPassword1을 가진 Administrator@test.com으로 로그인 한다. 오일 카테고리로 다시 이동한다. 이 사용자에 대해 Azure 함수는 요청을 다른 API로 라우팅하고 할인을 30%로 표시한다.
요약
Visual Studio에서 Azure Functions를 만들고 코딩하는 방법과 Functions 앱을 배포하기 위한 CI/CD 파이프라인을 설정하는 방법을 배웠다.